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