Support "root" attribute in <directory> elements
[reactos.git] / reactos / tools / rbuild / module.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 string
27 Right ( const string& s, size_t n )
28 {
29 if ( n > s.size() )
30 return s;
31 return string ( &s[s.size()-n] );
32 }
33
34 string
35 Replace ( const string& s, const string& find, const string& with )
36 {
37 string ret;
38 const char* p = s.c_str();
39 while ( p )
40 {
41 const char* p2 = strstr ( p, find.c_str() );
42 if ( !p2 )
43 break;
44 if ( p2 > p )
45 ret += string ( p, p2-p );
46 ret += with;
47 p = p2 + find.size();
48 }
49 if ( *p )
50 ret += p;
51 return ret;
52 }
53
54 string
55 ChangeSeparator ( const string& s,
56 const char fromSeparator,
57 const char toSeparator )
58 {
59 string s2(s);
60 char* p = strchr ( &s2[0], fromSeparator );
61 while ( p )
62 {
63 *p++ = toSeparator;
64 p = strchr ( p, fromSeparator );
65 }
66 return s2;
67 }
68
69 string
70 FixSeparator ( const string& s )
71 {
72 return ChangeSeparator ( s, cBadSep, cSep );
73 }
74
75 string
76 FixSeparatorForSystemCommand ( const string& s )
77 {
78 string s2(s);
79 char* p = strchr ( &s2[0], DEF_CBAD_SEP );
80 while ( p )
81 {
82 *p++ = DEF_CSEP;
83 p = strchr ( p, DEF_CBAD_SEP );
84 }
85 return s2;
86 }
87
88 string
89 DosSeparator ( const string& s )
90 {
91 string s2(s);
92 char* p = strchr ( &s2[0], '/' );
93 while ( p )
94 {
95 *p++ = '\\';
96 p = strchr ( p, '/' );
97 }
98 return s2;
99 }
100
101 string
102 ReplaceExtension (
103 const string& filename,
104 const string& newExtension )
105 {
106 size_t index = filename.find_last_of ( '/' );
107 if ( index == string::npos )
108 index = 0;
109 size_t index2 = filename.find_last_of ( '\\' );
110 if ( index2 != string::npos && index2 > index )
111 index = index2;
112 string tmp = filename.substr( index /*, filename.size() - index*/ );
113 size_t ext_index = tmp.find_last_of( '.' );
114 if ( ext_index != string::npos )
115 return filename.substr ( 0, index + ext_index ) + newExtension;
116 return filename + newExtension;
117 }
118
119 string
120 GetSubPath (
121 const Project& project,
122 const string& location,
123 const string& path,
124 const XMLAttribute* root,
125 const string& att_value )
126 {
127 if ( !att_value.size() )
128 throw XMLInvalidBuildFileException (
129 location,
130 "<directory> tag has empty 'name' attribute" );
131 if ( strpbrk ( att_value.c_str (), "/\\?*:<>|" ) )
132 throw XMLInvalidBuildFileException (
133 location,
134 "<directory> tag has invalid characters in 'name' attribute" );
135 if ( !path.size() )
136 return att_value;
137
138 string path_prefix;
139 if ( root )
140 {
141 if ( root->value == "intermediate" )
142 path_prefix = Environment::GetIntermediatePath() + cSep;
143 else if ( root->value == "output" )
144 path_prefix = Environment::GetOutputPath() + cSep;
145 else
146 {
147 throw InvalidAttributeValueException (
148 location,
149 "root",
150 root->value );
151 }
152 }
153 else
154 path_prefix = "";
155
156 return FixSeparator(path_prefix + path + cSep + att_value);
157 }
158
159 string
160 GetExtension ( const string& filename )
161 {
162 size_t index = filename.find_last_of ( '/' );
163 if (index == string::npos) index = 0;
164 string tmp = filename.substr( index, filename.size() - index );
165 size_t ext_index = tmp.find_last_of( '.' );
166 if (ext_index != string::npos)
167 return filename.substr ( index + ext_index, filename.size() );
168 return "";
169 }
170
171 string
172 GetDirectory ( const string& filename )
173 {
174 size_t index = filename.find_last_of ( cSep );
175 if ( index == string::npos )
176 return "";
177 else
178 return filename.substr ( 0, index );
179 }
180
181 string
182 GetFilename ( const string& filename )
183 {
184 size_t index = filename.find_last_of ( cSep );
185 if ( index == string::npos )
186 return filename;
187 else
188 return filename.substr ( index + 1, filename.length () - index );
189 }
190
191 string
192 NormalizeFilename ( const string& filename )
193 {
194 if ( filename == "" )
195 return "";
196 Path path;
197 string normalizedPath = path.Fixup ( filename, true );
198 string relativeNormalizedPath = path.RelativeFromWorkingDirectory ( normalizedPath );
199 return FixSeparator ( relativeNormalizedPath );
200 }
201
202 bool
203 GetBooleanValue ( const string& value )
204 {
205 if ( value == "1" )
206 return true;
207 else
208 return false;
209 }
210
211 string
212 ToLower ( string filename )
213 {
214 for ( size_t i = 1; i < filename.length (); i++ )
215 filename[i] = tolower ( filename[i] );
216 return filename;
217 }
218
219 void IfableData::ExtractModules( std::vector<Module*> &modules )
220 {
221 size_t i;
222 for ( i = 0; i < this->modules.size (); i++ )
223 modules.push_back(this->modules[i]);
224 }
225
226 IfableData::~IfableData()
227 {
228 size_t i;
229 for ( i = 0; i < includes.size (); i++ )
230 delete includes[i];
231 for ( i = 0; i < defines.size (); i++ )
232 delete defines[i];
233 for ( i = 0; i < libraries.size (); i++ )
234 delete libraries[i];
235 for ( i = 0; i < properties.size (); i++ )
236 delete properties[i];
237 for ( i = 0; i < compilerFlags.size (); i++ )
238 delete compilerFlags[i];
239 for ( i = 0; i < modules.size(); i++ )
240 delete modules[i];
241 for ( i = 0; i < ifs.size (); i++ )
242 delete ifs[i];
243 for ( i = 0; i < compilationUnits.size (); i++ )
244 delete compilationUnits[i];
245 }
246
247 void IfableData::ProcessXML ()
248 {
249 size_t i;
250 for ( i = 0; i < includes.size (); i++ )
251 includes[i]->ProcessXML ();
252 for ( i = 0; i < defines.size (); i++ )
253 defines[i]->ProcessXML ();
254 for ( i = 0; i < libraries.size (); i++ )
255 libraries[i]->ProcessXML ();
256 for ( i = 0; i < properties.size(); i++ )
257 properties[i]->ProcessXML ();
258 for ( i = 0; i < compilerFlags.size(); i++ )
259 compilerFlags[i]->ProcessXML ();
260 for ( i = 0; i < ifs.size (); i++ )
261 ifs[i]->ProcessXML ();
262 for ( i = 0; i < compilationUnits.size (); i++ )
263 compilationUnits[i]->ProcessXML ();
264 }
265
266 Module::Module ( const Project& project,
267 const XMLElement& moduleNode,
268 const string& modulePath )
269 : project (project),
270 node (moduleNode),
271 importLibrary (NULL),
272 bootstrap (NULL),
273 autoRegister(NULL),
274 linkerScript (NULL),
275 pch (NULL),
276 cplusplus (false),
277 host (HostDefault)
278 {
279 if ( node.name != "module" )
280 throw InvalidOperationException ( __FILE__,
281 __LINE__,
282 "Module created with non-<module> node" );
283
284 xmlbuildFile = Path::RelativeFromWorkingDirectory ( moduleNode.xmlFile->filename () );
285
286 path = FixSeparator ( modulePath );
287
288 enabled = true;
289
290 const XMLAttribute* att = moduleNode.GetAttribute ( "if", false );
291 if ( att != NULL )
292 enabled = GetBooleanValue ( project.ResolveProperties ( att->value ) );
293
294 att = moduleNode.GetAttribute ( "ifnot", false );
295 if ( att != NULL )
296 enabled = !GetBooleanValue ( project.ResolveProperties ( att->value ) );
297
298 att = moduleNode.GetAttribute ( "name", true );
299 assert(att);
300 name = att->value;
301
302 att = moduleNode.GetAttribute ( "type", true );
303 assert(att);
304 type = GetModuleType ( node.location, *att );
305
306 att = moduleNode.GetAttribute ( "extension", false );
307 if ( att != NULL )
308 extension = att->value;
309 else
310 extension = GetDefaultModuleExtension ();
311
312 att = moduleNode.GetAttribute ( "unicode", false );
313 if ( att != NULL )
314 {
315 const char* p = att->value.c_str();
316 if ( !stricmp ( p, "true" ) || !stricmp ( p, "yes" ) )
317 isUnicode = true;
318 else if ( !stricmp ( p, "false" ) || !stricmp ( p, "no" ) )
319 isUnicode = false;
320 else
321 {
322 throw InvalidAttributeValueException (
323 moduleNode.location,
324 "unicode",
325 att->value );
326 }
327 }
328 else
329 isUnicode = false;
330
331 att = moduleNode.GetAttribute ( "stdlib", false );
332 if ( att != NULL )
333 {
334 const char* p = att->value.c_str();
335 if ( !stricmp ( p, "host" ) )
336 useHostStdlib = true;
337 else if ( !stricmp ( p, "default" ) )
338 useHostStdlib = false;
339 else
340 {
341 throw InvalidAttributeValueException (
342 moduleNode.location,
343 "stdlib",
344 att->value );
345 }
346 }
347 else
348 useHostStdlib = false;
349
350 if (isUnicode)
351 {
352 // Always define UNICODE and _UNICODE
353 Define* pDefine = new Define ( project, this, "UNICODE" );
354 non_if_data.defines.push_back ( pDefine );
355
356 pDefine = new Define ( project, this, "_UNICODE" );
357 non_if_data.defines.push_back ( pDefine );
358 }
359
360 att = moduleNode.GetAttribute ( "entrypoint", false );
361 if ( att != NULL )
362 {
363 if ( att->value == "" )
364 {
365 throw InvalidAttributeValueException (
366 moduleNode.location,
367 "entrypoint",
368 att->value );
369 }
370
371 entrypoint = att->value;
372 isDefaultEntryPoint = false;
373 }
374 else
375 {
376 entrypoint = GetDefaultModuleEntrypoint ();
377 isDefaultEntryPoint = true;
378 }
379
380 att = moduleNode.GetAttribute ( "baseaddress", false );
381 if ( att != NULL )
382 baseaddress = att->value;
383 else
384 baseaddress = GetDefaultModuleBaseaddress ();
385
386 att = moduleNode.GetAttribute ( "mangledsymbols", false );
387 if ( att != NULL )
388 {
389 const char* p = att->value.c_str();
390 if ( !stricmp ( p, "true" ) || !stricmp ( p, "yes" ) )
391 mangledSymbols = true;
392 else if ( !stricmp ( p, "false" ) || !stricmp ( p, "no" ) )
393 mangledSymbols = false;
394 else
395 {
396 throw InvalidAttributeValueException (
397 moduleNode.location,
398 "mangledsymbols",
399 att->value );
400 }
401 }
402 else
403 mangledSymbols = false;
404
405 att = moduleNode.GetAttribute ( "underscoresymbols", false );
406 if ( att != NULL )
407 underscoreSymbols = att->value == "true";
408 else
409 underscoreSymbols = false;
410
411 att = moduleNode.GetAttribute ( "host", false );
412 if ( att != NULL )
413 {
414 const char* p = att->value.c_str();
415 if ( !stricmp ( p, "true" ) || !stricmp ( p, "yes" ) )
416 host = HostTrue;
417 else if ( !stricmp ( p, "false" ) || !stricmp ( p, "no" ) )
418 host = HostFalse;
419 else
420 {
421 throw InvalidAttributeValueException (
422 moduleNode.location,
423 "host",
424 att->value );
425 }
426 }
427
428 att = moduleNode.GetAttribute ( "isstartuplib", false );
429 if ( att != NULL )
430 {
431 const char* p = att->value.c_str();
432 if ( !stricmp ( p, "true" ) || !stricmp ( p, "yes" ) )
433 isStartupLib = true;
434 else if ( !stricmp ( p, "false" ) || !stricmp ( p, "no" ) )
435 isStartupLib = false;
436 else
437 {
438 throw InvalidAttributeValueException (
439 moduleNode.location,
440 "host",
441 att->value );
442 }
443 }
444 else
445 isStartupLib = false;
446
447 att = moduleNode.GetAttribute ( "prefix", false );
448 if ( att != NULL )
449 prefix = att->value;
450
451 att = moduleNode.GetAttribute ( "installbase", false );
452 if ( att != NULL )
453 installBase = att->value;
454 else
455 installBase = "";
456
457 att = moduleNode.GetAttribute ( "installname", false );
458 if ( att != NULL )
459 installName = att->value;
460 else
461 installName = "";
462
463 att = moduleNode.GetAttribute ( "usewrc", false );
464 if ( att != NULL )
465 useWRC = att->value == "true";
466 else
467 useWRC = true;
468
469 att = moduleNode.GetAttribute ( "allowwarnings", false );
470 if ( att == NULL )
471 {
472 att = moduleNode.GetAttribute ( "warnings", false );
473 if ( att != NULL )
474 {
475 printf ( "%s: WARNING: 'warnings' attribute of <module> is deprecated, use 'allowwarnings' instead\n",
476 moduleNode.location.c_str() );
477 }
478 }
479 if ( att != NULL )
480 allowWarnings = att->value == "true";
481 else
482 allowWarnings = false;
483
484 att = moduleNode.GetAttribute ( "aliasof", false );
485 if ( type == Alias && att != NULL )
486 aliasedModuleName = att->value;
487 else
488 aliasedModuleName = "";
489
490 if ( type == BootProgram )
491 {
492 att = moduleNode.GetAttribute ( "payload", true );
493 payload = att->value;
494 }
495 }
496
497 Module::~Module ()
498 {
499 size_t i;
500 for ( i = 0; i < invocations.size(); i++ )
501 delete invocations[i];
502 for ( i = 0; i < dependencies.size(); i++ )
503 delete dependencies[i];
504 for ( i = 0; i < compilerFlags.size(); i++ )
505 delete compilerFlags[i];
506 for ( i = 0; i < linkerFlags.size(); i++ )
507 delete linkerFlags[i];
508 for ( i = 0; i < stubbedComponents.size(); i++ )
509 delete stubbedComponents[i];
510 if ( linkerScript )
511 delete linkerScript;
512 if ( pch )
513 delete pch;
514 }
515
516 void
517 Module::ProcessXML()
518 {
519 if ( type == Alias )
520 {
521 if ( aliasedModuleName == name )
522 {
523 throw XMLInvalidBuildFileException (
524 node.location,
525 "module '%s' cannot link against itself",
526 name.c_str() );
527 }
528 const Module* m = project.LocateModule ( aliasedModuleName );
529 if ( !m )
530 {
531 throw XMLInvalidBuildFileException (
532 node.location,
533 "module '%s' trying to alias non-existant module '%s'",
534 name.c_str(),
535 aliasedModuleName.c_str() );
536 }
537 }
538
539 size_t i;
540 for ( i = 0; i < node.subElements.size(); i++ )
541 {
542 ParseContext parseContext;
543 ProcessXMLSubElement ( *node.subElements[i], path, parseContext );
544 }
545 for ( i = 0; i < invocations.size(); i++ )
546 invocations[i]->ProcessXML ();
547 for ( i = 0; i < dependencies.size(); i++ )
548 dependencies[i]->ProcessXML ();
549 for ( i = 0; i < compilerFlags.size(); i++ )
550 compilerFlags[i]->ProcessXML();
551 for ( i = 0; i < linkerFlags.size(); i++ )
552 linkerFlags[i]->ProcessXML();
553 for ( i = 0; i < stubbedComponents.size(); i++ )
554 stubbedComponents[i]->ProcessXML();
555 non_if_data.ProcessXML();
556 if ( linkerScript )
557 linkerScript->ProcessXML();
558 if ( pch )
559 pch->ProcessXML();
560 if ( autoRegister )
561 autoRegister->ProcessXML();
562 }
563
564 void
565 Module::ProcessXMLSubElement ( const XMLElement& e,
566 const string& path,
567 ParseContext& parseContext )
568 {
569 If* pOldIf = parseContext.ifData;
570 CompilationUnit* pOldCompilationUnit = parseContext.compilationUnit;
571 bool subs_invalid = false;
572 string subpath ( path );
573 if ( e.name == "file" && e.value.size () > 0 )
574 {
575 bool first = false;
576 const XMLAttribute* att = e.GetAttribute ( "first", false );
577 if ( att != NULL )
578 {
579 if ( !stricmp ( att->value.c_str(), "true" ) )
580 first = true;
581 else if ( stricmp ( att->value.c_str(), "false" ) )
582 {
583 throw XMLInvalidBuildFileException (
584 e.location,
585 "attribute 'first' of <file> element can only be 'true' or 'false'" );
586 }
587 }
588 string switches = "";
589 att = e.GetAttribute ( "switches", false );
590 if ( att != NULL )
591 switches = att->value;
592 if ( !cplusplus )
593 {
594 // check for c++ file
595 string ext = GetExtension ( e.value );
596 if ( !stricmp ( ext.c_str(), ".cpp" ) )
597 cplusplus = true;
598 else if ( !stricmp ( ext.c_str(), ".cc" ) )
599 cplusplus = true;
600 else if ( !stricmp ( ext.c_str(), ".cxx" ) )
601 cplusplus = true;
602 }
603 File* pFile = new File ( FixSeparator ( path + cSep + e.value ),
604 first,
605 switches,
606 false );
607 if ( parseContext.compilationUnit )
608 parseContext.compilationUnit->files.push_back ( pFile );
609 else
610 {
611 CompilationUnit* pCompilationUnit = new CompilationUnit ( pFile );
612 if ( parseContext.ifData )
613 parseContext.ifData->data.compilationUnits.push_back ( pCompilationUnit );
614 else
615 {
616 string ext = GetExtension ( e.value );
617 if ( !stricmp ( ext.c_str(), ".idl" ) )
618 non_if_data.compilationUnits.insert ( non_if_data.compilationUnits.begin(), pCompilationUnit );
619 else
620 non_if_data.compilationUnits.push_back ( pCompilationUnit );
621 }
622 }
623 if ( parseContext.ifData )
624 parseContext.ifData->data.files.push_back ( pFile );
625 else
626 non_if_data.files.push_back ( pFile );
627 subs_invalid = true;
628 }
629 else if ( e.name == "library" && e.value.size () )
630 {
631 Library* pLibrary = new Library ( e, *this, e.value );
632 if ( parseContext.ifData )
633 parseContext.ifData->data.libraries.push_back ( pLibrary );
634 else
635 non_if_data.libraries.push_back ( pLibrary );
636 subs_invalid = true;
637 }
638 else if ( e.name == "directory" )
639 {
640 const XMLAttribute* att = e.GetAttribute ( "name", true );
641 const XMLAttribute* base = e.GetAttribute ( "root", false );
642 assert(att);
643 subpath = GetSubPath ( this->project, e.location, path, base, att->value );
644 }
645 else if ( e.name == "include" )
646 {
647 Include* include = new Include ( project, this, &e );
648 if ( parseContext.ifData )
649 parseContext.ifData->data.includes.push_back ( include );
650 else
651 non_if_data.includes.push_back ( include );
652 subs_invalid = true;
653 }
654 else if ( e.name == "define" )
655 {
656 Define* pDefine = new Define ( project, this, e );
657 if ( parseContext.ifData )
658 parseContext.ifData->data.defines.push_back ( pDefine );
659 else
660 non_if_data.defines.push_back ( pDefine );
661 subs_invalid = true;
662 }
663 else if ( e.name == "invoke" )
664 {
665 if ( parseContext.ifData )
666 {
667 throw XMLInvalidBuildFileException (
668 e.location,
669 "<invoke> is not a valid sub-element of <if>" );
670 }
671 invocations.push_back ( new Invoke ( e, *this ) );
672 subs_invalid = false;
673 }
674 else if ( e.name == "dependency" )
675 {
676 if ( parseContext.ifData )
677 {
678 throw XMLInvalidBuildFileException (
679 e.location,
680 "<dependency> is not a valid sub-element of <if>" );
681 }
682 dependencies.push_back ( new Dependency ( e, *this ) );
683 subs_invalid = true;
684 }
685 else if ( e.name == "importlibrary" )
686 {
687 if ( parseContext.ifData )
688 {
689 throw XMLInvalidBuildFileException (
690 e.location,
691 "<importlibrary> is not a valid sub-element of <if>" );
692 }
693 if ( importLibrary )
694 {
695 throw XMLInvalidBuildFileException (
696 e.location,
697 "Only one <importlibrary> is valid per module" );
698 }
699 importLibrary = new ImportLibrary ( e, *this );
700 subs_invalid = true;
701 }
702 else if ( e.name == "if" )
703 {
704 parseContext.ifData = new If ( e, project, this );
705 if ( pOldIf )
706 pOldIf->data.ifs.push_back ( parseContext.ifData );
707 else
708 non_if_data.ifs.push_back ( parseContext.ifData );
709 subs_invalid = false;
710 }
711 else if ( e.name == "ifnot" )
712 {
713 parseContext.ifData = new If ( e, project, this, true );
714 if ( pOldIf )
715 pOldIf->data.ifs.push_back ( parseContext.ifData );
716 else
717 non_if_data.ifs.push_back ( parseContext.ifData );
718 subs_invalid = false;
719 }
720 else if ( e.name == "compilerflag" )
721 {
722 CompilerFlag* pCompilerFlag = new CompilerFlag ( project, this, e );
723 if ( parseContext.ifData )
724 parseContext.ifData->data.compilerFlags.push_back ( pCompilerFlag );
725 else
726 non_if_data.compilerFlags.push_back ( pCompilerFlag );
727 subs_invalid = true;
728 }
729 else if ( e.name == "linkerflag" )
730 {
731 linkerFlags.push_back ( new LinkerFlag ( project, this, e ) );
732 subs_invalid = true;
733 }
734 else if ( e.name == "linkerscript" )
735 {
736 if ( linkerScript )
737 {
738 throw XMLInvalidBuildFileException (
739 e.location,
740 "Only one <linkerscript> is valid per module" );
741 }
742 linkerScript = new LinkerScript ( project, this, e );
743 subs_invalid = true;
744 }
745 else if ( e.name == "component" )
746 {
747 stubbedComponents.push_back ( new StubbedComponent ( this, e ) );
748 subs_invalid = false;
749 }
750 else if ( e.name == "property" )
751 {
752 throw XMLInvalidBuildFileException (
753 e.location,
754 "<property> is not a valid sub-element of <module>" );
755 }
756 else if ( e.name == "bootstrap" )
757 {
758 bootstrap = new Bootstrap ( project, this, e );
759 subs_invalid = true;
760 }
761 else if ( e.name == "pch" )
762 {
763 if ( parseContext.ifData )
764 {
765 throw XMLInvalidBuildFileException (
766 e.location,
767 "<pch> is not a valid sub-element of <if>" );
768 }
769 if ( pch )
770 {
771 throw XMLInvalidBuildFileException (
772 e.location,
773 "Only one <pch> is valid per module" );
774 }
775 pch = new PchFile (
776 e, *this, File ( FixSeparator ( path + cSep + e.value ), false, "", true ) );
777 subs_invalid = true;
778 }
779 else if ( e.name == "compilationunit" )
780 {
781 if ( project.configuration.CompilationUnitsEnabled )
782 {
783 CompilationUnit* pCompilationUnit = new CompilationUnit ( &project, this, &e );
784 if ( parseContext.ifData )
785 parseContext.ifData->data.compilationUnits.push_back ( pCompilationUnit );
786 else
787 non_if_data.compilationUnits.push_back ( pCompilationUnit );
788 parseContext.compilationUnit = pCompilationUnit;
789 }
790 subs_invalid = false;
791 }
792 else if ( e.name == "autoregister" )
793 {
794 if ( autoRegister != NULL)
795 {
796 throw XMLInvalidBuildFileException (
797 e.location,
798 "there can be only one <%s> element for a module",
799 e.name.c_str() );
800 }
801 autoRegister = new AutoRegister ( project, this, e );
802 subs_invalid = true;
803 }
804 if ( subs_invalid && e.subElements.size() > 0 )
805 {
806 throw XMLInvalidBuildFileException (
807 e.location,
808 "<%s> cannot have sub-elements",
809 e.name.c_str() );
810 }
811 for ( size_t i = 0; i < e.subElements.size (); i++ )
812 ProcessXMLSubElement ( *e.subElements[i], subpath, parseContext );
813 parseContext.ifData = pOldIf;
814 parseContext.compilationUnit = pOldCompilationUnit;
815 }
816
817 ModuleType
818 Module::GetModuleType ( const string& location, const XMLAttribute& attribute )
819 {
820 if ( attribute.value == "buildtool" )
821 return BuildTool;
822 if ( attribute.value == "staticlibrary" )
823 return StaticLibrary;
824 if ( attribute.value == "objectlibrary" )
825 return ObjectLibrary;
826 if ( attribute.value == "kernel" )
827 return Kernel;
828 if ( attribute.value == "kernelmodedll" )
829 return KernelModeDLL;
830 if ( attribute.value == "kernelmodedriver" )
831 return KernelModeDriver;
832 if ( attribute.value == "nativedll" )
833 return NativeDLL;
834 if ( attribute.value == "nativecui" )
835 return NativeCUI;
836 if ( attribute.value == "win32dll" )
837 return Win32DLL;
838 if ( attribute.value == "win32ocx" )
839 return Win32OCX;
840 if ( attribute.value == "win32cui" )
841 return Win32CUI;
842 if ( attribute.value == "win32gui" )
843 return Win32GUI;
844 if ( attribute.value == "win32scr" )
845 return Win32SCR;
846 if ( attribute.value == "bootloader" )
847 return BootLoader;
848 if ( attribute.value == "bootsector" )
849 return BootSector;
850 if ( attribute.value == "bootprogram" )
851 return BootProgram;
852 if ( attribute.value == "iso" )
853 return Iso;
854 if ( attribute.value == "liveiso" )
855 return LiveIso;
856 if ( attribute.value == "isoregtest" )
857 return IsoRegTest;
858 if ( attribute.value == "liveisoregtest" )
859 return LiveIsoRegTest;
860 if ( attribute.value == "test" )
861 return Test;
862 if ( attribute.value == "rpcserver" )
863 return RpcServer;
864 if ( attribute.value == "rpcclient" )
865 return RpcClient;
866 if ( attribute.value == "alias" )
867 return Alias;
868 if ( attribute.value == "idlheader" )
869 return IdlHeader;
870 if ( attribute.value == "embeddedtypelib" )
871 return EmbeddedTypeLib;
872 throw InvalidAttributeValueException ( location,
873 attribute.name,
874 attribute.value );
875 }
876
877 string
878 Module::GetDefaultModuleExtension () const
879 {
880 switch (type)
881 {
882 case BuildTool:
883 return ExePostfix;
884 case StaticLibrary:
885 return ".a";
886 case ObjectLibrary:
887 return ".o";
888 case Kernel:
889 case NativeCUI:
890 case Win32CUI:
891 case Win32GUI:
892 return ".exe";
893 case Win32SCR:
894 return ".scr";
895
896 case KernelModeDLL:
897 case NativeDLL:
898 case Win32DLL:
899 return ".dll";
900 case Win32OCX:
901 return ".ocx";
902 case KernelModeDriver:
903 case BootLoader:
904 return ".sys";
905 case BootSector:
906 return ".o";
907 case Iso:
908 case LiveIso:
909 case IsoRegTest:
910 case LiveIsoRegTest:
911 return ".iso";
912 case Test:
913 return ".exe";
914 case RpcServer:
915 return ".o";
916 case RpcClient:
917 return ".o";
918 case Alias:
919 case BootProgram:
920 case IdlHeader:
921 return "";
922 case EmbeddedTypeLib:
923 return ".tlb";
924 }
925 throw InvalidOperationException ( __FILE__,
926 __LINE__ );
927 }
928
929 string
930 Module::GetDefaultModuleEntrypoint () const
931 {
932 switch ( type )
933 {
934 case Kernel:
935 return "NtProcessStartup";
936 case KernelModeDLL:
937 case KernelModeDriver:
938 return "DriverEntry@8";
939 case NativeDLL:
940 return "DllMainCRTStartup@12";
941 case NativeCUI:
942 return "NtProcessStartup@4";
943 case Win32DLL:
944 case Win32OCX:
945 return "DllMain@12";
946 case Win32CUI:
947 case Test:
948 if ( isUnicode )
949 return "wmainCRTStartup";
950 else
951 return "mainCRTStartup";
952 case Win32SCR:
953 case Win32GUI:
954 if ( isUnicode )
955 return "wWinMainCRTStartup";
956 else
957 return "WinMainCRTStartup";
958 case BuildTool:
959 case StaticLibrary:
960 case ObjectLibrary:
961 case BootLoader:
962 case BootSector:
963 case Iso:
964 case LiveIso:
965 case IsoRegTest:
966 case LiveIsoRegTest:
967 case RpcServer:
968 case RpcClient:
969 case Alias:
970 case BootProgram:
971 case IdlHeader:
972 case EmbeddedTypeLib:
973 return "";
974 }
975 throw InvalidOperationException ( __FILE__,
976 __LINE__ );
977 }
978
979 string
980 Module::GetDefaultModuleBaseaddress () const
981 {
982 switch ( type )
983 {
984 case Kernel:
985 return "0x80000000";
986 case Win32DLL:
987 case Win32OCX:
988 return "0x10000000";
989 case NativeDLL:
990 case NativeCUI:
991 case Win32CUI:
992 case Test:
993 return "0x00400000";
994 case Win32SCR:
995 case Win32GUI:
996 return "0x00400000";
997 case KernelModeDLL:
998 case KernelModeDriver:
999 return "0x00010000";
1000 case BuildTool:
1001 case StaticLibrary:
1002 case ObjectLibrary:
1003 case BootLoader:
1004 case BootSector:
1005 case Iso:
1006 case LiveIso:
1007 case IsoRegTest:
1008 case LiveIsoRegTest:
1009 case RpcServer:
1010 case RpcClient:
1011 case Alias:
1012 case BootProgram:
1013 case IdlHeader:
1014 case EmbeddedTypeLib:
1015 return "";
1016 }
1017 throw InvalidOperationException ( __FILE__,
1018 __LINE__ );
1019 }
1020
1021 bool
1022 Module::HasImportLibrary () const
1023 {
1024 return importLibrary != NULL && type != StaticLibrary;
1025 }
1026
1027 bool
1028 Module::IsDLL () const
1029 {
1030 switch ( type )
1031 {
1032 case Kernel:
1033 case KernelModeDLL:
1034 case NativeDLL:
1035 case Win32DLL:
1036 case Win32OCX:
1037 case KernelModeDriver:
1038 return true;
1039 case NativeCUI:
1040 case Win32CUI:
1041 case Test:
1042 case Win32SCR:
1043 case Win32GUI:
1044 case BuildTool:
1045 case StaticLibrary:
1046 case ObjectLibrary:
1047 case BootLoader:
1048 case BootSector:
1049 case BootProgram:
1050 case Iso:
1051 case LiveIso:
1052 case IsoRegTest:
1053 case LiveIsoRegTest:
1054 case RpcServer:
1055 case RpcClient:
1056 case Alias:
1057 case IdlHeader:
1058 case EmbeddedTypeLib:
1059 return false;
1060 }
1061 throw InvalidOperationException ( __FILE__,
1062 __LINE__ );
1063 }
1064
1065 bool
1066 Module::GenerateInOutputTree () const
1067 {
1068 switch ( type )
1069 {
1070 case Kernel:
1071 case KernelModeDLL:
1072 case NativeDLL:
1073 case Win32DLL:
1074 case Win32OCX:
1075 case KernelModeDriver:
1076 case NativeCUI:
1077 case Win32CUI:
1078 case Test:
1079 case Win32SCR:
1080 case Win32GUI:
1081 case BuildTool:
1082 case BootLoader:
1083 case BootSector:
1084 case BootProgram:
1085 case Iso:
1086 case LiveIso:
1087 case IsoRegTest:
1088 case LiveIsoRegTest:
1089 case EmbeddedTypeLib:
1090 return true;
1091 case StaticLibrary:
1092 case ObjectLibrary:
1093 case RpcServer:
1094 case RpcClient:
1095 case Alias:
1096 case IdlHeader:
1097 return false;
1098 }
1099 throw InvalidOperationException ( __FILE__,
1100 __LINE__ );
1101 }
1102
1103 string
1104 Module::GetTargetName () const
1105 {
1106 return name + extension;
1107 }
1108
1109 string
1110 Module::GetDependencyPath () const
1111 {
1112 if ( HasImportLibrary () )
1113 return ReplaceExtension ( GetPathWithPrefix ( "lib" ), ".a" );
1114 else
1115 return GetPath();
1116 }
1117
1118 string
1119 Module::GetBasePath () const
1120 {
1121 return path;
1122 }
1123
1124 string
1125 Module::GetPath () const
1126 {
1127 if ( path.length() > 0 )
1128 return path + cSep + GetTargetName ();
1129 else
1130 return GetTargetName ();
1131 }
1132
1133 string
1134 Module::GetPathWithPrefix ( const string& prefix ) const
1135 {
1136 return path + cSep + prefix + GetTargetName ();
1137 }
1138
1139 string
1140 Module::GetPathToBaseDir () const
1141 {
1142 string temp_path = path;
1143 string result = "..\\";
1144 while(temp_path.find ('\\') != string::npos)
1145 {
1146 temp_path.erase (0, temp_path.find('\\')+1);
1147 result += "..\\";
1148 }
1149 return result;
1150 }
1151
1152 string
1153 Module::GetInvocationTarget ( const int index ) const
1154 {
1155 return ssprintf ( "%s_invoke_%d",
1156 name.c_str (),
1157 index );
1158 }
1159
1160 string
1161 Module::GetEntryPoint(bool leadingUnderscore) const
1162 {
1163 string result = "";
1164 if (entrypoint == "0" || entrypoint == "0x0")
1165 return "0";
1166 if (leadingUnderscore)
1167 result = "_";
1168
1169 result += entrypoint;
1170 return result;
1171 }
1172
1173 bool
1174 Module::HasFileWithExtension (
1175 const IfableData& data,
1176 const std::string& extension ) const
1177 {
1178 size_t i;
1179 for ( i = 0; i < data.compilationUnits.size (); i++ )
1180 {
1181 CompilationUnit* compilationUnit = data.compilationUnits[i];
1182 if ( compilationUnit->HasFileWithExtension ( extension ) )
1183 return true;
1184 }
1185 for ( i = 0; i < data.ifs.size (); i++ )
1186 {
1187 if ( HasFileWithExtension ( data.ifs[i]->data, extension ) )
1188 return true;
1189 }
1190 return false;
1191 }
1192
1193 void
1194 Module::InvokeModule () const
1195 {
1196 for ( size_t i = 0; i < invocations.size (); i++ )
1197 {
1198 Invoke& invoke = *invocations[i];
1199 string command = FixSeparatorForSystemCommand(invoke.invokeModule->GetPath ()) + " " + invoke.GetParameters ();
1200 printf ( "Executing '%s'\n\n", command.c_str () );
1201 int exitcode = system ( command.c_str () );
1202 if ( exitcode != 0 )
1203 throw InvocationFailedException ( command,
1204 exitcode );
1205 }
1206 }
1207
1208
1209 File::File ( const string& _name, bool _first,
1210 std::string _switches,
1211 bool _isPreCompiledHeader )
1212 : name(_name),
1213 first(_first),
1214 switches(_switches),
1215 isPreCompiledHeader(_isPreCompiledHeader)
1216 {
1217 }
1218
1219 void
1220 File::ProcessXML()
1221 {
1222 }
1223
1224
1225 Library::Library ( const XMLElement& _node,
1226 const Module& _module,
1227 const string& _name )
1228 : node(&_node),
1229 module(_module),
1230 name(_name),
1231 importedModule(_module.project.LocateModule(_name))
1232 {
1233 if ( module.name == name )
1234 {
1235 throw XMLInvalidBuildFileException (
1236 node->location,
1237 "module '%s' cannot link against itself",
1238 name.c_str() );
1239 }
1240 if ( !importedModule )
1241 {
1242 throw XMLInvalidBuildFileException (
1243 node->location,
1244 "module '%s' trying to import non-existant module '%s'",
1245 module.name.c_str(),
1246 name.c_str() );
1247 }
1248 }
1249
1250 Library::Library ( const Module& _module,
1251 const string& _name )
1252 : node(NULL),
1253 module(_module),
1254 name(_name),
1255 importedModule(_module.project.LocateModule(_name))
1256 {
1257 }
1258
1259 void
1260 Library::ProcessXML()
1261 {
1262 if ( node && !module.project.LocateModule ( name ) )
1263 {
1264 throw XMLInvalidBuildFileException (
1265 node->location,
1266 "module '%s' is trying to link against non-existant module '%s'",
1267 module.name.c_str(),
1268 name.c_str() );
1269 }
1270 }
1271
1272
1273 Invoke::Invoke ( const XMLElement& _node,
1274 const Module& _module )
1275 : node (_node),
1276 module (_module)
1277 {
1278 }
1279
1280 void
1281 Invoke::ProcessXML()
1282 {
1283 const XMLAttribute* att = node.GetAttribute ( "module", false );
1284 if (att == NULL)
1285 invokeModule = &module;
1286 else
1287 {
1288 invokeModule = module.project.LocateModule ( att->value );
1289 if ( invokeModule == NULL )
1290 {
1291 throw XMLInvalidBuildFileException (
1292 node.location,
1293 "module '%s' is trying to invoke non-existant module '%s'",
1294 module.name.c_str(),
1295 att->value.c_str() );
1296 }
1297 }
1298
1299 for ( size_t i = 0; i < node.subElements.size (); i++ )
1300 ProcessXMLSubElement ( *node.subElements[i] );
1301 }
1302
1303 void
1304 Invoke::ProcessXMLSubElement ( const XMLElement& e )
1305 {
1306 bool subs_invalid = false;
1307 if ( e.name == "input" )
1308 {
1309 for ( size_t i = 0; i < e.subElements.size (); i++ )
1310 ProcessXMLSubElementInput ( *e.subElements[i] );
1311 }
1312 else if ( e.name == "output" )
1313 {
1314 for ( size_t i = 0; i < e.subElements.size (); i++ )
1315 ProcessXMLSubElementOutput ( *e.subElements[i] );
1316 }
1317 if ( subs_invalid && e.subElements.size() > 0 )
1318 {
1319 throw XMLInvalidBuildFileException (
1320 e.location,
1321 "<%s> cannot have sub-elements",
1322 e.name.c_str() );
1323 }
1324 }
1325
1326 void
1327 Invoke::ProcessXMLSubElementInput ( const XMLElement& e )
1328 {
1329 bool subs_invalid = false;
1330 if ( e.name == "inputfile" && e.value.size () > 0 )
1331 {
1332 input.push_back ( new InvokeFile (
1333 e, FixSeparator ( module.path + cSep + e.value ) ) );
1334 subs_invalid = true;
1335 }
1336 if ( subs_invalid && e.subElements.size() > 0 )
1337 {
1338 throw XMLInvalidBuildFileException (
1339 e.location,
1340 "<%s> cannot have sub-elements",
1341 e.name.c_str() );
1342 }
1343 }
1344
1345 void
1346 Invoke::ProcessXMLSubElementOutput ( const XMLElement& e )
1347 {
1348 bool subs_invalid = false;
1349 if ( e.name == "outputfile" && e.value.size () > 0 )
1350 {
1351 output.push_back ( new InvokeFile (
1352 e, FixSeparator ( module.path + cSep + e.value ) ) );
1353 subs_invalid = true;
1354 }
1355 if ( subs_invalid && e.subElements.size() > 0 )
1356 {
1357 throw XMLInvalidBuildFileException (
1358 e.location,
1359 "<%s> cannot have sub-elements",
1360 e.name.c_str() );
1361 }
1362 }
1363
1364 void
1365 Invoke::GetTargets ( string_list& targets ) const
1366 {
1367 for ( size_t i = 0; i < output.size (); i++ )
1368 {
1369 InvokeFile& file = *output[i];
1370 targets.push_back ( NormalizeFilename ( file.name ) );
1371 }
1372 }
1373
1374 string
1375 Invoke::GetParameters () const
1376 {
1377 string parameters ( "" );
1378 size_t i;
1379 for ( i = 0; i < output.size (); i++ )
1380 {
1381 if ( parameters.length () > 0)
1382 parameters += " ";
1383 InvokeFile& invokeFile = *output[i];
1384 if ( invokeFile.switches.length () > 0 )
1385 {
1386 parameters += invokeFile.switches + " ";
1387 }
1388 parameters += invokeFile.name;
1389 }
1390
1391 for ( i = 0; i < input.size (); i++ )
1392 {
1393 if ( parameters.length () > 0 )
1394 parameters += " ";
1395 InvokeFile& invokeFile = *input[i];
1396 if ( invokeFile.switches.length () > 0 )
1397 {
1398 parameters += invokeFile.switches;
1399 parameters += " ";
1400 }
1401 parameters += invokeFile.name ;
1402 }
1403
1404 return parameters;
1405 }
1406
1407
1408 InvokeFile::InvokeFile ( const XMLElement& _node,
1409 const string& _name )
1410 : node (_node),
1411 name (_name)
1412 {
1413 const XMLAttribute* att = _node.GetAttribute ( "switches", false );
1414 if (att != NULL)
1415 switches = att->value;
1416 else
1417 switches = "";
1418 }
1419
1420 void
1421 InvokeFile::ProcessXML()
1422 {
1423 }
1424
1425
1426 Dependency::Dependency ( const XMLElement& _node,
1427 const Module& _module )
1428 : node (_node),
1429 module (_module),
1430 dependencyModule (NULL)
1431 {
1432 }
1433
1434 void
1435 Dependency::ProcessXML()
1436 {
1437 dependencyModule = module.project.LocateModule ( node.value );
1438 if ( dependencyModule == NULL )
1439 {
1440 throw XMLInvalidBuildFileException (
1441 node.location,
1442 "module '%s' depend on non-existant module '%s'",
1443 module.name.c_str(),
1444 node.value.c_str() );
1445 }
1446 }
1447
1448
1449 ImportLibrary::ImportLibrary ( const XMLElement& _node,
1450 const Module& _module )
1451 : node (_node),
1452 module (_module)
1453 {
1454 const XMLAttribute* att = _node.GetAttribute ( "basename", false );
1455 if (att != NULL)
1456 basename = att->value;
1457 else
1458 basename = module.name;
1459
1460 att = _node.GetAttribute ( "dllname", false );
1461 if (att != NULL)
1462 dllname = att->value;
1463 else
1464 {
1465 if ( _module.type == StaticLibrary )
1466 {
1467 throw XMLInvalidBuildFileException (
1468 node.location,
1469 "<importlibrary> dllname attribute required." );
1470 }
1471
1472 dllname = "";
1473 }
1474
1475 att = _node.GetAttribute ( "definition", true );
1476 assert (att);
1477 definition = FixSeparator(att->value);
1478 }
1479
1480
1481 If::If ( const XMLElement& node_,
1482 const Project& project_,
1483 const Module* module_,
1484 const bool negated_ )
1485 : node(node_), project(project_), module(module_), negated(negated_)
1486 {
1487 const XMLAttribute* att;
1488
1489 att = node.GetAttribute ( "property", true );
1490 assert(att);
1491 property = att->value;
1492
1493 att = node.GetAttribute ( "value", true );
1494 assert(att);
1495 value = att->value;
1496 }
1497
1498 If::~If ()
1499 {
1500 }
1501
1502 void
1503 If::ProcessXML()
1504 {
1505
1506 }
1507
1508
1509 Property::Property ( const XMLElement& node_,
1510 const Project& project_,
1511 const Module* module_ )
1512 : node(node_), project(project_), module(module_)
1513 {
1514 const XMLAttribute* att;
1515
1516 att = node.GetAttribute ( "name", true );
1517 assert(att);
1518 name = att->value;
1519
1520 att = node.GetAttribute ( "value", true );
1521 assert(att);
1522 value = att->value;
1523 }
1524
1525 void
1526 Property::ProcessXML()
1527 {
1528 }
1529
1530
1531 PchFile::PchFile (
1532 const XMLElement& node_,
1533 const Module& module_,
1534 const File file_ )
1535 : node(node_), module(module_), file(file_)
1536 {
1537 }
1538
1539 void
1540 PchFile::ProcessXML()
1541 {
1542 }
1543
1544
1545 AutoRegister::AutoRegister ( const Project& project_,
1546 const Module* module_,
1547 const XMLElement& node_ )
1548 : project(project_),
1549 module(module_),
1550 node(node_)
1551 {
1552 Initialize();
1553 }
1554
1555 AutoRegister::~AutoRegister ()
1556 {
1557 }
1558
1559 bool
1560 AutoRegister::IsSupportedModuleType ( ModuleType type )
1561 {
1562 switch ( type )
1563 {
1564 case Win32DLL:
1565 case Win32OCX:
1566 return true;
1567 case Kernel:
1568 case KernelModeDLL:
1569 case NativeDLL:
1570 case NativeCUI:
1571 case Win32CUI:
1572 case Win32GUI:
1573 case Win32SCR:
1574 case KernelModeDriver:
1575 case BootSector:
1576 case BootLoader:
1577 case BootProgram:
1578 case BuildTool:
1579 case StaticLibrary:
1580 case ObjectLibrary:
1581 case Iso:
1582 case LiveIso:
1583 case IsoRegTest:
1584 case LiveIsoRegTest:
1585 case Test:
1586 case RpcServer:
1587 case RpcClient:
1588 case Alias:
1589 case IdlHeader:
1590 case EmbeddedTypeLib:
1591 return false;
1592 }
1593 throw InvalidOperationException ( __FILE__,
1594 __LINE__ );
1595 }
1596
1597 AutoRegisterType
1598 AutoRegister::GetAutoRegisterType( string type )
1599 {
1600 if ( type == "DllRegisterServer" )
1601 return DllRegisterServer;
1602 if ( type == "DllInstall" )
1603 return DllInstall;
1604 if ( type == "Both" )
1605 return Both;
1606 throw XMLInvalidBuildFileException (
1607 node.location,
1608 "<autoregister> type attribute must be DllRegisterServer, DllInstall or Both." );
1609 }
1610
1611 void
1612 AutoRegister::Initialize ()
1613 {
1614 if ( !IsSupportedModuleType ( module->type ) )
1615 {
1616 throw XMLInvalidBuildFileException (
1617 node.location,
1618 "<autoregister> is not applicable for this module type." );
1619 }
1620
1621 const XMLAttribute* att = node.GetAttribute ( "infsection", true );
1622 infSection = att->value;
1623
1624 att = node.GetAttribute ( "type", true );
1625 type = GetAutoRegisterType ( att->value );
1626 }
1627
1628 void
1629 AutoRegister::ProcessXML()
1630 {
1631 }