Rbuild rationalization commit, umpteenth of infinite
[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
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 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::GetOutputPath ()
67 {
68 string defaultOutput =
69 string( "output-" ) + GetArch ();
70 return GetEnvironmentVariablePathOrDefault ( "ROS_OUTPUT",
71 defaultOutput );
72 }
73
74 /* static */ string
75 Environment::GetInstallPath ()
76 {
77 string defaultInstall = GetCdOutputPath ();
78 return GetEnvironmentVariablePathOrDefault ( "ROS_INSTALL",
79 defaultInstall );
80 }
81
82 /* static */ string
83 Environment::GetCdOutputPath ()
84 {
85 return GetEnvironmentVariablePathOrDefault ( "ROS_CDOUTPUT",
86 "reactos" );
87 }
88
89 /* static */ string
90 Environment::GetAutomakeFile ( const std::string& defaultFile )
91 {
92 return GetEnvironmentVariablePathOrDefault ( "ROS_AUTOMAKE",
93 defaultFile );
94 }
95
96 ParseContext::ParseContext ()
97 : compilationUnit (NULL)
98 {
99 }
100
101
102 FileLocation::FileLocation ( const DirectoryLocation directory,
103 const std::string& relative_path,
104 const std::string& name,
105 const XMLElement *node )
106 : directory ( directory ),
107 relative_path ( NormalizeFilename ( relative_path ) ),
108 name ( name )
109 {
110 if ( relative_path[0] == '/' ||
111 relative_path[0] == '\\' ||
112 relative_path.find ( '$' ) != string::npos ||
113 ( relative_path.length () > 1 && ( relative_path[1] == ':' ||
114 relative_path.find_last_of ( "/\\" ) == relative_path.length () - 1 ) ) ||
115 ( relative_path.length () > 3 && relative_path.find ( ':' ) != string::npos )
116 )
117 {
118 if ( node )
119 throw InvalidOperationException ( __FILE__,
120 __LINE__,
121 "Invalid relative path '%s' at %s",
122 relative_path.c_str (),
123 node->location.c_str () );
124 else
125 throw InvalidOperationException ( __FILE__,
126 __LINE__,
127 "Invalid relative path '%s'",
128 relative_path.c_str () );
129 }
130
131 if ( name.find_first_of ( "/\\:" ) != string::npos )
132 {
133 if ( node )
134 throw InvalidOperationException ( __FILE__,
135 __LINE__,
136 "Invalid file name '%s' at %s",
137 name.c_str (),
138 node->location.c_str () );
139 else
140 throw InvalidOperationException ( __FILE__,
141 __LINE__,
142 "Invalid file name '%s'",
143 name.c_str () );
144 }
145 }
146
147
148 FileLocation::FileLocation ( const FileLocation& other )
149 : directory ( other.directory ),
150 relative_path ( other.relative_path ),
151 name ( other.name )
152 {
153 }
154
155
156 Project::Project ( const Configuration& configuration,
157 const string& filename,
158 const std::map<std::string, std::string>* properties )
159 : xmlfile (filename),
160 node (NULL),
161 head (NULL),
162 configuration (configuration)
163 {
164 _backend = NULL;
165
166 if ( properties )
167 {
168 std::map<string, string>::const_iterator it;
169 for (it = properties->begin (); it != properties->end (); it++)
170 {
171 const Property *existing = LookupProperty( it->first );
172 if ( !existing )
173 {
174 Property* property = new Property ( *this, NULL, it->first, it->second );
175 non_if_data.properties.insert ( std::make_pair ( property->name, property ) );
176 }
177 }
178 }
179
180 ReadXml();
181 }
182
183 Project::~Project ()
184 {
185 size_t i;
186 if ( _backend )
187 delete _backend;
188 #ifdef NOT_NEEDED_SINCE_THESE_ARE_CLEANED_BY_IFABLE_DATA
189 for ( i = 0; i < modules.size (); i++ )
190 delete modules[i];
191 #endif
192 for ( i = 0; i < linkerFlags.size (); i++ )
193 delete linkerFlags[i];
194 for ( i = 0; i < cdfiles.size (); i++ )
195 delete cdfiles[i];
196 for ( i = 0; i < installfiles.size (); i++ )
197 delete installfiles[i];
198 if ( head )
199 delete head;
200 }
201
202 const Property*
203 Project::LookupProperty ( const string& name ) const
204 {
205 std::map<std::string, Property*>::const_iterator p = non_if_data.properties.find(name);
206
207 if ( p == non_if_data.properties.end () )
208 return NULL;
209
210 return p->second;
211 }
212
213 string
214 Project::ResolveNextProperty ( const string& s ) const
215 {
216 size_t i = s.find ( "${" );
217 if ( i == string::npos )
218 i = s.find ( "$(" );
219 if ( i != string::npos )
220 {
221 string endCharacter;
222 if ( s[i + 1] == '{' )
223 endCharacter = "}";
224 else
225 endCharacter = ")";
226 size_t j = s.find ( endCharacter );
227 if ( j != string::npos )
228 {
229 int propertyNameLength = j - i - 2;
230 string propertyName = s.substr ( i + 2, propertyNameLength );
231 const Property* property = LookupProperty ( propertyName );
232 if ( property != NULL )
233 return string ( s ).replace ( i, propertyNameLength + 3, property->value );
234 }
235 }
236 return s;
237 }
238
239 string
240 Project::ResolveProperties ( const string& s ) const
241 {
242 string s2 = s;
243 string s3;
244 do
245 {
246 s3 = s2;
247 s2 = ResolveNextProperty ( s3 );
248 } while ( s2 != s3 );
249 return s2;
250 }
251
252 void
253 Project::ExecuteInvocations ()
254 {
255 for( std::map<std::string, Module*>::const_iterator p = modules.begin(); p != modules.end(); ++ p )
256 p->second->InvokeModule ();
257 }
258
259 void
260 Project::ReadXml ()
261 {
262 Path path;
263 head = XMLLoadFile ( xmlfile, path, xmlbuildfiles );
264 node = NULL;
265 for ( size_t i = 0; i < head->subElements.size (); i++ )
266 {
267 if ( head->subElements[i]->name == "project" )
268 {
269 node = head->subElements[i];
270 string path;
271 ProcessXML ( path );
272 return;
273 }
274 }
275
276 if (node == NULL)
277 node = head->subElements[0];
278
279 throw XMLInvalidBuildFileException (
280 node->location,
281 "Document contains no 'project' tag." );
282 }
283
284 void
285 Project::ProcessXML ( const string& path )
286 {
287 const XMLAttribute *att;
288 if ( node->name != "project" )
289 throw Exception ( "internal tool error: Project::ProcessXML() called with non-<project> node" );
290
291 att = node->GetAttribute ( "name", false );
292 if ( !att )
293 name = "Unnamed";
294 else
295 name = att->value;
296
297 att = node->GetAttribute ( "makefile", true );
298 assert(att);
299 makefile = Environment::GetAutomakeFile ( att->value );
300
301 att = node->GetAttribute ( "allowwarnings", false );
302 allowWarningsSet = att != NULL;
303 if ( att != NULL )
304 allowWarnings = att->value == "true";
305
306 size_t i;
307 for ( i = 0; i < node->subElements.size (); i++ )
308 {
309 ParseContext parseContext;
310 ProcessXMLSubElement ( *node->subElements[i], path, parseContext );
311 }
312
313 non_if_data.ProcessXML ();
314 host_non_if_data.ProcessXML ();
315
316 non_if_data.ExtractModules( modules );
317
318 for ( i = 0; i < linkerFlags.size (); i++ )
319 linkerFlags[i]->ProcessXML ();
320 for( std::map<std::string, Module*>::const_iterator p = modules.begin(); p != modules.end(); ++ p )
321 p->second->ProcessXML ();
322 for ( i = 0; i < cdfiles.size (); i++ )
323 cdfiles[i]->ProcessXML ();
324 for ( i = 0; i < installfiles.size (); i++ )
325 installfiles[i]->ProcessXML ();
326 }
327
328 void
329 Project::ProcessXMLSubElement ( const XMLElement& e,
330 const string& path,
331 ParseContext& parseContext )
332 {
333 bool subs_invalid = false;
334
335 string subpath(path);
336 if ( e.name == "module" )
337 {
338 Module* module = new Module ( *this, e, path );
339 if ( LocateModule ( module->name ) )
340 throw XMLInvalidBuildFileException (
341 node->location,
342 "module name conflict: '%s' (originally defined at %s)",
343 module->name.c_str(),
344 module->node.location.c_str() );
345 non_if_data.modules.push_back ( module );
346 return; // defer processing until later
347 }
348 else if ( e.name == "cdfile" )
349 {
350 CDFile* cdfile = new CDFile ( *this, e, path );
351 cdfiles.push_back ( cdfile );
352 subs_invalid = true;
353 }
354 else if ( e.name == "installfile" )
355 {
356 InstallFile* installfile = new InstallFile ( *this, e, path );
357 installfiles.push_back ( installfile );
358 subs_invalid = true;
359 }
360 else if ( e.name == "directory" )
361 {
362 const XMLAttribute* att = e.GetAttribute ( "name", true );
363 assert(att);
364 subpath = GetSubPath ( *this, e.location, path, att->value );
365 }
366 else if ( e.name == "include" )
367 {
368 const XMLAttribute* host = e.GetAttribute("host", false);
369 Include* include = new Include ( *this, &e );
370
371 if(host && host->value == "true")
372 host_non_if_data.includes.push_back(include);
373 else
374 non_if_data.includes.push_back ( include );
375
376 subs_invalid = true;
377 }
378 else if ( e.name == "define" || e.name == "redefine" )
379 {
380 const XMLAttribute* host = e.GetAttribute("host", false);
381 Define* define = new Define ( *this, e );
382
383 if(host && host->value == "true")
384 host_non_if_data.defines.push_back(define);
385 else
386 non_if_data.defines.push_back ( define );
387
388 subs_invalid = true;
389 }
390 else if ( e.name == "compilerflag" )
391 {
392 CompilerFlag* pCompilerFlag = new CompilerFlag ( *this, e );
393 non_if_data.compilerFlags.push_back ( pCompilerFlag );
394 subs_invalid = true;
395 }
396 else if ( e.name == "linkerflag" )
397 {
398 linkerFlags.push_back ( new LinkerFlag ( *this, e ) );
399 subs_invalid = true;
400 }
401 else if ( e.name == "if" || e.name == "ifnot" )
402 {
403 const XMLAttribute* name;
404 name = e.GetAttribute ( "property", true );
405 assert( name );
406 const Property *property = LookupProperty( name->value );
407 const string *PropertyValue;
408 const string EmptyString;
409
410 if (property)
411 {
412 PropertyValue = &property->value;
413 }
414 else
415 {
416 // Property does not exist, treat it as being empty
417 PropertyValue = &EmptyString;
418 }
419
420 const XMLAttribute* value;
421 value = e.GetAttribute ( "value", true );
422 assert( value );
423
424 bool negate = ( e.name == "ifnot" );
425 bool equality = ( *PropertyValue == value->value );
426 if ( equality == negate )
427 {
428 // Failed, skip this element
429 if ( configuration.Verbose )
430 printf("Skipping 'If' at %s\n", e.location.c_str () );
431 return;
432 }
433 subs_invalid = false;
434 }
435 else if ( e.name == "property" )
436 {
437 Property* property = new Property ( e, *this, NULL );
438 non_if_data.properties.insert ( std::make_pair ( property->name, property ) );
439 }
440 if ( subs_invalid && e.subElements.size() )
441 {
442 throw XMLInvalidBuildFileException (
443 e.location,
444 "<%s> cannot have sub-elements",
445 e.name.c_str() );
446 }
447 for ( size_t i = 0; i < e.subElements.size (); i++ )
448 ProcessXMLSubElement ( *e.subElements[i], subpath, parseContext );
449 }
450
451 Module*
452 Project::LocateModule ( const string& name )
453 {
454 std::map<std::string, Module*>::const_iterator p = modules.find(name);
455
456 if ( p == modules.end() )
457 return NULL;
458
459 return p->second;
460 }
461
462 const Module*
463 Project::LocateModule ( const string& name ) const
464 {
465 std::map<std::string, Module*>::const_iterator p = modules.find(name);
466
467 if ( p == modules.end() )
468 return NULL;
469
470 return p->second;
471 }
472
473 const std::string&
474 Project::GetProjectFilename () const
475 {
476 return xmlfile;
477 }