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