make_msvcX_install_[config] patch by Brezenbak
[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 else
302 {
303 If * if_data = non_if_data.ifs[i];
304 non_if_data.ifs.erase ( non_if_data.ifs.begin () + i );
305 delete if_data;
306 }
307 }
308 for ( i = 0; i < linkerFlags.size (); i++ )
309 linkerFlags[i]->ProcessXML ();
310 for ( i = 0; i < modules.size (); i++ )
311 modules[i]->ProcessXML ();
312 for ( i = 0; i < cdfiles.size (); i++ )
313 cdfiles[i]->ProcessXML ();
314 for ( i = 0; i < installfiles.size (); i++ )
315 installfiles[i]->ProcessXML ();
316 }
317
318 void
319 Project::ProcessXMLSubElement ( const XMLElement& e,
320 const string& path,
321 ParseContext& parseContext )
322 {
323 bool subs_invalid = false;
324 If* pOldIf = parseContext.ifData;
325
326 string subpath(path);
327 if ( e.name == "module" )
328 {
329 Module* module = new Module ( *this, e, path );
330 if ( LocateModule ( module->name ) )
331 throw XMLInvalidBuildFileException (
332 node->location,
333 "module name conflict: '%s' (originally defined at %s)",
334 module->name.c_str(),
335 module->node.location.c_str() );
336 if ( parseContext.ifData )
337 parseContext.ifData->data.modules.push_back( module );
338 else
339 non_if_data.modules.push_back ( module );
340 return; // defer processing until later
341 }
342 else if ( e.name == "cdfile" )
343 {
344 CDFile* cdfile = new CDFile ( *this, e, path );
345 cdfiles.push_back ( cdfile );
346 subs_invalid = true;
347 }
348 else if ( e.name == "installfile" )
349 {
350 InstallFile* installfile = new InstallFile ( *this, e, path );
351 installfiles.push_back ( installfile );
352 subs_invalid = true;
353 }
354 else if ( e.name == "directory" )
355 {
356 const XMLAttribute* att = e.GetAttribute ( "name", true );
357 assert(att);
358 subpath = GetSubPath ( e.location, path, att->value );
359 }
360 else if ( e.name == "include" )
361 {
362 Include* include = new Include ( *this, &e );
363 if ( parseContext.ifData )
364 parseContext.ifData->data.includes.push_back ( include );
365 else
366 non_if_data.includes.push_back ( include );
367 subs_invalid = true;
368 }
369 else if ( e.name == "define" )
370 {
371 Define* define = new Define ( *this, e );
372 if ( parseContext.ifData )
373 parseContext.ifData->data.defines.push_back ( define );
374 else
375 non_if_data.defines.push_back ( define );
376 subs_invalid = true;
377 }
378 else if ( e.name == "compilerflag" )
379 {
380 CompilerFlag* pCompilerFlag = new CompilerFlag ( *this, e );
381 if ( parseContext.ifData )
382 parseContext.ifData->data.compilerFlags.push_back ( pCompilerFlag );
383 else
384 non_if_data.compilerFlags.push_back ( pCompilerFlag );
385 subs_invalid = true;
386 }
387 else if ( e.name == "linkerflag" )
388 {
389 linkerFlags.push_back ( new LinkerFlag ( *this, e ) );
390 subs_invalid = true;
391 }
392 else if ( e.name == "if" )
393 {
394 parseContext.ifData = new If ( e, *this, NULL );
395 if ( pOldIf )
396 pOldIf->data.ifs.push_back ( parseContext.ifData );
397 else
398 non_if_data.ifs.push_back ( parseContext.ifData );
399 subs_invalid = false;
400 }
401 else if ( e.name == "ifnot" )
402 {
403 parseContext.ifData = new If ( e, *this, NULL, true );
404 if ( pOldIf )
405 pOldIf->data.ifs.push_back ( parseContext.ifData );
406 else
407 non_if_data.ifs.push_back ( parseContext.ifData );
408 subs_invalid = false;
409 }
410 else if ( e.name == "property" )
411 {
412 Property* property = new Property ( e, *this, NULL );
413 if ( parseContext.ifData )
414 parseContext.ifData->data.properties.push_back ( property );
415 else
416 non_if_data.properties.push_back ( property );
417 }
418 if ( subs_invalid && e.subElements.size() )
419 {
420 throw XMLInvalidBuildFileException (
421 e.location,
422 "<%s> cannot have sub-elements",
423 e.name.c_str() );
424 }
425 for ( size_t i = 0; i < e.subElements.size (); i++ )
426 ProcessXMLSubElement ( *e.subElements[i], subpath, parseContext );
427
428 parseContext.ifData = pOldIf;
429 }
430
431 Module*
432 Project::LocateModule ( const string& name )
433 {
434 for ( size_t i = 0; i < modules.size (); i++ )
435 {
436 if (modules[i]->name == name)
437 return modules[i];
438 }
439
440 return NULL;
441 }
442
443 const Module*
444 Project::LocateModule ( const string& name ) const
445 {
446 for ( size_t i = 0; i < modules.size (); i++ )
447 {
448 if ( modules[i]->name == name )
449 return modules[i];
450 }
451
452 return NULL;
453 }
454
455 std::string
456 Project::GetProjectFilename () const
457 {
458 return xmlfile;
459 }