e929c0cfe87b594710a3b586d7346195d9b0f3ae
[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 Project::Project ( const string& filename )
11 : xmlfile (filename),
12 node (NULL),
13 head (NULL)
14 {
15 ReadXml();
16 }
17
18 Project::~Project ()
19 {
20 size_t i;
21 for ( i = 0; i < modules.size (); i++ )
22 delete modules[i];
23 for ( i = 0; i < linkerFlags.size (); i++ )
24 delete linkerFlags[i];
25 for ( i = 0; i < cdfiles.size (); i++ )
26 delete cdfiles[i];
27 delete head;
28 }
29
30 const Property*
31 Project::LookupProperty ( const string& name ) const
32 {
33 for ( size_t i = 0; i < non_if_data.properties.size (); i++ )
34 {
35 const Property* property = non_if_data.properties[i];
36 if ( property->name == name )
37 return property;
38 }
39 return NULL;
40 }
41
42 void
43 Project::WriteIfChanged ( char* outbuf,
44 string filename )
45 {
46 FILE* out;
47 unsigned int end;
48 char* cmpbuf;
49 unsigned int stat;
50
51 out = fopen ( filename.c_str (), "rb" );
52 if ( out == NULL )
53 {
54 out = fopen ( filename.c_str (), "wb" );
55 if ( out == NULL )
56 throw AccessDeniedException ( filename );
57 fputs ( outbuf, out );
58 fclose ( out );
59 return;
60 }
61
62 fseek ( out, 0, SEEK_END );
63 end = ftell ( out );
64 cmpbuf = (char*) malloc ( end );
65 if ( cmpbuf == NULL )
66 {
67 fclose ( out );
68 throw OutOfMemoryException ();
69 }
70
71 fseek ( out, 0, SEEK_SET );
72 stat = fread ( cmpbuf, 1, end, out );
73 if ( stat != end )
74 {
75 free ( cmpbuf );
76 fclose ( out );
77 throw AccessDeniedException ( filename );
78 }
79 if ( end == strlen ( outbuf ) && memcmp ( cmpbuf, outbuf, end ) == 0 )
80 {
81 free ( cmpbuf );
82 fclose ( out );
83 return;
84 }
85
86 free ( cmpbuf );
87 fclose ( out );
88 out = fopen ( filename.c_str (), "wb" );
89 if ( out == NULL )
90 {
91 throw AccessDeniedException ( filename );
92 }
93
94 stat = fwrite ( outbuf, 1, strlen ( outbuf ), out);
95 if ( strlen ( outbuf ) != stat )
96 {
97 fclose ( out );
98 throw AccessDeniedException ( filename );
99 }
100
101 fclose ( out );
102 }
103
104 void
105 Project::SetConfigurationOption ( char* s,
106 string name,
107 string* alternativeName )
108 {
109 const Property* property = LookupProperty ( name );
110 if ( property != NULL && property->value.length () > 0 )
111 {
112 s = s + sprintf ( s,
113 "#define %s=%s\n",
114 property->name.c_str (),
115 property->value.c_str () );
116 }
117 else if ( property != NULL )
118 {
119 s = s + sprintf ( s,
120 "#define %s\n",
121 property->name.c_str () );
122 }
123 else if ( alternativeName != NULL )
124 {
125 s = s + sprintf ( s,
126 "#define %s\n",
127 alternativeName->c_str () );
128 }
129 }
130
131 void
132 Project::SetConfigurationOption ( char* s,
133 string name )
134 {
135 SetConfigurationOption ( s, name, NULL );
136 }
137
138 void
139 Project::WriteConfigurationFile ()
140 {
141 char* buf;
142 char* s;
143
144 buf = (char*) malloc ( 10*1024 );
145 if ( buf == NULL )
146 throw OutOfMemoryException ();
147
148 s = buf;
149 s = s + sprintf ( s, "/* Automatically generated. " );
150 s = s + sprintf ( s, "Edit config.xml to change configuration */\n" );
151 s = s + sprintf ( s, "#ifndef __INCLUDE_CONFIG_H\n" );
152 s = s + sprintf ( s, "#define __INCLUDE_CONFIG_H\n" );
153
154 SetConfigurationOption ( s, "ARCH" );
155 SetConfigurationOption ( s, "OPTIMIZED" );
156 SetConfigurationOption ( s, "MP", new string ( "UP" ) );
157 SetConfigurationOption ( s, "ACPI" );
158 SetConfigurationOption ( s, "_3GB" );
159
160 s = s + sprintf ( s, "#endif /* __INCLUDE_CONFIG_H */\n" );
161
162 WriteIfChanged ( buf, "include" SSEP "roscfg.h" );
163
164 free ( buf );
165 }
166
167 void
168 Project::ExecuteInvocations ()
169 {
170 for ( size_t i = 0; i < modules.size (); i++ )
171 modules[i]->InvokeModule ();
172 }
173
174 void
175 Project::ReadXml ()
176 {
177 Path path;
178 head = XMLLoadFile ( xmlfile, path, xmlbuildfiles );
179 node = NULL;
180 for ( size_t i = 0; i < head->subElements.size (); i++ )
181 {
182 if ( head->subElements[i]->name == "project" )
183 {
184 node = head->subElements[i];
185 this->ProcessXML ( "." );
186 return;
187 }
188 }
189
190 throw InvalidBuildFileException (
191 node->location,
192 "Document contains no 'project' tag." );
193 }
194
195 void
196 Project::ProcessXML ( const string& path )
197 {
198 const XMLAttribute *att;
199 if ( node->name != "project" )
200 throw Exception ( "internal tool error: Project::ProcessXML() called with non-<project> node" );
201
202 att = node->GetAttribute ( "name", false );
203 if ( !att )
204 name = "Unnamed";
205 else
206 name = att->value;
207
208 att = node->GetAttribute ( "makefile", true );
209 assert(att);
210 makefile = att->value;
211
212 size_t i;
213 for ( i = 0; i < node->subElements.size (); i++ )
214 ProcessXMLSubElement ( *node->subElements[i], path );
215 for ( i = 0; i < modules.size (); i++ )
216 modules[i]->ProcessXML ();
217 for ( i = 0; i < linkerFlags.size (); i++ )
218 linkerFlags[i]->ProcessXML ();
219 non_if_data.ProcessXML ();
220 for ( i = 0; i < cdfiles.size (); i++ )
221 cdfiles[i]->ProcessXML ();
222 }
223
224 void
225 Project::ProcessXMLSubElement ( const XMLElement& e,
226 const string& path,
227 If* pIf )
228 {
229 bool subs_invalid = false;
230 string subpath(path);
231 if ( e.name == "module" )
232 {
233 if ( pIf )
234 throw InvalidBuildFileException (
235 e.location,
236 "<module> is not a valid sub-element of <if>" );
237 Module* module = new Module ( *this, e, path );
238 if ( LocateModule ( module->name ) )
239 throw InvalidBuildFileException (
240 node->location,
241 "module name conflict: '%s' (originally defined at %s)",
242 module->name.c_str(),
243 module->node.location.c_str() );
244 modules.push_back ( module );
245 return; // defer processing until later
246 }
247 else if ( e.name == "cdfile" )
248 {
249 CDFile* cdfile = new CDFile ( *this, e, path );
250 cdfiles.push_back ( cdfile );
251 subs_invalid = true;
252 }
253 else if ( e.name == "directory" )
254 {
255 const XMLAttribute* att = e.GetAttribute ( "name", true );
256 assert(att);
257 subpath = path + CSEP + att->value;
258 }
259 else if ( e.name == "include" )
260 {
261 Include* include = new Include ( *this, e );
262 if ( pIf )
263 pIf->data.includes.push_back ( include );
264 else
265 non_if_data.includes.push_back ( include );
266 subs_invalid = true;
267 }
268 else if ( e.name == "define" )
269 {
270 Define* define = new Define ( *this, e );
271 if ( pIf )
272 pIf->data.defines.push_back ( define );
273 else
274 non_if_data.defines.push_back ( define );
275 subs_invalid = true;
276 }
277 else if ( e.name == "linkerflag" )
278 {
279 linkerFlags.push_back ( new LinkerFlag ( *this, e ) );
280 subs_invalid = true;
281 }
282 else if ( e.name == "if" )
283 {
284 If* pOldIf = pIf;
285 pIf = new If ( e, *this, NULL );
286 if ( pOldIf )
287 pOldIf->data.ifs.push_back ( pIf );
288 else
289 non_if_data.ifs.push_back ( pIf );
290 subs_invalid = false;
291 }
292 else if ( e.name == "property" )
293 {
294 Property* property = new Property ( e, *this, NULL );
295 if ( pIf )
296 pIf->data.properties.push_back ( property );
297 else
298 non_if_data.properties.push_back ( property );
299 }
300 if ( subs_invalid && e.subElements.size() )
301 throw InvalidBuildFileException (
302 e.location,
303 "<%s> cannot have sub-elements",
304 e.name.c_str() );
305 for ( size_t i = 0; i < e.subElements.size (); i++ )
306 ProcessXMLSubElement ( *e.subElements[i], subpath, pIf );
307 }
308
309 Module*
310 Project::LocateModule ( const string& name )
311 {
312 for ( size_t i = 0; i < modules.size (); i++ )
313 {
314 if (modules[i]->name == name)
315 return modules[i];
316 }
317
318 return NULL;
319 }
320
321 const Module*
322 Project::LocateModule ( const string& name ) const
323 {
324 for ( size_t i = 0; i < modules.size (); i++ )
325 {
326 if ( modules[i]->name == name )
327 return modules[i];
328 }
329
330 return NULL;
331 }
332
333 std::string
334 Project::GetProjectFilename () const
335 {
336 return xmlfile;
337 }
338
339