rename variable and change parameters in call to find_last_not_of() to hopefully...
[reactos.git] / reactos / tools / rbuild / backend / mingw / mingw.cpp
1 /*
2 * Copyright (C) 2005 Casper S. Hornstrup
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18 #include "../../pch.h"
19
20 #include "mingw.h"
21 #include <assert.h>
22 #ifdef _MSC_VER
23 #define popen _popen
24 #define pclose _pclose
25 #else
26 #include <dirent.h>
27 #endif//_MSC_VER
28 #include "modulehandler.h"
29
30 #ifdef WIN32
31 #define MKDIR(s) mkdir(s)
32 #else
33 #define MKDIR(s) mkdir(s, 0755)
34 #endif
35
36 using std::string;
37 using std::vector;
38 using std::set;
39 using std::map;
40
41 typedef set<string> set_string;
42
43
44 string
45 v2s ( const string_list& v, int wrap_at )
46 {
47 if ( !v.size() )
48 return "";
49 string s;
50 int wrap_count = 0;
51 for ( size_t i = 0; i < v.size(); i++ )
52 {
53 if ( !v[i].size() )
54 continue;
55 if ( wrap_at > 0 && wrap_count++ == wrap_at )
56 s += " \\\n\t\t";
57 else if ( s.size() )
58 s += " ";
59 s += v[i];
60 }
61 return s;
62 }
63
64
65 Directory::Directory ( const string& name_ )
66 : name(name_)
67 {
68 }
69
70 void
71 Directory::Add ( const char* subdir )
72 {
73 size_t i;
74 string s1 = string ( subdir );
75 if ( ( i = s1.find ( '$' ) ) != string::npos )
76 {
77 throw InvalidOperationException ( __FILE__,
78 __LINE__,
79 "No environment variables can be used here. Path was %s",
80 subdir );
81 }
82
83 const char* p = strpbrk ( subdir, "/\\" );
84 if ( !p )
85 p = subdir + strlen(subdir);
86 string s ( subdir, p-subdir );
87 if ( subdirs.find(s) == subdirs.end() )
88 subdirs[s] = new Directory(s);
89 if ( *p && *++p )
90 subdirs[s]->Add ( p );
91 }
92
93 bool
94 Directory::mkdir_p ( const char* path )
95 {
96 #ifndef _MSC_VER
97 DIR *directory;
98 directory = opendir ( path );
99 if ( directory != NULL )
100 {
101 closedir ( directory );
102 return false;
103 }
104 #endif//_MSC_VER
105
106 if ( MKDIR ( path ) != 0 )
107 {
108 #ifdef _MSC_VER
109 if ( errno == EEXIST )
110 return false;
111 #endif//_MSC_VER
112 throw AccessDeniedException ( string ( path ) );
113 }
114 return true;
115 }
116
117 bool
118 Directory::CreateDirectory ( string path )
119 {
120 size_t index = 0;
121 size_t nextIndex;
122 if ( isalpha ( path[0] ) && path[1] == ':' && path[2] == CSEP )
123 {
124 nextIndex = path.find ( CSEP, 3);
125 }
126 else
127 nextIndex = path.find ( CSEP );
128
129 bool directoryWasCreated = false;
130 while ( nextIndex != string::npos )
131 {
132 nextIndex = path.find ( CSEP, index + 1 );
133 directoryWasCreated = mkdir_p ( path.substr ( 0, nextIndex ).c_str () );
134 index = nextIndex;
135 }
136 return directoryWasCreated;
137 }
138
139 string
140 Directory::ReplaceVariable ( string name,
141 string value,
142 string path )
143 {
144 size_t i = path.find ( name );
145 if ( i != string::npos )
146 return path.replace ( i, name.length (), value );
147 else
148 return path;
149 }
150
151 void
152 Directory::ResolveVariablesInPath ( char* buf,
153 string path )
154 {
155 string s = ReplaceVariable ( "$(INTERMEDIATE)", Environment::GetIntermediatePath (), path );
156 s = ReplaceVariable ( "$(OUTPUT)", Environment::GetOutputPath (), s );
157 s = ReplaceVariable ( "$(INSTALL)", Environment::GetInstallPath (), s );
158 strcpy ( buf, s.c_str () );
159 }
160
161 void
162 Directory::GenerateTree ( const string& parent,
163 bool verbose )
164 {
165 string path;
166
167 if ( parent.size () > 0 )
168 {
169 char buf[256];
170
171 path = parent + SSEP + name;
172 ResolveVariablesInPath ( buf, path );
173 if ( CreateDirectory ( buf ) && verbose )
174 printf ( "Created %s\n", buf );
175 }
176 else
177 path = name;
178
179 for ( directory_map::iterator i = subdirs.begin ();
180 i != subdirs.end ();
181 ++i )
182 {
183 i->second->GenerateTree ( path, verbose );
184 }
185 }
186
187 string
188 Directory::EscapeSpaces ( string path )
189 {
190 string newpath;
191 char* p = &path[0];
192 while ( *p != 0 )
193 {
194 if ( *p == ' ' )
195 newpath = newpath + "\\ ";
196 else
197 newpath = newpath + *p;
198 *p++;
199 }
200 return newpath;
201 }
202
203 void
204 Directory::CreateRule ( FILE* f,
205 const string& parent )
206 {
207 string path;
208
209 if ( parent.size() > 0 )
210 {
211 string escapedParent = EscapeSpaces ( parent );
212 fprintf ( f,
213 "%s%c%s: | %s\n",
214 escapedParent.c_str (),
215 CSEP,
216 EscapeSpaces ( name ).c_str (),
217 escapedParent.c_str () );
218
219 fprintf ( f,
220 "\t$(ECHO_MKDIR)\n" );
221
222 fprintf ( f,
223 "\t${mkdir} $@\n" );
224
225 path = parent + SSEP + name;
226 }
227 else
228 path = name;
229
230 for ( directory_map::iterator i = subdirs.begin();
231 i != subdirs.end();
232 ++i )
233 {
234 i->second->CreateRule ( f, path );
235 }
236 }
237
238
239 static class MingwFactory : public Backend::Factory
240 {
241 public:
242 MingwFactory() : Factory ( "mingw" ) {}
243 Backend* operator() ( Project& project,
244 Configuration& configuration )
245 {
246 return new MingwBackend ( project,
247 configuration );
248 }
249 } factory;
250
251
252 MingwBackend::MingwBackend ( Project& project,
253 Configuration& configuration )
254 : Backend ( project, configuration ),
255 intermediateDirectory ( new Directory ("$(INTERMEDIATE)" ) ),
256 outputDirectory ( new Directory ( "$(OUTPUT)" ) ),
257 installDirectory ( new Directory ( "$(INSTALL)" ) )
258 {
259 compilerPrefix = "";
260 }
261
262 MingwBackend::~MingwBackend()
263 {
264 delete intermediateDirectory;
265 delete outputDirectory;
266 delete installDirectory;
267 }
268
269 string
270 MingwBackend::AddDirectoryTarget ( const string& directory,
271 Directory* directoryTree )
272 {
273 if ( directory.length () > 0)
274 directoryTree->Add ( directory.c_str() );
275 return directoryTree->name;
276 }
277
278 void
279 MingwBackend::ProcessModules ()
280 {
281 printf ( "Processing modules..." );
282
283 vector<MingwModuleHandler*> v;
284 size_t i;
285 for ( i = 0; i < ProjectNode.modules.size (); i++ )
286 {
287 Module& module = *ProjectNode.modules[i];
288 if ( !module.enabled )
289 continue;
290 MingwModuleHandler* h = MingwModuleHandler::InstanciateHandler (
291 module,
292 this );
293 if ( module.host == HostDefault )
294 {
295 module.host = h->DefaultHost();
296 assert ( module.host != HostDefault );
297 }
298 v.push_back ( h );
299 }
300
301 size_t iend = v.size ();
302
303 for ( i = 0; i < iend; i++ )
304 v[i]->GenerateObjectMacro();
305 fprintf ( fMakefile, "\n" );
306 for ( i = 0; i < iend; i++ )
307 v[i]->GenerateTargetMacro();
308 fprintf ( fMakefile, "\n" );
309
310 GenerateAllTarget ( v );
311 GenerateInitTarget ();
312 GenerateRegTestsRunTarget ();
313
314 for ( i = 0; i < iend; i++ )
315 v[i]->GenerateOtherMacros();
316
317 for ( i = 0; i < iend; i++ )
318 {
319 MingwModuleHandler& h = *v[i];
320 h.GeneratePreconditionDependencies ();
321 h.Process ();
322 h.GenerateInvocations ();
323 h.GenerateCleanTarget ();
324 h.GenerateInstallTarget ();
325 h.GenerateDependsTarget ();
326 delete v[i];
327 }
328
329 printf ( "done\n" );
330 }
331
332 void
333 MingwBackend::Process ()
334 {
335 if ( configuration.CheckDependenciesForModuleOnly )
336 CheckAutomaticDependenciesForModuleOnly ();
337 else
338 ProcessNormal ();
339 }
340
341 void
342 MingwBackend::CheckAutomaticDependenciesForModuleOnly ()
343 {
344 if ( configuration.AutomaticDependencies )
345 {
346 Module* module = ProjectNode.LocateModule ( configuration.CheckDependenciesForModuleOnlyModule );
347 if ( module == NULL )
348 {
349 printf ( "Module '%s' does not exist\n",
350 configuration.CheckDependenciesForModuleOnlyModule.c_str () );
351 return;
352 }
353
354 printf ( "Checking automatic dependencies for module '%s'...",
355 module->name.c_str () );
356 AutomaticDependency automaticDependency ( ProjectNode );
357 automaticDependency.CheckAutomaticDependenciesForModule ( *module,
358 configuration.Verbose );
359 printf ( "done\n" );
360 }
361 }
362
363 void
364 MingwBackend::ProcessNormal ()
365 {
366 DetectCompiler ();
367 DetectBinutils ();
368 DetectNetwideAssembler ();
369 DetectPipeSupport ();
370 DetectPCHSupport ();
371 CreateMakefile ();
372 GenerateHeader ();
373 GenerateGlobalVariables ();
374 GenerateXmlBuildFilesMacro ();
375 ProcessModules ();
376 GenerateInstallTarget ();
377 GenerateTestTarget ();
378 GenerateDirectoryTargets ();
379 GenerateDirectories ();
380 UnpackWineResources ();
381 GenerateTestSupportCode ();
382 GenerateProxyMakefiles ();
383 CheckAutomaticDependencies ();
384 CloseMakefile ();
385 }
386
387 void
388 MingwBackend::CreateMakefile ()
389 {
390 fMakefile = fopen ( ProjectNode.makefile.c_str (), "w" );
391 if ( !fMakefile )
392 throw AccessDeniedException ( ProjectNode.makefile );
393 MingwModuleHandler::SetBackend ( this );
394 MingwModuleHandler::SetMakefile ( fMakefile );
395 MingwModuleHandler::SetUsePch ( use_pch );
396 }
397
398 void
399 MingwBackend::CloseMakefile () const
400 {
401 if (fMakefile)
402 fclose ( fMakefile );
403 }
404
405 void
406 MingwBackend::GenerateHeader () const
407 {
408 fprintf ( fMakefile, "# THIS FILE IS AUTOMATICALLY GENERATED, EDIT 'ReactOS.xml' INSTEAD\n\n" );
409 }
410
411 string
412 MingwBackend::GenerateIncludesAndDefines ( IfableData& data ) const
413 {
414 string includeParameters = MingwModuleHandler::GenerateGccIncludeParametersFromVector ( data.includes );
415 string defineParameters = MingwModuleHandler::GenerateGccDefineParametersFromVector ( data.defines );
416 return includeParameters + " " + defineParameters;
417 }
418
419 void
420 MingwBackend::GenerateProjectCFlagsMacro ( const char* assignmentOperation,
421 IfableData& data ) const
422 {
423 fprintf (
424 fMakefile,
425 "PROJECT_CFLAGS %s",
426 assignmentOperation );
427
428 fprintf ( fMakefile,
429 " %s",
430 GenerateIncludesAndDefines ( data ).c_str() );
431
432 fprintf ( fMakefile, "\n" );
433 }
434
435 void
436 MingwBackend::GenerateGlobalCFlagsAndProperties (
437 const char* assignmentOperation,
438 IfableData& data ) const
439 {
440 size_t i;
441
442 for ( i = 0; i < data.properties.size(); i++ )
443 {
444 Property& prop = *data.properties[i];
445 fprintf ( fMakefile, "%s := %s\n",
446 prop.name.c_str(),
447 prop.value.c_str() );
448 }
449
450 if ( data.includes.size() || data.defines.size() )
451 {
452 GenerateProjectCFlagsMacro ( assignmentOperation,
453 data );
454 }
455
456 for ( i = 0; i < data.ifs.size(); i++ )
457 {
458 If& rIf = *data.ifs[i];
459 if ( rIf.data.defines.size()
460 || rIf.data.includes.size()
461 || rIf.data.ifs.size() )
462 {
463 fprintf (
464 fMakefile,
465 "ifeq (\"$(%s)\",\"%s\")\n",
466 rIf.property.c_str(),
467 rIf.value.c_str() );
468 GenerateGlobalCFlagsAndProperties (
469 "+=",
470 rIf.data );
471 fprintf (
472 fMakefile,
473 "endif\n\n" );
474 }
475 }
476 }
477
478 void
479 MingwBackend::GenerateProjectGccOptionsMacro ( const char* assignmentOperation,
480 IfableData& data ) const
481 {
482 size_t i;
483
484 fprintf (
485 fMakefile,
486 "PROJECT_GCCOPTIONS %s",
487 assignmentOperation );
488
489 for ( i = 0; i < data.compilerFlags.size(); i++ )
490 {
491 fprintf (
492 fMakefile,
493 " %s",
494 data.compilerFlags[i]->flag.c_str() );
495 }
496
497 fprintf ( fMakefile, "\n" );
498 }
499
500 void
501 MingwBackend::GenerateProjectGccOptions (
502 const char* assignmentOperation,
503 IfableData& data ) const
504 {
505 size_t i;
506
507 if ( data.compilerFlags.size() )
508 {
509 GenerateProjectGccOptionsMacro ( assignmentOperation,
510 data );
511 }
512
513 for ( i = 0; i < data.ifs.size(); i++ )
514 {
515 If& rIf = *data.ifs[i];
516 if ( rIf.data.compilerFlags.size()
517 || rIf.data.ifs.size() )
518 {
519 fprintf (
520 fMakefile,
521 "ifeq (\"$(%s)\",\"%s\")\n",
522 rIf.property.c_str(),
523 rIf.value.c_str() );
524 GenerateProjectGccOptions (
525 "+=",
526 rIf.data );
527 fprintf (
528 fMakefile,
529 "endif\n\n" );
530 }
531 }
532 }
533
534 string
535 MingwBackend::GenerateProjectLFLAGS () const
536 {
537 string lflags;
538 for ( size_t i = 0; i < ProjectNode.linkerFlags.size (); i++ )
539 {
540 LinkerFlag& linkerFlag = *ProjectNode.linkerFlags[i];
541 if ( lflags.length () > 0 )
542 lflags += " ";
543 lflags += linkerFlag.flag;
544 }
545 return lflags;
546 }
547
548 void
549 MingwBackend::GenerateGlobalVariables () const
550 {
551 fprintf ( fMakefile,
552 "PREFIX := %s\n",
553 compilerPrefix.c_str () );
554 fprintf ( fMakefile,
555 "nasm := %s\n",
556 nasmCommand.c_str () );
557
558 GenerateGlobalCFlagsAndProperties ( "=", ProjectNode.non_if_data );
559 GenerateProjectGccOptions ( "=", ProjectNode.non_if_data );
560
561 fprintf ( fMakefile, "PROJECT_RCFLAGS := $(PROJECT_CFLAGS)\n" );
562 fprintf ( fMakefile, "PROJECT_WIDLFLAGS := $(PROJECT_CFLAGS)\n" );
563 fprintf ( fMakefile, "PROJECT_LFLAGS := %s\n",
564 GenerateProjectLFLAGS ().c_str () );
565 fprintf ( fMakefile, "PROJECT_CFLAGS += -Wall\n" );
566 fprintf ( fMakefile, "PROJECT_CFLAGS += $(PROJECT_GCCOPTIONS)\n" );
567 fprintf ( fMakefile, "\n" );
568 }
569
570 bool
571 MingwBackend::IncludeInAllTarget ( const Module& module ) const
572 {
573 if ( MingwModuleHandler::ReferenceObjects ( module ) )
574 return false;
575 if ( module.type == BootSector )
576 return false;
577 if ( module.type == Iso )
578 return false;
579 if ( module.type == LiveIso )
580 return false;
581 if ( module.type == Test )
582 return false;
583 if ( module.type == Alias )
584 return false;
585 return true;
586 }
587
588 void
589 MingwBackend::GenerateAllTarget ( const vector<MingwModuleHandler*>& handlers ) const
590 {
591 fprintf ( fMakefile, "all:" );
592 int wrap_count = 0;
593 size_t iend = handlers.size ();
594 for ( size_t i = 0; i < iend; i++ )
595 {
596 const Module& module = handlers[i]->module;
597 if ( IncludeInAllTarget ( module ) )
598 {
599 if ( wrap_count++ == 5 )
600 fprintf ( fMakefile, " \\\n\t\t" ), wrap_count = 0;
601 fprintf ( fMakefile,
602 " %s",
603 GetTargetMacro(module).c_str () );
604 }
605 }
606 fprintf ( fMakefile, "\n\t\n\n" );
607 }
608
609 string
610 MingwBackend::GetBuildToolDependencies () const
611 {
612 string dependencies;
613 for ( size_t i = 0; i < ProjectNode.modules.size (); i++ )
614 {
615 Module& module = *ProjectNode.modules[i];
616 if ( !module.enabled )
617 continue;
618 if ( module.type == BuildTool )
619 {
620 if ( dependencies.length () > 0 )
621 dependencies += " ";
622 dependencies += module.GetDependencyPath ();
623 }
624 }
625 return dependencies;
626 }
627
628 void
629 MingwBackend::GenerateInitTarget () const
630 {
631 fprintf ( fMakefile,
632 "INIT = %s\n",
633 GetBuildToolDependencies ().c_str () );
634 fprintf ( fMakefile, "\n" );
635 }
636
637 void
638 MingwBackend::GenerateRegTestsRunTarget () const
639 {
640 fprintf ( fMakefile,
641 "REGTESTS_RUN_TARGET = regtests.dll\n" );
642 fprintf ( fMakefile,
643 "$(REGTESTS_RUN_TARGET): $(REGTESTS_TARGET)\n" );
644 fprintf ( fMakefile,
645 "\t$(cp) $(REGTESTS_TARGET) $(REGTESTS_RUN_TARGET)\n" );
646 fprintf ( fMakefile, "\n" );
647 }
648
649 void
650 MingwBackend::GenerateXmlBuildFilesMacro() const
651 {
652 fprintf ( fMakefile,
653 "XMLBUILDFILES = %s \\\n",
654 ProjectNode.GetProjectFilename ().c_str () );
655 string xmlbuildFilenames;
656 int numberOfExistingFiles = 0;
657 for ( size_t i = 0; i < ProjectNode.xmlbuildfiles.size (); i++ )
658 {
659 XMLInclude& xmlbuildfile = *ProjectNode.xmlbuildfiles[i];
660 if ( !xmlbuildfile.fileExists )
661 continue;
662 numberOfExistingFiles++;
663 if ( xmlbuildFilenames.length () > 0 )
664 xmlbuildFilenames += " ";
665 xmlbuildFilenames += NormalizeFilename ( xmlbuildfile.topIncludeFilename );
666 if ( numberOfExistingFiles % 5 == 4 || i == ProjectNode.xmlbuildfiles.size () - 1 )
667 {
668 fprintf ( fMakefile,
669 "\t%s",
670 xmlbuildFilenames.c_str ());
671 if ( i == ProjectNode.xmlbuildfiles.size () - 1 )
672 {
673 fprintf ( fMakefile, "\n" );
674 }
675 else
676 {
677 fprintf ( fMakefile,
678 " \\\n" );
679 }
680 xmlbuildFilenames.resize ( 0 );
681 }
682 numberOfExistingFiles++;
683 }
684 fprintf ( fMakefile, "\n" );
685 }
686
687 string
688 MingwBackend::GetBin2ResExecutable ()
689 {
690 return NormalizeFilename ( Environment::GetOutputPath () + SSEP + "tools/bin2res/bin2res" + EXEPOSTFIX );
691 }
692
693 void
694 MingwBackend::UnpackWineResources ()
695 {
696 printf ( "Unpacking WINE resources..." );
697 WineResource wineResource ( ProjectNode,
698 GetBin2ResExecutable () );
699 wineResource.UnpackResources ( configuration.Verbose );
700 printf ( "done\n" );
701 }
702
703 void
704 MingwBackend::GenerateTestSupportCode ()
705 {
706 printf ( "Generating test support code..." );
707 TestSupportCode testSupportCode ( ProjectNode );
708 testSupportCode.GenerateTestSupportCode ( configuration.Verbose );
709 printf ( "done\n" );
710 }
711
712 string
713 MingwBackend::GetProxyMakefileTree () const
714 {
715 if ( configuration.GenerateProxyMakefilesInSourceTree )
716 return "";
717 else
718 return Environment::GetOutputPath ();
719 }
720
721 void
722 MingwBackend::GenerateProxyMakefiles ()
723 {
724 printf ( "Generating proxy makefiles..." );
725 ProxyMakefile proxyMakefile ( ProjectNode );
726 proxyMakefile.GenerateProxyMakefiles ( configuration.Verbose,
727 GetProxyMakefileTree () );
728 printf ( "done\n" );
729 }
730
731 void
732 MingwBackend::CheckAutomaticDependencies ()
733 {
734 if ( configuration.AutomaticDependencies )
735 {
736 printf ( "Checking automatic dependencies..." );
737 AutomaticDependency automaticDependency ( ProjectNode );
738 automaticDependency.CheckAutomaticDependencies ( configuration.Verbose );
739 printf ( "done\n" );
740 }
741 }
742
743 bool
744 MingwBackend::IncludeDirectoryTarget ( const string& directory ) const
745 {
746 if ( directory == "$(INTERMEDIATE)" SSEP "tools")
747 return false;
748 else
749 return true;
750 }
751
752 void
753 MingwBackend::GenerateDirectories ()
754 {
755 printf ( "Creating directories..." );
756 intermediateDirectory->GenerateTree ( "", configuration.Verbose );
757 outputDirectory->GenerateTree ( "", configuration.Verbose );
758 if ( !configuration.MakeHandlesInstallDirectories )
759 installDirectory->GenerateTree ( "", configuration.Verbose );
760 printf ( "done\n" );
761 }
762
763 bool
764 MingwBackend::TryToDetectThisCompiler ( const string& compiler )
765 {
766 string command = ssprintf (
767 "%s -v 1>%s 2>%s",
768 compiler.c_str (),
769 NUL,
770 NUL );
771 int exitcode = system ( command.c_str () );
772 return (exitcode == 0);
773 }
774
775 void
776 MingwBackend::DetectCompiler ()
777 {
778 printf ( "Detecting compiler..." );
779
780 bool detectedCompiler = false;
781 const string& ROS_PREFIXValue = Environment::GetVariable ( "ROS_PREFIX" );
782 if ( ROS_PREFIXValue.length () > 0 )
783 {
784 compilerPrefix = ROS_PREFIXValue;
785 compilerCommand = compilerPrefix + "-gcc";
786 detectedCompiler = TryToDetectThisCompiler ( compilerCommand );
787 }
788 #if defined(WIN32)
789 if ( !detectedCompiler )
790 {
791 compilerPrefix = "";
792 compilerCommand = "gcc";
793 detectedCompiler = TryToDetectThisCompiler ( compilerCommand );
794 }
795 #endif
796 if ( !detectedCompiler )
797 {
798 compilerPrefix = "mingw32";
799 compilerCommand = compilerPrefix + "-gcc";
800 detectedCompiler = TryToDetectThisCompiler ( compilerCommand );
801 }
802 if ( detectedCompiler )
803 printf ( "detected (%s)\n", compilerCommand.c_str () );
804 else
805 printf ( "not detected\n" );
806 }
807
808 bool
809 MingwBackend::TryToDetectThisNetwideAssembler ( const string& assembler )
810 {
811 string command = ssprintf (
812 "%s -h 1>%s 2>%s",
813 assembler.c_str (),
814 NUL,
815 NUL );
816 int exitcode = system ( command.c_str () );
817 return (exitcode == 0);
818 }
819
820 bool
821 MingwBackend::TryToDetectThisBinutils ( const string& binutils )
822 {
823 string command = ssprintf (
824 "%s -v 1>%s",
825 binutils.c_str (),
826 NUL,
827 NUL );
828 int exitcode = system ( command.c_str () );
829 return (exitcode == 0);
830 }
831
832 string
833 MingwBackend::GetBinutilsVersion ( const string& binutilsCommand )
834 {
835 FILE *fp;
836 int ch, i;
837 char buffer[81];
838
839 string versionCommand = ssprintf ( "%s -v",
840 binutilsCommand.c_str (),
841 NUL,
842 NUL );
843 fp = popen ( versionCommand.c_str () , "r" );
844 for( i = 0;
845 ( i < 80 ) &&
846 ( feof ( fp ) == 0 &&
847 ( ( ch = fgetc( fp ) ) != -1 ) );
848 i++ )
849 {
850 buffer[i] = (char) ch;
851 }
852 buffer[i] = '\0';
853 pclose ( fp );
854
855 char separators[] = " ";
856 char *token;
857 char *prevtoken;
858
859 token = strtok ( buffer, separators );
860 while ( token != NULL )
861 {
862 prevtoken = token;
863 token = strtok ( NULL, separators );
864 }
865 string version = string ( prevtoken );
866 int lastDigit = version.find_last_not_of ( "\t\r\n" );
867 if ( lastDigit != -1 )
868 return string ( version, 0, lastDigit+1 );
869 else
870 return version;
871 }
872
873 bool
874 MingwBackend::IsSupportedBinutilsVersion ( const string& binutilsVersion )
875 {
876 if ( ( ( strcmp ( binutilsVersion.c_str (), "20040902") >= 0 ) &&
877 ( strcmp ( binutilsVersion.c_str (), "20041008") <= 0 ) ) ||
878 ( strcmp ( binutilsVersion.c_str (), "20031001") < 0 ) )
879 return false;
880 else
881 return true;
882 }
883
884 void
885 MingwBackend::DetectBinutils ()
886 {
887 printf ( "Detecting binutils..." );
888
889 bool detectedBinutils = false;
890 const string& ROS_PREFIXValue = Environment::GetVariable ( "ROS_PREFIX" );
891 if ( ROS_PREFIXValue.length () > 0 )
892 {
893 binutilsPrefix = ROS_PREFIXValue;
894 binutilsCommand = binutilsPrefix + "-ld";
895 detectedBinutils = TryToDetectThisBinutils ( binutilsCommand );
896 }
897 #if defined(WIN32)
898 if ( !detectedBinutils )
899 {
900 binutilsPrefix = "";
901 binutilsCommand = "ld";
902 detectedBinutils = TryToDetectThisBinutils ( binutilsCommand );
903 }
904 #endif
905 if ( !detectedBinutils )
906 {
907 binutilsPrefix = "mingw32";
908 binutilsCommand = binutilsPrefix + "-ld";
909 detectedBinutils = TryToDetectThisBinutils ( binutilsCommand );
910 }
911 if ( detectedBinutils )
912 {
913 string binutilsVersion = GetBinutilsVersion ( binutilsCommand );
914 if ( IsSupportedBinutilsVersion ( binutilsVersion ) )
915 printf ( "detected (%s)\n", binutilsCommand.c_str () );
916 else
917 {
918 printf ( "detected (%s), but with unsupported version (%s)\n",
919 binutilsCommand.c_str (),
920 binutilsVersion.c_str () );
921 throw UnsupportedBuildToolException ( binutilsCommand, binutilsVersion );
922 }
923 }
924 else
925 printf ( "not detected\n" );
926 }
927
928 void
929 MingwBackend::DetectNetwideAssembler ()
930 {
931 printf ( "Detecting netwide assembler..." );
932
933 nasmCommand = "nasm";
934 bool detectedNasm = TryToDetectThisNetwideAssembler ( nasmCommand );
935 #if defined(WIN32)
936 if ( !detectedNasm )
937 {
938 nasmCommand = "nasmw";
939 detectedNasm = TryToDetectThisNetwideAssembler ( nasmCommand );
940 }
941 #endif
942 if ( !detectedNasm )
943 {
944 nasmCommand = "yasm";
945 detectedNasm = TryToDetectThisNetwideAssembler ( nasmCommand );
946 }
947 if ( detectedNasm )
948 printf ( "detected (%s)\n", nasmCommand.c_str () );
949 else
950 printf ( "not detected\n" );
951 }
952
953 void
954 MingwBackend::DetectPipeSupport ()
955 {
956 printf ( "Detecting compiler -pipe support..." );
957
958 string pipe_detection = "tools" SSEP "rbuild" SSEP "backend" SSEP "mingw" SSEP "pipe_detection.c";
959 string pipe_detectionObjectFilename = ReplaceExtension ( pipe_detection,
960 ".o" );
961 string command = ssprintf (
962 "%s -pipe -c %s -o %s 1>%s 2>%s",
963 compilerCommand.c_str (),
964 pipe_detection.c_str (),
965 pipe_detectionObjectFilename.c_str (),
966 NUL,
967 NUL );
968 int exitcode = system ( command.c_str () );
969 FILE* f = fopen ( pipe_detectionObjectFilename.c_str (), "rb" );
970 if ( f )
971 {
972 usePipe = (exitcode == 0);
973 fclose ( f );
974 unlink ( pipe_detectionObjectFilename.c_str () );
975 }
976 else
977 usePipe = false;
978
979 if ( usePipe )
980 printf ( "detected\n" );
981 else
982 printf ( "not detected\n" );
983 }
984
985 void
986 MingwBackend::DetectPCHSupport ()
987 {
988 printf ( "Detecting compiler pre-compiled header support..." );
989
990 string path = "tools" SSEP "rbuild" SSEP "backend" SSEP "mingw" SSEP "pch_detection.h";
991 string cmd = ssprintf (
992 "%s -c %s 1>%s 2>%s",
993 compilerCommand.c_str (),
994 path.c_str (),
995 NUL,
996 NUL );
997 system ( cmd.c_str () );
998 path += ".gch";
999
1000 FILE* f = fopen ( path.c_str (), "rb" );
1001 if ( f )
1002 {
1003 use_pch = true;
1004 fclose ( f );
1005 unlink ( path.c_str () );
1006 }
1007 else
1008 use_pch = false;
1009
1010 if ( use_pch )
1011 printf ( "detected\n" );
1012 else
1013 printf ( "not detected\n" );
1014 }
1015
1016 void
1017 MingwBackend::GetNonModuleInstallTargetFiles (
1018 vector<string>& out ) const
1019 {
1020 for ( size_t i = 0; i < ProjectNode.installfiles.size (); i++ )
1021 {
1022 const InstallFile& installfile = *ProjectNode.installfiles[i];
1023 string targetFilenameNoFixup = installfile.base + SSEP + installfile.newname;
1024 string targetFilename = MingwModuleHandler::PassThruCacheDirectory (
1025 NormalizeFilename ( targetFilenameNoFixup ),
1026 installDirectory );
1027 out.push_back ( targetFilename );
1028 }
1029 }
1030
1031 void
1032 MingwBackend::GetModuleInstallTargetFiles (
1033 vector<string>& out ) const
1034 {
1035 for ( size_t i = 0; i < ProjectNode.modules.size (); i++ )
1036 {
1037 const Module& module = *ProjectNode.modules[i];
1038 if ( !module.enabled )
1039 continue;
1040 if ( module.installName.length () > 0 )
1041 {
1042 string targetFilenameNoFixup;
1043 if ( module.installBase.length () > 0 )
1044 targetFilenameNoFixup = module.installBase + SSEP + module.installName;
1045 else
1046 targetFilenameNoFixup = module.installName;
1047 string targetFilename = MingwModuleHandler::PassThruCacheDirectory (
1048 NormalizeFilename ( targetFilenameNoFixup ),
1049 installDirectory );
1050 out.push_back ( targetFilename );
1051 }
1052 }
1053 }
1054
1055 void
1056 MingwBackend::GetInstallTargetFiles (
1057 vector<string>& out ) const
1058 {
1059 GetNonModuleInstallTargetFiles ( out );
1060 GetModuleInstallTargetFiles ( out );
1061 }
1062
1063 void
1064 MingwBackend::OutputInstallTarget ( const string& sourceFilename,
1065 const string& targetFilename,
1066 const string& targetDirectory )
1067 {
1068 string fullTargetFilename;
1069 if ( targetDirectory.length () > 0)
1070 fullTargetFilename = targetDirectory + SSEP + targetFilename;
1071 else
1072 fullTargetFilename = targetFilename;
1073 string normalizedTargetFilename = MingwModuleHandler::PassThruCacheDirectory (
1074 NormalizeFilename ( fullTargetFilename ),
1075 installDirectory );
1076 string normalizedTargetDirectory = MingwModuleHandler::PassThruCacheDirectory (
1077 NormalizeFilename ( targetDirectory ),
1078 installDirectory );
1079 fprintf ( fMakefile,
1080 "%s: %s | %s\n",
1081 normalizedTargetFilename.c_str (),
1082 sourceFilename.c_str (),
1083 normalizedTargetDirectory.c_str () );
1084 fprintf ( fMakefile,
1085 "\t$(ECHO_CP)\n" );
1086 fprintf ( fMakefile,
1087 "\t${cp} %s %s 1>$(NUL)\n",
1088 sourceFilename.c_str (),
1089 normalizedTargetFilename.c_str () );
1090 }
1091
1092 void
1093 MingwBackend::OutputNonModuleInstallTargets ()
1094 {
1095 for ( size_t i = 0; i < ProjectNode.installfiles.size (); i++ )
1096 {
1097 const InstallFile& installfile = *ProjectNode.installfiles[i];
1098 OutputInstallTarget ( installfile.GetPath (),
1099 installfile.newname,
1100 installfile.base );
1101 }
1102 }
1103
1104 const Module&
1105 MingwBackend::GetAliasedModuleOrModule ( const Module& module ) const
1106 {
1107 if ( module.aliasedModuleName.size () > 0 )
1108 {
1109 const Module* aliasedModule = ProjectNode.LocateModule ( module.aliasedModuleName );
1110 assert ( aliasedModule );
1111 return *aliasedModule;
1112 }
1113 else
1114 return module;
1115 }
1116
1117 void
1118 MingwBackend::OutputModuleInstallTargets ()
1119 {
1120 for ( size_t i = 0; i < ProjectNode.modules.size (); i++ )
1121 {
1122 const Module& module = *ProjectNode.modules[i];
1123 if ( !module.enabled )
1124 continue;
1125 if ( module.installName.length () > 0 )
1126 {
1127 const Module& aliasedModule = GetAliasedModuleOrModule ( module );
1128 string sourceFilename = MingwModuleHandler::PassThruCacheDirectory (
1129 NormalizeFilename ( aliasedModule.GetPath () ),
1130 outputDirectory );
1131 OutputInstallTarget ( sourceFilename,
1132 module.installName,
1133 module.installBase );
1134 }
1135 }
1136 }
1137
1138 string
1139 MingwBackend::GetRegistrySourceFiles ()
1140 {
1141 return "bootdata" SSEP "hivecls.inf "
1142 "bootdata" SSEP "hivedef.inf "
1143 "bootdata" SSEP "hiveinst.inf "
1144 "bootdata" SSEP "hivesft.inf "
1145 "bootdata" SSEP "hivesys.inf";
1146 }
1147
1148 string
1149 MingwBackend::GetRegistryTargetFiles ()
1150 {
1151 string system32ConfigDirectory = NormalizeFilename (
1152 MingwModuleHandler::PassThruCacheDirectory (
1153 "system32" SSEP "config" SSEP,
1154 installDirectory ) );
1155 return system32ConfigDirectory + SSEP "default " +
1156 system32ConfigDirectory + SSEP "sam " +
1157 system32ConfigDirectory + SSEP "security " +
1158 system32ConfigDirectory + SSEP "software " +
1159 system32ConfigDirectory + SSEP "system";
1160 }
1161
1162 void
1163 MingwBackend::OutputRegistryInstallTarget ()
1164 {
1165 string system32ConfigDirectory = NormalizeFilename (
1166 MingwModuleHandler::PassThruCacheDirectory (
1167 "system32" SSEP "config" SSEP,
1168 installDirectory ) );
1169
1170 string registrySourceFiles = GetRegistrySourceFiles ();
1171 string registryTargetFiles = GetRegistryTargetFiles ();
1172 fprintf ( fMakefile,
1173 "install_registry: %s\n",
1174 registryTargetFiles.c_str () );
1175 fprintf ( fMakefile,
1176 "%s: %s %s $(MKHIVE_TARGET)\n",
1177 registryTargetFiles.c_str (),
1178 registrySourceFiles.c_str (),
1179 system32ConfigDirectory.c_str () );
1180 fprintf ( fMakefile,
1181 "\t$(ECHO_MKHIVE)\n" );
1182 fprintf ( fMakefile,
1183 "\t$(MKHIVE_TARGET) bootdata %s bootdata" SSEP "hiveinst.inf\n",
1184 system32ConfigDirectory.c_str () );
1185 fprintf ( fMakefile,
1186 "\n" );
1187 }
1188
1189 void
1190 MingwBackend::GenerateInstallTarget ()
1191 {
1192 vector<string> vInstallTargetFiles;
1193 GetInstallTargetFiles ( vInstallTargetFiles );
1194 string installTargetFiles = v2s ( vInstallTargetFiles, 5 );
1195 string registryTargetFiles = GetRegistryTargetFiles ();
1196
1197 fprintf ( fMakefile,
1198 "install: %s %s\n",
1199 installTargetFiles.c_str (),
1200 registryTargetFiles.c_str () );
1201 OutputNonModuleInstallTargets ();
1202 OutputModuleInstallTargets ();
1203 OutputRegistryInstallTarget ();
1204 fprintf ( fMakefile,
1205 "\n" );
1206 }
1207
1208 void
1209 MingwBackend::GetModuleTestTargets (
1210 vector<string>& out ) const
1211 {
1212 for ( size_t i = 0; i < ProjectNode.modules.size (); i++ )
1213 {
1214 const Module& module = *ProjectNode.modules[i];
1215 if ( !module.enabled )
1216 continue;
1217 if ( module.type == Test )
1218 out.push_back ( module.name );
1219 }
1220 }
1221
1222 void
1223 MingwBackend::GenerateTestTarget ()
1224 {
1225 vector<string> vTestTargets;
1226 GetModuleTestTargets ( vTestTargets );
1227 string testTargets = v2s ( vTestTargets, 5 );
1228
1229 fprintf ( fMakefile,
1230 "test: %s\n",
1231 testTargets.c_str () );
1232 fprintf ( fMakefile,
1233 "\n" );
1234 }
1235
1236 void
1237 MingwBackend::GenerateDirectoryTargets ()
1238 {
1239 intermediateDirectory->CreateRule ( fMakefile, "" );
1240 outputDirectory->CreateRule ( fMakefile, "" );
1241 installDirectory->CreateRule ( fMakefile, "" );
1242 }