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