4a20129ecbaa4c5896d4714abb5ea7d315d7a330
[reactos.git] / reactos / tools / rbuild / project.cpp
1 /*
2 * Copyright (C) 2005 Casper S. Hornstrup
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18 #include "pch.h"
19 #include <assert.h>
20
21 #include "rbuild.h"
22 #include "backend/backend.h"
23
24 using std::string;
25 using std::vector;
26
27 /* static */ string
28 Environment::GetVariable ( const string& name )
29 {
30 char* value = getenv ( name.c_str () );
31 if ( value != NULL && strlen ( value ) > 0 )
32 return ssprintf ( "%s",
33 value );
34 else
35 return "";
36 }
37
38 /* static */ string
39 Environment::GetEnvironmentVariablePathOrDefault ( const string& name,
40 const string& defaultValue )
41 {
42 const string& environmentVariableValue = Environment::GetVariable ( name );
43 if ( environmentVariableValue.length () > 0 )
44 return NormalizeFilename ( environmentVariableValue );
45 else
46 return defaultValue;
47 }
48
49 /* static */ string
50 Environment::GetIntermediatePath ()
51 {
52 string defaultIntermediate =
53 string( "obj-" ) + GetEnvironmentVariablePathOrDefault ( "ROS_CDOUTPUT", "i386" );
54 return GetEnvironmentVariablePathOrDefault ( "ROS_INTERMEDIATE",
55 defaultIntermediate );
56 }
57
58 /* static */ string
59 Environment::GetOutputPath ()
60 {
61 string defaultOutput =
62 string( "output-" ) + GetEnvironmentVariablePathOrDefault ( "ROS_CDOUTPUT", "i386" );
63 return GetEnvironmentVariablePathOrDefault ( "ROS_OUTPUT",
64 defaultOutput );
65 }
66
67 /* static */ string
68 Environment::GetInstallPath ()
69 {
70 string defaultInstall =
71 string( "reactos." ) + GetEnvironmentVariablePathOrDefault ( "ROS_CDOUTPUT", "" );
72 return GetEnvironmentVariablePathOrDefault ( "ROS_INSTALL",
73 defaultInstall );
74 }
75
76 /* static */ string
77 Environment::GetCdOutputPath ()
78 {
79 return GetEnvironmentVariablePathOrDefault ( "ROS_CDOUTPUT",
80 "reactos" );
81 }
82
83 /* static */ string
84 Environment::GetAutomakeFile ( const std::string& defaultFile )
85 {
86 return GetEnvironmentVariablePathOrDefault ( "ROS_AUTOMAKE",
87 defaultFile );
88 }
89
90 ParseContext::ParseContext ()
91 : ifData (NULL),
92 compilationUnit (NULL)
93 {
94 }
95
96
97 FileLocation::FileLocation ( Directory* directory,
98 std::string filename )
99 : directory (directory),
100 filename (filename)
101 {
102 }
103
104
105 Project::Project ( const Configuration& configuration,
106 const string& filename )
107 : xmlfile (filename),
108 node (NULL),
109 head (NULL),
110 configuration (configuration)
111 {
112 _backend = NULL;
113 ReadXml();
114 }
115
116 Project::~Project ()
117 {
118 size_t i;
119 if ( _backend )
120 delete _backend;
121 #ifdef NOT_NEEDED_SINCE_THESE_ARE_CLEANED_BY_IFABLE_DATA
122 for ( i = 0; i < modules.size (); i++ )
123 delete modules[i];
124 #endif
125 for ( i = 0; i < linkerFlags.size (); i++ )
126 delete linkerFlags[i];
127 for ( i = 0; i < cdfiles.size (); i++ )
128 delete cdfiles[i];
129 for ( i = 0; i < installfiles.size (); i++ )
130 delete installfiles[i];
131 delete head;
132 }
133
134 const Property*
135 Project::LookupProperty ( const string& name ) const
136 {
137 for ( size_t i = 0; i < non_if_data.properties.size (); i++ )
138 {
139 const Property* property = non_if_data.properties[i];
140 if ( property->name == name )
141 return property;
142 }
143 return NULL;
144 }
145
146 string
147 Project::ResolveNextProperty ( string& s ) const
148 {
149 size_t i = s.find ( "${" );
150 if ( i == string::npos )
151 i = s.find ( "$(" );
152 if ( i != string::npos )
153 {
154 string endCharacter;
155 if ( s[i + 1] == '{' )
156 endCharacter = "}";
157 else
158 endCharacter = ")";
159 size_t j = s.find ( endCharacter );
160 if ( j != string::npos )
161 {
162 int propertyNameLength = j - i - 2;
163 string propertyName = s.substr ( i + 2, propertyNameLength );
164 const Property* property = LookupProperty ( propertyName );
165 if ( property != NULL )
166 return s.replace ( i, propertyNameLength + 3, property->value );
167 }
168 }
169 return s;
170 }
171
172 string
173 Project::ResolveProperties ( const string& s ) const
174 {
175 string s2 = s;
176 string s3;
177 do
178 {
179 s3 = s2;
180 s2 = ResolveNextProperty ( s3 );
181 } while ( s2 != s3 );
182 return s2;
183 }
184
185 void
186 Project::SetConfigurationOption ( char* s,
187 string name,
188 string* alternativeName )
189 {
190 const Property* property = LookupProperty ( name );
191 if ( property != NULL && property->value.length () > 0 )
192 {
193 s = s + sprintf ( s,
194 "#define %s=%s\n",
195 property->name.c_str (),
196 property->value.c_str () );
197 }
198 else if ( property != NULL )
199 {
200 s = s + sprintf ( s,
201 "#define %s\n",
202 property->name.c_str () );
203 }
204 else if ( alternativeName != NULL )
205 {
206 s = s + sprintf ( s,
207 "#define %s\n",
208 alternativeName->c_str () );
209 }
210 }
211
212 void
213 Project::SetConfigurationOption ( char* s,
214 string name )
215 {
216 SetConfigurationOption ( s, name, NULL );
217 }
218
219 void
220 Project::WriteConfigurationFile ()
221 {
222 char* buf;
223 char* s;
224
225 buf = (char*) malloc ( 10*1024 );
226 if ( buf == NULL )
227 throw OutOfMemoryException ();
228
229 s = buf;
230 s = s + sprintf ( s, "/* Automatically generated. " );
231 s = s + sprintf ( s, "Edit config.xml to change configuration */\n" );
232 s = s + sprintf ( s, "#ifndef __INCLUDE_CONFIG_H\n" );
233 s = s + sprintf ( s, "#define __INCLUDE_CONFIG_H\n" );
234
235 SetConfigurationOption ( s, "ARCH" );
236 SetConfigurationOption ( s, "OPTIMIZED" );
237 SetConfigurationOption ( s, "MP", new string ( "UP" ) );
238 SetConfigurationOption ( s, "ACPI" );
239 SetConfigurationOption ( s, "_3GB" );
240
241 s = s + sprintf ( s, "#endif /* __INCLUDE_CONFIG_H */\n" );
242
243 FileSupportCode::WriteIfChanged ( buf, "include" + sSep + "roscfg.h" );
244
245 free ( buf );
246 }
247
248 void
249 Project::ExecuteInvocations ()
250 {
251 for ( size_t i = 0; i < modules.size (); i++ )
252 modules[i]->InvokeModule ();
253 }
254
255 void
256 Project::ReadXml ()
257 {
258 Path path;
259 head = XMLLoadFile ( xmlfile, path, xmlbuildfiles );
260 node = NULL;
261 for ( size_t i = 0; i < head->subElements.size (); i++ )
262 {
263 if ( head->subElements[i]->name == "project" )
264 {
265 node = head->subElements[i];
266 string path;
267 ProcessXML ( path );
268 return;
269 }
270 }
271
272 if (node == NULL)
273 node = head->subElements[0];
274
275 throw XMLInvalidBuildFileException (
276 node->location,
277 "Document contains no 'project' tag." );
278 }
279
280 void
281 Project::ProcessXML ( const string& path )
282 {
283 const XMLAttribute *att;
284 if ( node->name != "project" )
285 throw Exception ( "internal tool error: Project::ProcessXML() called with non-<project> node" );
286
287 att = node->GetAttribute ( "name", false );
288 if ( !att )
289 name = "Unnamed";
290 else
291 name = att->value;
292
293 att = node->GetAttribute ( "makefile", true );
294 assert(att);
295 makefile = Environment::GetAutomakeFile ( att->value );
296
297 size_t i;
298 for ( i = 0; i < node->subElements.size (); i++ )
299 {
300 ParseContext parseContext;
301 ProcessXMLSubElement ( *node->subElements[i], path, parseContext );
302 }
303
304 non_if_data.ProcessXML ();
305
306 non_if_data.ExtractModules( modules );
307
308 for ( i = 0; i < non_if_data.ifs.size (); i++ )
309 {
310 const Property *property =
311 LookupProperty( non_if_data.ifs[i]->property );
312
313 if( !property ) continue;
314
315 bool conditionTrue =
316 (non_if_data.ifs[i]->negated &&
317 (property->value != non_if_data.ifs[i]->value)) ||
318 (property->value == non_if_data.ifs[i]->value);
319 if ( conditionTrue )
320 non_if_data.ifs[i]->data.ExtractModules( modules );
321 else
322 {
323 If * if_data = non_if_data.ifs[i];
324 non_if_data.ifs.erase ( non_if_data.ifs.begin () + i );
325 delete if_data;
326 i--;
327 }
328 }
329 for ( i = 0; i < linkerFlags.size (); i++ )
330 linkerFlags[i]->ProcessXML ();
331 for ( i = 0; i < modules.size (); i++ )
332 modules[i]->ProcessXML ();
333 for ( i = 0; i < cdfiles.size (); i++ )
334 cdfiles[i]->ProcessXML ();
335 for ( i = 0; i < installfiles.size (); i++ )
336 installfiles[i]->ProcessXML ();
337 }
338
339 void
340 Project::ProcessXMLSubElement ( const XMLElement& e,
341 const string& path,
342 ParseContext& parseContext )
343 {
344 bool subs_invalid = false;
345 If* pOldIf = parseContext.ifData;
346
347 string subpath(path);
348 if ( e.name == "module" )
349 {
350 Module* module = new Module ( *this, e, path );
351 if ( LocateModule ( module->name ) )
352 throw XMLInvalidBuildFileException (
353 node->location,
354 "module name conflict: '%s' (originally defined at %s)",
355 module->name.c_str(),
356 module->node.location.c_str() );
357 if ( parseContext.ifData )
358 parseContext.ifData->data.modules.push_back( module );
359 else
360 non_if_data.modules.push_back ( module );
361 return; // defer processing until later
362 }
363 else if ( e.name == "cdfile" )
364 {
365 CDFile* cdfile = new CDFile ( *this, e, path );
366 cdfiles.push_back ( cdfile );
367 subs_invalid = true;
368 }
369 else if ( e.name == "installfile" )
370 {
371 InstallFile* installfile = new InstallFile ( *this, e, path );
372 installfiles.push_back ( installfile );
373 subs_invalid = true;
374 }
375 else if ( e.name == "directory" )
376 {
377 const XMLAttribute* att = e.GetAttribute ( "name", true );
378 const XMLAttribute* base = e.GetAttribute ( "root", false );
379 assert(att);
380 subpath = GetSubPath ( *this, e.location, path, base, att->value );
381 }
382 else if ( e.name == "include" )
383 {
384 Include* include = new Include ( *this, &e );
385 if ( parseContext.ifData )
386 parseContext.ifData->data.includes.push_back ( include );
387 else
388 non_if_data.includes.push_back ( include );
389 subs_invalid = true;
390 }
391 else if ( e.name == "define" )
392 {
393 Define* define = new Define ( *this, e );
394 if ( parseContext.ifData )
395 parseContext.ifData->data.defines.push_back ( define );
396 else
397 non_if_data.defines.push_back ( define );
398 subs_invalid = true;
399 }
400 else if ( e.name == "compilerflag" )
401 {
402 CompilerFlag* pCompilerFlag = new CompilerFlag ( *this, e );
403 if ( parseContext.ifData )
404 parseContext.ifData->data.compilerFlags.push_back ( pCompilerFlag );
405 else
406 non_if_data.compilerFlags.push_back ( pCompilerFlag );
407 subs_invalid = true;
408 }
409 else if ( e.name == "linkerflag" )
410 {
411 linkerFlags.push_back ( new LinkerFlag ( *this, e ) );
412 subs_invalid = true;
413 }
414 else if ( e.name == "if" )
415 {
416 parseContext.ifData = new If ( e, *this, NULL );
417 if ( pOldIf )
418 pOldIf->data.ifs.push_back ( parseContext.ifData );
419 else
420 non_if_data.ifs.push_back ( parseContext.ifData );
421 subs_invalid = false;
422 }
423 else if ( e.name == "ifnot" )
424 {
425 parseContext.ifData = new If ( e, *this, NULL, true );
426 if ( pOldIf )
427 pOldIf->data.ifs.push_back ( parseContext.ifData );
428 else
429 non_if_data.ifs.push_back ( parseContext.ifData );
430 subs_invalid = false;
431 }
432 else if ( e.name == "property" )
433 {
434 Property* property = new Property ( e, *this, NULL );
435 if ( parseContext.ifData )
436 parseContext.ifData->data.properties.push_back ( property );
437 else
438 non_if_data.properties.push_back ( 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 parseContext.ifData = pOldIf;
451 }
452
453 Module*
454 Project::LocateModule ( const string& name )
455 {
456 for ( size_t i = 0; i < modules.size (); i++ )
457 {
458 if (modules[i]->name == name)
459 return modules[i];
460 }
461
462 return NULL;
463 }
464
465 const Module*
466 Project::LocateModule ( const string& name ) const
467 {
468 for ( size_t i = 0; i < modules.size (); i++ )
469 {
470 if ( modules[i]->name == name )
471 return modules[i];
472 }
473
474 return NULL;
475 }
476
477 const std::string&
478 Project::GetProjectFilename () const
479 {
480 return xmlfile;
481 }