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