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