00ab3ab50a2482c4f8440182c47747df301c20f1
[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 throw InvalidBuildFileException (
231 node->location,
232 "Document contains no 'project' tag." );
233 }
234
235 void
236 Project::ProcessXML ( const string& path )
237 {
238 const XMLAttribute *att;
239 if ( node->name != "project" )
240 throw Exception ( "internal tool error: Project::ProcessXML() called with non-<project> node" );
241
242 att = node->GetAttribute ( "name", false );
243 if ( !att )
244 name = "Unnamed";
245 else
246 name = att->value;
247
248 att = node->GetAttribute ( "makefile", true );
249 assert(att);
250 makefile = att->value;
251
252 size_t i;
253 for ( i = 0; i < node->subElements.size (); i++ )
254 ProcessXMLSubElement ( *node->subElements[i], path );
255 for ( i = 0; i < modules.size (); i++ )
256 modules[i]->ProcessXML ();
257 for ( i = 0; i < linkerFlags.size (); i++ )
258 linkerFlags[i]->ProcessXML ();
259 non_if_data.ProcessXML ();
260 for ( i = 0; i < cdfiles.size (); i++ )
261 cdfiles[i]->ProcessXML ();
262 for ( i = 0; i < installfiles.size (); i++ )
263 installfiles[i]->ProcessXML ();
264 }
265
266 void
267 Project::ProcessXMLSubElement ( const XMLElement& e,
268 const string& path,
269 If* pIf )
270 {
271 bool subs_invalid = false;
272 string subpath(path);
273 if ( e.name == "module" )
274 {
275 if ( pIf )
276 throw InvalidBuildFileException (
277 e.location,
278 "<module> is not a valid sub-element of <if>" );
279 Module* module = new Module ( *this, e, path );
280 if ( LocateModule ( module->name ) )
281 throw InvalidBuildFileException (
282 node->location,
283 "module name conflict: '%s' (originally defined at %s)",
284 module->name.c_str(),
285 module->node.location.c_str() );
286 modules.push_back ( module );
287 return; // defer processing until later
288 }
289 else if ( e.name == "cdfile" )
290 {
291 CDFile* cdfile = new CDFile ( *this, e, path );
292 cdfiles.push_back ( cdfile );
293 subs_invalid = true;
294 }
295 else if ( e.name == "installfile" )
296 {
297 InstallFile* installfile = new InstallFile ( *this, e, path );
298 installfiles.push_back ( installfile );
299 subs_invalid = true;
300 }
301 else if ( e.name == "directory" )
302 {
303 const XMLAttribute* att = e.GetAttribute ( "name", true );
304 assert(att);
305 subpath = GetSubPath ( e.location, path, att->value );
306 }
307 else if ( e.name == "include" )
308 {
309 Include* include = new Include ( *this, &e );
310 if ( pIf )
311 pIf->data.includes.push_back ( include );
312 else
313 non_if_data.includes.push_back ( include );
314 subs_invalid = true;
315 }
316 else if ( e.name == "define" )
317 {
318 Define* define = new Define ( *this, e );
319 if ( pIf )
320 pIf->data.defines.push_back ( define );
321 else
322 non_if_data.defines.push_back ( define );
323 subs_invalid = true;
324 }
325 else if ( e.name == "compilerflag" )
326 {
327 CompilerFlag* pCompilerFlag = new CompilerFlag ( *this, e );
328 if ( pIf )
329 pIf->data.compilerFlags.push_back ( pCompilerFlag );
330 else
331 non_if_data.compilerFlags.push_back ( pCompilerFlag );
332 subs_invalid = true;
333 }
334 else if ( e.name == "linkerflag" )
335 {
336 linkerFlags.push_back ( new LinkerFlag ( *this, e ) );
337 subs_invalid = true;
338 }
339 else if ( e.name == "if" )
340 {
341 If* pOldIf = pIf;
342 pIf = new If ( e, *this, NULL );
343 if ( pOldIf )
344 pOldIf->data.ifs.push_back ( pIf );
345 else
346 non_if_data.ifs.push_back ( pIf );
347 subs_invalid = false;
348 }
349 else if ( e.name == "ifnot" )
350 {
351 If* pOldIf = pIf;
352 pIf = new If ( e, *this, NULL, true );
353 if ( pOldIf )
354 pOldIf->data.ifs.push_back ( pIf );
355 else
356 non_if_data.ifs.push_back ( pIf );
357 subs_invalid = false;
358 }
359 else if ( e.name == "property" )
360 {
361 Property* property = new Property ( e, *this, NULL );
362 if ( pIf )
363 pIf->data.properties.push_back ( property );
364 else
365 non_if_data.properties.push_back ( property );
366 }
367 if ( subs_invalid && e.subElements.size() )
368 throw InvalidBuildFileException (
369 e.location,
370 "<%s> cannot have sub-elements",
371 e.name.c_str() );
372 for ( size_t i = 0; i < e.subElements.size (); i++ )
373 ProcessXMLSubElement ( *e.subElements[i], subpath, pIf );
374 }
375
376 Module*
377 Project::LocateModule ( const string& name )
378 {
379 for ( size_t i = 0; i < modules.size (); i++ )
380 {
381 if (modules[i]->name == name)
382 return modules[i];
383 }
384
385 return NULL;
386 }
387
388 const Module*
389 Project::LocateModule ( const string& name ) const
390 {
391 for ( size_t i = 0; i < modules.size (); i++ )
392 {
393 if ( modules[i]->name == name )
394 return modules[i];
395 }
396
397 return NULL;
398 }
399
400 std::string
401 Project::GetProjectFilename () const
402 {
403 return xmlfile;
404 }
405
406