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