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