[RBUILD]
[reactos.git] / reactos / tools / rbuild / backend / mingw / mingw.cpp
1 /*
2 * Copyright (C) 2005 Casper S. Hornstrup
3 * 2006 Christoph von Wittich
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 #include "../../pch.h"
20
21 #include "mingw.h"
22 #include <assert.h>
23 #include "modulehandler.h"
24
25 #ifdef _MSC_VER
26 #define popen _popen
27 #define pclose _pclose
28 #endif//_MSC_VER
29
30 using std::string;
31 using std::vector;
32 using std::set;
33 using std::map;
34
35 typedef set<string> set_string;
36
37 const struct ModuleHandlerInformations ModuleHandlerInformations[] = {
38 { HostTrue, "", "", "" }, // BuildTool
39 { HostFalse, "", "", "" }, // StaticLibrary
40 { HostFalse, "", "", "" }, // ObjectLibrary
41 { HostFalse, "", "", "$(LDFLAG_DRIVER)" }, // Kernel
42 { HostFalse, "", "", "$(LDFLAG_DRIVER)" }, // KernelModeDLL
43 { HostFalse, "-D__NTDRIVER__", "", "$(LDFLAG_DRIVER)" }, // KernelModeDriver
44 { HostFalse, "", "", "$(LDFLAG_DLL)" }, // NativeDLL
45 { HostFalse, "-D__NTAPP__", "", "$(LDFLAG_NATIVE)" }, // NativeCUI
46 { HostFalse, "", "", "$(LDFLAG_DLL)" }, // Win32DLL
47 { HostFalse, "", "", "$(LDFLAG_DLL)" }, // Win32OCX
48 { HostFalse, "", "", "$(LDFLAG_CONSOLE)" }, // Win32CUI
49 { HostFalse, "", "", "$(LDFLAG_WINDOWS)" }, // Win32GUI
50 { HostFalse, "", "", "" }, // BootLoader
51 { HostFalse, "", "-f bin", "" }, // BootSector
52 { HostFalse, "", "", "" }, // Iso
53 { HostFalse, "", "", "" }, // LiveIso
54 { HostFalse, "", "", "" }, // Test
55 { HostFalse, "", "", "" }, // RpcServer
56 { HostFalse, "", "", "" }, // RpcClient
57 { HostFalse, "", "", "" }, // Alias
58 { HostFalse, "", "", "" }, // BootProgram
59 { HostFalse, "", "", "$(LDFLAG_WINDOWS)" }, // Win32SCR
60 { HostFalse, "", "", "" }, // IdlHeader
61 { HostFalse, "", "", "" }, // IdlInterface
62 { HostFalse, "", "", "" }, // EmbeddedTypeLib
63 { HostFalse, "", "", "" }, // ElfExecutable
64 { HostFalse, "", "", "" }, // RpcProxy
65 { HostTrue, "", "", "" }, // HostStaticLibrary
66 { HostFalse, "", "", "" }, // Cabinet
67 { HostFalse, "", "", "$(LDFLAG_DLL)" }, // KeyboardLayout
68 { HostFalse, "", "", "" }, // MessageHeader
69 };
70
71 static std::string mscPath;
72 static std::string mslinkPath;
73
74 string
75 MingwBackend::GetFullPath ( const FileLocation& file ) const
76 {
77 MingwModuleHandler::PassThruCacheDirectory ( &file );
78
79 string directory;
80 switch ( file.directory )
81 {
82 case SourceDirectory:
83 directory = "";
84 break;
85 case IntermediateDirectory:
86 directory = "$(INTERMEDIATE)";
87 break;
88 case OutputDirectory:
89 directory = "$(OUTPUT)";
90 break;
91 case InstallDirectory:
92 directory = "$(INSTALL)";
93 break;
94 case TemporaryDirectory:
95 directory = "$(TEMPORARY)";
96 break;
97 default:
98 throw InvalidOperationException ( __FILE__,
99 __LINE__,
100 "Invalid directory %d.",
101 file.directory );
102 }
103
104 if ( file.relative_path.length () > 0 )
105 {
106 if ( directory.length () > 0 )
107 directory += sSep;
108 directory += file.relative_path;
109 }
110 return directory;
111 }
112
113 string
114 MingwBackend::GetFullName ( const FileLocation& file ) const
115 {
116 string directory;
117 switch ( file.directory )
118 {
119 case SourceDirectory:
120 directory = "";
121 break;
122 case IntermediateDirectory:
123 directory = "$(INTERMEDIATE)";
124 break;
125 case OutputDirectory:
126 directory = "$(OUTPUT)";
127 break;
128 case InstallDirectory:
129 directory = "$(INSTALL)";
130 break;
131 case TemporaryDirectory:
132 directory = "$(TEMPORARY)";
133 break;
134 default:
135 throw InvalidOperationException ( __FILE__,
136 __LINE__,
137 "Invalid directory %d.",
138 file.directory );
139 }
140
141 if ( file.relative_path.length () > 0 )
142 {
143 if ( directory.length () > 0 )
144 directory += sSep;
145 directory += file.relative_path;
146 }
147
148 if ( directory.length () > 0 )
149 directory += sSep;
150
151 return directory + file.name;
152 }
153
154 string
155 v2s ( const Backend* backend, const vector<FileLocation>& files, int wrap_at )
156 {
157 if ( !files.size() )
158 return "";
159 string s;
160 int wrap_count = 0;
161 for ( size_t i = 0; i < files.size(); i++ )
162 {
163 const FileLocation& file = files[i];
164 if ( wrap_at > 0 && wrap_count++ == wrap_at )
165 {
166 s += " \\\n\t\t";
167 wrap_count = 1;
168 }
169 else if ( s.size() )
170 s += " ";
171 s += backend->GetFullName ( file );
172 }
173 return s;
174 }
175
176
177 string
178 v2s ( const string_list& v, int wrap_at )
179 {
180 if ( !v.size() )
181 return "";
182 string s;
183 int wrap_count = 0;
184 for ( size_t i = 0; i < v.size(); i++ )
185 {
186 if ( !v[i].size() )
187 continue;
188 if ( wrap_at > 0 && wrap_count++ == wrap_at )
189 {
190 s += " \\\n\t\t";
191 wrap_count = 1;
192 }
193 else if ( s.size() )
194 s += " ";
195 s += v[i];
196 }
197 return s;
198 }
199
200
201 static class MingwFactory : public Backend::Factory
202 {
203 public:
204 MingwFactory() : Factory ( "mingw", "Minimalist GNU Win32" ) {}
205 Backend* operator() ( Project& project,
206 Configuration& configuration )
207 {
208 return new MingwBackend ( project,
209 configuration );
210 }
211 } factory;
212
213
214 MingwBackend::MingwBackend ( Project& project,
215 Configuration& configuration )
216 : Backend ( project, configuration ),
217 manualBinutilsSetting( false ),
218 intermediateDirectory ( new Directory ( "" ) ),
219 outputDirectory ( new Directory ( "" ) ),
220 installDirectory ( new Directory ( "" ) )
221 {
222 compilerPrefix = "";
223 }
224
225 MingwBackend::~MingwBackend()
226 {
227 delete intermediateDirectory;
228 delete outputDirectory;
229 delete installDirectory;
230 }
231
232 string
233 MingwBackend::AddDirectoryTarget ( const string& directory,
234 Directory* directoryTree )
235 {
236 if ( directory.length () > 0)
237 directoryTree->Add ( directory.c_str() );
238 return directoryTree->name;
239 }
240
241 bool
242 MingwBackend::CanEnablePreCompiledHeaderSupportForModule ( const Module& module )
243 {
244 if ( !configuration.CompilationUnitsEnabled )
245 return true;
246
247 const vector<CompilationUnit*>& compilationUnits = module.non_if_data.compilationUnits;
248 size_t i;
249 for ( i = 0; i < compilationUnits.size (); i++ )
250 {
251 CompilationUnit& compilationUnit = *compilationUnits[i];
252 if ( compilationUnit.GetFiles ().size () != 1 )
253 return false;
254 }
255 return true;
256 }
257
258 void
259 MingwBackend::ProcessModules ()
260 {
261 printf ( "Processing modules..." );
262
263 vector<MingwModuleHandler*> v;
264 size_t i;
265
266 for ( std::map<std::string, Module*>::iterator p = ProjectNode.modules.begin (); p != ProjectNode.modules.end (); ++ p )
267 fprintf ( fMakefile, "%s_TYPE:=%u\n", p->second->name.c_str(), p->second->type );
268
269 for ( std::map<std::string, Module*>::iterator p = ProjectNode.modules.begin (); p != ProjectNode.modules.end (); ++ p )
270 {
271 Module& module = *p->second;
272 if ( !module.enabled )
273 continue;
274 MingwModuleHandler* h = MingwModuleHandler::InstanciateHandler (
275 module,
276 this );
277 h->AddImplicitLibraries ( module );
278 if ( use_pch && CanEnablePreCompiledHeaderSupportForModule ( module ) )
279 h->EnablePreCompiledHeaderSupport ();
280 v.push_back ( h );
281 }
282
283 size_t iend = v.size ();
284
285 for ( i = 0; i < iend; i++ )
286 v[i]->GenerateSourceMacro();
287 for ( i = 0; i < iend; i++ )
288 v[i]->GenerateObjectMacro();
289 fprintf ( fMakefile, "\n" );
290 for ( i = 0; i < iend; i++ )
291 v[i]->GenerateTargetMacro();
292 fprintf ( fMakefile, "\n" );
293
294 GenerateAllTarget ( v );
295 GenerateRegTestsRunTarget ();
296
297 for ( i = 0; i < iend; i++ )
298 v[i]->GenerateOtherMacros();
299
300 for ( i = 0; i < iend; i++ )
301 {
302 MingwModuleHandler& h = *v[i];
303 h.GeneratePreconditionDependencies ();
304 h.Process ();
305 h.GenerateInvocations ();
306 h.GenerateCleanTarget ();
307 h.GenerateInstallTarget ();
308 h.GenerateDependsTarget ();
309 delete v[i];
310 }
311
312 printf ( "done\n" );
313 }
314
315 void
316 MingwBackend::Process ()
317 {
318 if ( configuration.CheckDependenciesForModuleOnly )
319 CheckAutomaticDependenciesForModuleOnly ();
320 else
321 ProcessNormal ();
322 }
323
324 void
325 MingwBackend::CheckAutomaticDependenciesForModuleOnly ()
326 {
327 if ( configuration.Dependencies == AutomaticDependencies )
328 {
329 Module* module = ProjectNode.LocateModule ( configuration.CheckDependenciesForModuleOnlyModule );
330 if ( module == NULL )
331 {
332 printf ( "Module '%s' does not exist\n",
333 configuration.CheckDependenciesForModuleOnlyModule.c_str () );
334 return;
335 }
336
337 printf ( "Checking automatic dependencies for module '%s'...",
338 module->name.c_str () );
339 AutomaticDependency automaticDependency ( ProjectNode );
340 automaticDependency.CheckAutomaticDependenciesForModule ( *module,
341 configuration.Verbose );
342 printf ( "done\n" );
343 }
344 }
345
346 void
347 MingwBackend::ProcessNormal ()
348 {
349 assert(sizeof(ModuleHandlerInformations)/sizeof(ModuleHandlerInformations[0]) == TypeDontCare);
350
351 DetectCompiler ();
352 DetectBinutils ();
353 DetectNetwideAssembler ();
354 DetectPipeSupport ();
355 DetectPCHSupport ();
356 CreateMakefile ();
357 GenerateHeader ();
358 GenerateGlobalVariables ();
359 GenerateXmlBuildFilesMacro ();
360 ProcessModules ();
361 GenerateInstallTarget ();
362 GenerateTestTarget ();
363 GenerateDirectoryTargets ();
364 GenerateDirectories ();
365 GenerateTestSupportCode ();
366 GenerateCompilationUnitSupportCode ();
367 GenerateSysSetup ();
368 GenerateProxyMakefiles ();
369 CheckAutomaticDependencies ();
370 CloseMakefile ();
371 }
372
373 void
374 MingwBackend::CreateMakefile ()
375 {
376 fMakefile = fopen ( ProjectNode.makefile.c_str (), "w" );
377 if ( !fMakefile )
378 throw AccessDeniedException ( ProjectNode.makefile );
379 MingwModuleHandler::SetBackend ( this );
380 MingwModuleHandler::SetMakefile ( fMakefile );
381 }
382
383 void
384 MingwBackend::CloseMakefile () const
385 {
386 if (fMakefile)
387 fclose ( fMakefile );
388 }
389
390 void
391 MingwBackend::GenerateHeader () const
392 {
393 fprintf ( fMakefile, "# THIS FILE IS AUTOMATICALLY GENERATED, EDIT '%s' INSTEAD\n\n",
394 ProjectNode.GetProjectFilename ().c_str () );
395 }
396
397 void
398 MingwBackend::GenerateGlobalProperties (
399 const char* assignmentOperation,
400 const IfableData& data ) const
401 {
402 for ( std::map<std::string, Property*>::const_iterator p = data.properties.begin(); p != data.properties.end(); ++ p )
403 {
404 Property& prop = *p->second;
405
406 if (!prop.isInternal)
407 {
408 fprintf ( fMakefile, "%s := %s\n",
409 prop.name.c_str(),
410 prop.value.c_str() );
411 }
412 }
413 }
414
415 string
416 MingwBackend::GenerateProjectLDFLAGS () const
417 {
418 string ldflags;
419 for ( size_t i = 0; i < ProjectNode.linkerFlags.size (); i++ )
420 {
421 LinkerFlag& linkerFlag = *ProjectNode.linkerFlags[i];
422 if ( ldflags.length () > 0 )
423 ldflags += " ";
424 ldflags += linkerFlag.flag;
425 }
426 return ldflags;
427 }
428
429 void
430 MingwBackend::GenerateGlobalVariables () const
431 {
432 fputs ( "include tools$(SEP)rbuild$(SEP)backend$(SEP)mingw$(SEP)rules.mak\n", fMakefile );
433 fprintf ( fMakefile, "include tools$(SEP)rbuild$(SEP)backend$(SEP)mingw$(SEP)linkers$(SEP)%s.mak\n", ProjectNode.GetLinkerSet ().c_str () );
434 fprintf ( fMakefile, "include tools$(SEP)rbuild$(SEP)backend$(SEP)mingw$(SEP)compilers$(SEP)%s.mak\n", ProjectNode.GetCompilerSet ().c_str () );
435
436 if ( mscPath.length() )
437 fprintf ( fMakefile, "export RBUILD_CL_PATH=%s\n", mscPath.c_str () );
438
439 if ( mslinkPath.length() )
440 fprintf ( fMakefile, "export RBUILD_LINK_PATH=%s\n", mslinkPath.c_str () );
441
442 if ( configuration.Dependencies == FullDependencies )
443 {
444 fprintf ( fMakefile,
445 "ifeq ($(ROS_BUILDDEPS),)\n"
446 "ROS_BUILDDEPS:=%s\n"
447 "endif\n",
448 "full" );
449 }
450
451 fprintf ( fMakefile,
452 "PREFIX := %s\n",
453 compilerPrefix.c_str () );
454 fprintf ( fMakefile,
455 "nasm := $(Q)%s\n",
456 nasmCommand.c_str () );
457
458 GenerateGlobalProperties ( "=", ProjectNode.non_if_data );
459
460 if ( ProjectNode.configuration.Compiler == GnuGcc )
461 {
462 fprintf ( fMakefile, "ifneq ($(OARCH),)\n" );
463 fprintf ( fMakefile, "PROJECT_ASFLAGS += -march=$(OARCH)\n" );
464 fprintf ( fMakefile, "PROJECT_CFLAGS += -march=$(OARCH)\n" );
465 fprintf ( fMakefile, "PROJECT_CXXFLAGS += -march=$(OARCH)\n" );
466 fprintf ( fMakefile, "endif\n" );
467 fprintf ( fMakefile, "ifneq ($(TUNE),)\n" );
468 fprintf ( fMakefile, "PROJECT_CFLAGS += -mtune=$(TUNE)\n" );
469 fprintf ( fMakefile, "PROJECT_CXXFLAGS += -mtune=$(TUNE)\n" );
470 fprintf ( fMakefile, "endif\n" );
471
472 if ( usePipe )
473 {
474 fprintf ( fMakefile, "PROJECT_CFLAGS += -pipe\n" );
475 fprintf ( fMakefile, "PROJECT_CXXFLAGS += -pipe\n" );
476 fprintf ( fMakefile, "PROJECT_ASFLAGS += -pipe\n" );
477 }
478
479 // Would be nice to have our own C++ runtime
480 fputs ( "BUILTIN_CXXINCLUDES+= $(TARGET_CPPFLAGS)\n", fMakefile );
481
482 fprintf ( fMakefile, "PROJECT_CCLIBS := \"$(shell ${TARGET_CC} -print-libgcc-file-name)\"\n" );
483
484 fprintf ( fMakefile, "PROJECT_CXXLIBS := \"$(shell ${TARGET_CPP} -print-file-name=libsupc++.a)\" \"$(shell ${TARGET_CPP} -print-file-name=libstdc++.a)\" \"$(shell ${TARGET_CPP} -print-libgcc-file-name)\" \"$(shell ${TARGET_CPP} -print-file-name=libmingw32.a)\" \"$(shell ${TARGET_CPP} -print-file-name=libmingwex.a)\"" );
485
486 /* hack to get _get_output_format, needed by libmingwex */
487 if (Environment::GetArch() == "i386")
488 fprintf ( fMakefile, " \"$(shell ${TARGET_CPP} -print-file-name=libcoldname.a)\" ");
489 fprintf ( fMakefile,"\n");
490 }
491 MingwModuleHandler::GenerateParameters ( "PROJECT", "+=", ProjectNode.non_if_data );
492 MingwModuleHandler::GenerateParameters ( "PROJECT_HOST", "+=", ProjectNode.host_non_if_data );
493
494 fprintf ( fMakefile, "PROJECT_LDFLAGS := %s\n", GenerateProjectLDFLAGS ().c_str () );
495
496 // TODO: use symbolic names for module types
497 for ( size_t i = 0; i < sizeof(ModuleHandlerInformations) / sizeof(ModuleHandlerInformations[0]); ++ i )
498 {
499 if ( ModuleHandlerInformations[i].cflags && ModuleHandlerInformations[i].cflags[0] )
500 {
501 fprintf ( fMakefile,
502 "MODULETYPE%d_%sFLAGS:=%s\n",
503 (int)i,
504 "C",
505 ModuleHandlerInformations[i].cflags );
506 }
507
508 if ( ModuleHandlerInformations[i].nasmflags && ModuleHandlerInformations[i].nasmflags[0] )
509 {
510 fprintf ( fMakefile,
511 "MODULETYPE%d_%sFLAGS:=%s\n",
512 (int)i,
513 "NASM",
514 ModuleHandlerInformations[i].nasmflags );
515 }
516
517 if ( ModuleHandlerInformations[i].linkerflags && ModuleHandlerInformations[i].linkerflags[0] )
518 {
519 fprintf ( fMakefile,
520 "MODULETYPE%d_%sFLAGS:=%s\n",
521 (int)i,
522 "LD",
523 ModuleHandlerInformations[i].linkerflags );
524 }
525 }
526
527 fprintf ( fMakefile,
528 "MODULETYPE%d_KMODE:=yes\n",
529 (int)Kernel );
530
531 fprintf ( fMakefile,
532 "MODULETYPE%d_KMODE:=yes\n",
533 (int)KernelModeDLL );
534
535 fprintf ( fMakefile,
536 "MODULETYPE%d_KMODE:=yes\n",
537 (int)KernelModeDriver );
538
539 fprintf ( fMakefile, "\n" );
540 }
541
542 bool
543 MingwBackend::IncludeInAllTarget ( const Module& module ) const
544 {
545 if ( MingwModuleHandler::ReferenceObjects ( module ) )
546 return false;
547 if ( module.type == BootSector )
548 return false;
549 if ( module.type == Iso )
550 return false;
551 if ( module.type == LiveIso )
552 return false;
553 if ( module.type == Test )
554 return false;
555 if ( module.type == Alias )
556 return false;
557 return true;
558 }
559
560 void
561 MingwBackend::GenerateAllTarget ( const vector<MingwModuleHandler*>& handlers ) const
562 {
563 fprintf ( fMakefile, "all:" );
564 int wrap_count = 0;
565 size_t iend = handlers.size ();
566 for ( size_t i = 0; i < iend; i++ )
567 {
568 const Module& module = handlers[i]->module;
569 if ( IncludeInAllTarget ( module ) )
570 {
571 if ( wrap_count++ == 5 )
572 fprintf ( fMakefile, " \\\n\t\t" ), wrap_count = 0;
573 fprintf ( fMakefile,
574 " %s",
575 GetTargetMacro(module).c_str () );
576 }
577 }
578 fprintf ( fMakefile, "\n\t\n\n" );
579 }
580
581 void
582 MingwBackend::GenerateRegTestsRunTarget () const
583 {
584 fprintf ( fMakefile,
585 "REGTESTS_RUN_TARGET = regtests.dll\n" );
586 fprintf ( fMakefile,
587 "$(REGTESTS_RUN_TARGET): $(REGTESTS_TARGET)\n" );
588 fprintf ( fMakefile,
589 "\t$(cp) $(REGTESTS_TARGET) $(REGTESTS_RUN_TARGET)\n" );
590 fprintf ( fMakefile, "\n" );
591 }
592
593 void
594 MingwBackend::GenerateXmlBuildFilesMacro() const
595 {
596 fprintf ( fMakefile,
597 "XMLBUILDFILES = %s \\\n",
598 ProjectNode.GetProjectFilename ().c_str () );
599 string xmlbuildFilenames;
600 int numberOfExistingFiles = 0;
601 struct stat statbuf;
602 time_t SystemTime, lastWriteTime;
603
604 for ( size_t i = 0; i < ProjectNode.xmlbuildfiles.size (); i++ )
605 {
606 XMLInclude& xmlbuildfile = *ProjectNode.xmlbuildfiles[i];
607 if ( !xmlbuildfile.fileExists )
608 continue;
609 numberOfExistingFiles++;
610 if ( xmlbuildFilenames.length () > 0 )
611 xmlbuildFilenames += " ";
612
613 FILE* f = fopen ( xmlbuildfile.topIncludeFilename.c_str (), "rb" );
614 if ( !f )
615 throw FileNotFoundException ( NormalizeFilename ( xmlbuildfile.topIncludeFilename ) );
616
617 if ( fstat ( fileno ( f ), &statbuf ) != 0 )
618 {
619 fclose ( f );
620 throw AccessDeniedException ( NormalizeFilename ( xmlbuildfile.topIncludeFilename ) );
621 }
622
623 lastWriteTime = statbuf.st_mtime;
624 SystemTime = time(NULL);
625
626 if (SystemTime != -1)
627 {
628 if (difftime (lastWriteTime, SystemTime) > 0)
629 throw InvalidDateException ( NormalizeFilename ( xmlbuildfile.topIncludeFilename ) );
630 }
631
632 fclose ( f );
633
634 xmlbuildFilenames += NormalizeFilename ( xmlbuildfile.topIncludeFilename );
635 if ( numberOfExistingFiles % 5 == 4 || i == ProjectNode.xmlbuildfiles.size () - 1 )
636 {
637 fprintf ( fMakefile,
638 "\t%s",
639 xmlbuildFilenames.c_str ());
640 if ( i == ProjectNode.xmlbuildfiles.size () - 1 )
641 {
642 fprintf ( fMakefile, "\n" );
643 }
644 else
645 {
646 fprintf ( fMakefile,
647 " \\\n" );
648 }
649 xmlbuildFilenames.resize ( 0 );
650 }
651 numberOfExistingFiles++;
652 }
653 fprintf ( fMakefile, "\n" );
654 }
655
656 void
657 MingwBackend::GenerateTestSupportCode ()
658 {
659 printf ( "Generating test support code..." );
660 TestSupportCode testSupportCode ( ProjectNode );
661 testSupportCode.GenerateTestSupportCode ( configuration.Verbose );
662 printf ( "done\n" );
663 }
664
665 void
666 MingwBackend::GenerateCompilationUnitSupportCode ()
667 {
668 if ( configuration.CompilationUnitsEnabled )
669 {
670 printf ( "Generating compilation unit support code..." );
671 CompilationUnitSupportCode compilationUnitSupportCode ( ProjectNode );
672 compilationUnitSupportCode.Generate ( configuration.Verbose );
673 printf ( "done\n" );
674 }
675 }
676
677 void
678 MingwBackend::GenerateSysSetup ()
679 {
680 printf ( "Generating syssetup.inf..." );
681 SysSetupGenerator sysSetupGenerator ( ProjectNode );
682 sysSetupGenerator.Generate ();
683 printf ( "done\n" );
684 }
685
686 string
687 MingwBackend::GetProxyMakefileTree () const
688 {
689 if ( configuration.GenerateProxyMakefilesInSourceTree )
690 return "";
691 else
692 return Environment::GetOutputPath ();
693 }
694
695 void
696 MingwBackend::GenerateProxyMakefiles ()
697 {
698 printf ( "Generating proxy makefiles..." );
699 ProxyMakefile proxyMakefile ( ProjectNode );
700 proxyMakefile.GenerateProxyMakefiles ( configuration.Verbose,
701 GetProxyMakefileTree () );
702 printf ( "done\n" );
703 }
704
705 void
706 MingwBackend::CheckAutomaticDependencies ()
707 {
708 if ( configuration.Dependencies == AutomaticDependencies )
709 {
710 printf ( "Checking automatic dependencies..." );
711 AutomaticDependency automaticDependency ( ProjectNode );
712 automaticDependency.CheckAutomaticDependencies ( configuration.Verbose );
713 printf ( "done\n" );
714 }
715 }
716
717 void
718 MingwBackend::GenerateDirectories ()
719 {
720 printf ( "Creating directories..." );
721 intermediateDirectory->GenerateTree ( IntermediateDirectory, configuration.Verbose );
722 outputDirectory->GenerateTree ( OutputDirectory, configuration.Verbose );
723 if ( !configuration.MakeHandlesInstallDirectories )
724 installDirectory->GenerateTree ( InstallDirectory, configuration.Verbose );
725 printf ( "done\n" );
726 }
727
728 bool
729 MingwBackend::TryToDetectThisCompiler ( const string& compiler )
730 {
731 string command = ssprintf (
732 "%s -v 1>%s 2>%s",
733 FixSeparatorForSystemCommand(compiler).c_str (),
734 NUL,
735 NUL );
736 int exitcode = system ( command.c_str () );
737 return (bool) (exitcode == 0);
738 }
739
740 void
741 MingwBackend::DetectCompiler ()
742 {
743 printf ( "Detecting compiler..." );
744
745 bool detectedCompiler = false;
746 bool supportedCompiler = false;
747 string compilerVersion;
748
749 if ( ProjectNode.configuration.Compiler == GnuGcc )
750 {
751 const string& TARGET_CCValue = Environment::GetVariable ( "TARGET_CC" );
752 const string& ROS_PREFIXValue = Environment::GetVariable ( "ROS_PREFIX" );
753
754 if ( TARGET_CCValue.length () > 0 )
755 {
756 compilerPrefix = "";
757 compilerCommand = TARGET_CCValue;
758 detectedCompiler = TryToDetectThisCompiler ( compilerCommand );
759 }
760
761 if ( !detectedCompiler )
762 {
763 if ( ROS_PREFIXValue.length () > 0 )
764 {
765 compilerPrefix = ROS_PREFIXValue;
766 compilerCommand = compilerPrefix + "-gcc";
767 detectedCompiler = TryToDetectThisCompiler ( compilerCommand );
768 }
769 }
770 #if defined(WIN32)
771 if ( !detectedCompiler )
772 {
773 compilerPrefix = "";
774 compilerCommand = "gcc";
775 detectedCompiler = TryToDetectThisCompiler ( compilerCommand );
776 }
777 #endif
778 if ( !detectedCompiler )
779 {
780 compilerPrefix = "mingw32";
781 compilerCommand = compilerPrefix + "-gcc";
782 detectedCompiler = TryToDetectThisCompiler ( compilerCommand );
783 }
784
785 if ( detectedCompiler )
786 compilerVersion = GetCompilerVersion ( compilerCommand );
787
788 supportedCompiler = IsSupportedCompilerVersion ( compilerVersion );
789 }
790 else if ( ProjectNode.configuration.Compiler == MicrosoftC )
791 {
792 compilerCommand = "cl";
793 detectedCompiler = DetectMicrosoftCompiler ( compilerVersion, mscPath );
794 supportedCompiler = true; // TODO
795 }
796
797 if ( detectedCompiler )
798 {
799 if ( supportedCompiler )
800 printf ( "detected (%s %s)\n", compilerCommand.c_str (), compilerVersion.c_str() );
801 else
802 {
803 printf ( "detected (%s), but with unsupported version (%s)\n",
804 compilerCommand.c_str (),
805 compilerVersion.c_str () );
806 throw UnsupportedBuildToolException ( compilerCommand, compilerVersion );
807 }
808 }
809 else
810 printf ( "not detected\n" );
811
812 }
813
814 bool
815 MingwBackend::TryToDetectThisNetwideAssembler ( const string& assembler )
816 {
817 string command = ssprintf (
818 "%s -h 1>%s 2>%s",
819 FixSeparatorForSystemCommand(assembler).c_str (),
820 NUL,
821 NUL );
822 int exitcode = system ( command.c_str () );
823 return (bool) (exitcode == 0);
824 }
825
826 string
827 MingwBackend::GetVersionString ( const string& versionCommand )
828 {
829 FILE *fp;
830 int ch, i;
831 size_t newline;
832 char buffer[81];
833
834 fp = popen ( versionCommand.c_str () , "r" );
835 for( i = 0;
836 ( i < 80 ) &&
837 ( feof ( fp ) == 0 &&
838 ( ( ch = fgetc( fp ) ) != -1 ) );
839 i++ )
840 {
841 buffer[i] = (char) ch;
842 }
843 buffer[i] = '\0';
844 pclose ( fp );
845
846 char separators[] = " ()\n";
847 char *token;
848 char *prevtoken = NULL;
849
850 string version;
851
852 token = strtok ( buffer, separators );
853 while ( token != NULL )
854 {
855 prevtoken = token;
856 version = string( prevtoken );
857 if ( (newline = version.find('\n')) != std::string::npos )
858 version.erase(newline, 1);
859 if ( version.find('.') != std::string::npos )
860 break;
861 token = strtok ( NULL, separators );
862 }
863 return version;
864 }
865
866 string
867 MingwBackend::GetNetwideAssemblerVersion ( const string& nasmCommand )
868 {
869 string versionCommand;
870 if ( nasmCommand.find("yasm") != std::string::npos )
871 {
872 versionCommand = ssprintf ( "%s --version",
873 nasmCommand.c_str (),
874 NUL,
875 NUL );
876 }
877 else
878 {
879 versionCommand = ssprintf ( "%s -v",
880 nasmCommand.c_str (),
881 NUL,
882 NUL );
883 }
884 return GetVersionString( versionCommand );
885 }
886
887 string
888 MingwBackend::GetCompilerVersion ( const string& compilerCommand )
889 {
890 string versionCommand = ssprintf ( "%s --version",
891 compilerCommand.c_str (),
892 NUL,
893 NUL );
894 return GetVersionString( versionCommand );
895 }
896
897 string
898 MingwBackend::GetBinutilsVersion ( const string& binutilsCommand )
899 {
900 string versionCommand = ssprintf ( "%s -v",
901 binutilsCommand.c_str (),
902 NUL,
903 NUL );
904 return GetVersionString( versionCommand );
905 }
906
907 bool
908 MingwBackend::IsSupportedCompilerVersion ( const string& compilerVersion )
909 {
910 if ( strcmp ( compilerVersion.c_str (), "4.4.0") < 0 )
911 return false;
912 else
913 return true;
914 }
915
916 bool
917 MingwBackend::TryToDetectThisBinutils ( const string& binutils )
918 {
919 string command = ssprintf (
920 "%s -v 1>%s 2>%s",
921 FixSeparatorForSystemCommand(binutils).c_str (),
922 NUL,
923 NUL );
924 int exitcode = system ( command.c_str () );
925 return (exitcode == 0);
926 }
927
928 string
929 MingwBackend::GetBinutilsVersionDate ( const string& binutilsCommand )
930 {
931 FILE *fp;
932 int ch, i;
933 char buffer[81];
934
935 string versionCommand = ssprintf ( "%s -v",
936 binutilsCommand.c_str (),
937 NUL,
938 NUL );
939 fp = popen ( versionCommand.c_str () , "r" );
940 for( i = 0;
941 ( i < 80 ) &&
942 ( feof ( fp ) == 0 &&
943 ( ( ch = fgetc( fp ) ) != -1 ) );
944 i++ )
945 {
946 buffer[i] = (char) ch;
947 }
948 buffer[i] = '\0';
949 pclose ( fp );
950
951 char separators[] = " ";
952 char *token;
953 char *prevtoken = NULL;
954
955 token = strtok ( buffer, separators );
956 while ( token != NULL )
957 {
958 prevtoken = token;
959 token = strtok ( NULL, separators );
960 }
961 string version = string ( prevtoken );
962 int lastDigit = version.find_last_not_of ( "\t\r\n" );
963 if ( lastDigit != -1 )
964 return string ( version, 0, lastDigit+1 );
965 else
966 return version;
967 }
968
969 bool
970 MingwBackend::IsSupportedBinutilsVersion ( const string& binutilsVersion )
971 {
972 int digit = binutilsVersion.find_last_of(".");
973 if(digit == -1)
974 {
975 printf("Unable to detect binutils version!\n");
976 return false;
977 }
978
979 string date = string(binutilsVersion, digit + 1);
980 if(date.length() == 8)
981 {
982 /* This is a real date in the format YYYYMMDD.
983 Check whether we have at least Binutils 20091016 (the oldest one
984 we were still using after upgrading to RosBE 1.5). */
985 if(strcmp(date.c_str(), "20091016") < 0)
986 return false;
987 }
988 else
989 {
990 /* This is no date, so binutilsVersion should just contain the version
991 number.
992 Binutils 2.20 will hopefully contain the required features. */
993 if(strcmp(binutilsVersion.c_str(), "2.20") < 0)
994 return false;
995 }
996
997 return true;
998 }
999
1000 void
1001 MingwBackend::DetectBinutils ()
1002 {
1003 printf ( "Detecting binutils..." );
1004
1005 bool detectedBinutils = false;
1006 bool supportedBinutils = false;
1007 string binutilsVersion;
1008
1009 if ( ProjectNode.configuration.Linker == GnuLd )
1010 {
1011 const string& ROS_PREFIXValue = Environment::GetVariable ( "ROS_PREFIX" );
1012
1013 if ( ROS_PREFIXValue.length () > 0 )
1014 {
1015 binutilsPrefix = ROS_PREFIXValue;
1016 binutilsCommand = binutilsPrefix + "-ld";
1017 manualBinutilsSetting = true;
1018 detectedBinutils = true;
1019 }
1020 #if defined(WIN32)
1021 if ( !detectedBinutils )
1022 {
1023 binutilsPrefix = "";
1024 binutilsCommand = "ld";
1025 detectedBinutils = TryToDetectThisBinutils ( binutilsCommand );
1026 }
1027 #endif
1028 if ( !detectedBinutils )
1029 {
1030 binutilsPrefix = "mingw32";
1031 binutilsCommand = binutilsPrefix + "-ld";
1032 detectedBinutils = TryToDetectThisBinutils ( binutilsCommand );
1033 }
1034 if ( detectedBinutils )
1035 {
1036 binutilsVersion = GetBinutilsVersionDate ( binutilsCommand );
1037 supportedBinutils = IsSupportedBinutilsVersion ( binutilsVersion );
1038 }
1039 }
1040 else if ( ProjectNode.configuration.Linker == MicrosoftLink )
1041 {
1042 compilerCommand = "link";
1043 detectedBinutils = DetectMicrosoftLinker ( binutilsVersion, mslinkPath );
1044 supportedBinutils = true; // TODO
1045 }
1046
1047 if ( detectedBinutils )
1048 {
1049 if ( supportedBinutils )
1050 printf ( "detected (%s %s)\n", binutilsCommand.c_str (), binutilsVersion.c_str() );
1051 else
1052 {
1053 printf ( "detected (%s), but with unsupported version (%s)\n",
1054 binutilsCommand.c_str (),
1055 binutilsVersion.c_str () );
1056 throw UnsupportedBuildToolException ( binutilsCommand, binutilsVersion );
1057 }
1058 }
1059 else
1060 printf ( "not detected\n" );
1061
1062 }
1063
1064 void
1065 MingwBackend::DetectNetwideAssembler ()
1066 {
1067 printf ( "Detecting netwide assembler..." );
1068
1069 nasmCommand = "nasm";
1070 bool detectedNasm = TryToDetectThisNetwideAssembler ( nasmCommand );
1071 #if defined(WIN32)
1072 if ( !detectedNasm )
1073 {
1074 nasmCommand = "nasmw";
1075 detectedNasm = TryToDetectThisNetwideAssembler ( nasmCommand );
1076 }
1077 #endif
1078 if ( !detectedNasm )
1079 {
1080 nasmCommand = "yasm";
1081 detectedNasm = TryToDetectThisNetwideAssembler ( nasmCommand );
1082 }
1083 if ( detectedNasm )
1084 printf ( "detected (%s %s)\n", nasmCommand.c_str (), GetNetwideAssemblerVersion( nasmCommand ).c_str() );
1085 else
1086 printf ( "not detected\n" );
1087 }
1088
1089 void
1090 MingwBackend::DetectPipeSupport ()
1091 {
1092 if ( ProjectNode.configuration.Compiler == GnuGcc )
1093 {
1094 printf ( "Detecting compiler -pipe support..." );
1095
1096 string pipe_detection = "tools" + sSep + "rbuild" + sSep + "backend" + sSep + "mingw" + sSep + "pipe_detection.c";
1097 string pipe_detectionObjectFilename = ReplaceExtension ( pipe_detection,
1098 ".o" );
1099 string command = ssprintf (
1100 "%s -pipe -c %s -o %s 1>%s 2>%s",
1101 FixSeparatorForSystemCommand(compilerCommand).c_str (),
1102 pipe_detection.c_str (),
1103 pipe_detectionObjectFilename.c_str (),
1104 NUL,
1105 NUL );
1106 int exitcode = system ( command.c_str () );
1107 FILE* f = fopen ( pipe_detectionObjectFilename.c_str (), "rb" );
1108 if ( f )
1109 {
1110 usePipe = (exitcode == 0);
1111 fclose ( f );
1112 unlink ( pipe_detectionObjectFilename.c_str () );
1113 }
1114 else
1115 usePipe = false;
1116
1117 if ( usePipe )
1118 printf ( "detected\n" );
1119 else
1120 printf ( "not detected\n" );
1121 }
1122 else
1123 usePipe = false;
1124 }
1125
1126 void
1127 MingwBackend::DetectPCHSupport ()
1128 {
1129 printf ( "Detecting compiler pre-compiled header support..." );
1130
1131 if ( configuration.PrecompiledHeadersEnabled && ProjectNode.configuration.Compiler == GnuGcc )
1132 {
1133 string path = "tools" + sSep + "rbuild" + sSep + "backend" + sSep + "mingw" + sSep + "pch_detection.h";
1134 string cmd = ssprintf (
1135 "%s -c %s 1>%s 2>%s",
1136 FixSeparatorForSystemCommand(compilerCommand).c_str (),
1137 path.c_str (),
1138 NUL,
1139 NUL );
1140 system ( cmd.c_str () );
1141 path += ".gch";
1142
1143 FILE* f = fopen ( path.c_str (), "rb" );
1144 if ( f )
1145 {
1146 use_pch = true;
1147 fclose ( f );
1148 unlink ( path.c_str () );
1149 }
1150 else
1151 use_pch = false;
1152
1153 if ( use_pch )
1154 printf ( "detected\n" );
1155 else
1156 printf ( "not detected\n" );
1157 }
1158 else
1159 {
1160 use_pch = false;
1161 printf ( "disabled\n" );
1162 }
1163 }
1164
1165 void
1166 MingwBackend::GetNonModuleInstallTargetFiles (
1167 vector<FileLocation>& out ) const
1168 {
1169 for ( size_t i = 0; i < ProjectNode.installfiles.size (); i++ )
1170 {
1171 const InstallFile& installfile = *ProjectNode.installfiles[i];
1172 out.push_back ( *installfile.target );
1173 }
1174 }
1175
1176 void
1177 MingwBackend::GetModuleInstallTargetFiles (
1178 vector<FileLocation>& out ) const
1179 {
1180 for ( std::map<std::string, Module*>::const_iterator p = ProjectNode.modules.begin (); p != ProjectNode.modules.end (); ++ p )
1181 {
1182 const Module& module = *p->second;
1183 if ( !module.enabled )
1184 continue;
1185 if ( module.install )
1186 out.push_back ( *module.install );
1187 }
1188 }
1189
1190 void
1191 MingwBackend::GetInstallTargetFiles (
1192 vector<FileLocation>& out ) const
1193 {
1194 GetNonModuleInstallTargetFiles ( out );
1195 GetModuleInstallTargetFiles ( out );
1196 }
1197
1198 void
1199 MingwBackend::OutputInstallTarget ( const FileLocation& source,
1200 const FileLocation& target )
1201 {
1202 fprintf ( fMakefile,
1203 "%s: %s | %s\n",
1204 GetFullName ( target ).c_str (),
1205 GetFullName ( source ).c_str (),
1206 GetFullPath ( target ).c_str () );
1207 fprintf ( fMakefile,
1208 "\t$(ECHO_CP)\n" );
1209 fprintf ( fMakefile,
1210 "\t${cp} %s %s 1>$(NUL)\n",
1211 GetFullName ( source ).c_str (),
1212 GetFullName ( target ).c_str () );
1213 }
1214
1215 void
1216 MingwBackend::OutputNonModuleInstallTargets ()
1217 {
1218 for ( size_t i = 0; i < ProjectNode.installfiles.size (); i++ )
1219 {
1220 const InstallFile& installfile = *ProjectNode.installfiles[i];
1221 OutputInstallTarget ( *installfile.source, *installfile.target );
1222 }
1223 }
1224
1225 const Module&
1226 MingwBackend::GetAliasedModuleOrModule ( const Module& module ) const
1227 {
1228 if ( module.aliasedModuleName.size () > 0 )
1229 {
1230 const Module* aliasedModule = ProjectNode.LocateModule ( module.aliasedModuleName );
1231 assert ( aliasedModule );
1232 return *aliasedModule;
1233 }
1234 else
1235 return module;
1236 }
1237
1238 void
1239 MingwBackend::OutputModuleInstallTargets ()
1240 {
1241 for ( std::map<std::string, Module*>::const_iterator p = ProjectNode.modules.begin (); p != ProjectNode.modules.end (); ++ p )
1242 {
1243 const Module& module = *p->second;
1244 if ( !module.enabled )
1245 continue;
1246 if ( module.install )
1247 {
1248 const Module& aliasedModule = GetAliasedModuleOrModule ( module );
1249 OutputInstallTarget ( *aliasedModule.output, *module.install );
1250 }
1251 }
1252 }
1253
1254 string
1255 MingwBackend::GetRegistrySourceFiles ()
1256 {
1257 return "boot" + sSep + "bootdata" + sSep + "hivecls_" + Environment::GetArch() + ".inf "
1258 "boot" + sSep + "bootdata" + sSep + "hivedef_" + Environment::GetArch() + ".inf "
1259 "boot" + sSep + "bootdata" + sSep + "hiveinst_" + Environment::GetArch() + ".inf "
1260 "boot" + sSep + "bootdata" + sSep + "hivesft_" + Environment::GetArch() + ".inf "
1261 "boot" + sSep + "bootdata" + sSep + "hivesys_" + Environment::GetArch() + ".inf ";
1262 }
1263
1264 string
1265 MingwBackend::GetRegistryTargetFiles ()
1266 {
1267 string system32ConfigDirectory = "system32" + sSep + "config";
1268 FileLocation system32 ( InstallDirectory, system32ConfigDirectory, "" );
1269
1270 vector<FileLocation> registry_files;
1271 registry_files.push_back ( FileLocation ( InstallDirectory, system32ConfigDirectory, "default" ) );
1272 registry_files.push_back ( FileLocation ( InstallDirectory, system32ConfigDirectory, "sam" ) );
1273 registry_files.push_back ( FileLocation ( InstallDirectory, system32ConfigDirectory, "security" ) );
1274 registry_files.push_back ( FileLocation ( InstallDirectory, system32ConfigDirectory, "software" ) );
1275 registry_files.push_back ( FileLocation ( InstallDirectory, system32ConfigDirectory, "system" ) );
1276
1277 return v2s( this, registry_files, 6 );
1278 }
1279
1280 void
1281 MingwBackend::OutputRegistryInstallTarget ()
1282 {
1283 FileLocation system32 ( InstallDirectory, "system32" + sSep + "config", "" );
1284
1285 string registrySourceFiles = GetRegistrySourceFiles ();
1286 string registryTargetFiles = GetRegistryTargetFiles ();
1287 fprintf ( fMakefile,
1288 "install_registry: %s\n",
1289 registryTargetFiles.c_str () );
1290 fprintf ( fMakefile,
1291 "%s: %s %s $(mkhive_TARGET)\n",
1292 registryTargetFiles.c_str (),
1293 registrySourceFiles.c_str (),
1294 GetFullPath ( system32 ).c_str () );
1295 fprintf ( fMakefile,
1296 "\t$(ECHO_MKHIVE)\n" );
1297 fprintf ( fMakefile,
1298 "\t$(mkhive_TARGET) boot%cbootdata %s $(ARCH) boot%cbootdata%chiveinst_$(ARCH).inf\n",
1299 cSep, GetFullPath ( system32 ).c_str (),
1300 cSep, cSep );
1301 fprintf ( fMakefile,
1302 "\n" );
1303 }
1304
1305 void
1306 MingwBackend::GenerateInstallTarget ()
1307 {
1308 vector<FileLocation> vInstallTargetFiles;
1309 GetInstallTargetFiles ( vInstallTargetFiles );
1310 string installTargetFiles = v2s ( this, vInstallTargetFiles, 5 );
1311 string registryTargetFiles = GetRegistryTargetFiles ();
1312
1313 fprintf ( fMakefile,
1314 "install: %s %s\n",
1315 installTargetFiles.c_str (),
1316 registryTargetFiles.c_str () );
1317 OutputNonModuleInstallTargets ();
1318 OutputModuleInstallTargets ();
1319 OutputRegistryInstallTarget ();
1320 fprintf ( fMakefile,
1321 "\n" );
1322 }
1323
1324 void
1325 MingwBackend::GetModuleTestTargets (
1326 vector<string>& out ) const
1327 {
1328 for ( std::map<std::string, Module*>::const_iterator p = ProjectNode.modules.begin (); p != ProjectNode.modules.end (); ++ p )
1329 {
1330 const Module& module = *p->second;
1331 if ( !module.enabled )
1332 continue;
1333 if ( module.type == Test )
1334 out.push_back ( module.name );
1335 }
1336 }
1337
1338 void
1339 MingwBackend::GenerateTestTarget ()
1340 {
1341 vector<string> vTestTargets;
1342 GetModuleTestTargets ( vTestTargets );
1343 string testTargets = v2s ( vTestTargets, 5 );
1344
1345 fprintf ( fMakefile,
1346 "test: %s\n",
1347 testTargets.c_str () );
1348 fprintf ( fMakefile,
1349 "\n" );
1350 }
1351
1352 void
1353 MingwBackend::GenerateDirectoryTargets ()
1354 {
1355 intermediateDirectory->CreateRule ( fMakefile, "$(INTERMEDIATE)" );
1356 outputDirectory->CreateRule ( fMakefile, "$(OUTPUT)" );
1357 installDirectory->CreateRule ( fMakefile, "$(INSTALL)" );
1358 }