added validation for <directory>'s name attribute
[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 string path;
186 this->ProcessXML ( path );
187 return;
188 }
189 }
190
191 throw InvalidBuildFileException (
192 node->location,
193 "Document contains no 'project' tag." );
194 }
195
196 void
197 Project::ProcessXML ( const string& path )
198 {
199 const XMLAttribute *att;
200 if ( node->name != "project" )
201 throw Exception ( "internal tool error: Project::ProcessXML() called with non-<project> node" );
202
203 att = node->GetAttribute ( "name", false );
204 if ( !att )
205 name = "Unnamed";
206 else
207 name = att->value;
208
209 att = node->GetAttribute ( "makefile", true );
210 assert(att);
211 makefile = att->value;
212
213 size_t i;
214 for ( i = 0; i < node->subElements.size (); i++ )
215 ProcessXMLSubElement ( *node->subElements[i], path );
216 for ( i = 0; i < modules.size (); i++ )
217 modules[i]->ProcessXML ();
218 for ( i = 0; i < linkerFlags.size (); i++ )
219 linkerFlags[i]->ProcessXML ();
220 non_if_data.ProcessXML ();
221 for ( i = 0; i < cdfiles.size (); i++ )
222 cdfiles[i]->ProcessXML ();
223 }
224
225 void
226 Project::ProcessXMLSubElement ( const XMLElement& e,
227 const string& path,
228 If* pIf )
229 {
230 bool subs_invalid = false;
231 string subpath(path);
232 if ( e.name == "module" )
233 {
234 if ( pIf )
235 throw InvalidBuildFileException (
236 e.location,
237 "<module> is not a valid sub-element of <if>" );
238 Module* module = new Module ( *this, e, path );
239 if ( LocateModule ( module->name ) )
240 throw InvalidBuildFileException (
241 node->location,
242 "module name conflict: '%s' (originally defined at %s)",
243 module->name.c_str(),
244 module->node.location.c_str() );
245 modules.push_back ( module );
246 return; // defer processing until later
247 }
248 else if ( e.name == "cdfile" )
249 {
250 CDFile* cdfile = new CDFile ( *this, e, path );
251 cdfiles.push_back ( cdfile );
252 subs_invalid = true;
253 }
254 else if ( e.name == "directory" )
255 {
256 const XMLAttribute* att = e.GetAttribute ( "name", true );
257 assert(att);
258 subpath = GetSubPath ( e.location, path, att->value );
259 }
260 else if ( e.name == "include" )
261 {
262 Include* include = new Include ( *this, e );
263 if ( pIf )
264 pIf->data.includes.push_back ( include );
265 else
266 non_if_data.includes.push_back ( include );
267 subs_invalid = true;
268 }
269 else if ( e.name == "define" )
270 {
271 Define* define = new Define ( *this, e );
272 if ( pIf )
273 pIf->data.defines.push_back ( define );
274 else
275 non_if_data.defines.push_back ( define );
276 subs_invalid = true;
277 }
278 else if ( e.name == "linkerflag" )
279 {
280 linkerFlags.push_back ( new LinkerFlag ( *this, e ) );
281 subs_invalid = true;
282 }
283 else if ( e.name == "if" )
284 {
285 If* pOldIf = pIf;
286 pIf = new If ( e, *this, NULL );
287 if ( pOldIf )
288 pOldIf->data.ifs.push_back ( pIf );
289 else
290 non_if_data.ifs.push_back ( pIf );
291 subs_invalid = false;
292 }
293 else if ( e.name == "property" )
294 {
295 Property* property = new Property ( e, *this, NULL );
296 if ( pIf )
297 pIf->data.properties.push_back ( property );
298 else
299 non_if_data.properties.push_back ( property );
300 }
301 if ( subs_invalid && e.subElements.size() )
302 throw InvalidBuildFileException (
303 e.location,
304 "<%s> cannot have sub-elements",
305 e.name.c_str() );
306 for ( size_t i = 0; i < e.subElements.size (); i++ )
307 ProcessXMLSubElement ( *e.subElements[i], subpath, pIf );
308 }
309
310 Module*
311 Project::LocateModule ( const string& name )
312 {
313 for ( size_t i = 0; i < modules.size (); i++ )
314 {
315 if (modules[i]->name == name)
316 return modules[i];
317 }
318
319 return NULL;
320 }
321
322 const Module*
323 Project::LocateModule ( const string& name ) const
324 {
325 for ( size_t i = 0; i < modules.size (); i++ )
326 {
327 if ( modules[i]->name == name )
328 return modules[i];
329 }
330
331 return NULL;
332 }
333
334 std::string
335 Project::GetProjectFilename () const
336 {
337 return xmlfile;
338 }
339
340