Merge trunk HEAD (r46369)
[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 GenerateInstallerFileList();
369 GenerateProxyMakefiles ();
370 CheckAutomaticDependencies ();
371 CloseMakefile ();
372 }
373
374 void
375 MingwBackend::GenerateInstallerFileList()
376 {
377 this->ProjectNode.GenerateInstallerFileList();
378 }
379
380 void
381 MingwBackend::CreateMakefile ()
382 {
383 fMakefile = fopen ( ProjectNode.makefile.c_str (), "w" );
384 if ( !fMakefile )
385 throw AccessDeniedException ( ProjectNode.makefile );
386 MingwModuleHandler::SetBackend ( this );
387 MingwModuleHandler::SetMakefile ( fMakefile );
388 }
389
390 void
391 MingwBackend::CloseMakefile () const
392 {
393 if (fMakefile)
394 fclose ( fMakefile );
395 }
396
397 void
398 MingwBackend::GenerateHeader () const
399 {
400 fprintf ( fMakefile, "# THIS FILE IS AUTOMATICALLY GENERATED, EDIT '%s' INSTEAD\n\n",
401 ProjectNode.GetProjectFilename ().c_str () );
402 }
403
404 void
405 MingwBackend::GenerateGlobalProperties (
406 const char* assignmentOperation,
407 const IfableData& data ) const
408 {
409 for ( std::map<std::string, Property*>::const_iterator p = data.properties.begin(); p != data.properties.end(); ++ p )
410 {
411 Property& prop = *p->second;
412
413 if (!prop.isInternal)
414 {
415 fprintf ( fMakefile, "%s := %s\n",
416 prop.name.c_str(),
417 prop.value.c_str() );
418 }
419 }
420 }
421
422 string
423 MingwBackend::GenerateProjectLDFLAGS () const
424 {
425 string ldflags;
426 for ( size_t i = 0; i < ProjectNode.linkerFlags.size (); i++ )
427 {
428 LinkerFlag& linkerFlag = *ProjectNode.linkerFlags[i];
429 if ( ldflags.length () > 0 )
430 ldflags += " ";
431 ldflags += linkerFlag.flag;
432 }
433 return ldflags;
434 }
435
436 void
437 MingwBackend::GenerateGlobalVariables () const
438 {
439 fputs ( "include tools$(SEP)rbuild$(SEP)backend$(SEP)mingw$(SEP)rules.mak\n", fMakefile );
440 fprintf ( fMakefile, "include tools$(SEP)rbuild$(SEP)backend$(SEP)mingw$(SEP)linkers$(SEP)%s.mak\n", ProjectNode.GetLinkerSet ().c_str () );
441 fprintf ( fMakefile, "include tools$(SEP)rbuild$(SEP)backend$(SEP)mingw$(SEP)compilers$(SEP)%s.mak\n", ProjectNode.GetCompilerSet ().c_str () );
442
443 if ( mscPath.length() )
444 fprintf ( fMakefile, "export RBUILD_CL_PATH=%s\n", mscPath.c_str () );
445
446 if ( mslinkPath.length() )
447 fprintf ( fMakefile, "export RBUILD_LINK_PATH=%s\n", mslinkPath.c_str () );
448
449 if ( configuration.Dependencies == FullDependencies )
450 {
451 fprintf ( fMakefile,
452 "ifeq ($(ROS_BUILDDEPS),)\n"
453 "ROS_BUILDDEPS:=%s\n"
454 "endif\n",
455 "full" );
456 }
457
458 fprintf ( fMakefile,
459 "PREFIX := %s\n",
460 compilerPrefix.c_str () );
461 fprintf ( fMakefile,
462 "nasm := $(Q)%s\n",
463 nasmCommand.c_str () );
464
465 GenerateGlobalProperties ( "=", ProjectNode.non_if_data );
466
467 if ( ProjectNode.configuration.Compiler == GnuGcc )
468 {
469 fprintf ( fMakefile, "ifneq ($(OARCH),)\n" );
470 fprintf ( fMakefile, "PROJECT_ASFLAGS += -march=$(OARCH)\n" );
471 fprintf ( fMakefile, "PROJECT_CFLAGS += -march=$(OARCH)\n" );
472 fprintf ( fMakefile, "PROJECT_CXXFLAGS += -march=$(OARCH)\n" );
473 fprintf ( fMakefile, "endif\n" );
474 fprintf ( fMakefile, "ifneq ($(TUNE),)\n" );
475 fprintf ( fMakefile, "PROJECT_CFLAGS += -mtune=$(TUNE)\n" );
476 fprintf ( fMakefile, "PROJECT_CXXFLAGS += -mtune=$(TUNE)\n" );
477 fprintf ( fMakefile, "endif\n" );
478
479 if ( usePipe )
480 {
481 fprintf ( fMakefile, "PROJECT_CFLAGS += -pipe\n" );
482 fprintf ( fMakefile, "PROJECT_CXXFLAGS += -pipe\n" );
483 fprintf ( fMakefile, "PROJECT_ASFLAGS += -pipe\n" );
484 }
485
486 // Would be nice to have our own C++ runtime
487 fputs ( "BUILTIN_CXXINCLUDES+= $(TARGET_CPPFLAGS)\n", fMakefile );
488
489 fprintf ( fMakefile, "PROJECT_CCLIBS := \"$(shell ${TARGET_CC} -print-libgcc-file-name)\"\n" );
490
491 // We use our proprietary "ofmt_stub.a" to implement a stub for "_get_output_format" required by "libmingwex.a".
492 // This archive just contains the compiled "ofmt_stub.s" supplied with the MinGW Runtime sources.
493 fprintf ( fMakefile, "PROJECT_CXXLIBS := \"$(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)\" \"$(shell ${TARGET_CPP} -print-file-name=ofmt_stub.a)\" \"$(shell ${TARGET_CPP} -print-file-name=libcoldname.a)\"\n" );
494
495 /* hack to get libgcc_eh.a, should check mingw version or something */
496 if (Environment::GetArch() == "amd64")
497 fprintf ( fMakefile, " \"$(shell ${TARGET_CPP} -print-file-name=libgcc_eh.a)\"" );
498 /* hack to get _get_output_format, needed by libmingwex */
499 else if (Environment::GetArch() == "i386")
500 fprintf ( fMakefile, "\"$(shell ${TARGET_CPP} -print-file-name=ofmt_stub.a)\"");
501 fprintf ( fMakefile,"\n");
502 }
503 MingwModuleHandler::GenerateParameters ( "PROJECT", "+=", ProjectNode.non_if_data );
504 MingwModuleHandler::GenerateParameters ( "PROJECT_HOST", "+=", ProjectNode.host_non_if_data );
505
506 fprintf ( fMakefile, "PROJECT_LDFLAGS := %s\n", GenerateProjectLDFLAGS ().c_str () );
507
508 // TODO: use symbolic names for module types
509 for ( size_t i = 0; i < sizeof(ModuleHandlerInformations) / sizeof(ModuleHandlerInformations[0]); ++ i )
510 {
511 if ( ModuleHandlerInformations[i].cflags && ModuleHandlerInformations[i].cflags[0] )
512 {
513 fprintf ( fMakefile,
514 "MODULETYPE%d_%sFLAGS:=%s\n",
515 (int)i,
516 "C",
517 ModuleHandlerInformations[i].cflags );
518 }
519
520 if ( ModuleHandlerInformations[i].nasmflags && ModuleHandlerInformations[i].nasmflags[0] )
521 {
522 fprintf ( fMakefile,
523 "MODULETYPE%d_%sFLAGS:=%s\n",
524 (int)i,
525 "NASM",
526 ModuleHandlerInformations[i].nasmflags );
527 }
528
529 if ( ModuleHandlerInformations[i].linkerflags && ModuleHandlerInformations[i].linkerflags[0] )
530 {
531 fprintf ( fMakefile,
532 "MODULETYPE%d_%sFLAGS:=%s\n",
533 (int)i,
534 "LD",
535 ModuleHandlerInformations[i].linkerflags );
536 }
537 }
538
539 fprintf ( fMakefile,
540 "MODULETYPE%d_KMODE:=yes\n",
541 (int)Kernel );
542
543 fprintf ( fMakefile,
544 "MODULETYPE%d_KMODE:=yes\n",
545 (int)KernelModeDLL );
546
547 fprintf ( fMakefile,
548 "MODULETYPE%d_KMODE:=yes\n",
549 (int)KernelModeDriver );
550
551 fprintf ( fMakefile, "\n" );
552 }
553
554 bool
555 MingwBackend::IncludeInAllTarget ( const Module& module ) const
556 {
557 if ( MingwModuleHandler::ReferenceObjects ( module ) )
558 return false;
559 if ( module.type == BootSector )
560 return false;
561 if ( module.type == Iso )
562 return false;
563 if ( module.type == LiveIso )
564 return false;
565 if ( module.type == Test )
566 return false;
567 if ( module.type == Alias )
568 return false;
569 return true;
570 }
571
572 void
573 MingwBackend::GenerateAllTarget ( const vector<MingwModuleHandler*>& handlers ) const
574 {
575 fprintf ( fMakefile, "all:" );
576 int wrap_count = 0;
577 size_t iend = handlers.size ();
578 for ( size_t i = 0; i < iend; i++ )
579 {
580 const Module& module = handlers[i]->module;
581 if ( IncludeInAllTarget ( module ) )
582 {
583 if ( wrap_count++ == 5 )
584 fprintf ( fMakefile, " \\\n\t\t" ), wrap_count = 0;
585 fprintf ( fMakefile,
586 " %s",
587 GetTargetMacro(module).c_str () );
588 }
589 }
590 fprintf ( fMakefile, "\n\t\n\n" );
591 }
592
593 void
594 MingwBackend::GenerateRegTestsRunTarget () const
595 {
596 fprintf ( fMakefile,
597 "REGTESTS_RUN_TARGET = regtests.dll\n" );
598 fprintf ( fMakefile,
599 "$(REGTESTS_RUN_TARGET): $(REGTESTS_TARGET)\n" );
600 fprintf ( fMakefile,
601 "\t$(cp) $(REGTESTS_TARGET) $(REGTESTS_RUN_TARGET)\n" );
602 fprintf ( fMakefile, "\n" );
603 }
604
605 void
606 MingwBackend::GenerateXmlBuildFilesMacro() const
607 {
608 fprintf ( fMakefile,
609 "XMLBUILDFILES = %s \\\n",
610 ProjectNode.GetProjectFilename ().c_str () );
611 string xmlbuildFilenames;
612 int numberOfExistingFiles = 0;
613 struct stat statbuf;
614 time_t SystemTime, lastWriteTime;
615
616 for ( size_t i = 0; i < ProjectNode.xmlbuildfiles.size (); i++ )
617 {
618 XMLInclude& xmlbuildfile = *ProjectNode.xmlbuildfiles[i];
619 if ( !xmlbuildfile.fileExists )
620 continue;
621 numberOfExistingFiles++;
622 if ( xmlbuildFilenames.length () > 0 )
623 xmlbuildFilenames += " ";
624
625 FILE* f = fopen ( xmlbuildfile.topIncludeFilename.c_str (), "rb" );
626 if ( !f )
627 throw FileNotFoundException ( NormalizeFilename ( xmlbuildfile.topIncludeFilename ) );
628
629 if ( fstat ( fileno ( f ), &statbuf ) != 0 )
630 {
631 fclose ( f );
632 throw AccessDeniedException ( NormalizeFilename ( xmlbuildfile.topIncludeFilename ) );
633 }
634
635 lastWriteTime = statbuf.st_mtime;
636 SystemTime = time(NULL);
637
638 if (SystemTime != -1)
639 {
640 if (difftime (lastWriteTime, SystemTime) > 0)
641 throw InvalidDateException ( NormalizeFilename ( xmlbuildfile.topIncludeFilename ) );
642 }
643
644 fclose ( f );
645
646 xmlbuildFilenames += NormalizeFilename ( xmlbuildfile.topIncludeFilename );
647 if ( numberOfExistingFiles % 5 == 4 || i == ProjectNode.xmlbuildfiles.size () - 1 )
648 {
649 fprintf ( fMakefile,
650 "\t%s",
651 xmlbuildFilenames.c_str ());
652 if ( i == ProjectNode.xmlbuildfiles.size () - 1 )
653 {
654 fprintf ( fMakefile, "\n" );
655 }
656 else
657 {
658 fprintf ( fMakefile,
659 " \\\n" );
660 }
661 xmlbuildFilenames.resize ( 0 );
662 }
663 numberOfExistingFiles++;
664 }
665 fprintf ( fMakefile, "\n" );
666 }
667
668 void
669 MingwBackend::GenerateTestSupportCode ()
670 {
671 printf ( "Generating test support code..." );
672 TestSupportCode testSupportCode ( ProjectNode );
673 testSupportCode.GenerateTestSupportCode ( configuration.Verbose );
674 printf ( "done\n" );
675 }
676
677 void
678 MingwBackend::GenerateCompilationUnitSupportCode ()
679 {
680 if ( configuration.CompilationUnitsEnabled )
681 {
682 printf ( "Generating compilation unit support code..." );
683 CompilationUnitSupportCode compilationUnitSupportCode ( ProjectNode );
684 compilationUnitSupportCode.Generate ( configuration.Verbose );
685 printf ( "done\n" );
686 }
687 }
688
689 void
690 MingwBackend::GenerateSysSetup ()
691 {
692 printf ( "Generating syssetup.inf..." );
693 SysSetupGenerator sysSetupGenerator ( ProjectNode );
694 sysSetupGenerator.Generate ();
695 printf ( "done\n" );
696 }
697
698 string
699 MingwBackend::GetProxyMakefileTree () const
700 {
701 if ( configuration.GenerateProxyMakefilesInSourceTree )
702 return "";
703 else
704 return Environment::GetOutputPath ();
705 }
706
707 void
708 MingwBackend::GenerateProxyMakefiles ()
709 {
710 printf ( "Generating proxy makefiles..." );
711 ProxyMakefile proxyMakefile ( ProjectNode );
712 proxyMakefile.GenerateProxyMakefiles ( configuration.Verbose,
713 GetProxyMakefileTree () );
714 printf ( "done\n" );
715 }
716
717 void
718 MingwBackend::CheckAutomaticDependencies ()
719 {
720 if ( configuration.Dependencies == AutomaticDependencies )
721 {
722 printf ( "Checking automatic dependencies..." );
723 AutomaticDependency automaticDependency ( ProjectNode );
724 automaticDependency.CheckAutomaticDependencies ( configuration.Verbose );
725 printf ( "done\n" );
726 }
727 }
728
729 void
730 MingwBackend::GenerateDirectories ()
731 {
732 printf ( "Creating directories..." );
733 intermediateDirectory->GenerateTree ( IntermediateDirectory, configuration.Verbose );
734 outputDirectory->GenerateTree ( OutputDirectory, configuration.Verbose );
735 if ( !configuration.MakeHandlesInstallDirectories )
736 installDirectory->GenerateTree ( InstallDirectory, configuration.Verbose );
737 printf ( "done\n" );
738 }
739
740 bool
741 MingwBackend::TryToDetectThisCompiler ( const string& compiler )
742 {
743 string command = ssprintf (
744 "%s -v 1>%s 2>%s",
745 FixSeparatorForSystemCommand(compiler).c_str (),
746 NUL,
747 NUL );
748 int exitcode = system ( command.c_str () );
749 return (bool) (exitcode == 0);
750 }
751
752 void
753 MingwBackend::DetectCompiler ()
754 {
755 printf ( "Detecting compiler..." );
756
757 bool detectedCompiler = false;
758 bool supportedCompiler = false;
759 string compilerVersion;
760
761 if ( ProjectNode.configuration.Compiler == GnuGcc )
762 {
763 const string& TARGET_CCValue = Environment::GetVariable ( "TARGET_CC" );
764 const string& ROS_PREFIXValue = Environment::GetVariable ( "ROS_PREFIX" );
765
766 if ( TARGET_CCValue.length () > 0 )
767 {
768 compilerPrefix = "";
769 compilerCommand = TARGET_CCValue;
770 detectedCompiler = TryToDetectThisCompiler ( compilerCommand );
771 }
772
773 if ( !detectedCompiler )
774 {
775 if ( ROS_PREFIXValue.length () > 0 )
776 {
777 compilerPrefix = ROS_PREFIXValue;
778 compilerCommand = compilerPrefix + "-gcc";
779 detectedCompiler = TryToDetectThisCompiler ( compilerCommand );
780 }
781 }
782 #if defined(WIN32)
783 if ( !detectedCompiler )
784 {
785 compilerPrefix = "";
786 compilerCommand = "gcc";
787 detectedCompiler = TryToDetectThisCompiler ( compilerCommand );
788 }
789 #endif
790 if ( !detectedCompiler )
791 {
792 compilerPrefix = "mingw32";
793 compilerCommand = compilerPrefix + "-gcc";
794 detectedCompiler = TryToDetectThisCompiler ( compilerCommand );
795 }
796
797 if ( detectedCompiler )
798 compilerVersion = GetCompilerVersion ( compilerCommand );
799
800 supportedCompiler = IsSupportedCompilerVersion ( compilerVersion );
801 }
802 else if ( ProjectNode.configuration.Compiler == MicrosoftC )
803 {
804 compilerCommand = "cl";
805 detectedCompiler = DetectMicrosoftCompiler ( compilerVersion, mscPath );
806 supportedCompiler = true; // TODO
807 }
808
809 if ( detectedCompiler )
810 {
811 if ( supportedCompiler )
812 printf ( "detected (%s %s)\n", compilerCommand.c_str (), compilerVersion.c_str() );
813 else
814 {
815 printf ( "detected (%s), but with unsupported version (%s)\n",
816 compilerCommand.c_str (),
817 compilerVersion.c_str () );
818 throw UnsupportedBuildToolException ( compilerCommand, compilerVersion );
819 }
820 }
821 else
822 printf ( "not detected\n" );
823
824 }
825
826 bool
827 MingwBackend::TryToDetectThisNetwideAssembler ( const string& assembler )
828 {
829 string command = ssprintf (
830 "%s -h 1>%s 2>%s",
831 FixSeparatorForSystemCommand(assembler).c_str (),
832 NUL,
833 NUL );
834 int exitcode = system ( command.c_str () );
835 return (bool) (exitcode == 0);
836 }
837
838 string
839 MingwBackend::GetVersionString ( const string& versionCommand )
840 {
841 FILE *fp;
842 int ch, i;
843 size_t newline;
844 char buffer[81];
845
846 fp = popen ( versionCommand.c_str () , "r" );
847 for( i = 0;
848 ( i < 80 ) &&
849 ( feof ( fp ) == 0 &&
850 ( ( ch = fgetc( fp ) ) != -1 ) );
851 i++ )
852 {
853 buffer[i] = (char) ch;
854 }
855 buffer[i] = '\0';
856 pclose ( fp );
857
858 char separators[] = " ()\n";
859 char *token;
860 char *prevtoken = NULL;
861
862 string version;
863
864 token = strtok ( buffer, separators );
865 while ( token != NULL )
866 {
867 prevtoken = token;
868 version = string( prevtoken );
869 if ( (newline = version.find('\n')) != std::string::npos )
870 version.erase(newline, 1);
871 if ( version.find('.') != std::string::npos )
872 break;
873 token = strtok ( NULL, separators );
874 }
875 return version;
876 }
877
878 string
879 MingwBackend::GetNetwideAssemblerVersion ( const string& nasmCommand )
880 {
881 string versionCommand;
882 if ( nasmCommand.find("yasm") != std::string::npos )
883 {
884 versionCommand = ssprintf ( "%s --version",
885 nasmCommand.c_str (),
886 NUL,
887 NUL );
888 }
889 else
890 {
891 versionCommand = ssprintf ( "%s -v",
892 nasmCommand.c_str (),
893 NUL,
894 NUL );
895 }
896 return GetVersionString( versionCommand );
897 }
898
899 string
900 MingwBackend::GetCompilerVersion ( const string& compilerCommand )
901 {
902 string versionCommand = ssprintf ( "%s --version",
903 compilerCommand.c_str (),
904 NUL,
905 NUL );
906 return GetVersionString( versionCommand );
907 }
908
909 string
910 MingwBackend::GetBinutilsVersion ( const string& binutilsCommand )
911 {
912 string versionCommand = ssprintf ( "%s -v",
913 binutilsCommand.c_str (),
914 NUL,
915 NUL );
916 return GetVersionString( versionCommand );
917 }
918
919 bool
920 MingwBackend::IsSupportedCompilerVersion ( const string& compilerVersion )
921 {
922 if ( strcmp ( compilerVersion.c_str (), "4.4.0") < 0 )
923 return false;
924 else
925 return true;
926 }
927
928 bool
929 MingwBackend::TryToDetectThisBinutils ( const string& binutils )
930 {
931 string command = ssprintf (
932 "%s -v 1>%s 2>%s",
933 FixSeparatorForSystemCommand(binutils).c_str (),
934 NUL,
935 NUL );
936 int exitcode = system ( command.c_str () );
937 return (exitcode == 0);
938 }
939
940 string
941 MingwBackend::GetBinutilsVersionDate ( const string& binutilsCommand )
942 {
943 FILE *fp;
944 int ch, i;
945 char buffer[81];
946
947 string versionCommand = ssprintf ( "%s -v",
948 binutilsCommand.c_str (),
949 NUL,
950 NUL );
951 fp = popen ( versionCommand.c_str () , "r" );
952 for( i = 0;
953 ( i < 80 ) &&
954 ( feof ( fp ) == 0 &&
955 ( ( ch = fgetc( fp ) ) != -1 ) );
956 i++ )
957 {
958 buffer[i] = (char) ch;
959 }
960 buffer[i] = '\0';
961 pclose ( fp );
962
963 char separators[] = " ";
964 char *token;
965 char *prevtoken = NULL;
966
967 token = strtok ( buffer, separators );
968 while ( token != NULL )
969 {
970 prevtoken = token;
971 token = strtok ( NULL, separators );
972 }
973 string version = string ( prevtoken );
974 int lastDigit = version.find_last_not_of ( "\t\r\n" );
975 if ( lastDigit != -1 )
976 return string ( version, 0, lastDigit+1 );
977 else
978 return version;
979 }
980
981 bool
982 MingwBackend::IsSupportedBinutilsVersion ( const string& binutilsVersion )
983 {
984 int digit = binutilsVersion.find_last_of(".");
985 if(digit == -1)
986 {
987 printf("Unable to detect binutils version!\n");
988 return false;
989 }
990
991 string date = string(binutilsVersion, digit + 1);
992 if(date.length() == 8)
993 {
994 /* This is a real date in the format YYYYMMDD.
995 Check whether we have at least Binutils 20091016 (the oldest one
996 we were still using after upgrading to RosBE 1.5). */
997 if(strcmp(date.c_str(), "20091016") < 0)
998 return false;
999 }
1000 else
1001 {
1002 /* This is no date, so binutilsVersion should just contain the version
1003 number.
1004 Binutils 2.20 will hopefully contain the required features. */
1005 if(strcmp(binutilsVersion.c_str(), "2.20") < 0)
1006 return false;
1007 }
1008
1009 return true;
1010 }
1011
1012 void
1013 MingwBackend::DetectBinutils ()
1014 {
1015 printf ( "Detecting binutils..." );
1016
1017 bool detectedBinutils = false;
1018 bool supportedBinutils = false;
1019 string binutilsVersion;
1020
1021 if ( ProjectNode.configuration.Linker == GnuLd )
1022 {
1023 const string& ROS_PREFIXValue = Environment::GetVariable ( "ROS_PREFIX" );
1024
1025 if ( ROS_PREFIXValue.length () > 0 )
1026 {
1027 binutilsPrefix = ROS_PREFIXValue;
1028 binutilsCommand = binutilsPrefix + "-ld";
1029 manualBinutilsSetting = true;
1030 detectedBinutils = true;
1031 }
1032 #if defined(WIN32)
1033 if ( !detectedBinutils )
1034 {
1035 binutilsPrefix = "";
1036 binutilsCommand = "ld";
1037 detectedBinutils = TryToDetectThisBinutils ( binutilsCommand );
1038 }
1039 #endif
1040 if ( !detectedBinutils )
1041 {
1042 binutilsPrefix = "mingw32";
1043 binutilsCommand = binutilsPrefix + "-ld";
1044 detectedBinutils = TryToDetectThisBinutils ( binutilsCommand );
1045 }
1046 if ( detectedBinutils )
1047 {
1048 binutilsVersion = GetBinutilsVersionDate ( binutilsCommand );
1049 supportedBinutils = IsSupportedBinutilsVersion ( binutilsVersion );
1050 }
1051 }
1052 else if ( ProjectNode.configuration.Linker == MicrosoftLink )
1053 {
1054 compilerCommand = "link";
1055 detectedBinutils = DetectMicrosoftLinker ( binutilsVersion, mslinkPath );
1056 supportedBinutils = true; // TODO
1057 }
1058
1059 if ( detectedBinutils )
1060 {
1061 if ( supportedBinutils )
1062 printf ( "detected (%s %s)\n", binutilsCommand.c_str (), binutilsVersion.c_str() );
1063 else
1064 {
1065 printf ( "detected (%s), but with unsupported version (%s)\n",
1066 binutilsCommand.c_str (),
1067 binutilsVersion.c_str () );
1068 throw UnsupportedBuildToolException ( binutilsCommand, binutilsVersion );
1069 }
1070 }
1071 else
1072 printf ( "not detected\n" );
1073
1074 }
1075
1076 void
1077 MingwBackend::DetectNetwideAssembler ()
1078 {
1079 printf ( "Detecting netwide assembler..." );
1080
1081 nasmCommand = "nasm";
1082 bool detectedNasm = TryToDetectThisNetwideAssembler ( nasmCommand );
1083 #if defined(WIN32)
1084 if ( !detectedNasm )
1085 {
1086 nasmCommand = "nasmw";
1087 detectedNasm = TryToDetectThisNetwideAssembler ( nasmCommand );
1088 }
1089 #endif
1090 if ( !detectedNasm )
1091 {
1092 nasmCommand = "yasm";
1093 detectedNasm = TryToDetectThisNetwideAssembler ( nasmCommand );
1094 }
1095 if ( detectedNasm )
1096 printf ( "detected (%s %s)\n", nasmCommand.c_str (), GetNetwideAssemblerVersion( nasmCommand ).c_str() );
1097 else
1098 printf ( "not detected\n" );
1099 }
1100
1101 void
1102 MingwBackend::DetectPipeSupport ()
1103 {
1104 if ( ProjectNode.configuration.Compiler == GnuGcc )
1105 {
1106 printf ( "Detecting compiler -pipe support..." );
1107
1108 string pipe_detection = "tools" + sSep + "rbuild" + sSep + "backend" + sSep + "mingw" + sSep + "pipe_detection.c";
1109 string pipe_detectionObjectFilename = ReplaceExtension ( pipe_detection,
1110 ".o" );
1111 string command = ssprintf (
1112 "%s -pipe -c %s -o %s 1>%s 2>%s",
1113 FixSeparatorForSystemCommand(compilerCommand).c_str (),
1114 pipe_detection.c_str (),
1115 pipe_detectionObjectFilename.c_str (),
1116 NUL,
1117 NUL );
1118 int exitcode = system ( command.c_str () );
1119 FILE* f = fopen ( pipe_detectionObjectFilename.c_str (), "rb" );
1120 if ( f )
1121 {
1122 usePipe = (exitcode == 0);
1123 fclose ( f );
1124 unlink ( pipe_detectionObjectFilename.c_str () );
1125 }
1126 else
1127 usePipe = false;
1128
1129 if ( usePipe )
1130 printf ( "detected\n" );
1131 else
1132 printf ( "not detected\n" );
1133 }
1134 else
1135 usePipe = false;
1136 }
1137
1138 void
1139 MingwBackend::DetectPCHSupport ()
1140 {
1141 printf ( "Detecting compiler pre-compiled header support..." );
1142
1143 if ( configuration.PrecompiledHeadersEnabled && ProjectNode.configuration.Compiler == GnuGcc )
1144 {
1145 string path = "tools" + sSep + "rbuild" + sSep + "backend" + sSep + "mingw" + sSep + "pch_detection.h";
1146 string cmd = ssprintf (
1147 "%s -c %s 1>%s 2>%s",
1148 FixSeparatorForSystemCommand(compilerCommand).c_str (),
1149 path.c_str (),
1150 NUL,
1151 NUL );
1152 system ( cmd.c_str () );
1153 path += ".gch";
1154
1155 FILE* f = fopen ( path.c_str (), "rb" );
1156 if ( f )
1157 {
1158 use_pch = true;
1159 fclose ( f );
1160 unlink ( path.c_str () );
1161 }
1162 else
1163 use_pch = false;
1164
1165 if ( use_pch )
1166 printf ( "detected\n" );
1167 else
1168 printf ( "not detected\n" );
1169 }
1170 else
1171 {
1172 use_pch = false;
1173 printf ( "disabled\n" );
1174 }
1175 }
1176
1177 void
1178 MingwBackend::GetNonModuleInstallTargetFiles (
1179 vector<FileLocation>& out ) const
1180 {
1181 for ( size_t i = 0; i < ProjectNode.installfiles.size (); i++ )
1182 {
1183 const InstallFile& installfile = *ProjectNode.installfiles[i];
1184 out.push_back ( *installfile.target );
1185 }
1186 }
1187
1188 void
1189 MingwBackend::GetModuleInstallTargetFiles (
1190 vector<FileLocation>& out ) const
1191 {
1192 for ( std::map<std::string, Module*>::const_iterator p = ProjectNode.modules.begin (); p != ProjectNode.modules.end (); ++ p )
1193 {
1194 const Module& module = *p->second;
1195 if ( !module.enabled )
1196 continue;
1197 if ( module.install )
1198 out.push_back ( *module.install );
1199 }
1200 }
1201
1202 void
1203 MingwBackend::GetInstallTargetFiles (
1204 vector<FileLocation>& out ) const
1205 {
1206 GetNonModuleInstallTargetFiles ( out );
1207 GetModuleInstallTargetFiles ( out );
1208 }
1209
1210 void
1211 MingwBackend::OutputInstallTarget ( const FileLocation& source,
1212 const FileLocation& target )
1213 {
1214 fprintf ( fMakefile,
1215 "%s: %s | %s\n",
1216 GetFullName ( target ).c_str (),
1217 GetFullName ( source ).c_str (),
1218 GetFullPath ( target ).c_str () );
1219 fprintf ( fMakefile,
1220 "\t$(ECHO_CP)\n" );
1221 fprintf ( fMakefile,
1222 "\t${cp} %s %s 1>$(NUL)\n",
1223 GetFullName ( source ).c_str (),
1224 GetFullName ( target ).c_str () );
1225 }
1226
1227 void
1228 MingwBackend::OutputNonModuleInstallTargets ()
1229 {
1230 for ( size_t i = 0; i < ProjectNode.installfiles.size (); i++ )
1231 {
1232 const InstallFile& installfile = *ProjectNode.installfiles[i];
1233 OutputInstallTarget ( *installfile.source, *installfile.target );
1234 }
1235 }
1236
1237 const Module&
1238 MingwBackend::GetAliasedModuleOrModule ( const Module& module ) const
1239 {
1240 if ( module.aliasedModuleName.size () > 0 )
1241 {
1242 const Module* aliasedModule = ProjectNode.LocateModule ( module.aliasedModuleName );
1243 assert ( aliasedModule );
1244 return *aliasedModule;
1245 }
1246 else
1247 return module;
1248 }
1249
1250 void
1251 MingwBackend::OutputModuleInstallTargets ()
1252 {
1253 for ( std::map<std::string, Module*>::const_iterator p = ProjectNode.modules.begin (); p != ProjectNode.modules.end (); ++ p )
1254 {
1255 const Module& module = *p->second;
1256 if ( !module.enabled )
1257 continue;
1258 if ( module.install )
1259 {
1260 const Module& aliasedModule = GetAliasedModuleOrModule ( module );
1261 OutputInstallTarget ( *aliasedModule.output, *module.install );
1262 }
1263 }
1264 }
1265
1266 string
1267 MingwBackend::GetRegistrySourceFiles ()
1268 {
1269 return "boot" + sSep + "bootdata" + sSep + "hivecls_" + Environment::GetArch() + ".inf "
1270 "boot" + sSep + "bootdata" + sSep + "hivedef_" + Environment::GetArch() + ".inf "
1271 "boot" + sSep + "bootdata" + sSep + "hiveinst_" + Environment::GetArch() + ".inf "
1272 "boot" + sSep + "bootdata" + sSep + "hivesft_" + Environment::GetArch() + ".inf "
1273 "boot" + sSep + "bootdata" + sSep + "hivesys_" + Environment::GetArch() + ".inf ";
1274 }
1275
1276 string
1277 MingwBackend::GetRegistryTargetFiles ()
1278 {
1279 string system32ConfigDirectory = "system32" + sSep + "config";
1280 FileLocation system32 ( InstallDirectory, system32ConfigDirectory, "" );
1281
1282 vector<FileLocation> registry_files;
1283 registry_files.push_back ( FileLocation ( InstallDirectory, system32ConfigDirectory, "default" ) );
1284 registry_files.push_back ( FileLocation ( InstallDirectory, system32ConfigDirectory, "sam" ) );
1285 registry_files.push_back ( FileLocation ( InstallDirectory, system32ConfigDirectory, "security" ) );
1286 registry_files.push_back ( FileLocation ( InstallDirectory, system32ConfigDirectory, "software" ) );
1287 registry_files.push_back ( FileLocation ( InstallDirectory, system32ConfigDirectory, "system" ) );
1288
1289 return v2s( this, registry_files, 6 );
1290 }
1291
1292 void
1293 MingwBackend::OutputRegistryInstallTarget ()
1294 {
1295 FileLocation system32 ( InstallDirectory, "system32" + sSep + "config", "" );
1296
1297 string registrySourceFiles = GetRegistrySourceFiles ();
1298 string registryTargetFiles = GetRegistryTargetFiles ();
1299 fprintf ( fMakefile,
1300 "install_registry: %s\n",
1301 registryTargetFiles.c_str () );
1302 fprintf ( fMakefile,
1303 "%s: %s %s $(mkhive_TARGET)\n",
1304 registryTargetFiles.c_str (),
1305 registrySourceFiles.c_str (),
1306 GetFullPath ( system32 ).c_str () );
1307 fprintf ( fMakefile,
1308 "\t$(ECHO_MKHIVE)\n" );
1309 fprintf ( fMakefile,
1310 "\t$(mkhive_TARGET) boot%cbootdata %s $(ARCH) boot%cbootdata%chiveinst_$(ARCH).inf\n",
1311 cSep, GetFullPath ( system32 ).c_str (),
1312 cSep, cSep );
1313 fprintf ( fMakefile,
1314 "\n" );
1315 }
1316
1317 void
1318 MingwBackend::GenerateInstallTarget ()
1319 {
1320 vector<FileLocation> vInstallTargetFiles;
1321 GetInstallTargetFiles ( vInstallTargetFiles );
1322 string installTargetFiles = v2s ( this, vInstallTargetFiles, 5 );
1323 string registryTargetFiles = GetRegistryTargetFiles ();
1324
1325 fprintf ( fMakefile,
1326 "install: %s %s\n",
1327 installTargetFiles.c_str (),
1328 registryTargetFiles.c_str () );
1329 OutputNonModuleInstallTargets ();
1330 OutputModuleInstallTargets ();
1331 OutputRegistryInstallTarget ();
1332 fprintf ( fMakefile,
1333 "\n" );
1334 }
1335
1336 void
1337 MingwBackend::GetModuleTestTargets (
1338 vector<string>& out ) const
1339 {
1340 for ( std::map<std::string, Module*>::const_iterator p = ProjectNode.modules.begin (); p != ProjectNode.modules.end (); ++ p )
1341 {
1342 const Module& module = *p->second;
1343 if ( !module.enabled )
1344 continue;
1345 if ( module.type == Test )
1346 out.push_back ( module.name );
1347 }
1348 }
1349
1350 void
1351 MingwBackend::GenerateTestTarget ()
1352 {
1353 vector<string> vTestTargets;
1354 GetModuleTestTargets ( vTestTargets );
1355 string testTargets = v2s ( vTestTargets, 5 );
1356
1357 fprintf ( fMakefile,
1358 "test: %s\n",
1359 testTargets.c_str () );
1360 fprintf ( fMakefile,
1361 "\n" );
1362 }
1363
1364 void
1365 MingwBackend::GenerateDirectoryTargets ()
1366 {
1367 intermediateDirectory->CreateRule ( fMakefile, "$(INTERMEDIATE)" );
1368 outputDirectory->CreateRule ( fMakefile, "$(OUTPUT)" );
1369 installDirectory->CreateRule ( fMakefile, "$(INSTALL)" );
1370 }