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