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