943ef623427977ae8daa49400016e2a997488f83
[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]->GenerateObjectMacro();
146 for ( i = 0; i < iend; i++ )
147 v[i]->GenerateTargetMacro();
148 fprintf ( fMakefile, "\n" );
149
150 GenerateAllTarget ( v );
151 GenerateInitTarget ();
152
153 for ( i = 0; i < iend; i++ )
154 v[i]->GenerateOtherMacros();
155
156 for ( i = 0; i < iend; i++ )
157 {
158 MingwModuleHandler& h = *v[i];
159 h.GeneratePreconditionDependencies ();
160 h.Process ();
161 h.GenerateInvocations ();
162 h.GenerateCleanTarget ();
163 delete v[i];
164 }
165
166 GenerateDirectoryTargets ();
167 CheckAutomaticDependencies ();
168 CloseMakefile ();
169 }
170
171 void
172 MingwBackend::CreateMakefile ()
173 {
174 fMakefile = fopen ( ProjectNode.makefile.c_str (), "w" );
175 if ( !fMakefile )
176 throw AccessDeniedException ( ProjectNode.makefile );
177 MingwModuleHandler::SetBackend ( this );
178 MingwModuleHandler::SetMakefile ( fMakefile );
179 MingwModuleHandler::SetUsePch ( use_pch );
180 }
181
182 void
183 MingwBackend::CloseMakefile () const
184 {
185 if (fMakefile)
186 fclose ( fMakefile );
187 }
188
189 void
190 MingwBackend::GenerateHeader () const
191 {
192 fprintf ( fMakefile, "# THIS FILE IS AUTOMATICALLY GENERATED, EDIT 'ReactOS.xml' INSTEAD\n\n" );
193 }
194
195 void
196 MingwBackend::GenerateProjectCFlagsMacro ( const char* assignmentOperation,
197 IfableData& data ) const
198 {
199 size_t i;
200
201 fprintf (
202 fMakefile,
203 "PROJECT_CFLAGS %s",
204 assignmentOperation );
205 for ( i = 0; i < data.includes.size(); i++ )
206 {
207 fprintf (
208 fMakefile,
209 " -I%s",
210 data.includes[i]->directory.c_str() );
211 }
212
213 for ( i = 0; i < data.defines.size(); i++ )
214 {
215 Define& d = *data.defines[i];
216 fprintf (
217 fMakefile,
218 " -D%s",
219 d.name.c_str() );
220 if ( d.value.size() )
221 fprintf (
222 fMakefile,
223 "=%s",
224 d.value.c_str() );
225 }
226 fprintf ( fMakefile, "\n" );
227 }
228
229 void
230 MingwBackend::GenerateGlobalCFlagsAndProperties (
231 const char* assignmentOperation,
232 IfableData& data ) const
233 {
234 size_t i;
235
236 for ( i = 0; i < data.properties.size(); i++ )
237 {
238 Property& prop = *data.properties[i];
239 fprintf ( fMakefile, "%s := %s\n",
240 prop.name.c_str(),
241 prop.value.c_str() );
242 }
243
244 if ( data.includes.size() || data.defines.size() )
245 {
246 GenerateProjectCFlagsMacro ( assignmentOperation,
247 data );
248 }
249
250 for ( i = 0; i < data.ifs.size(); i++ )
251 {
252 If& rIf = *data.ifs[i];
253 if ( rIf.data.defines.size()
254 || rIf.data.includes.size()
255 || rIf.data.ifs.size() )
256 {
257 fprintf (
258 fMakefile,
259 "ifeq (\"$(%s)\",\"%s\")\n",
260 rIf.property.c_str(),
261 rIf.value.c_str() );
262 GenerateGlobalCFlagsAndProperties (
263 "+=",
264 rIf.data );
265 fprintf (
266 fMakefile,
267 "endif\n\n" );
268 }
269 }
270 }
271
272 string
273 MingwBackend::GenerateProjectLFLAGS () const
274 {
275 string lflags;
276 for ( size_t i = 0; i < ProjectNode.linkerFlags.size (); i++ )
277 {
278 LinkerFlag& linkerFlag = *ProjectNode.linkerFlags[i];
279 if ( lflags.length () > 0 )
280 lflags += " ";
281 lflags += linkerFlag.flag;
282 }
283 return lflags;
284 }
285
286 void
287 MingwBackend::GenerateGlobalVariables () const
288 {
289 GenerateGlobalCFlagsAndProperties (
290 "=",
291 ProjectNode.non_if_data );
292 fprintf ( fMakefile, "PROJECT_RCFLAGS = $(PROJECT_CFLAGS)\n" );
293 fprintf ( fMakefile, "PROJECT_LFLAGS = %s\n",
294 GenerateProjectLFLAGS ().c_str () );
295 fprintf ( fMakefile, "\n" );
296 }
297
298 bool
299 MingwBackend::IncludeInAllTarget ( const Module& module ) const
300 {
301 if ( module.type == ObjectLibrary )
302 return false;
303 if ( module.type == BootSector )
304 return false;
305 if ( module.type == Iso )
306 return false;
307 return true;
308 }
309
310 void
311 MingwBackend::GenerateAllTarget ( const vector<MingwModuleHandler*>& handlers ) const
312 {
313 fprintf ( fMakefile, "all:" );
314 int wrap_count = 0;
315 size_t iend = handlers.size ();
316 for ( size_t i = 0; i < iend; i++ )
317 {
318 const Module& module = handlers[i]->module;
319 if ( IncludeInAllTarget ( module ) )
320 {
321 if ( wrap_count++ == 5 )
322 fprintf ( fMakefile, " \\\n\t\t" ), wrap_count = 0;
323 fprintf ( fMakefile,
324 " %s",
325 GetTargetMacro(module).c_str () );
326 }
327 }
328 fprintf ( fMakefile, "\n\t\n\n" );
329 }
330
331 string
332 MingwBackend::GetBuildToolDependencies () const
333 {
334 string dependencies;
335 for ( size_t i = 0; i < ProjectNode.modules.size (); i++ )
336 {
337 Module& module = *ProjectNode.modules[i];
338 if ( module.type == BuildTool )
339 {
340 if ( dependencies.length () > 0 )
341 dependencies += " ";
342 dependencies += module.GetDependencyPath ();
343 }
344 }
345 return dependencies;
346 }
347
348 void
349 MingwBackend::GenerateInitTarget () const
350 {
351 fprintf ( fMakefile,
352 "INIT = %s\n",
353 GetBuildToolDependencies ().c_str () );
354 fprintf ( fMakefile, "\n" );
355 }
356
357 void
358 MingwBackend::GenerateXmlBuildFilesMacro() const
359 {
360 fprintf ( fMakefile,
361 "XMLBUILDFILES = %s \\\n",
362 ProjectNode.GetProjectFilename ().c_str () );
363 string xmlbuildFilenames;
364 int numberOfExistingFiles = 0;
365 for ( size_t i = 0; i < ProjectNode.xmlbuildfiles.size (); i++ )
366 {
367 XMLInclude& xmlbuildfile = *ProjectNode.xmlbuildfiles[i];
368 if ( !xmlbuildfile.fileExists )
369 continue;
370 numberOfExistingFiles++;
371 if ( xmlbuildFilenames.length () > 0 )
372 xmlbuildFilenames += " ";
373 xmlbuildFilenames += NormalizeFilename ( xmlbuildfile.topIncludeFilename );
374 if ( numberOfExistingFiles % 5 == 4 || i == ProjectNode.xmlbuildfiles.size () - 1 )
375 {
376 fprintf ( fMakefile,
377 "\t%s",
378 xmlbuildFilenames.c_str ());
379 if ( i == ProjectNode.xmlbuildfiles.size () - 1 )
380 {
381 fprintf ( fMakefile, "\n" );
382 }
383 else
384 {
385 fprintf ( fMakefile,
386 " \\\n",
387 xmlbuildFilenames.c_str () );
388 }
389 xmlbuildFilenames.resize ( 0 );
390 }
391 numberOfExistingFiles++;
392 }
393 fprintf ( fMakefile, "\n" );
394 }
395
396 void
397 MingwBackend::CheckAutomaticDependencies ()
398 {
399 AutomaticDependency automaticDependency ( ProjectNode );
400 automaticDependency.Process ();
401 automaticDependency.CheckAutomaticDependencies ();
402 }
403
404 bool
405 MingwBackend::IncludeDirectoryTarget ( const string& directory ) const
406 {
407 if ( directory == "$(INTERMEDIATE)" SSEP "tools")
408 return false;
409 else
410 return true;
411 }
412
413 void
414 MingwBackend::GenerateDirectoryTargets ()
415 {
416 // TODO FIXME - write new directory creation
417 for ( int i = 0; i < 2; i++ )
418 {
419 Directory& d = *(!i ? int_directories : out_directories);
420 if ( i ) fprintf ( fMakefile, "ifneq ($(INTERMEDIATE),$(OUTPUT))\n" );
421 d.CreateRule ( fMakefile, "" );
422 if ( i ) fprintf ( fMakefile, "endif\n" );
423 }
424 }
425
426 string
427 FixupTargetFilename ( const string& targetFilename )
428 {
429 return NormalizeFilename ( targetFilename );
430 }
431
432 void
433 MingwBackend::DetectPCHSupport()
434 {
435 #ifdef WIN32
436 string sNUL = "NUL";
437 #else
438 string sNUL = "/dev/null";
439 #endif
440 string path = "tools" SSEP "rbuild" SSEP "backend" SSEP "mingw" SSEP "pch_detection.h";
441 string cmd = ssprintf(
442 "gcc -c %s 2>%s",
443 path.c_str (),
444 sNUL.c_str () );
445 system ( cmd.c_str() );
446 path += ".gch";
447
448 FILE* f = fopen ( path.c_str(), "rb" );
449 if ( f )
450 {
451 use_pch = true;
452 fclose(f);
453 unlink ( path.c_str() );
454 }
455 else
456 use_pch = false;
457
458 // TODO FIXME - eventually check for ROS_USE_PCH env var and
459 // allow that to override use_pch if true
460 }