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