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