935d6965eb7919003cf4295582bb94b329f9f6a9
[reactos.git] / reactos / tools / rbuild / backend / mingw / mingw.cpp
1
2 #include "../../pch.h"
3
4 #include "mingw.h"
5 #include <assert.h>
6 #include <dirent.h>
7 #include "modulehandler.h"
8
9 #ifdef WIN32
10 #define MKDIR(s) mkdir(s)
11 #else
12 #define MKDIR(s) mkdir(s, 0755)
13 #endif
14
15 using std::string;
16 using std::vector;
17 using std::set;
18 using std::map;
19
20 typedef set<string> set_string;
21
22
23 string
24 v2s ( const string_list& v, int wrap_at )
25 {
26 if ( !v.size() )
27 return "";
28 string s;
29 int wrap_count = 0;
30 for ( size_t i = 0; i < v.size(); i++ )
31 {
32 if ( !v[i].size() )
33 continue;
34 if ( wrap_at > 0 && wrap_count++ == wrap_at )
35 s += " \\\n\t\t";
36 else if ( s.size() )
37 s += " ";
38 s += v[i];
39 }
40 return s;
41 }
42
43
44 Directory::Directory ( const string& name_ )
45 : name(name_)
46 {
47 }
48
49 void
50 Directory::Add ( const char* subdir )
51 {
52 size_t i;
53 string s1 = string ( subdir );
54 if ( ( i = s1.find ( '$' ) ) != string::npos )
55 {
56 throw InvalidOperationException ( __FILE__,
57 __LINE__,
58 "No environment variables can be used here. Path was %s",
59 subdir );
60 }
61
62 const char* p = strpbrk ( subdir, "/\\" );
63 if ( !p )
64 p = subdir + strlen(subdir);
65 string s ( subdir, p-subdir );
66 if ( subdirs.find(s) == subdirs.end() )
67 subdirs[s] = new Directory(s);
68 if ( *p && *++p )
69 subdirs[s]->Add ( p );
70 }
71
72 bool
73 Directory::mkdir_p ( const char* path )
74 {
75 DIR *directory;
76 directory = opendir ( path );
77 if ( directory != NULL )
78 {
79 closedir ( directory );
80 return false;
81 }
82
83 if ( MKDIR ( path ) != 0 )
84 throw AccessDeniedException ( string ( path ) );
85 return true;
86 }
87
88 bool
89 Directory::CreateDirectory ( string path )
90 {
91 size_t index = 0;
92 size_t nextIndex;
93 if ( isalpha ( path[0] ) && path[1] == ':' && path[2] == CSEP )
94 {
95 nextIndex = path.find ( CSEP, 3);
96 }
97 else
98 nextIndex = path.find ( CSEP );
99
100 bool directoryWasCreated = false;
101 while ( nextIndex != string::npos )
102 {
103 nextIndex = path.find ( CSEP, index + 1 );
104 directoryWasCreated = mkdir_p ( path.substr ( 0, nextIndex ).c_str () );
105 index = nextIndex;
106 }
107 return directoryWasCreated;
108 }
109
110 string
111 Directory::ReplaceVariable ( string name,
112 string value,
113 string path )
114 {
115 size_t i = path.find ( name );
116 if ( i != string::npos )
117 return path.replace ( i, name.length (), value );
118 else
119 return path;
120 }
121
122 string
123 Directory::GetEnvironmentVariable ( const string& name )
124 {
125 char* value = getenv ( name.c_str () );
126 if ( value != NULL && strlen ( value ) > 0 )
127 return ssprintf ( "%s",
128 value );
129 else
130 return "";
131 }
132
133 string
134 Directory::GetEnvironmentVariablePathOrDefault ( const string& name,
135 const string& defaultValue )
136 {
137 const string& environmentVariableValue = GetEnvironmentVariable ( name );
138 if ( environmentVariableValue.length () > 0 )
139 return NormalizeFilename ( environmentVariableValue );
140 else
141 return defaultValue;
142 }
143
144 string
145 Directory::GetIntermediatePath ()
146 {
147 return "obj-i386";
148 }
149
150 string
151 Directory::GetOutputPath ()
152 {
153 return "output-i386";
154 }
155
156 string
157 Directory::GetInstallPath ()
158 {
159 return GetEnvironmentVariablePathOrDefault ( "ROS_INSTALL",
160 "reactos" );
161 }
162
163 void
164 Directory::ResolveVariablesInPath ( char* buf,
165 string path )
166 {
167 string s = ReplaceVariable ( "$(INTERMEDIATE)", GetIntermediatePath (), path );
168 s = ReplaceVariable ( "$(OUTPUT)", GetOutputPath (), s );
169 s = ReplaceVariable ( "$(INSTALL)", GetInstallPath (), s );
170 strcpy ( buf, s.c_str () );
171 }
172
173 void
174 Directory::GenerateTree ( const string& parent,
175 bool verbose )
176 {
177 string path;
178
179 if ( parent.size() )
180 {
181 char buf[256];
182
183 path = parent + SSEP + name;
184 ResolveVariablesInPath ( buf, path );
185 if ( CreateDirectory ( buf ) && verbose )
186 printf ( "Created %s\n", buf );
187 }
188 else
189 path = name;
190
191 for ( directory_map::iterator i = subdirs.begin();
192 i != subdirs.end();
193 ++i )
194 {
195 i->second->GenerateTree ( path, verbose );
196 }
197 }
198
199
200 static class MingwFactory : public Backend::Factory
201 {
202 public:
203 MingwFactory() : Factory ( "mingw" ) {}
204 Backend* operator() ( Project& project, bool verbose )
205 {
206 return new MingwBackend ( project, verbose );
207 }
208 } factory;
209
210
211 MingwBackend::MingwBackend ( Project& project, bool verbose )
212 : Backend ( project, verbose ),
213 intermediateDirectory ( new Directory ("$(INTERMEDIATE)" ) ),
214 outputDirectory ( new Directory ( "$(OUTPUT)" ) ),
215 installDirectory ( new Directory ( "$(INSTALL)" ) )
216 {
217 }
218
219 MingwBackend::~MingwBackend()
220 {
221 delete intermediateDirectory;
222 delete outputDirectory;
223 delete installDirectory;
224 }
225
226 string
227 MingwBackend::AddDirectoryTarget ( const string& directory,
228 Directory* directoryTree )
229 {
230 directoryTree->Add ( directory.c_str() );
231 return directoryTree->name;
232 }
233
234 void
235 MingwBackend::ProcessModules ()
236 {
237 printf ( "Processing modules..." );
238
239 vector<MingwModuleHandler*> v;
240 size_t i;
241 for ( i = 0; i < ProjectNode.modules.size (); i++ )
242 {
243 Module& module = *ProjectNode.modules[i];
244 MingwModuleHandler* h = MingwModuleHandler::InstanciateHandler (
245 module,
246 this );
247 if ( module.host == HostDefault )
248 {
249 module.host = h->DefaultHost();
250 assert ( module.host != HostDefault );
251 }
252 v.push_back ( h );
253 }
254
255 size_t iend = v.size ();
256
257 for ( i = 0; i < iend; i++ )
258 v[i]->GenerateObjectMacro();
259 fprintf ( fMakefile, "\n" );
260 for ( i = 0; i < iend; i++ )
261 v[i]->GenerateTargetMacro();
262 fprintf ( fMakefile, "\n" );
263
264 GenerateAllTarget ( v );
265 GenerateInitTarget ();
266
267 for ( i = 0; i < iend; i++ )
268 v[i]->GenerateOtherMacros();
269
270 for ( i = 0; i < iend; i++ )
271 {
272 MingwModuleHandler& h = *v[i];
273 h.GeneratePreconditionDependencies ();
274 h.Process ();
275 h.GenerateInvocations ();
276 h.GenerateCleanTarget ();
277 delete v[i];
278 }
279
280 printf ( "done\n" );
281 }
282
283 void
284 MingwBackend::Process ()
285 {
286 DetectPipeSupport ();
287 DetectPCHSupport ();
288 CreateMakefile ();
289 GenerateHeader ();
290 GenerateGlobalVariables ();
291 GenerateXmlBuildFilesMacro ();
292 ProcessModules ();
293 GenerateInstallTarget ();
294 GenerateDirectories ();
295 CheckAutomaticDependencies ();
296 CloseMakefile ();
297 }
298
299 void
300 MingwBackend::CreateMakefile ()
301 {
302 fMakefile = fopen ( ProjectNode.makefile.c_str (), "w" );
303 if ( !fMakefile )
304 throw AccessDeniedException ( ProjectNode.makefile );
305 MingwModuleHandler::SetBackend ( this );
306 MingwModuleHandler::SetMakefile ( fMakefile );
307 MingwModuleHandler::SetUsePch ( use_pch );
308 }
309
310 void
311 MingwBackend::CloseMakefile () const
312 {
313 if (fMakefile)
314 fclose ( fMakefile );
315 }
316
317 void
318 MingwBackend::GenerateHeader () const
319 {
320 fprintf ( fMakefile, "# THIS FILE IS AUTOMATICALLY GENERATED, EDIT 'ReactOS.xml' INSTEAD\n\n" );
321 }
322
323 void
324 MingwBackend::GenerateProjectCFlagsMacro ( const char* assignmentOperation,
325 IfableData& data ) const
326 {
327 size_t i;
328
329 fprintf (
330 fMakefile,
331 "PROJECT_CFLAGS %s",
332 assignmentOperation );
333 for ( i = 0; i < data.includes.size(); i++ )
334 {
335 fprintf (
336 fMakefile,
337 " -I%s",
338 data.includes[i]->directory.c_str() );
339 }
340
341 for ( i = 0; i < data.defines.size(); i++ )
342 {
343 Define& d = *data.defines[i];
344 fprintf (
345 fMakefile,
346 " -D%s",
347 d.name.c_str() );
348 if ( d.value.size() )
349 fprintf (
350 fMakefile,
351 "=%s",
352 d.value.c_str() );
353 }
354 fprintf ( fMakefile, "\n" );
355 }
356
357 void
358 MingwBackend::GenerateGlobalCFlagsAndProperties (
359 const char* assignmentOperation,
360 IfableData& data ) const
361 {
362 size_t i;
363
364 for ( i = 0; i < data.properties.size(); i++ )
365 {
366 Property& prop = *data.properties[i];
367 fprintf ( fMakefile, "%s := %s\n",
368 prop.name.c_str(),
369 prop.value.c_str() );
370 }
371
372 if ( data.includes.size() || data.defines.size() )
373 {
374 GenerateProjectCFlagsMacro ( assignmentOperation,
375 data );
376 }
377
378 for ( i = 0; i < data.ifs.size(); i++ )
379 {
380 If& rIf = *data.ifs[i];
381 if ( rIf.data.defines.size()
382 || rIf.data.includes.size()
383 || rIf.data.ifs.size() )
384 {
385 fprintf (
386 fMakefile,
387 "ifeq (\"$(%s)\",\"%s\")\n",
388 rIf.property.c_str(),
389 rIf.value.c_str() );
390 GenerateGlobalCFlagsAndProperties (
391 "+=",
392 rIf.data );
393 fprintf (
394 fMakefile,
395 "endif\n\n" );
396 }
397 }
398 }
399
400 string
401 MingwBackend::GenerateProjectLFLAGS () const
402 {
403 string lflags;
404 for ( size_t i = 0; i < ProjectNode.linkerFlags.size (); i++ )
405 {
406 LinkerFlag& linkerFlag = *ProjectNode.linkerFlags[i];
407 if ( lflags.length () > 0 )
408 lflags += " ";
409 lflags += linkerFlag.flag;
410 }
411 return lflags;
412 }
413
414 void
415 MingwBackend::GenerateGlobalVariables () const
416 {
417 GenerateGlobalCFlagsAndProperties (
418 "=",
419 ProjectNode.non_if_data );
420 fprintf ( fMakefile, "PROJECT_RCFLAGS := $(PROJECT_CFLAGS)\n" );
421 fprintf ( fMakefile, "PROJECT_WIDLFLAGS := $(PROJECT_CFLAGS)\n" );
422 fprintf ( fMakefile, "PROJECT_LFLAGS := %s\n",
423 GenerateProjectLFLAGS ().c_str () );
424 fprintf ( fMakefile, "PROJECT_CFLAGS += -Wall\n" );
425 fprintf ( fMakefile, "\n" );
426 }
427
428 bool
429 MingwBackend::IncludeInAllTarget ( const Module& module ) const
430 {
431 if ( MingwModuleHandler::ReferenceObjects ( module ) )
432 return false;
433 if ( module.type == BootSector )
434 return false;
435 if ( module.type == Iso )
436 return false;
437 if ( module.type == Test )
438 return false;
439 return true;
440 }
441
442 void
443 MingwBackend::GenerateAllTarget ( const vector<MingwModuleHandler*>& handlers ) const
444 {
445 fprintf ( fMakefile, "all:" );
446 int wrap_count = 0;
447 size_t iend = handlers.size ();
448 for ( size_t i = 0; i < iend; i++ )
449 {
450 const Module& module = handlers[i]->module;
451 if ( IncludeInAllTarget ( module ) )
452 {
453 if ( wrap_count++ == 5 )
454 fprintf ( fMakefile, " \\\n\t\t" ), wrap_count = 0;
455 fprintf ( fMakefile,
456 " %s",
457 GetTargetMacro(module).c_str () );
458 }
459 }
460 fprintf ( fMakefile, "\n\t\n\n" );
461 }
462
463 string
464 MingwBackend::GetBuildToolDependencies () const
465 {
466 string dependencies;
467 for ( size_t i = 0; i < ProjectNode.modules.size (); i++ )
468 {
469 Module& module = *ProjectNode.modules[i];
470 if ( module.type == BuildTool )
471 {
472 if ( dependencies.length () > 0 )
473 dependencies += " ";
474 dependencies += module.GetDependencyPath ();
475 }
476 }
477 return dependencies;
478 }
479
480 void
481 MingwBackend::GenerateInitTarget () const
482 {
483 fprintf ( fMakefile,
484 "INIT = %s\n",
485 GetBuildToolDependencies ().c_str () );
486 fprintf ( fMakefile, "\n" );
487 }
488
489 void
490 MingwBackend::GenerateXmlBuildFilesMacro() const
491 {
492 fprintf ( fMakefile,
493 "XMLBUILDFILES = %s \\\n",
494 ProjectNode.GetProjectFilename ().c_str () );
495 string xmlbuildFilenames;
496 int numberOfExistingFiles = 0;
497 for ( size_t i = 0; i < ProjectNode.xmlbuildfiles.size (); i++ )
498 {
499 XMLInclude& xmlbuildfile = *ProjectNode.xmlbuildfiles[i];
500 if ( !xmlbuildfile.fileExists )
501 continue;
502 numberOfExistingFiles++;
503 if ( xmlbuildFilenames.length () > 0 )
504 xmlbuildFilenames += " ";
505 xmlbuildFilenames += NormalizeFilename ( xmlbuildfile.topIncludeFilename );
506 if ( numberOfExistingFiles % 5 == 4 || i == ProjectNode.xmlbuildfiles.size () - 1 )
507 {
508 fprintf ( fMakefile,
509 "\t%s",
510 xmlbuildFilenames.c_str ());
511 if ( i == ProjectNode.xmlbuildfiles.size () - 1 )
512 {
513 fprintf ( fMakefile, "\n" );
514 }
515 else
516 {
517 fprintf ( fMakefile,
518 " \\\n",
519 xmlbuildFilenames.c_str () );
520 }
521 xmlbuildFilenames.resize ( 0 );
522 }
523 numberOfExistingFiles++;
524 }
525 fprintf ( fMakefile, "\n" );
526 }
527
528 void
529 MingwBackend::CheckAutomaticDependencies ()
530 {
531 printf ( "Checking automatic dependencies..." );
532 AutomaticDependency automaticDependency ( ProjectNode );
533 automaticDependency.Process ();
534 automaticDependency.CheckAutomaticDependencies ( verbose );
535 printf ( "done\n" );
536 }
537
538 bool
539 MingwBackend::IncludeDirectoryTarget ( const string& directory ) const
540 {
541 if ( directory == "$(INTERMEDIATE)" SSEP "tools")
542 return false;
543 else
544 return true;
545 }
546
547 void
548 MingwBackend::GenerateDirectories ()
549 {
550 printf ( "Creating directories..." );
551 intermediateDirectory->GenerateTree ( "", verbose );
552 outputDirectory->GenerateTree ( "", verbose );
553 installDirectory->GenerateTree ( "", verbose );
554 printf ( "done\n" );
555 }
556
557 void
558 MingwBackend::DetectPipeSupport ()
559 {
560 printf ( "Detecting compiler -pipe support..." );
561
562 string pipe_detection = "tools" SSEP "rbuild" SSEP "backend" SSEP "mingw" SSEP "pipe_detection.c";
563 string pipe_detectionObjectFilename = ReplaceExtension ( pipe_detection,
564 ".o" );
565 string command = ssprintf (
566 "gcc -pipe -c %s -o %s 2>%s",
567 pipe_detection.c_str (),
568 pipe_detectionObjectFilename.c_str (),
569 NUL );
570 int exitcode = system ( command.c_str () );
571 FILE* f = fopen ( pipe_detectionObjectFilename.c_str (), "rb" );
572 if ( f )
573 {
574 usePipe = (exitcode == 0);
575 fclose ( f );
576 unlink ( pipe_detectionObjectFilename.c_str () );
577 }
578 else
579 usePipe = false;
580
581 if ( usePipe )
582 printf ( "detected\n" );
583 else
584 printf ( "not detected\n" );
585 }
586
587 void
588 MingwBackend::DetectPCHSupport ()
589 {
590 printf ( "Detecting compiler pre-compiled header support..." );
591
592 string path = "tools" SSEP "rbuild" SSEP "backend" SSEP "mingw" SSEP "pch_detection.h";
593 string cmd = ssprintf (
594 "gcc -c %s 2>%s",
595 path.c_str (),
596 NUL );
597 system ( cmd.c_str () );
598 path += ".gch";
599
600 FILE* f = fopen ( path.c_str (), "rb" );
601 if ( f )
602 {
603 use_pch = true;
604 fclose ( f );
605 unlink ( path.c_str () );
606 }
607 else
608 use_pch = false;
609
610 if ( use_pch )
611 printf ( "detected\n" );
612 else
613 printf ( "not detected\n" );
614 }
615
616 void
617 MingwBackend::GetNonModuleInstallTargetFiles (
618 vector<string>& out ) const
619 {
620 for ( size_t i = 0; i < ProjectNode.installfiles.size (); i++ )
621 {
622 const InstallFile& installfile = *ProjectNode.installfiles[i];
623 string targetFilenameNoFixup = installfile.base + SSEP + installfile.newname;
624 string targetFilename = MingwModuleHandler::PassThruCacheDirectory (
625 NormalizeFilename ( targetFilenameNoFixup ),
626 installDirectory );
627 out.push_back ( targetFilename );
628 }
629 }
630
631 void
632 MingwBackend::GetModuleInstallTargetFiles (
633 vector<string>& out ) const
634 {
635 for ( size_t i = 0; i < ProjectNode.modules.size (); i++ )
636 {
637 const Module& module = *ProjectNode.modules[i];
638 if ( module.installName.length () > 0 )
639 {
640 string targetFilenameNoFixup = module.installBase + SSEP + module.installName;
641 string targetFilename = MingwModuleHandler::PassThruCacheDirectory (
642 NormalizeFilename ( targetFilenameNoFixup ),
643 installDirectory );
644 out.push_back ( targetFilename );
645 }
646 }
647 }
648
649 void
650 MingwBackend::GetInstallTargetFiles (
651 vector<string>& out ) const
652 {
653 GetNonModuleInstallTargetFiles ( out );
654 GetModuleInstallTargetFiles ( out );
655 }
656
657 void
658 MingwBackend::OutputInstallTarget ( const string& sourceFilename,
659 const string& targetFilename,
660 const string& targetDirectory )
661 {
662 string normalizedTargetFilename = MingwModuleHandler::PassThruCacheDirectory (
663 NormalizeFilename ( targetDirectory + SSEP + targetFilename ),
664 installDirectory );
665 string normalizedTargetDirectory = MingwModuleHandler::PassThruCacheDirectory (
666 NormalizeFilename ( targetDirectory ),
667 installDirectory );
668 fprintf ( fMakefile,
669 "%s: %s %s\n",
670 normalizedTargetFilename.c_str (),
671 sourceFilename.c_str (),
672 normalizedTargetDirectory.c_str () );
673 fprintf ( fMakefile,
674 "\t$(ECHO_CP)\n" );
675 fprintf ( fMakefile,
676 "\t${cp} %s %s 1>$(NUL)\n",
677 sourceFilename.c_str (),
678 normalizedTargetFilename.c_str () );
679 }
680
681 void
682 MingwBackend::OutputNonModuleInstallTargets ()
683 {
684 for ( size_t i = 0; i < ProjectNode.installfiles.size (); i++ )
685 {
686 const InstallFile& installfile = *ProjectNode.installfiles[i];
687 OutputInstallTarget ( installfile.GetPath (),
688 installfile.newname,
689 installfile.base );
690 }
691 }
692
693 void
694 MingwBackend::OutputModuleInstallTargets ()
695 {
696 for ( size_t i = 0; i < ProjectNode.modules.size (); i++ )
697 {
698 const Module& module = *ProjectNode.modules[i];
699 if ( module.installName.length () > 0 )
700 {
701 string sourceFilename = MingwModuleHandler::PassThruCacheDirectory (
702 NormalizeFilename ( module.GetPath () ),
703 outputDirectory );
704 OutputInstallTarget ( sourceFilename,
705 module.installName,
706 module.installBase );
707 }
708 }
709 }
710
711 string
712 MingwBackend::GetRegistrySourceFiles ()
713 {
714 return "bootdata" SSEP "hivecls.inf "
715 "bootdata" SSEP "hivedef.inf "
716 "bootdata" SSEP "hiveinst.inf "
717 "bootdata" SSEP "hivesft.inf "
718 "bootdata" SSEP "hivesys.inf";
719 }
720
721 string
722 MingwBackend::GetRegistryTargetFiles ()
723 {
724 string system32ConfigDirectory = NormalizeFilename (
725 MingwModuleHandler::PassThruCacheDirectory (
726 "system32" SSEP "config" SSEP,
727 installDirectory ) );
728 return system32ConfigDirectory + SSEP "default " +
729 system32ConfigDirectory + SSEP "sam " +
730 system32ConfigDirectory + SSEP "security " +
731 system32ConfigDirectory + SSEP "software " +
732 system32ConfigDirectory + SSEP "system";
733 }
734
735 void
736 MingwBackend::OutputRegistryInstallTarget ()
737 {
738 string system32ConfigDirectory = NormalizeFilename (
739 MingwModuleHandler::PassThruCacheDirectory (
740 "system32" SSEP "config" SSEP,
741 installDirectory ) );
742
743 string registrySourceFiles = GetRegistrySourceFiles ();
744 string registryTargetFiles = GetRegistryTargetFiles ();
745 fprintf ( fMakefile,
746 "install_registry: %s\n",
747 registryTargetFiles.c_str () );
748 fprintf ( fMakefile,
749 "%s: %s %s $(MKHIVE_TARGET)\n",
750 registryTargetFiles.c_str (),
751 registrySourceFiles.c_str (),
752 system32ConfigDirectory.c_str () );
753 fprintf ( fMakefile,
754 "\t$(ECHO_MKHIVE)\n" );
755 fprintf ( fMakefile,
756 "\t$(MKHIVE_TARGET) bootdata %s bootdata" SSEP "hiveinst.inf\n",
757 system32ConfigDirectory.c_str () );
758 fprintf ( fMakefile,
759 "\n" );
760 }
761
762 void
763 MingwBackend::GenerateInstallTarget ()
764 {
765 vector<string> vInstallTargetFiles;
766 GetInstallTargetFiles ( vInstallTargetFiles );
767 string installTargetFiles = v2s ( vInstallTargetFiles, 5 );
768
769 fprintf ( fMakefile,
770 "install: %s install_registry\n",
771 installTargetFiles.c_str () );
772 OutputNonModuleInstallTargets ();
773 OutputModuleInstallTargets ();
774 OutputRegistryInstallTarget ();
775 fprintf ( fMakefile,
776 "\n" );
777 }