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