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