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