eliminate rmkdir and generate directory dependencies the right way.
[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 "modulehandler.h"
7
8 using std::string;
9 using std::vector;
10 using std::set;
11 using std::map;
12
13 typedef set<string> set_string;
14 typedef map<string,Directory*> directory_map;
15
16 class Directory
17 {
18 public:
19 string name;
20 directory_map subdirs;
21 Directory ( const string& name );
22 void Add ( const char* subdir );
23 void CreateRule ( FILE* f, const string& parent );
24 };
25
26 Directory::Directory ( const string& name_ )
27 : name(name_)
28 {
29 }
30
31 void Directory::Add ( const char* subdir )
32 {
33 const char* p = strpbrk ( subdir, "/\\" );
34 if ( !p )
35 p = subdir + strlen(subdir);
36 string s ( subdir, p-subdir );
37 if ( subdirs.find(s) == subdirs.end() )
38 subdirs[s] = new Directory(s);
39 if ( *p && *++p )
40 subdirs[s]->Add ( p );
41 }
42
43 void
44 Directory::CreateRule ( FILE* f, const string& parent )
45 {
46 string path;
47
48 if ( parent.size() )
49 {
50 fprintf ( f,
51 "%s%c%s: %s\n",
52 parent.c_str (),
53 CSEP,
54 name.c_str (),
55 parent.c_str () );
56
57 fprintf ( f,
58 "\t$(ECHO_MKDIR)\n" );
59
60 fprintf ( f,
61 "\t${mkdir} $@\n" );
62
63 path = parent + SSEP + name;
64 }
65 else
66 path = name;
67
68 for ( directory_map::iterator i = subdirs.begin();
69 i != subdirs.end();
70 ++i )
71 {
72 i->second->CreateRule ( f, path );
73 }
74 }
75
76 static class MingwFactory : public Backend::Factory
77 {
78 public:
79 MingwFactory() : Factory ( "mingw" ) {}
80 Backend* operator() ( Project& project )
81 {
82 return new MingwBackend ( project );
83 }
84 } factory;
85
86
87 MingwBackend::MingwBackend ( Project& project )
88 : Backend ( project ),
89 int_directories ( new Directory("$(INTERMEDIATE)") ),
90 out_directories ( new Directory("$(OUTPUT)") )
91 {
92 }
93
94 MingwBackend::~MingwBackend()
95 {
96 delete int_directories;
97 delete out_directories;
98 }
99
100 string
101 MingwBackend::AddDirectoryTarget ( const string& directory, bool out )
102 {
103 const char* dir_name = "$(INTERMEDIATE)";
104 Directory* dir = int_directories;
105 if ( out )
106 {
107 dir_name = "$(OUTPUT)";
108 dir = out_directories;
109 }
110 dir->Add ( directory.c_str() );
111 return dir_name;
112 }
113
114 void
115 MingwBackend::Process ()
116 {
117 size_t i;
118
119 DetectPCHSupport();
120
121 CreateMakefile ();
122 GenerateHeader ();
123 GenerateGlobalVariables ();
124 GenerateXmlBuildFilesMacro();
125
126 vector<MingwModuleHandler*> v;
127
128 for ( i = 0; i < ProjectNode.modules.size (); i++ )
129 {
130 Module& module = *ProjectNode.modules[i];
131 MingwModuleHandler* h = MingwModuleHandler::InstanciateHandler (
132 module,
133 this );
134 if ( module.host == HostDefault )
135 {
136 module.host = h->DefaultHost();
137 assert ( module.host != HostDefault );
138 }
139 v.push_back ( h );
140 }
141
142 size_t iend = v.size ();
143
144 for ( i = 0; i < iend; i++ )
145 v[i]->GenerateTargetMacro();
146 fprintf ( fMakefile, "\n" );
147
148 GenerateAllTarget ( v );
149 GenerateInitTarget ();
150
151 for ( i = 0; i < iend; i++ )
152 v[i]->GenerateOtherMacros();
153
154 for ( i = 0; i < iend; i++ )
155 {
156 MingwModuleHandler& h = *v[i];
157 h.GeneratePreconditionDependencies ();
158 h.Process ();
159 h.GenerateInvocations ();
160 h.GenerateCleanTarget ();
161 delete v[i];
162 }
163
164 GenerateDirectoryTargets ();
165 CheckAutomaticDependencies ();
166 CloseMakefile ();
167 }
168
169 void
170 MingwBackend::CreateMakefile ()
171 {
172 fMakefile = fopen ( ProjectNode.makefile.c_str (), "w" );
173 if ( !fMakefile )
174 throw AccessDeniedException ( ProjectNode.makefile );
175 MingwModuleHandler::SetBackend ( this );
176 MingwModuleHandler::SetMakefile ( fMakefile );
177 MingwModuleHandler::SetUsePch ( use_pch );
178 }
179
180 void
181 MingwBackend::CloseMakefile () const
182 {
183 if (fMakefile)
184 fclose ( fMakefile );
185 }
186
187 void
188 MingwBackend::GenerateHeader () const
189 {
190 fprintf ( fMakefile, "# THIS FILE IS AUTOMATICALLY GENERATED, EDIT 'ReactOS.xml' INSTEAD\n\n" );
191 }
192
193 void
194 MingwBackend::GenerateProjectCFlagsMacro ( const char* assignmentOperation,
195 IfableData& data ) const
196 {
197 size_t i;
198
199 fprintf (
200 fMakefile,
201 "PROJECT_CFLAGS %s",
202 assignmentOperation );
203 for ( i = 0; i < data.includes.size(); i++ )
204 {
205 fprintf (
206 fMakefile,
207 " -I%s",
208 data.includes[i]->directory.c_str() );
209 }
210
211 for ( i = 0; i < data.defines.size(); i++ )
212 {
213 Define& d = *data.defines[i];
214 fprintf (
215 fMakefile,
216 " -D%s",
217 d.name.c_str() );
218 if ( d.value.size() )
219 fprintf (
220 fMakefile,
221 "=%s",
222 d.value.c_str() );
223 }
224 fprintf ( fMakefile, "\n" );
225 }
226
227 void
228 MingwBackend::GenerateGlobalCFlagsAndProperties (
229 const char* assignmentOperation,
230 IfableData& data ) const
231 {
232 size_t i;
233
234 for ( i = 0; i < data.properties.size(); i++ )
235 {
236 Property& prop = *data.properties[i];
237 fprintf ( fMakefile, "%s := %s\n",
238 prop.name.c_str(),
239 prop.value.c_str() );
240 }
241
242 if ( data.includes.size() || data.defines.size() )
243 {
244 GenerateProjectCFlagsMacro ( assignmentOperation,
245 data );
246 }
247
248 for ( i = 0; i < data.ifs.size(); i++ )
249 {
250 If& rIf = *data.ifs[i];
251 if ( rIf.data.defines.size()
252 || rIf.data.includes.size()
253 || rIf.data.ifs.size() )
254 {
255 fprintf (
256 fMakefile,
257 "ifeq (\"$(%s)\",\"%s\")\n",
258 rIf.property.c_str(),
259 rIf.value.c_str() );
260 GenerateGlobalCFlagsAndProperties (
261 "+=",
262 rIf.data );
263 fprintf (
264 fMakefile,
265 "endif\n\n" );
266 }
267 }
268 }
269
270 string
271 MingwBackend::GenerateProjectLFLAGS () const
272 {
273 string lflags;
274 for ( size_t i = 0; i < ProjectNode.linkerFlags.size (); i++ )
275 {
276 LinkerFlag& linkerFlag = *ProjectNode.linkerFlags[i];
277 if ( lflags.length () > 0 )
278 lflags += " ";
279 lflags += linkerFlag.flag;
280 }
281 return lflags;
282 }
283
284 void
285 MingwBackend::GenerateGlobalVariables () const
286 {
287 GenerateGlobalCFlagsAndProperties (
288 "=",
289 ProjectNode.non_if_data );
290 fprintf ( fMakefile, "PROJECT_RCFLAGS = $(PROJECT_CFLAGS)\n" );
291 fprintf ( fMakefile, "PROJECT_LFLAGS = %s\n",
292 GenerateProjectLFLAGS ().c_str () );
293 fprintf ( fMakefile, "\n" );
294 }
295
296 bool
297 MingwBackend::IncludeInAllTarget ( const Module& module ) const
298 {
299 if ( module.type == ObjectLibrary )
300 return false;
301 if ( module.type == BootSector )
302 return false;
303 if ( module.type == Iso )
304 return false;
305 return true;
306 }
307
308 void
309 MingwBackend::GenerateAllTarget ( const vector<MingwModuleHandler*>& handlers ) const
310 {
311 fprintf ( fMakefile, "all:" );
312 int wrap_count = 0;
313 size_t iend = handlers.size ();
314 for ( size_t i = 0; i < iend; i++ )
315 {
316 const Module& module = handlers[i]->module;
317 if ( IncludeInAllTarget ( module ) )
318 {
319 if ( wrap_count++ == 5 )
320 fprintf ( fMakefile, " \\\n\t\t" ), wrap_count = 0;
321 fprintf ( fMakefile,
322 " %s",
323 GetTargetMacro(module).c_str () );
324 }
325 }
326 fprintf ( fMakefile, "\n\t\n\n" );
327 }
328
329 string
330 MingwBackend::GetBuildToolDependencies () const
331 {
332 string dependencies;
333 for ( size_t i = 0; i < ProjectNode.modules.size (); i++ )
334 {
335 Module& module = *ProjectNode.modules[i];
336 if ( module.type == BuildTool )
337 {
338 if ( dependencies.length () > 0 )
339 dependencies += " ";
340 dependencies += module.GetDependencyPath ();
341 }
342 }
343 return dependencies;
344 }
345
346 void
347 MingwBackend::GenerateInitTarget () const
348 {
349 fprintf ( fMakefile,
350 "INIT = %s\n",
351 GetBuildToolDependencies ().c_str () );
352 fprintf ( fMakefile, "\n" );
353 }
354
355 void
356 MingwBackend::GenerateXmlBuildFilesMacro() const
357 {
358 fprintf ( fMakefile,
359 "XMLBUILDFILES = %s \\\n",
360 ProjectNode.GetProjectFilename ().c_str () );
361 string xmlbuildFilenames;
362 int numberOfExistingFiles = 0;
363 for ( size_t i = 0; i < ProjectNode.xmlbuildfiles.size (); i++ )
364 {
365 XMLInclude& xmlbuildfile = *ProjectNode.xmlbuildfiles[i];
366 if ( !xmlbuildfile.fileExists )
367 continue;
368 numberOfExistingFiles++;
369 if ( xmlbuildFilenames.length () > 0 )
370 xmlbuildFilenames += " ";
371 xmlbuildFilenames += NormalizeFilename ( xmlbuildfile.topIncludeFilename );
372 if ( numberOfExistingFiles % 5 == 4 || i == ProjectNode.xmlbuildfiles.size () - 1 )
373 {
374 fprintf ( fMakefile,
375 "\t%s",
376 xmlbuildFilenames.c_str ());
377 if ( i == ProjectNode.xmlbuildfiles.size () - 1 )
378 {
379 fprintf ( fMakefile, "\n" );
380 }
381 else
382 {
383 fprintf ( fMakefile,
384 " \\\n",
385 xmlbuildFilenames.c_str () );
386 }
387 xmlbuildFilenames.resize ( 0 );
388 }
389 numberOfExistingFiles++;
390 }
391 fprintf ( fMakefile, "\n" );
392 }
393
394 void
395 MingwBackend::CheckAutomaticDependencies ()
396 {
397 AutomaticDependency automaticDependency ( ProjectNode );
398 automaticDependency.Process ();
399 automaticDependency.CheckAutomaticDependencies ();
400 }
401
402 bool
403 MingwBackend::IncludeDirectoryTarget ( const string& directory ) const
404 {
405 if ( directory == "$(INTERMEDIATE)" SSEP "tools")
406 return false;
407 else
408 return true;
409 }
410
411 void
412 MingwBackend::GenerateDirectoryTargets ()
413 {
414 // TODO FIXME - write new directory creation
415 for ( int i = 0; i < 2; i++ )
416 {
417 Directory& d = *(!i ? int_directories : out_directories);
418 if ( i ) fprintf ( fMakefile, "ifneq ($(INTERMEDIATE),$(OUTPUT))\n" );
419 d.CreateRule ( fMakefile, "" );
420 if ( i ) fprintf ( fMakefile, "endif\n" );
421 }
422 /*if ( directories.size () == 0 )
423 return;
424
425 set_string::iterator i;
426 for ( i = directories.begin ();
427 i != directories.end ();
428 i++ )
429 {
430 if ( IncludeDirectoryTarget ( *i ) )
431 {
432 fprintf ( fMakefile,
433 "%s: $(RMKDIR_TARGET)\n",
434 i->c_str () );
435 fprintf ( fMakefile,
436 "\t${mkdir} %s\n\n",
437 i->c_str () );
438 }
439 }
440
441 directories.clear ();*/
442 }
443
444 string
445 FixupTargetFilename ( const string& targetFilename )
446 {
447 return NormalizeFilename ( targetFilename );
448 }
449
450 void
451 MingwBackend::DetectPCHSupport()
452 {
453 #ifdef WIN32
454 string sNUL = "NUL";
455 #else
456 string sNUL = "/dev/null";
457 #endif
458 string path = "tools" SSEP "rbuild" SSEP "backend" SSEP "mingw" SSEP "pch_detection.h";
459 string cmd = ssprintf(
460 "gcc -c %s 2>%s",
461 path.c_str (),
462 sNUL.c_str () );
463 system ( cmd.c_str() );
464 path += ".gch";
465
466 FILE* f = fopen ( path.c_str(), "rb" );
467 if ( f )
468 {
469 use_pch = true;
470 fclose(f);
471 unlink ( path.c_str() );
472 }
473 else
474 use_pch = false;
475
476 // TODO FIXME - eventually check for ROS_USE_PCH env var and
477 // allow that to override use_pch if true
478 }