[DXSDK]
[reactos.git] / reactos / tools / rbuild / project.cpp
1 /*
2 * Copyright (C) 2005 Casper S. Hornstrup
3 * Copyright (C) 2008 Hervé Poussineau
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 #include "pch.h"
20 #include <assert.h>
21
22 #include "rbuild.h"
23 #include "backend/backend.h"
24
25 using std::string;
26 using std::vector;
27
28 /* static */ string
29 Environment::GetVariable ( const string& name )
30 {
31 char* value = getenv ( name.c_str () );
32 if ( value != NULL && strlen ( value ) > 0 )
33 return ssprintf ( "%s",
34 value );
35 else
36 return "";
37 }
38
39 string
40 Environment::GetArch ()
41 {
42 return GetEnvironmentVariablePathOrDefault ( "ROS_ARCH", "i386" );
43 }
44
45 /* static */ string
46 Environment::GetEnvironmentVariablePathOrDefault ( const string& name,
47 const string& defaultValue )
48 {
49 const string& environmentVariableValue = Environment::GetVariable ( name );
50 if ( environmentVariableValue.length () > 0 )
51 return NormalizeFilename ( environmentVariableValue );
52 else
53 return defaultValue;
54 }
55
56 /* static */ string
57 Environment::GetIntermediatePath ()
58 {
59 string defaultIntermediate =
60 string( "obj-" ) + GetArch ();
61 return GetEnvironmentVariablePathOrDefault ( "ROS_INTERMEDIATE",
62 defaultIntermediate );
63 }
64
65 /* static */ string
66 Environment::GetSourcePath ()
67 {
68 char temp[_MAX_PATH];
69 getcwd(temp, _MAX_PATH);
70 return string(temp);
71 }
72
73 string
74 Environment::GetOutputPath ()
75 {
76 string defaultOutput =
77 string( "output-" ) + GetArch ();
78 return GetEnvironmentVariablePathOrDefault ( "ROS_OUTPUT",
79 defaultOutput );
80 }
81
82 /* static */ string
83 Environment::GetInstallPath ()
84 {
85 string defaultInstall = GetCdOutputPath ();
86 return GetEnvironmentVariablePathOrDefault ( "ROS_INSTALL",
87 defaultInstall );
88 }
89
90 /* static */ string
91 Environment::GetCdOutputPath ()
92 {
93 return GetEnvironmentVariablePathOrDefault ( "ROS_CDOUTPUT",
94 "reactos" );
95 }
96
97 /* static */ string
98 Environment::GetAutomakeFile ( const std::string& defaultFile )
99 {
100 return GetEnvironmentVariablePathOrDefault ( "ROS_AUTOMAKE",
101 defaultFile );
102 }
103
104 ParseContext::ParseContext ()
105 : compilationUnit (NULL)
106 {
107 }
108
109
110 FileLocation::FileLocation ( const DirectoryLocation directory,
111 const std::string& relative_path,
112 const std::string& name,
113 const XMLElement *node )
114 : directory ( directory ),
115 relative_path ( NormalizeFilename ( relative_path ) ),
116 name ( name )
117 {
118 if ( relative_path[0] == '/' ||
119 relative_path[0] == '\\' ||
120 relative_path.find ( '$' ) != string::npos ||
121 ( relative_path.length () > 1 && ( relative_path[1] == ':' ||
122 relative_path.find_last_of ( "/\\" ) == relative_path.length () - 1 ) ) ||
123 ( relative_path.length () > 3 && relative_path.find ( ':' ) != string::npos )
124 )
125 {
126 if ( node )
127 throw InvalidOperationException ( __FILE__,
128 __LINE__,
129 "Invalid relative path '%s' at %s",
130 relative_path.c_str (),
131 node->location.c_str () );
132 else
133 throw InvalidOperationException ( __FILE__,
134 __LINE__,
135 "Invalid relative path '%s'",
136 relative_path.c_str () );
137 }
138
139 if ( name.find_first_of ( "/\\:" ) != string::npos )
140 {
141 if ( node )
142 throw InvalidOperationException ( __FILE__,
143 __LINE__,
144 "Invalid file name '%s' at %s",
145 name.c_str (),
146 node->location.c_str () );
147 else
148 throw InvalidOperationException ( __FILE__,
149 __LINE__,
150 "Invalid file name '%s'",
151 name.c_str () );
152 }
153 }
154
155
156 FileLocation::FileLocation ( const FileLocation& other )
157 : directory ( other.directory ),
158 relative_path ( other.relative_path ),
159 name ( other.name )
160 {
161 }
162
163
164 Project::Project ( const Configuration& configuration,
165 const string& filename,
166 const std::map<std::string, std::string>* properties )
167 : xmlfile (filename),
168 node (NULL),
169 head (NULL),
170 configuration (configuration)
171 {
172 _backend = NULL;
173
174 if ( properties )
175 {
176 std::map<string, string>::const_iterator it;
177 for (it = properties->begin (); it != properties->end (); it++)
178 {
179 const Property *existing = LookupProperty( it->first );
180 if ( !existing )
181 {
182 Property* property = new Property ( *this, NULL, it->first, it->second );
183 non_if_data.properties.insert ( std::make_pair ( property->name, property ) );
184 }
185 }
186 }
187
188 ReadXml();
189 }
190
191 Project::~Project ()
192 {
193 size_t i;
194 if ( _backend )
195 delete _backend;
196 #ifdef NOT_NEEDED_SINCE_THESE_ARE_CLEANED_BY_IFABLE_DATA
197 for ( i = 0; i < modules.size (); i++ )
198 delete modules[i];
199 #endif
200 for ( i = 0; i < linkerFlags.size (); i++ )
201 delete linkerFlags[i];
202 for ( i = 0; i < cdfiles.size (); i++ )
203 delete cdfiles[i];
204 for ( i = 0; i < installfiles.size (); i++ )
205 delete installfiles[i];
206 if ( head )
207 delete head;
208 }
209
210 const Property*
211 Project::LookupProperty ( const string& name ) const
212 {
213 std::map<std::string, Property*>::const_iterator p = non_if_data.properties.find(name);
214
215 if ( p == non_if_data.properties.end () )
216 return NULL;
217
218 return p->second;
219 }
220
221 string
222 Project::ResolveNextProperty ( const string& s ) const
223 {
224 size_t i = s.find ( "${" );
225 if ( i == string::npos )
226 i = s.find ( "$(" );
227 if ( i != string::npos )
228 {
229 string endCharacter;
230 if ( s[i + 1] == '{' )
231 endCharacter = "}";
232 else
233 endCharacter = ")";
234 size_t j = s.find ( endCharacter );
235 if ( j != string::npos )
236 {
237 int propertyNameLength = j - i - 2;
238 string propertyName = s.substr ( i + 2, propertyNameLength );
239 const Property* property = LookupProperty ( propertyName );
240 if ( property != NULL )
241 return string ( s ).replace ( i, propertyNameLength + 3, property->value );
242 }
243 }
244 return s;
245 }
246
247 string
248 Project::ResolveProperties ( const string& s ) const
249 {
250 string s2 = s;
251 string s3;
252 do
253 {
254 s3 = s2;
255 s2 = ResolveNextProperty ( s3 );
256 } while ( s2 != s3 );
257 return s2;
258 }
259
260 void
261 Project::ExecuteInvocations ()
262 {
263 for( std::map<std::string, Module*>::const_iterator p = modules.begin(); p != modules.end(); ++ p )
264 p->second->InvokeModule ();
265 }
266
267 void
268 Project::ReadXml ()
269 {
270 Path path;
271 head = XMLLoadFile ( xmlfile, path, xmlbuildfiles );
272 node = NULL;
273 for ( size_t i = 0; i < head->subElements.size (); i++ )
274 {
275 if ( head->subElements[i]->name == "project" )
276 {
277 node = head->subElements[i];
278 string path;
279 ProcessXML ( path );
280 return;
281 }
282 }
283
284 if (node == NULL)
285 node = head->subElements[0];
286
287 throw XMLInvalidBuildFileException (
288 node->location,
289 "Document contains no 'project' tag." );
290 }
291
292 void
293 Project::ProcessXML ( const string& path )
294 {
295 const XMLAttribute *att;
296 if ( node->name != "project" )
297 throw Exception ( "internal tool error: Project::ProcessXML() called with non-<project> node" );
298
299 att = node->GetAttribute ( "name", false );
300 if ( !att )
301 name = "Unnamed";
302 else
303 name = att->value;
304
305 att = node->GetAttribute ( "makefile", true );
306 assert(att);
307 makefile = Environment::GetAutomakeFile ( att->value );
308
309 att = node->GetAttribute ( "allowwarnings", false );
310 allowWarningsSet = att != NULL;
311 if ( att != NULL )
312 allowWarnings = att->value == "true";
313
314 size_t i;
315 for ( i = 0; i < node->subElements.size (); i++ )
316 {
317 ParseContext parseContext;
318 ProcessXMLSubElement ( *node->subElements[i], path, parseContext );
319 }
320
321 non_if_data.ProcessXML ();
322 host_non_if_data.ProcessXML ();
323
324 non_if_data.ExtractModules( modules );
325
326 for ( i = 0; i < linkerFlags.size (); i++ )
327 linkerFlags[i]->ProcessXML ();
328 for( std::map<std::string, Module*>::const_iterator p = modules.begin(); p != modules.end(); ++ p )
329 p->second->ProcessXML ();
330 for ( i = 0; i < cdfiles.size (); i++ )
331 cdfiles[i]->ProcessXML ();
332 for ( i = 0; i < installfiles.size (); i++ )
333 installfiles[i]->ProcessXML ();
334 }
335
336 void
337 Project::ProcessXMLSubElement ( const XMLElement& e,
338 const string& path,
339 ParseContext& parseContext )
340 {
341 const XMLAttribute* att;
342
343 att = e.GetAttribute ( "compilerset", false );
344
345 if ( att )
346 {
347 CompilerSet compilerSet;
348
349 if ( att->value == "msc" )
350 compilerSet = MicrosoftC;
351 else if ( att->value == "gcc" )
352 compilerSet = GnuGcc;
353 else
354 throw InvalidAttributeValueException (
355 e.location,
356 "compilerset",
357 att->value );
358
359 if ( compilerSet != configuration.Compiler )
360 return;
361 }
362
363 att = e.GetAttribute ( "linkerset", false );
364
365 if ( att )
366 {
367 LinkerSet linkerSet;
368
369 if ( att->value == "mslink" )
370 linkerSet = MicrosoftLink;
371 else if ( att->value == "ld" )
372 linkerSet = GnuLd;
373 else
374 throw InvalidAttributeValueException (
375 e.location,
376 "linkerset",
377 att->value );
378
379 if ( linkerSet != configuration.Linker )
380 return;
381 }
382
383 bool subs_invalid = false;
384
385 string subpath(path);
386 if ( e.name == "module" )
387 {
388 Module* module = new Module ( *this, e, path );
389 if ( LocateModule ( module->name ) )
390 throw XMLInvalidBuildFileException (
391 node->location,
392 "module name conflict: '%s' (originally defined at %s)",
393 module->name.c_str(),
394 module->node.location.c_str() );
395 non_if_data.modules.push_back ( module );
396 return; // defer processing until later
397 }
398 else if ( e.name == "cdfile" )
399 {
400 CDFile* cdfile = new CDFile ( *this, e, path );
401 cdfiles.push_back ( cdfile );
402 subs_invalid = true;
403 }
404 else if ( e.name == "installfile" )
405 {
406 InstallFile* installfile = new InstallFile ( *this, e, path );
407 installfiles.push_back ( installfile );
408 subs_invalid = true;
409 }
410 else if ( e.name == "directory" )
411 {
412 const XMLAttribute* att = e.GetAttribute ( "name", true );
413 assert(att);
414 subpath = GetSubPath ( *this, e.location, path, att->value );
415 }
416 else if ( e.name == "include" )
417 {
418 const XMLAttribute* host = e.GetAttribute("host", false);
419 Include* include = new Include ( *this, &e );
420
421 if(host && host->value == "true")
422 host_non_if_data.includes.push_back(include);
423 else
424 non_if_data.includes.push_back ( include );
425
426 subs_invalid = true;
427 }
428 else if ( e.name == "define" || e.name == "redefine" )
429 {
430 const XMLAttribute* host = e.GetAttribute("host", false);
431 Define* define = new Define ( *this, e );
432
433 if(host && host->value == "true")
434 host_non_if_data.defines.push_back(define);
435 else
436 non_if_data.defines.push_back ( define );
437
438 subs_invalid = true;
439 }
440 else if ( e.name == "compilerflag" )
441 {
442 CompilerFlag* pCompilerFlag = new CompilerFlag ( *this, e );
443 non_if_data.compilerFlags.push_back ( pCompilerFlag );
444 subs_invalid = true;
445 }
446 else if ( e.name == "linkerflag" )
447 {
448 linkerFlags.push_back ( new LinkerFlag ( *this, e ) );
449 subs_invalid = true;
450 }
451 else if ( e.name == "if" || e.name == "ifnot" )
452 {
453 const XMLAttribute* name;
454 name = e.GetAttribute ( "property", true );
455 assert( name );
456 const Property *property = LookupProperty( name->value );
457 const string *PropertyValue;
458 const string EmptyString;
459
460 if (property)
461 {
462 PropertyValue = &property->value;
463 }
464 else
465 {
466 // Property does not exist, treat it as being empty
467 PropertyValue = &EmptyString;
468 }
469
470 const XMLAttribute* value;
471 value = e.GetAttribute ( "value", true );
472 assert( value );
473
474 bool negate = ( e.name == "ifnot" );
475 bool equality = ( *PropertyValue == value->value );
476 if ( equality == negate )
477 {
478 // Failed, skip this element
479 if ( configuration.Verbose )
480 printf("Skipping 'If' at %s\n", e.location.c_str () );
481 return;
482 }
483 subs_invalid = false;
484 }
485 else if ( e.name == "property" )
486 {
487 Property* property = new Property ( e, *this, NULL );
488 non_if_data.properties.insert ( std::make_pair ( property->name, property ) );
489 }
490 if ( subs_invalid && e.subElements.size() )
491 {
492 throw XMLInvalidBuildFileException (
493 e.location,
494 "<%s> cannot have sub-elements",
495 e.name.c_str() );
496 }
497 for ( size_t i = 0; i < e.subElements.size (); i++ )
498 ProcessXMLSubElement ( *e.subElements[i], subpath, parseContext );
499 }
500
501 Module*
502 Project::LocateModule ( const string& name )
503 {
504 std::map<std::string, Module*>::const_iterator p = modules.find(name);
505
506 if ( p == modules.end() )
507 return NULL;
508
509 return p->second;
510 }
511
512 const Module*
513 Project::LocateModule ( const string& name ) const
514 {
515 std::map<std::string, Module*>::const_iterator p = modules.find(name);
516
517 if ( p == modules.end() )
518 return NULL;
519
520 return p->second;
521 }
522
523 const std::string&
524 Project::GetProjectFilename () const
525 {
526 return xmlfile;
527 }
528
529 std::string
530 Project::GetCompilerSet () const
531 {
532 switch ( configuration.Compiler )
533 {
534 case GnuGcc: return "gcc";
535 case MicrosoftC: return "msc";
536 default: assert ( false );
537 }
538 }
539
540 std::string
541 Project::GetLinkerSet () const
542 {
543 switch ( configuration.Linker )
544 {
545 case GnuLd: return "ld";
546 case MicrosoftLink: return "mslink";
547 default: assert ( false );
548 }
549 }