Implement full dependencies for rbuild: changing a header file will now automatically...
[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.Dependencies == 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 if ( configuration.Dependencies == FullDependencies )
523 {
524 fprintf ( fMakefile,
525 "ifeq ($(ROS_BUILDDEPS),)\n"
526 "ROS_BUILDDEPS:=%s\n"
527 "endif\n",
528 "full" );
529 }
530
531 fprintf ( fMakefile,
532 "PREFIX := %s\n",
533 compilerPrefix.c_str () );
534 fprintf ( fMakefile,
535 "nasm := %s\n",
536 nasmCommand.c_str () );
537
538 GenerateGlobalCFlagsAndProperties ( "=", ProjectNode.non_if_data );
539 GenerateProjectGccOptions ( "=", ProjectNode.non_if_data );
540
541 fprintf ( fMakefile, "PROJECT_RCFLAGS := $(PROJECT_CINCLUDES) $(PROJECT_CDEFINES)\n" );
542 fprintf ( fMakefile, "PROJECT_WIDLFLAGS := $(PROJECT_CINCLUDES) $(PROJECT_CDEFINES)\n" );
543 fprintf ( fMakefile, "PROJECT_LFLAGS := '$(shell ${TARGET_CC} -print-libgcc-file-name)' %s\n", GenerateProjectLFLAGS ().c_str () );
544 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" );
545 fprintf ( fMakefile, "PROJECT_GCCOPTIONS += -Wall\n" );
546 fprintf ( fMakefile, "ifneq ($(OARCH),)\n" );
547 fprintf ( fMakefile, "PROJECT_GCCOPTIONS += -march=$(OARCH)\n" );
548 fprintf ( fMakefile, "endif\n" );
549 fprintf ( fMakefile, "ifneq ($(TUNE),)\n" );
550 fprintf ( fMakefile, "PROJECT_GCCOPTIONS += -mtune=$(TUNE)\n" );
551 fprintf ( fMakefile, "endif\n" );
552 fprintf ( fMakefile, "PROJECT_CFLAGS = $(PROJECT_GCCOPTIONS) $(PROJECT_GCC_CFLAGS)\n" );
553 fprintf ( fMakefile, "PROJECT_CXXFLAGS = $(PROJECT_GCCOPTIONS) $(PROJECT_GCC_CXXFLAGS)\n" );
554 fprintf ( fMakefile, "\n" );
555 }
556
557 bool
558 MingwBackend::IncludeInAllTarget ( const Module& module ) const
559 {
560 if ( MingwModuleHandler::ReferenceObjects ( module ) )
561 return false;
562 if ( module.type == BootSector )
563 return false;
564 if ( module.type == Iso )
565 return false;
566 if ( module.type == LiveIso )
567 return false;
568 if ( module.type == IsoRegTest )
569 return false;
570 if ( module.type == LiveIsoRegTest )
571 return false;
572 if ( module.type == Test )
573 return false;
574 if ( module.type == Alias )
575 return false;
576 return true;
577 }
578
579 void
580 MingwBackend::GenerateAllTarget ( const vector<MingwModuleHandler*>& handlers ) const
581 {
582 fprintf ( fMakefile, "all:" );
583 int wrap_count = 0;
584 size_t iend = handlers.size ();
585 for ( size_t i = 0; i < iend; i++ )
586 {
587 const Module& module = handlers[i]->module;
588 if ( IncludeInAllTarget ( module ) )
589 {
590 if ( wrap_count++ == 5 )
591 fprintf ( fMakefile, " \\\n\t\t" ), wrap_count = 0;
592 fprintf ( fMakefile,
593 " %s",
594 GetTargetMacro(module).c_str () );
595 }
596 }
597 fprintf ( fMakefile, "\n\t\n\n" );
598 }
599
600 void
601 MingwBackend::GenerateRegTestsRunTarget () const
602 {
603 fprintf ( fMakefile,
604 "REGTESTS_RUN_TARGET = regtests.dll\n" );
605 fprintf ( fMakefile,
606 "$(REGTESTS_RUN_TARGET): $(REGTESTS_TARGET)\n" );
607 fprintf ( fMakefile,
608 "\t$(cp) $(REGTESTS_TARGET) $(REGTESTS_RUN_TARGET)\n" );
609 fprintf ( fMakefile, "\n" );
610 }
611
612 void
613 MingwBackend::GenerateXmlBuildFilesMacro() const
614 {
615 fprintf ( fMakefile,
616 "XMLBUILDFILES = %s \\\n",
617 ProjectNode.GetProjectFilename ().c_str () );
618 string xmlbuildFilenames;
619 int numberOfExistingFiles = 0;
620 struct stat statbuf;
621 time_t SystemTime, lastWriteTime;
622
623 for ( size_t i = 0; i < ProjectNode.xmlbuildfiles.size (); i++ )
624 {
625 XMLInclude& xmlbuildfile = *ProjectNode.xmlbuildfiles[i];
626 if ( !xmlbuildfile.fileExists )
627 continue;
628 numberOfExistingFiles++;
629 if ( xmlbuildFilenames.length () > 0 )
630 xmlbuildFilenames += " ";
631
632 FILE* f = fopen ( xmlbuildfile.topIncludeFilename.c_str (), "rb" );
633 if ( !f )
634 throw FileNotFoundException ( NormalizeFilename ( xmlbuildfile.topIncludeFilename ) );
635
636 if ( fstat ( fileno ( f ), &statbuf ) != 0 )
637 {
638 fclose ( f );
639 throw AccessDeniedException ( NormalizeFilename ( xmlbuildfile.topIncludeFilename ) );
640 }
641
642 lastWriteTime = statbuf.st_mtime;
643 SystemTime = time(NULL);
644
645 if (SystemTime != -1)
646 {
647 if (difftime (lastWriteTime, SystemTime) > 0)
648 throw InvalidDateException ( NormalizeFilename ( xmlbuildfile.topIncludeFilename ) );
649 }
650
651 fclose ( f );
652
653 xmlbuildFilenames += NormalizeFilename ( xmlbuildfile.topIncludeFilename );
654 if ( numberOfExistingFiles % 5 == 4 || i == ProjectNode.xmlbuildfiles.size () - 1 )
655 {
656 fprintf ( fMakefile,
657 "\t%s",
658 xmlbuildFilenames.c_str ());
659 if ( i == ProjectNode.xmlbuildfiles.size () - 1 )
660 {
661 fprintf ( fMakefile, "\n" );
662 }
663 else
664 {
665 fprintf ( fMakefile,
666 " \\\n" );
667 }
668 xmlbuildFilenames.resize ( 0 );
669 }
670 numberOfExistingFiles++;
671 }
672 fprintf ( fMakefile, "\n" );
673 }
674
675 void
676 MingwBackend::GenerateTestSupportCode ()
677 {
678 printf ( "Generating test support code..." );
679 TestSupportCode testSupportCode ( ProjectNode );
680 testSupportCode.GenerateTestSupportCode ( configuration.Verbose );
681 printf ( "done\n" );
682 }
683
684 void
685 MingwBackend::GenerateCompilationUnitSupportCode ()
686 {
687 if ( configuration.CompilationUnitsEnabled )
688 {
689 printf ( "Generating compilation unit support code..." );
690 CompilationUnitSupportCode compilationUnitSupportCode ( ProjectNode );
691 compilationUnitSupportCode.Generate ( configuration.Verbose );
692 printf ( "done\n" );
693 }
694 }
695
696 void
697 MingwBackend::GenerateSysSetup ()
698 {
699 printf ( "Generating syssetup.inf..." );
700 SysSetupGenerator sysSetupGenerator ( ProjectNode );
701 sysSetupGenerator.Generate ();
702 printf ( "done\n" );
703 }
704
705 string
706 MingwBackend::GetProxyMakefileTree () const
707 {
708 if ( configuration.GenerateProxyMakefilesInSourceTree )
709 return "";
710 else
711 return Environment::GetOutputPath ();
712 }
713
714 void
715 MingwBackend::GenerateProxyMakefiles ()
716 {
717 printf ( "Generating proxy makefiles..." );
718 ProxyMakefile proxyMakefile ( ProjectNode );
719 proxyMakefile.GenerateProxyMakefiles ( configuration.Verbose,
720 GetProxyMakefileTree () );
721 printf ( "done\n" );
722 }
723
724 void
725 MingwBackend::CheckAutomaticDependencies ()
726 {
727 if ( configuration.Dependencies == AutomaticDependencies )
728 {
729 printf ( "Checking automatic dependencies..." );
730 AutomaticDependency automaticDependency ( ProjectNode );
731 automaticDependency.CheckAutomaticDependencies ( configuration.Verbose );
732 printf ( "done\n" );
733 }
734 }
735
736 void
737 MingwBackend::GenerateDirectories ()
738 {
739 printf ( "Creating directories..." );
740 intermediateDirectory->GenerateTree ( IntermediateDirectory, configuration.Verbose );
741 outputDirectory->GenerateTree ( OutputDirectory, configuration.Verbose );
742 if ( !configuration.MakeHandlesInstallDirectories )
743 installDirectory->GenerateTree ( InstallDirectory, configuration.Verbose );
744 printf ( "done\n" );
745 }
746
747 bool
748 MingwBackend::TryToDetectThisCompiler ( const string& compiler )
749 {
750 string command = ssprintf (
751 "%s -v 1>%s 2>%s",
752 FixSeparatorForSystemCommand(compiler).c_str (),
753 NUL,
754 NUL );
755 int exitcode = system ( command.c_str () );
756 return (bool) (exitcode == 0);
757 }
758
759 void
760 MingwBackend::DetectCompiler ()
761 {
762 printf ( "Detecting compiler..." );
763
764 bool detectedCompiler = false;
765 const string& ROS_PREFIXValue = Environment::GetVariable ( "ROS_PREFIX" );
766 if ( ROS_PREFIXValue.length () > 0 )
767 {
768 compilerPrefix = ROS_PREFIXValue;
769 compilerCommand = compilerPrefix + "-gcc";
770 detectedCompiler = TryToDetectThisCompiler ( compilerCommand );
771 }
772 #if defined(WIN32)
773 if ( !detectedCompiler )
774 {
775 compilerPrefix = "";
776 compilerCommand = "gcc";
777 detectedCompiler = TryToDetectThisCompiler ( compilerCommand );
778 }
779 #endif
780 if ( !detectedCompiler )
781 {
782 compilerPrefix = "mingw32";
783 compilerCommand = compilerPrefix + "-gcc";
784 detectedCompiler = TryToDetectThisCompiler ( compilerCommand );
785 }
786
787 if ( detectedCompiler )
788 {
789 string compilerVersion = GetCompilerVersion ( compilerCommand );
790 if ( IsSupportedCompilerVersion ( compilerVersion ) )
791 printf ( "detected (%s %s)\n", compilerCommand.c_str (), compilerVersion.c_str() );
792 else
793 {
794 printf ( "detected (%s), but with unsupported version (%s)\n",
795 compilerCommand.c_str (),
796 compilerVersion.c_str () );
797 throw UnsupportedBuildToolException ( compilerCommand, compilerVersion );
798 }
799 }
800 else
801 printf ( "not detected\n" );
802
803 }
804
805 bool
806 MingwBackend::TryToDetectThisNetwideAssembler ( const string& assembler )
807 {
808 string command = ssprintf (
809 "%s -h 1>%s 2>%s",
810 FixSeparatorForSystemCommand(assembler).c_str (),
811 NUL,
812 NUL );
813 int exitcode = system ( command.c_str () );
814 return (bool) (exitcode == 0);
815 }
816
817 string
818 MingwBackend::GetVersionString ( const string& versionCommand )
819 {
820 FILE *fp;
821 int ch, i;
822 size_t newline;
823 char buffer[81];
824
825 fp = popen ( versionCommand.c_str () , "r" );
826 for( i = 0;
827 ( i < 80 ) &&
828 ( feof ( fp ) == 0 &&
829 ( ( ch = fgetc( fp ) ) != -1 ) );
830 i++ )
831 {
832 buffer[i] = (char) ch;
833 }
834 buffer[i] = '\0';
835 pclose ( fp );
836
837 char separators[] = " ()";
838 char *token;
839 char *prevtoken = NULL;
840
841 string version;
842
843 token = strtok ( buffer, separators );
844 while ( token != NULL )
845 {
846 prevtoken = token;
847 version = string( prevtoken );
848 if ( (newline = version.find('\n')) != std::string::npos )
849 version.erase(newline, 1);
850 if ( version.find('.') != std::string::npos )
851 break;
852 token = strtok ( NULL, separators );
853 }
854 return version;
855 }
856
857 string
858 MingwBackend::GetNetwideAssemblerVersion ( const string& nasmCommand )
859 {
860 string versionCommand;
861 if ( nasmCommand.find("yasm") != std::string::npos )
862 {
863 versionCommand = ssprintf ( "%s --version",
864 nasmCommand.c_str (),
865 NUL,
866 NUL );
867 }
868 else
869 {
870 versionCommand = ssprintf ( "%s -v",
871 nasmCommand.c_str (),
872 NUL,
873 NUL );
874 }
875 return GetVersionString( versionCommand );
876 }
877
878 string
879 MingwBackend::GetCompilerVersion ( const string& compilerCommand )
880 {
881 string versionCommand = ssprintf ( "%s --version gcc",
882 compilerCommand.c_str (),
883 NUL,
884 NUL );
885 return GetVersionString( versionCommand );
886 }
887
888 string
889 MingwBackend::GetBinutilsVersion ( const string& binutilsCommand )
890 {
891 string versionCommand = ssprintf ( "%s -v",
892 binutilsCommand.c_str (),
893 NUL,
894 NUL );
895 return GetVersionString( versionCommand );
896 }
897
898 bool
899 MingwBackend::IsSupportedCompilerVersion ( const string& compilerVersion )
900 {
901 if ( strcmp ( compilerVersion.c_str (), "3.4.2") < 0 )
902 return false;
903 else
904 return true;
905 }
906
907 bool
908 MingwBackend::TryToDetectThisBinutils ( const string& binutils )
909 {
910 string command = ssprintf (
911 "%s -v 1>%s 2>%s",
912 FixSeparatorForSystemCommand(binutils).c_str (),
913 NUL,
914 NUL );
915 int exitcode = system ( command.c_str () );
916 return (exitcode == 0);
917 }
918
919 string
920 MingwBackend::GetBinutilsVersionDate ( const string& binutilsCommand )
921 {
922 FILE *fp;
923 int ch, i;
924 char buffer[81];
925
926 string versionCommand = ssprintf ( "%s -v",
927 binutilsCommand.c_str (),
928 NUL,
929 NUL );
930 fp = popen ( versionCommand.c_str () , "r" );
931 for( i = 0;
932 ( i < 80 ) &&
933 ( feof ( fp ) == 0 &&
934 ( ( ch = fgetc( fp ) ) != -1 ) );
935 i++ )
936 {
937 buffer[i] = (char) ch;
938 }
939 buffer[i] = '\0';
940 pclose ( fp );
941
942 char separators[] = " ";
943 char *token;
944 char *prevtoken = NULL;
945
946 token = strtok ( buffer, separators );
947 while ( token != NULL )
948 {
949 prevtoken = token;
950 token = strtok ( NULL, separators );
951 }
952 string version = string ( prevtoken );
953 int lastDigit = version.find_last_not_of ( "\t\r\n" );
954 if ( lastDigit != -1 )
955 return string ( version, 0, lastDigit+1 );
956 else
957 return version;
958 }
959
960 bool
961 MingwBackend::IsSupportedBinutilsVersion ( const string& binutilsVersion )
962 {
963 if ( manualBinutilsSetting ) return true;
964
965 /* linux */
966 if ( binutilsVersion.find('.') != std::string::npos )
967 {
968 /* TODO: blacklist versions on version number instead of date */
969 return true;
970 }
971
972 /*
973 * - Binutils older than 2003/10/01 have broken windres which can't handle
974 * icons with alpha channel.
975 * - Binutils between 2004/09/02 and 2004/10/08 have broken handling of
976 * forward exports in dlltool.
977 */
978 if ( ( ( strcmp ( binutilsVersion.c_str (), "20040902") >= 0 ) &&
979 ( strcmp ( binutilsVersion.c_str (), "20041008") <= 0 ) ) ||
980 ( strcmp ( binutilsVersion.c_str (), "20031001") < 0 ) )
981 return false;
982 else
983 return true;
984 }
985
986 void
987 MingwBackend::DetectBinutils ()
988 {
989 printf ( "Detecting binutils..." );
990
991 bool detectedBinutils = false;
992 const string& ROS_PREFIXValue = Environment::GetVariable ( "ROS_PREFIX" );
993
994 if ( ROS_PREFIXValue.length () > 0 )
995 {
996 binutilsPrefix = ROS_PREFIXValue;
997 binutilsCommand = binutilsPrefix + "-ld";
998 manualBinutilsSetting = true;
999 detectedBinutils = true;
1000 }
1001 #if defined(WIN32)
1002 if ( !detectedBinutils )
1003 {
1004 binutilsPrefix = "";
1005 binutilsCommand = "ld";
1006 detectedBinutils = TryToDetectThisBinutils ( binutilsCommand );
1007 }
1008 #endif
1009 if ( !detectedBinutils )
1010 {
1011 binutilsPrefix = "mingw32";
1012 binutilsCommand = binutilsPrefix + "-ld";
1013 detectedBinutils = TryToDetectThisBinutils ( binutilsCommand );
1014 }
1015 if ( detectedBinutils )
1016 {
1017 string binutilsVersion = GetBinutilsVersionDate ( binutilsCommand );
1018 if ( IsSupportedBinutilsVersion ( binutilsVersion ) )
1019 printf ( "detected (%s %s)\n", binutilsCommand.c_str (), GetBinutilsVersion( binutilsCommand ).c_str() );
1020 else
1021 {
1022 printf ( "detected (%s), but with unsupported version (%s)\n",
1023 binutilsCommand.c_str (),
1024 binutilsVersion.c_str () );
1025 throw UnsupportedBuildToolException ( binutilsCommand, binutilsVersion );
1026 }
1027 }
1028 else
1029 printf ( "not detected\n" );
1030
1031 }
1032
1033 void
1034 MingwBackend::DetectNetwideAssembler ()
1035 {
1036 printf ( "Detecting netwide assembler..." );
1037
1038 nasmCommand = "nasm";
1039 bool detectedNasm = TryToDetectThisNetwideAssembler ( nasmCommand );
1040 #if defined(WIN32)
1041 if ( !detectedNasm )
1042 {
1043 nasmCommand = "nasmw";
1044 detectedNasm = TryToDetectThisNetwideAssembler ( nasmCommand );
1045 }
1046 #endif
1047 if ( !detectedNasm )
1048 {
1049 nasmCommand = "yasm";
1050 detectedNasm = TryToDetectThisNetwideAssembler ( nasmCommand );
1051 }
1052 if ( detectedNasm )
1053 printf ( "detected (%s %s)\n", nasmCommand.c_str (), GetNetwideAssemblerVersion( nasmCommand ).c_str() );
1054 else
1055 printf ( "not detected\n" );
1056 }
1057
1058 void
1059 MingwBackend::DetectPipeSupport ()
1060 {
1061 printf ( "Detecting compiler -pipe support..." );
1062
1063 string pipe_detection = "tools" + sSep + "rbuild" + sSep + "backend" + sSep + "mingw" + sSep + "pipe_detection.c";
1064 string pipe_detectionObjectFilename = ReplaceExtension ( pipe_detection,
1065 ".o" );
1066 string command = ssprintf (
1067 "%s -pipe -c %s -o %s 1>%s 2>%s",
1068 FixSeparatorForSystemCommand(compilerCommand).c_str (),
1069 pipe_detection.c_str (),
1070 pipe_detectionObjectFilename.c_str (),
1071 NUL,
1072 NUL );
1073 int exitcode = system ( command.c_str () );
1074 FILE* f = fopen ( pipe_detectionObjectFilename.c_str (), "rb" );
1075 if ( f )
1076 {
1077 usePipe = (exitcode == 0);
1078 fclose ( f );
1079 unlink ( pipe_detectionObjectFilename.c_str () );
1080 }
1081 else
1082 usePipe = false;
1083
1084 if ( usePipe )
1085 printf ( "detected\n" );
1086 else
1087 printf ( "not detected\n" );
1088 }
1089
1090 void
1091 MingwBackend::DetectPCHSupport ()
1092 {
1093 printf ( "Detecting compiler pre-compiled header support..." );
1094
1095 if ( configuration.PrecompiledHeadersEnabled )
1096 {
1097 string path = "tools" + sSep + "rbuild" + sSep + "backend" + sSep + "mingw" + sSep + "pch_detection.h";
1098 string cmd = ssprintf (
1099 "%s -c %s 1>%s 2>%s",
1100 FixSeparatorForSystemCommand(compilerCommand).c_str (),
1101 path.c_str (),
1102 NUL,
1103 NUL );
1104 system ( cmd.c_str () );
1105 path += ".gch";
1106
1107 FILE* f = fopen ( path.c_str (), "rb" );
1108 if ( f )
1109 {
1110 use_pch = true;
1111 fclose ( f );
1112 unlink ( path.c_str () );
1113 }
1114 else
1115 use_pch = false;
1116
1117 if ( use_pch )
1118 printf ( "detected\n" );
1119 else
1120 printf ( "not detected\n" );
1121 }
1122 else
1123 {
1124 use_pch = false;
1125 printf ( "disabled\n" );
1126 }
1127 }
1128
1129 void
1130 MingwBackend::GetNonModuleInstallTargetFiles (
1131 vector<FileLocation>& out ) const
1132 {
1133 for ( size_t i = 0; i < ProjectNode.installfiles.size (); i++ )
1134 {
1135 const InstallFile& installfile = *ProjectNode.installfiles[i];
1136 out.push_back ( *installfile.target );
1137 }
1138 }
1139
1140 void
1141 MingwBackend::GetModuleInstallTargetFiles (
1142 vector<FileLocation>& out ) const
1143 {
1144 for ( std::map<std::string, Module*>::const_iterator p = ProjectNode.modules.begin (); p != ProjectNode.modules.end (); ++ p )
1145 {
1146 const Module& module = *p->second;
1147 if ( !module.enabled )
1148 continue;
1149 if ( module.install )
1150 out.push_back ( *module.install );
1151 }
1152 }
1153
1154 void
1155 MingwBackend::GetInstallTargetFiles (
1156 vector<FileLocation>& out ) const
1157 {
1158 GetNonModuleInstallTargetFiles ( out );
1159 GetModuleInstallTargetFiles ( out );
1160 }
1161
1162 void
1163 MingwBackend::OutputInstallTarget ( const FileLocation& source,
1164 const FileLocation& target )
1165 {
1166 fprintf ( fMakefile,
1167 "%s: %s | %s\n",
1168 GetFullName ( target ).c_str (),
1169 GetFullName ( source ).c_str (),
1170 GetFullPath ( target ).c_str () );
1171 fprintf ( fMakefile,
1172 "\t$(ECHO_CP)\n" );
1173 fprintf ( fMakefile,
1174 "\t${cp} %s %s 1>$(NUL)\n",
1175 GetFullName ( source ).c_str (),
1176 GetFullName ( target ).c_str () );
1177 }
1178
1179 void
1180 MingwBackend::OutputNonModuleInstallTargets ()
1181 {
1182 for ( size_t i = 0; i < ProjectNode.installfiles.size (); i++ )
1183 {
1184 const InstallFile& installfile = *ProjectNode.installfiles[i];
1185 OutputInstallTarget ( *installfile.source, *installfile.target );
1186 }
1187 }
1188
1189 const Module&
1190 MingwBackend::GetAliasedModuleOrModule ( const Module& module ) const
1191 {
1192 if ( module.aliasedModuleName.size () > 0 )
1193 {
1194 const Module* aliasedModule = ProjectNode.LocateModule ( module.aliasedModuleName );
1195 assert ( aliasedModule );
1196 return *aliasedModule;
1197 }
1198 else
1199 return module;
1200 }
1201
1202 void
1203 MingwBackend::OutputModuleInstallTargets ()
1204 {
1205 for ( std::map<std::string, Module*>::const_iterator p = ProjectNode.modules.begin (); p != ProjectNode.modules.end (); ++ p )
1206 {
1207 const Module& module = *p->second;
1208 if ( !module.enabled )
1209 continue;
1210 if ( module.install )
1211 {
1212 const Module& aliasedModule = GetAliasedModuleOrModule ( module );
1213 OutputInstallTarget ( *aliasedModule.output, *module.install );
1214 }
1215 }
1216 }
1217
1218 string
1219 MingwBackend::GetRegistrySourceFiles ()
1220 {
1221 return "boot" + sSep + "bootdata" + sSep + "hivecls_" + Environment::GetArch() + ".inf "
1222 "boot" + sSep + "bootdata" + sSep + "hivedef_" + Environment::GetArch() + ".inf "
1223 "boot" + sSep + "bootdata" + sSep + "hiveinst_" + Environment::GetArch() + ".inf "
1224 "boot" + sSep + "bootdata" + sSep + "hivesft_" + Environment::GetArch() + ".inf "
1225 "boot" + sSep + "bootdata" + sSep + "hivesys_" + Environment::GetArch() + ".inf ";
1226 }
1227
1228 string
1229 MingwBackend::GetRegistryTargetFiles ()
1230 {
1231 string system32ConfigDirectory = "system32" + sSep + "config";
1232 FileLocation system32 ( InstallDirectory, system32ConfigDirectory, "" );
1233
1234 vector<FileLocation> registry_files;
1235 registry_files.push_back ( FileLocation ( InstallDirectory, system32ConfigDirectory, "default" ) );
1236 registry_files.push_back ( FileLocation ( InstallDirectory, system32ConfigDirectory, "sam" ) );
1237 registry_files.push_back ( FileLocation ( InstallDirectory, system32ConfigDirectory, "security" ) );
1238 registry_files.push_back ( FileLocation ( InstallDirectory, system32ConfigDirectory, "software" ) );
1239 registry_files.push_back ( FileLocation ( InstallDirectory, system32ConfigDirectory, "system" ) );
1240
1241 return v2s( this, registry_files, 6 );
1242 }
1243
1244 void
1245 MingwBackend::OutputRegistryInstallTarget ()
1246 {
1247 FileLocation system32 ( InstallDirectory, "system32" + sSep + "config", "" );
1248
1249 string registrySourceFiles = GetRegistrySourceFiles ();
1250 string registryTargetFiles = GetRegistryTargetFiles ();
1251 fprintf ( fMakefile,
1252 "install_registry: %s\n",
1253 registryTargetFiles.c_str () );
1254 fprintf ( fMakefile,
1255 "%s: %s %s $(MKHIVE_TARGET)\n",
1256 registryTargetFiles.c_str (),
1257 registrySourceFiles.c_str (),
1258 GetFullPath ( system32 ).c_str () );
1259 fprintf ( fMakefile,
1260 "\t$(ECHO_MKHIVE)\n" );
1261 fprintf ( fMakefile,
1262 "\t$(MKHIVE_TARGET) boot%cbootdata %s $(ARCH) boot%cbootdata%chiveinst_$(ARCH).inf\n",
1263 cSep, GetFullPath ( system32 ).c_str (),
1264 cSep, cSep );
1265 fprintf ( fMakefile,
1266 "\n" );
1267 }
1268
1269 void
1270 MingwBackend::GenerateInstallTarget ()
1271 {
1272 vector<FileLocation> vInstallTargetFiles;
1273 GetInstallTargetFiles ( vInstallTargetFiles );
1274 string installTargetFiles = v2s ( this, vInstallTargetFiles, 5 );
1275 string registryTargetFiles = GetRegistryTargetFiles ();
1276
1277 fprintf ( fMakefile,
1278 "install: %s %s\n",
1279 installTargetFiles.c_str (),
1280 registryTargetFiles.c_str () );
1281 OutputNonModuleInstallTargets ();
1282 OutputModuleInstallTargets ();
1283 OutputRegistryInstallTarget ();
1284 fprintf ( fMakefile,
1285 "\n" );
1286 }
1287
1288 void
1289 MingwBackend::GetModuleTestTargets (
1290 vector<string>& out ) const
1291 {
1292 for ( std::map<std::string, Module*>::const_iterator p = ProjectNode.modules.begin (); p != ProjectNode.modules.end (); ++ p )
1293 {
1294 const Module& module = *p->second;
1295 if ( !module.enabled )
1296 continue;
1297 if ( module.type == Test )
1298 out.push_back ( module.name );
1299 }
1300 }
1301
1302 void
1303 MingwBackend::GenerateTestTarget ()
1304 {
1305 vector<string> vTestTargets;
1306 GetModuleTestTargets ( vTestTargets );
1307 string testTargets = v2s ( vTestTargets, 5 );
1308
1309 fprintf ( fMakefile,
1310 "test: %s\n",
1311 testTargets.c_str () );
1312 fprintf ( fMakefile,
1313 "\n" );
1314 }
1315
1316 void
1317 MingwBackend::GenerateDirectoryTargets ()
1318 {
1319 intermediateDirectory->CreateRule ( fMakefile, "$(INTERMEDIATE)" );
1320 outputDirectory->CreateRule ( fMakefile, "$(OUTPUT)" );
1321 installDirectory->CreateRule ( fMakefile, "$(INSTALL)" );
1322 }