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