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