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