Add a variable to change the name of the ReactOS directory on bootcd.
[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 ParseContext::ParseContext ()
84 : ifData (NULL),
85 compilationUnit (NULL)
86 {
87 }
88
89
90 FileLocation::FileLocation ( Directory* directory,
91 std::string filename )
92 : directory (directory),
93 filename (filename)
94 {
95 }
96
97
98 Project::Project ( const Configuration& configuration,
99 const string& filename )
100 : xmlfile (filename),
101 node (NULL),
102 head (NULL),
103 configuration (configuration)
104 {
105 ReadXml();
106 }
107
108 Project::~Project ()
109 {
110 size_t i;
111 if ( _backend )
112 delete _backend;
113 #ifdef NOT_NEEDED_SINCE_THESE_ARE_CLEANED_BY_IFABLE_DATA
114 for ( i = 0; i < modules.size (); i++ )
115 delete modules[i];
116 #endif
117 for ( i = 0; i < linkerFlags.size (); i++ )
118 delete linkerFlags[i];
119 for ( i = 0; i < cdfiles.size (); i++ )
120 delete cdfiles[i];
121 for ( i = 0; i < installfiles.size (); i++ )
122 delete installfiles[i];
123 delete head;
124 }
125
126 const Property*
127 Project::LookupProperty ( const string& name ) const
128 {
129 for ( size_t i = 0; i < non_if_data.properties.size (); i++ )
130 {
131 const Property* property = non_if_data.properties[i];
132 if ( property->name == name )
133 return property;
134 }
135 return NULL;
136 }
137
138 string
139 Project::ResolveNextProperty ( string& s ) const
140 {
141 size_t i = s.find ( "${" );
142 if ( i == string::npos )
143 i = s.find ( "$(" );
144 if ( i != string::npos )
145 {
146 string endCharacter;
147 if ( s[i + 1] == '{' )
148 endCharacter = "}";
149 else
150 endCharacter = ")";
151 size_t j = s.find ( endCharacter );
152 if ( j != string::npos )
153 {
154 int propertyNameLength = j - i - 2;
155 string propertyName = s.substr ( i + 2, propertyNameLength );
156 const Property* property = LookupProperty ( propertyName );
157 if ( property != NULL )
158 return s.replace ( i, propertyNameLength + 3, property->value );
159 }
160 }
161 return s;
162 }
163
164 string
165 Project::ResolveProperties ( const string& s ) const
166 {
167 string s2 = s;
168 string s3;
169 do
170 {
171 s3 = s2;
172 s2 = ResolveNextProperty ( s3 );
173 } while ( s2 != s3 );
174 return s2;
175 }
176
177 void
178 Project::SetConfigurationOption ( char* s,
179 string name,
180 string* alternativeName )
181 {
182 const Property* property = LookupProperty ( name );
183 if ( property != NULL && property->value.length () > 0 )
184 {
185 s = s + sprintf ( s,
186 "#define %s=%s\n",
187 property->name.c_str (),
188 property->value.c_str () );
189 }
190 else if ( property != NULL )
191 {
192 s = s + sprintf ( s,
193 "#define %s\n",
194 property->name.c_str () );
195 }
196 else if ( alternativeName != NULL )
197 {
198 s = s + sprintf ( s,
199 "#define %s\n",
200 alternativeName->c_str () );
201 }
202 }
203
204 void
205 Project::SetConfigurationOption ( char* s,
206 string name )
207 {
208 SetConfigurationOption ( s, name, NULL );
209 }
210
211 void
212 Project::WriteConfigurationFile ()
213 {
214 char* buf;
215 char* s;
216
217 buf = (char*) malloc ( 10*1024 );
218 if ( buf == NULL )
219 throw OutOfMemoryException ();
220
221 s = buf;
222 s = s + sprintf ( s, "/* Automatically generated. " );
223 s = s + sprintf ( s, "Edit config.xml to change configuration */\n" );
224 s = s + sprintf ( s, "#ifndef __INCLUDE_CONFIG_H\n" );
225 s = s + sprintf ( s, "#define __INCLUDE_CONFIG_H\n" );
226
227 SetConfigurationOption ( s, "ARCH" );
228 SetConfigurationOption ( s, "OPTIMIZED" );
229 SetConfigurationOption ( s, "MP", new string ( "UP" ) );
230 SetConfigurationOption ( s, "ACPI" );
231 SetConfigurationOption ( s, "_3GB" );
232
233 s = s + sprintf ( s, "#endif /* __INCLUDE_CONFIG_H */\n" );
234
235 FileSupportCode::WriteIfChanged ( buf, "include" + sSep + "roscfg.h" );
236
237 free ( buf );
238 }
239
240 void
241 Project::ExecuteInvocations ()
242 {
243 for ( size_t i = 0; i < modules.size (); i++ )
244 modules[i]->InvokeModule ();
245 }
246
247 void
248 Project::ReadXml ()
249 {
250 Path path;
251 head = XMLLoadFile ( xmlfile, path, xmlbuildfiles );
252 node = NULL;
253 for ( size_t i = 0; i < head->subElements.size (); i++ )
254 {
255 if ( head->subElements[i]->name == "project" )
256 {
257 node = head->subElements[i];
258 string path;
259 ProcessXML ( path );
260 return;
261 }
262 }
263
264 if (node == NULL)
265 node = head->subElements[0];
266
267 throw XMLInvalidBuildFileException (
268 node->location,
269 "Document contains no 'project' tag." );
270 }
271
272 void
273 Project::ProcessXML ( const string& path )
274 {
275 const XMLAttribute *att;
276 if ( node->name != "project" )
277 throw Exception ( "internal tool error: Project::ProcessXML() called with non-<project> node" );
278
279 att = node->GetAttribute ( "name", false );
280 if ( !att )
281 name = "Unnamed";
282 else
283 name = att->value;
284
285 att = node->GetAttribute ( "makefile", true );
286 assert(att);
287 makefile = att->value;
288
289 size_t i;
290 for ( i = 0; i < node->subElements.size (); i++ )
291 {
292 ParseContext parseContext;
293 ProcessXMLSubElement ( *node->subElements[i], path, parseContext );
294 }
295
296 non_if_data.ProcessXML ();
297
298 non_if_data.ExtractModules( modules );
299
300 for ( i = 0; i < non_if_data.ifs.size (); i++ )
301 {
302 const Property *property =
303 LookupProperty( non_if_data.ifs[i]->property );
304
305 if( !property ) continue;
306
307 bool conditionTrue =
308 (non_if_data.ifs[i]->negated &&
309 (property->value != non_if_data.ifs[i]->value)) ||
310 (property->value == non_if_data.ifs[i]->value);
311 if ( conditionTrue )
312 non_if_data.ifs[i]->data.ExtractModules( modules );
313 else
314 {
315 If * if_data = non_if_data.ifs[i];
316 non_if_data.ifs.erase ( non_if_data.ifs.begin () + i );
317 delete if_data;
318 i--;
319 }
320 }
321 for ( i = 0; i < linkerFlags.size (); i++ )
322 linkerFlags[i]->ProcessXML ();
323 for ( i = 0; i < modules.size (); i++ )
324 modules[i]->ProcessXML ();
325 for ( i = 0; i < cdfiles.size (); i++ )
326 cdfiles[i]->ProcessXML ();
327 for ( i = 0; i < installfiles.size (); i++ )
328 installfiles[i]->ProcessXML ();
329 }
330
331 void
332 Project::ProcessXMLSubElement ( const XMLElement& e,
333 const string& path,
334 ParseContext& parseContext )
335 {
336 bool subs_invalid = false;
337 If* pOldIf = parseContext.ifData;
338
339 string subpath(path);
340 if ( e.name == "module" )
341 {
342 Module* module = new Module ( *this, e, path );
343 if ( LocateModule ( module->name ) )
344 throw XMLInvalidBuildFileException (
345 node->location,
346 "module name conflict: '%s' (originally defined at %s)",
347 module->name.c_str(),
348 module->node.location.c_str() );
349 if ( parseContext.ifData )
350 parseContext.ifData->data.modules.push_back( module );
351 else
352 non_if_data.modules.push_back ( module );
353 return; // defer processing until later
354 }
355 else if ( e.name == "cdfile" )
356 {
357 CDFile* cdfile = new CDFile ( *this, e, path );
358 cdfiles.push_back ( cdfile );
359 subs_invalid = true;
360 }
361 else if ( e.name == "installfile" )
362 {
363 InstallFile* installfile = new InstallFile ( *this, e, path );
364 installfiles.push_back ( installfile );
365 subs_invalid = true;
366 }
367 else if ( e.name == "directory" )
368 {
369 const XMLAttribute* att = e.GetAttribute ( "name", true );
370 assert(att);
371 subpath = GetSubPath ( e.location, path, att->value );
372 }
373 else if ( e.name == "include" )
374 {
375 Include* include = new Include ( *this, &e );
376 if ( parseContext.ifData )
377 parseContext.ifData->data.includes.push_back ( include );
378 else
379 non_if_data.includes.push_back ( include );
380 subs_invalid = true;
381 }
382 else if ( e.name == "define" )
383 {
384 Define* define = new Define ( *this, e );
385 if ( parseContext.ifData )
386 parseContext.ifData->data.defines.push_back ( define );
387 else
388 non_if_data.defines.push_back ( define );
389 subs_invalid = true;
390 }
391 else if ( e.name == "compilerflag" )
392 {
393 CompilerFlag* pCompilerFlag = new CompilerFlag ( *this, e );
394 if ( parseContext.ifData )
395 parseContext.ifData->data.compilerFlags.push_back ( pCompilerFlag );
396 else
397 non_if_data.compilerFlags.push_back ( pCompilerFlag );
398 subs_invalid = true;
399 }
400 else if ( e.name == "linkerflag" )
401 {
402 linkerFlags.push_back ( new LinkerFlag ( *this, e ) );
403 subs_invalid = true;
404 }
405 else if ( e.name == "if" )
406 {
407 parseContext.ifData = new If ( e, *this, NULL );
408 if ( pOldIf )
409 pOldIf->data.ifs.push_back ( parseContext.ifData );
410 else
411 non_if_data.ifs.push_back ( parseContext.ifData );
412 subs_invalid = false;
413 }
414 else if ( e.name == "ifnot" )
415 {
416 parseContext.ifData = new If ( e, *this, NULL, true );
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 == "property" )
424 {
425 Property* property = new Property ( e, *this, NULL );
426 if ( parseContext.ifData )
427 parseContext.ifData->data.properties.push_back ( property );
428 else
429 non_if_data.properties.push_back ( property );
430 }
431 if ( subs_invalid && e.subElements.size() )
432 {
433 throw XMLInvalidBuildFileException (
434 e.location,
435 "<%s> cannot have sub-elements",
436 e.name.c_str() );
437 }
438 for ( size_t i = 0; i < e.subElements.size (); i++ )
439 ProcessXMLSubElement ( *e.subElements[i], subpath, parseContext );
440
441 parseContext.ifData = pOldIf;
442 }
443
444 Module*
445 Project::LocateModule ( const string& name )
446 {
447 for ( size_t i = 0; i < modules.size (); i++ )
448 {
449 if (modules[i]->name == name)
450 return modules[i];
451 }
452
453 return NULL;
454 }
455
456 const Module*
457 Project::LocateModule ( const string& name ) const
458 {
459 for ( size_t i = 0; i < modules.size (); i++ )
460 {
461 if ( modules[i]->name == name )
462 return modules[i];
463 }
464
465 return NULL;
466 }
467
468 std::string
469 Project::GetProjectFilename () const
470 {
471 return xmlfile;
472 }