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