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