eb2e0a1bce13dc225f09f40b0c9f78673ce30dd8
[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.files.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.files.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 char buffer[81];
844
845 fp = popen ( versionCommand.c_str () , "r" );
846 for( i = 0;
847 ( i < 80 ) &&
848 ( feof ( fp ) == 0 &&
849 ( ( ch = fgetc( fp ) ) != -1 ) );
850 i++ )
851 {
852 buffer[i] = (char) ch;
853 }
854 buffer[i] = '\0';
855 pclose ( fp );
856
857 char separators[] = " ";
858 char *token;
859 char *prevtoken = NULL;
860
861 string version;
862
863 token = strtok ( buffer, separators );
864 while ( token != NULL )
865 {
866 prevtoken = token;
867 version = string( prevtoken );
868 if ( version.find('.') != std::string::npos )
869 break;
870 token = strtok ( NULL, separators );
871 }
872 return version;
873 }
874
875 string
876 MingwBackend::GetNetwideAssemblerVersion ( const string& nasmCommand )
877 {
878 string versionCommand;
879 if ( nasmCommand.find("yasm") != std::string::npos )
880 {
881 versionCommand = ssprintf ( "%s --version",
882 nasmCommand.c_str (),
883 NUL,
884 NUL );
885 }
886 else
887 {
888 versionCommand = ssprintf ( "%s -v",
889 nasmCommand.c_str (),
890 NUL,
891 NUL );
892 }
893 return GetVersionString( versionCommand );
894 }
895
896 string
897 MingwBackend::GetCompilerVersion ( const string& compilerCommand )
898 {
899 string versionCommand = ssprintf ( "%s --version gcc",
900 compilerCommand.c_str (),
901 NUL,
902 NUL );
903 return GetVersionString( versionCommand );
904 }
905
906 string
907 MingwBackend::GetBinutilsVersion ( const string& binutilsCommand )
908 {
909 string versionCommand = ssprintf ( "%s -v",
910 binutilsCommand.c_str (),
911 NUL,
912 NUL );
913 return GetVersionString( versionCommand );
914 }
915
916 bool
917 MingwBackend::IsSupportedCompilerVersion ( const string& compilerVersion )
918 {
919 if ( strcmp ( compilerVersion.c_str (), "3.4.2") < 0 )
920 return false;
921 else
922 return true;
923 }
924
925 bool
926 MingwBackend::TryToDetectThisBinutils ( const string& binutils )
927 {
928 string command = ssprintf (
929 "%s -v 1>%s",
930 FixSeparatorForSystemCommand(binutils).c_str (),
931 NUL,
932 NUL );
933 int exitcode = system ( command.c_str () );
934 return (exitcode == 0);
935 }
936
937 string
938 MingwBackend::GetBinutilsVersionDate ( const string& binutilsCommand )
939 {
940 FILE *fp;
941 int ch, i;
942 char buffer[81];
943
944 string versionCommand = ssprintf ( "%s -v",
945 binutilsCommand.c_str (),
946 NUL,
947 NUL );
948 fp = popen ( versionCommand.c_str () , "r" );
949 for( i = 0;
950 ( i < 80 ) &&
951 ( feof ( fp ) == 0 &&
952 ( ( ch = fgetc( fp ) ) != -1 ) );
953 i++ )
954 {
955 buffer[i] = (char) ch;
956 }
957 buffer[i] = '\0';
958 pclose ( fp );
959
960 char separators[] = " ";
961 char *token;
962 char *prevtoken = NULL;
963
964 token = strtok ( buffer, separators );
965 while ( token != NULL )
966 {
967 prevtoken = token;
968 token = strtok ( NULL, separators );
969 }
970 string version = string ( prevtoken );
971 int lastDigit = version.find_last_not_of ( "\t\r\n" );
972 if ( lastDigit != -1 )
973 return string ( version, 0, lastDigit+1 );
974 else
975 return version;
976 }
977
978 bool
979 MingwBackend::IsSupportedBinutilsVersion ( const string& binutilsVersion )
980 {
981 if ( manualBinutilsSetting ) return true;
982
983 /* linux */
984 if ( binutilsVersion.find('.') != std::string::npos )
985 {
986 /* TODO: blacklist versions on version number instead of date */
987 return true;
988 }
989
990 /*
991 * - Binutils older than 2003/10/01 have broken windres which can't handle
992 * icons with alpha channel.
993 * - Binutils between 2004/09/02 and 2004/10/08 have broken handling of
994 * forward exports in dlltool.
995 */
996 if ( ( ( strcmp ( binutilsVersion.c_str (), "20040902") >= 0 ) &&
997 ( strcmp ( binutilsVersion.c_str (), "20041008") <= 0 ) ) ||
998 ( strcmp ( binutilsVersion.c_str (), "20031001") < 0 ) )
999 return false;
1000 else
1001 return true;
1002 }
1003
1004 void
1005 MingwBackend::DetectBinutils ()
1006 {
1007 printf ( "Detecting binutils..." );
1008
1009 bool detectedBinutils = false;
1010 const string& ROS_PREFIXValue = Environment::GetVariable ( "ROS_PREFIX" );
1011
1012 if ( ROS_PREFIXValue.length () > 0 )
1013 {
1014 binutilsPrefix = ROS_PREFIXValue;
1015 binutilsCommand = binutilsPrefix + "-ld";
1016 manualBinutilsSetting = true;
1017 detectedBinutils = true;
1018 }
1019 #if defined(WIN32)
1020 if ( !detectedBinutils )
1021 {
1022 binutilsPrefix = "";
1023 binutilsCommand = "ld";
1024 detectedBinutils = TryToDetectThisBinutils ( binutilsCommand );
1025 }
1026 #endif
1027 if ( !detectedBinutils )
1028 {
1029 binutilsPrefix = "mingw32";
1030 binutilsCommand = binutilsPrefix + "-ld";
1031 detectedBinutils = TryToDetectThisBinutils ( binutilsCommand );
1032 }
1033 if ( detectedBinutils )
1034 {
1035 string binutilsVersion = GetBinutilsVersionDate ( binutilsCommand );
1036 if ( IsSupportedBinutilsVersion ( binutilsVersion ) )
1037 printf ( "detected (%s %s)\n", binutilsCommand.c_str (), GetBinutilsVersion( binutilsCommand ).c_str() );
1038 else
1039 {
1040 printf ( "detected (%s), but with unsupported version (%s)\n",
1041 binutilsCommand.c_str (),
1042 binutilsVersion.c_str () );
1043 throw UnsupportedBuildToolException ( binutilsCommand, binutilsVersion );
1044 }
1045 }
1046 else
1047 printf ( "not detected\n" );
1048
1049 }
1050
1051 void
1052 MingwBackend::DetectNetwideAssembler ()
1053 {
1054 printf ( "Detecting netwide assembler..." );
1055
1056 nasmCommand = "nasm";
1057 bool detectedNasm = TryToDetectThisNetwideAssembler ( nasmCommand );
1058 #if defined(WIN32)
1059 if ( !detectedNasm )
1060 {
1061 nasmCommand = "nasmw";
1062 detectedNasm = TryToDetectThisNetwideAssembler ( nasmCommand );
1063 }
1064 #endif
1065 if ( !detectedNasm )
1066 {
1067 nasmCommand = "yasm";
1068 detectedNasm = TryToDetectThisNetwideAssembler ( nasmCommand );
1069 }
1070 if ( detectedNasm )
1071 printf ( "detected (%s %s)\n", nasmCommand.c_str (), GetNetwideAssemblerVersion( nasmCommand ).c_str() );
1072 else
1073 printf ( "not detected\n" );
1074 }
1075
1076 void
1077 MingwBackend::DetectPipeSupport ()
1078 {
1079 printf ( "Detecting compiler -pipe support..." );
1080
1081 string pipe_detection = "tools" + sSep + "rbuild" + sSep + "backend" + sSep + "mingw" + sSep + "pipe_detection.c";
1082 string pipe_detectionObjectFilename = ReplaceExtension ( pipe_detection,
1083 ".o" );
1084 string command = ssprintf (
1085 "%s -pipe -c %s -o %s 1>%s 2>%s",
1086 FixSeparatorForSystemCommand(compilerCommand).c_str (),
1087 pipe_detection.c_str (),
1088 pipe_detectionObjectFilename.c_str (),
1089 NUL,
1090 NUL );
1091 int exitcode = system ( command.c_str () );
1092 FILE* f = fopen ( pipe_detectionObjectFilename.c_str (), "rb" );
1093 if ( f )
1094 {
1095 usePipe = (exitcode == 0);
1096 fclose ( f );
1097 unlink ( pipe_detectionObjectFilename.c_str () );
1098 }
1099 else
1100 usePipe = false;
1101
1102 if ( usePipe )
1103 printf ( "detected\n" );
1104 else
1105 printf ( "not detected\n" );
1106 }
1107
1108 void
1109 MingwBackend::DetectPCHSupport ()
1110 {
1111 printf ( "Detecting compiler pre-compiled header support..." );
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
1139 void
1140 MingwBackend::GetNonModuleInstallTargetFiles (
1141 vector<FileLocation>& out ) const
1142 {
1143 for ( size_t i = 0; i < ProjectNode.installfiles.size (); i++ )
1144 {
1145 const InstallFile& installfile = *ProjectNode.installfiles[i];
1146 out.push_back ( *installfile.target );
1147 }
1148 }
1149
1150 void
1151 MingwBackend::GetModuleInstallTargetFiles (
1152 vector<FileLocation>& out ) const
1153 {
1154 for ( size_t i = 0; i < ProjectNode.modules.size (); i++ )
1155 {
1156 const Module& module = *ProjectNode.modules[i];
1157 if ( !module.enabled )
1158 continue;
1159 if ( module.install )
1160 out.push_back ( *module.install );
1161 }
1162 }
1163
1164 void
1165 MingwBackend::GetInstallTargetFiles (
1166 vector<FileLocation>& out ) const
1167 {
1168 GetNonModuleInstallTargetFiles ( out );
1169 GetModuleInstallTargetFiles ( out );
1170 }
1171
1172 void
1173 MingwBackend::OutputInstallTarget ( const FileLocation& source,
1174 const FileLocation& target )
1175 {
1176 fprintf ( fMakefile,
1177 "%s: %s | %s\n",
1178 GetFullName ( target ).c_str (),
1179 GetFullName ( source ).c_str (),
1180 GetFullPath ( target ).c_str () );
1181 fprintf ( fMakefile,
1182 "\t$(ECHO_CP)\n" );
1183 fprintf ( fMakefile,
1184 "\t${cp} %s %s 1>$(NUL)\n",
1185 GetFullName ( source ).c_str (),
1186 GetFullName ( target ).c_str () );
1187 }
1188
1189 void
1190 MingwBackend::OutputNonModuleInstallTargets ()
1191 {
1192 for ( size_t i = 0; i < ProjectNode.installfiles.size (); i++ )
1193 {
1194 const InstallFile& installfile = *ProjectNode.installfiles[i];
1195 OutputInstallTarget ( *installfile.source, *installfile.target );
1196 }
1197 }
1198
1199 const Module&
1200 MingwBackend::GetAliasedModuleOrModule ( const Module& module ) const
1201 {
1202 if ( module.aliasedModuleName.size () > 0 )
1203 {
1204 const Module* aliasedModule = ProjectNode.LocateModule ( module.aliasedModuleName );
1205 assert ( aliasedModule );
1206 return *aliasedModule;
1207 }
1208 else
1209 return module;
1210 }
1211
1212 void
1213 MingwBackend::OutputModuleInstallTargets ()
1214 {
1215 for ( size_t i = 0; i < ProjectNode.modules.size (); i++ )
1216 {
1217 const Module& module = *ProjectNode.modules[i];
1218 if ( !module.enabled )
1219 continue;
1220 if ( module.install )
1221 {
1222 const Module& aliasedModule = GetAliasedModuleOrModule ( module );
1223 OutputInstallTarget ( *aliasedModule.output, *module.install );
1224 }
1225 }
1226 }
1227
1228 string
1229 MingwBackend::GetRegistrySourceFiles ()
1230 {
1231 return "boot" + sSep + "bootdata" + sSep + "hivecls.inf "
1232 "boot" + sSep + "bootdata" + sSep + "hivedef.inf "
1233 "boot" + sSep + "bootdata" + sSep + "hiveinst.inf "
1234 "boot" + sSep + "bootdata" + sSep + "hivesft.inf "
1235 "boot" + sSep + "bootdata" + sSep + "hivesys.inf";
1236 }
1237
1238 string
1239 MingwBackend::GetRegistryTargetFiles ()
1240 {
1241 string system32ConfigDirectory = "system32" + sSep + "config";
1242 FileLocation system32 ( InstallDirectory, system32ConfigDirectory, "" );
1243
1244 vector<FileLocation> registry_files;
1245 registry_files.push_back ( FileLocation ( InstallDirectory, system32ConfigDirectory, "default" ) );
1246 registry_files.push_back ( FileLocation ( InstallDirectory, system32ConfigDirectory, "sam" ) );
1247 registry_files.push_back ( FileLocation ( InstallDirectory, system32ConfigDirectory, "security" ) );
1248 registry_files.push_back ( FileLocation ( InstallDirectory, system32ConfigDirectory, "software" ) );
1249 registry_files.push_back ( FileLocation ( InstallDirectory, system32ConfigDirectory, "system" ) );
1250
1251 return v2s( this, registry_files, 6 );
1252 }
1253
1254 void
1255 MingwBackend::OutputRegistryInstallTarget ()
1256 {
1257 FileLocation system32 ( InstallDirectory, "system32" + sSep + "config", "" );
1258
1259 string registrySourceFiles = GetRegistrySourceFiles ();
1260 string registryTargetFiles = GetRegistryTargetFiles ();
1261 fprintf ( fMakefile,
1262 "install_registry: %s\n",
1263 registryTargetFiles.c_str () );
1264 fprintf ( fMakefile,
1265 "%s: %s %s $(MKHIVE_TARGET)\n",
1266 registryTargetFiles.c_str (),
1267 registrySourceFiles.c_str (),
1268 GetFullPath ( system32 ).c_str () );
1269 fprintf ( fMakefile,
1270 "\t$(ECHO_MKHIVE)\n" );
1271 fprintf ( fMakefile,
1272 "\t$(MKHIVE_TARGET) boot%cbootdata %s boot%cbootdata%chiveinst.inf\n",
1273 cSep, GetFullPath ( system32 ).c_str (),
1274 cSep, cSep );
1275 fprintf ( fMakefile,
1276 "\n" );
1277 }
1278
1279 void
1280 MingwBackend::GenerateInstallTarget ()
1281 {
1282 vector<FileLocation> vInstallTargetFiles;
1283 GetInstallTargetFiles ( vInstallTargetFiles );
1284 string installTargetFiles = v2s ( this, vInstallTargetFiles, 5 );
1285 string registryTargetFiles = GetRegistryTargetFiles ();
1286
1287 fprintf ( fMakefile,
1288 "install: %s %s\n",
1289 installTargetFiles.c_str (),
1290 registryTargetFiles.c_str () );
1291 OutputNonModuleInstallTargets ();
1292 OutputModuleInstallTargets ();
1293 OutputRegistryInstallTarget ();
1294 fprintf ( fMakefile,
1295 "\n" );
1296 }
1297
1298 void
1299 MingwBackend::GetModuleTestTargets (
1300 vector<string>& out ) const
1301 {
1302 for ( size_t i = 0; i < ProjectNode.modules.size (); i++ )
1303 {
1304 const Module& module = *ProjectNode.modules[i];
1305 if ( !module.enabled )
1306 continue;
1307 if ( module.type == Test )
1308 out.push_back ( module.name );
1309 }
1310 }
1311
1312 void
1313 MingwBackend::GenerateTestTarget ()
1314 {
1315 vector<string> vTestTargets;
1316 GetModuleTestTargets ( vTestTargets );
1317 string testTargets = v2s ( vTestTargets, 5 );
1318
1319 fprintf ( fMakefile,
1320 "test: %s\n",
1321 testTargets.c_str () );
1322 fprintf ( fMakefile,
1323 "\n" );
1324 }
1325
1326 void
1327 MingwBackend::GenerateDirectoryTargets ()
1328 {
1329 intermediateDirectory->CreateRule ( fMakefile, "$(INTERMEDIATE)" );
1330 outputDirectory->CreateRule ( fMakefile, "$(OUTPUT)" );
1331 installDirectory->CreateRule ( fMakefile, "$(INSTALL)" );
1332 }