Replace() bugfix #2 - forgot to insert replacement text ( yes yes I suck you can...
[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 Replace ( const string& s, const string& find, const string& with )
28 {
29 string ret;
30 const char* p = s.c_str();
31 while ( p )
32 {
33 const char* p2 = strstr ( p, find.c_str() );
34 if ( !p2 )
35 break;
36 if ( p2 > p )
37 ret += string ( p, p2-p );
38 ret += with;
39 p = p2 + find.size();
40 }
41 if ( *p )
42 ret += p;
43 return ret;
44 }
45
46 string
47 FixSeparator ( const string& s )
48 {
49 string s2(s);
50 char* p = strchr ( &s2[0], CBAD_SEP );
51 while ( p )
52 {
53 *p++ = CSEP;
54 p = strchr ( p, CBAD_SEP );
55 }
56 return s2;
57 }
58
59 string
60 DosSeparator ( const string& s )
61 {
62 string s2(s);
63 char* p = strchr ( &s2[0], '/' );
64 while ( p )
65 {
66 *p++ = '\\';
67 p = strchr ( p, '/' );
68 }
69 return s2;
70 }
71
72 string
73 ReplaceExtension (
74 const string& filename,
75 const string& newExtension )
76 {
77 size_t index = filename.find_last_of ( '/' );
78 if ( index == string::npos )
79 index = 0;
80 size_t index2 = filename.find_last_of ( '\\' );
81 if ( index2 != string::npos && index2 > index )
82 index = index2;
83 string tmp = filename.substr( index /*, filename.size() - index*/ );
84 size_t ext_index = tmp.find_last_of( '.' );
85 if ( ext_index != string::npos )
86 return filename.substr ( 0, index + ext_index ) + newExtension;
87 return filename + newExtension;
88 }
89
90 string
91 GetSubPath (
92 const string& location,
93 const string& path,
94 const string& att_value )
95 {
96 if ( !att_value.size() )
97 throw InvalidBuildFileException (
98 location,
99 "<directory> tag has empty 'name' attribute" );
100 if ( strpbrk ( att_value.c_str (), "/\\?*:<>|" ) )
101 throw InvalidBuildFileException (
102 location,
103 "<directory> tag has invalid characters in 'name' attribute" );
104 if ( !path.size() )
105 return att_value;
106 return FixSeparator(path + CSEP + att_value);
107 }
108
109 string
110 GetExtension ( const string& filename )
111 {
112 size_t index = filename.find_last_of ( '/' );
113 if (index == string::npos) index = 0;
114 string tmp = filename.substr( index, filename.size() - index );
115 size_t ext_index = tmp.find_last_of( '.' );
116 if (ext_index != string::npos)
117 return filename.substr ( index + ext_index, filename.size() );
118 return "";
119 }
120
121 string
122 GetDirectory ( const string& filename )
123 {
124 size_t index = filename.find_last_of ( CSEP );
125 if ( index == string::npos )
126 return "";
127 else
128 return filename.substr ( 0, index );
129 }
130
131 string
132 GetFilename ( const string& filename )
133 {
134 size_t index = filename.find_last_of ( CSEP );
135 if ( index == string::npos )
136 return filename;
137 else
138 return filename.substr ( index + 1, filename.length () - index );
139 }
140
141 string
142 NormalizeFilename ( const string& filename )
143 {
144 if ( filename == "" )
145 return "";
146 Path path;
147 string normalizedPath = path.Fixup ( filename, true );
148 string relativeNormalizedPath = path.RelativeFromWorkingDirectory ( normalizedPath );
149 return FixSeparator ( relativeNormalizedPath );
150 }
151
152 bool
153 GetBooleanValue ( const string& value )
154 {
155 if ( value == "1" )
156 return true;
157 else
158 return false;
159 }
160
161 IfableData::~IfableData()
162 {
163 size_t i;
164 for ( i = 0; i < files.size(); i++ )
165 delete files[i];
166 for ( i = 0; i < includes.size(); i++ )
167 delete includes[i];
168 for ( i = 0; i < defines.size(); i++ )
169 delete defines[i];
170 for ( i = 0; i < libraries.size(); i++ )
171 delete libraries[i];
172 for ( i = 0; i < properties.size(); i++ )
173 delete properties[i];
174 for ( i = 0; i < compilerFlags.size(); i++ )
175 delete compilerFlags[i];
176 for ( i = 0; i < ifs.size(); i++ )
177 delete ifs[i];
178 }
179
180 void IfableData::ProcessXML ()
181 {
182 size_t i;
183 for ( i = 0; i < files.size (); i++ )
184 files[i]->ProcessXML ();
185 for ( i = 0; i < includes.size (); i++ )
186 includes[i]->ProcessXML ();
187 for ( i = 0; i < defines.size (); i++ )
188 defines[i]->ProcessXML ();
189 for ( i = 0; i < libraries.size (); i++ )
190 libraries[i]->ProcessXML ();
191 for ( i = 0; i < properties.size(); i++ )
192 properties[i]->ProcessXML ();
193 for ( i = 0; i < compilerFlags.size(); i++ )
194 compilerFlags[i]->ProcessXML ();
195 for ( i = 0; i < ifs.size (); i++ )
196 ifs[i]->ProcessXML ();
197 }
198
199 Module::Module ( const Project& project,
200 const XMLElement& moduleNode,
201 const string& modulePath )
202 : project (project),
203 node (moduleNode),
204 importLibrary (NULL),
205 bootstrap (NULL),
206 pch (NULL),
207 cplusplus (false),
208 host (HostDefault)
209 {
210 if ( node.name != "module" )
211 throw InvalidOperationException ( __FILE__,
212 __LINE__,
213 "Module created with non-<module> node" );
214
215 xmlbuildFile = Path::RelativeFromWorkingDirectory ( moduleNode.xmlFile->filename () );
216
217 path = FixSeparator ( modulePath );
218
219 enabled = true;
220
221 const XMLAttribute* att = moduleNode.GetAttribute ( "if", false );
222 if ( att != NULL )
223 enabled = GetBooleanValue ( project.ResolveProperties ( att->value ) );
224
225 att = moduleNode.GetAttribute ( "ifnot", false );
226 if ( att != NULL )
227 enabled = !GetBooleanValue ( project.ResolveProperties ( att->value ) );
228
229 att = moduleNode.GetAttribute ( "name", true );
230 assert(att);
231 name = att->value;
232
233 att = moduleNode.GetAttribute ( "type", true );
234 assert(att);
235 type = GetModuleType ( node.location, *att );
236
237 att = moduleNode.GetAttribute ( "extension", false );
238 if ( att != NULL )
239 extension = att->value;
240 else
241 extension = GetDefaultModuleExtension ();
242
243 att = moduleNode.GetAttribute ( "entrypoint", false );
244 if ( att != NULL )
245 entrypoint = att->value;
246 else
247 entrypoint = GetDefaultModuleEntrypoint ();
248
249 att = moduleNode.GetAttribute ( "baseaddress", false );
250 if ( att != NULL )
251 baseaddress = att->value;
252 else
253 baseaddress = GetDefaultModuleBaseaddress ();
254
255 att = moduleNode.GetAttribute ( "mangledsymbols", false );
256 if ( att != NULL )
257 {
258 const char* p = att->value.c_str();
259 if ( !stricmp ( p, "true" ) || !stricmp ( p, "yes" ) )
260 mangledSymbols = true;
261 else if ( !stricmp ( p, "false" ) || !stricmp ( p, "no" ) )
262 mangledSymbols = false;
263 else
264 {
265 throw InvalidAttributeValueException (
266 moduleNode.location,
267 "mangledsymbols",
268 att->value );
269 }
270 }
271 else
272 mangledSymbols = false;
273
274 att = moduleNode.GetAttribute ( "host", false );
275 if ( att != NULL )
276 {
277 const char* p = att->value.c_str();
278 if ( !stricmp ( p, "true" ) || !stricmp ( p, "yes" ) )
279 host = HostTrue;
280 else if ( !stricmp ( p, "false" ) || !stricmp ( p, "no" ) )
281 host = HostFalse;
282 else
283 {
284 throw InvalidAttributeValueException (
285 moduleNode.location,
286 "host",
287 att->value );
288 }
289 }
290
291 att = moduleNode.GetAttribute ( "prefix", false );
292 if ( att != NULL )
293 prefix = att->value;
294
295 att = moduleNode.GetAttribute ( "installbase", false );
296 if ( att != NULL )
297 installBase = att->value;
298 else
299 installBase = "";
300
301 att = moduleNode.GetAttribute ( "installname", false );
302 if ( att != NULL )
303 installName = att->value;
304 else
305 installName = "";
306
307 att = moduleNode.GetAttribute ( "usewrc", false );
308 if ( att != NULL )
309 useWRC = att->value == "true";
310 else
311 useWRC = true;
312
313 att = moduleNode.GetAttribute ( "warnings", false );
314 if ( att != NULL )
315 enableWarnings = att->value == "true";
316 else
317 enableWarnings = false;
318
319 att = moduleNode.GetAttribute ( "aliasof", false );
320 if ( type == Alias && att != NULL )
321 aliasedModuleName = att->value;
322 else
323 aliasedModuleName = "";
324 }
325
326 Module::~Module ()
327 {
328 size_t i;
329 for ( i = 0; i < invocations.size(); i++ )
330 delete invocations[i];
331 for ( i = 0; i < dependencies.size(); i++ )
332 delete dependencies[i];
333 for ( i = 0; i < compilerFlags.size(); i++ )
334 delete compilerFlags[i];
335 for ( i = 0; i < linkerFlags.size(); i++ )
336 delete linkerFlags[i];
337 for ( i = 0; i < stubbedComponents.size(); i++ )
338 delete stubbedComponents[i];
339 if ( pch )
340 delete pch;
341 }
342
343 void
344 Module::ProcessXML()
345 {
346 if ( type == Alias )
347 {
348 if ( aliasedModuleName == name )
349 throw InvalidBuildFileException (
350 node.location,
351 "module '%s' cannot link against itself",
352 name.c_str() );
353 const Module* m = project.LocateModule ( aliasedModuleName );
354 if ( !m )
355 throw InvalidBuildFileException (
356 node.location,
357 "module '%s' trying to alias non-existant module '%s'",
358 name.c_str(),
359 aliasedModuleName.c_str() );
360 }
361
362 size_t i;
363 for ( i = 0; i < node.subElements.size(); i++ )
364 ProcessXMLSubElement ( *node.subElements[i], path );
365 for ( i = 0; i < invocations.size(); i++ )
366 invocations[i]->ProcessXML ();
367 for ( i = 0; i < dependencies.size(); i++ )
368 dependencies[i]->ProcessXML ();
369 for ( i = 0; i < compilerFlags.size(); i++ )
370 compilerFlags[i]->ProcessXML();
371 for ( i = 0; i < linkerFlags.size(); i++ )
372 linkerFlags[i]->ProcessXML();
373 for ( i = 0; i < stubbedComponents.size(); i++ )
374 stubbedComponents[i]->ProcessXML();
375 non_if_data.ProcessXML();
376 if ( pch )
377 pch->ProcessXML();
378 }
379
380 void
381 Module::ProcessXMLSubElement ( const XMLElement& e,
382 const string& path,
383 If* pIf /*= NULL*/ )
384 {
385 bool subs_invalid = false;
386 string subpath ( path );
387 if ( e.name == "file" && e.value.size () > 0 )
388 {
389 bool first = false;
390 const XMLAttribute* att = e.GetAttribute ( "first", false );
391 if ( att != NULL )
392 {
393 if ( !stricmp ( att->value.c_str(), "true" ) )
394 first = true;
395 else if ( stricmp ( att->value.c_str(), "false" ) )
396 throw InvalidBuildFileException (
397 e.location,
398 "attribute 'first' of <file> element can only be 'true' or 'false'" );
399 }
400 string switches = "";
401 att = e.GetAttribute ( "switches", false );
402 if ( att != NULL )
403 switches = att->value;
404 if ( !cplusplus )
405 {
406 // check for c++ file
407 string ext = GetExtension ( e.value );
408 if ( !stricmp ( ext.c_str(), ".cpp" ) )
409 cplusplus = true;
410 else if ( !stricmp ( ext.c_str(), ".cc" ) )
411 cplusplus = true;
412 else if ( !stricmp ( ext.c_str(), ".cxx" ) )
413 cplusplus = true;
414 }
415 File* pFile = new File ( FixSeparator ( path + CSEP + e.value ),
416 first,
417 switches,
418 false );
419 if ( pIf )
420 pIf->data.files.push_back ( pFile );
421 else
422 non_if_data.files.push_back ( pFile );
423 subs_invalid = true;
424 }
425 else if ( e.name == "library" && e.value.size () )
426 {
427 Library* pLibrary = new Library ( e, *this, e.value );
428 if ( pIf )
429 pIf->data.libraries.push_back ( pLibrary );
430 else
431 non_if_data.libraries.push_back ( pLibrary );
432 subs_invalid = true;
433 }
434 else if ( e.name == "directory" )
435 {
436 const XMLAttribute* att = e.GetAttribute ( "name", true );
437 assert(att);
438 subpath = GetSubPath ( e.location, path, att->value );
439 }
440 else if ( e.name == "include" )
441 {
442 Include* include = new Include ( project, this, &e );
443 if ( pIf )
444 pIf->data.includes.push_back ( include );
445 else
446 non_if_data.includes.push_back ( include );
447 subs_invalid = true;
448 }
449 else if ( e.name == "define" )
450 {
451 Define* pDefine = new Define ( project, this, e );
452 if ( pIf )
453 pIf->data.defines.push_back ( pDefine );
454 else
455 non_if_data.defines.push_back ( pDefine );
456 subs_invalid = true;
457 }
458 else if ( e.name == "invoke" )
459 {
460 if ( pIf )
461 throw InvalidBuildFileException (
462 e.location,
463 "<invoke> is not a valid sub-element of <if>" );
464 invocations.push_back ( new Invoke ( e, *this ) );
465 subs_invalid = false;
466 }
467 else if ( e.name == "dependency" )
468 {
469 if ( pIf )
470 throw InvalidBuildFileException (
471 e.location,
472 "<dependency> is not a valid sub-element of <if>" );
473 dependencies.push_back ( new Dependency ( e, *this ) );
474 subs_invalid = true;
475 }
476 else if ( e.name == "importlibrary" )
477 {
478 if ( pIf )
479 throw InvalidBuildFileException (
480 e.location,
481 "<importlibrary> is not a valid sub-element of <if>" );
482 if ( importLibrary )
483 throw InvalidBuildFileException (
484 e.location,
485 "Only one <importlibrary> is valid per module" );
486 importLibrary = new ImportLibrary ( e, *this );
487 subs_invalid = true;
488 }
489 else if ( e.name == "if" )
490 {
491 If* pOldIf = pIf;
492 pIf = new If ( e, project, this );
493 if ( pOldIf )
494 pOldIf->data.ifs.push_back ( pIf );
495 else
496 non_if_data.ifs.push_back ( pIf );
497 subs_invalid = false;
498 }
499 else if ( e.name == "ifnot" )
500 {
501 If* pOldIf = pIf;
502 pIf = new If ( e, project, this, true );
503 if ( pOldIf )
504 pOldIf->data.ifs.push_back ( pIf );
505 else
506 non_if_data.ifs.push_back ( pIf );
507 subs_invalid = false;
508 }
509 else if ( e.name == "compilerflag" )
510 {
511 CompilerFlag* pCompilerFlag = new CompilerFlag ( project, this, e );
512 if ( pIf )
513 pIf->data.compilerFlags.push_back ( pCompilerFlag );
514 else
515 non_if_data.compilerFlags.push_back ( pCompilerFlag );
516 subs_invalid = true;
517 }
518 else if ( e.name == "linkerflag" )
519 {
520 linkerFlags.push_back ( new LinkerFlag ( project, this, e ) );
521 subs_invalid = true;
522 }
523 else if ( e.name == "component" )
524 {
525 stubbedComponents.push_back ( new StubbedComponent ( this, e ) );
526 subs_invalid = false;
527 }
528 else if ( e.name == "property" )
529 {
530 throw InvalidBuildFileException (
531 e.location,
532 "<property> is not a valid sub-element of <module>" );
533 }
534 else if ( e.name == "bootstrap" )
535 {
536 bootstrap = new Bootstrap ( project, this, e );
537 subs_invalid = true;
538 }
539 else if ( e.name == "pch" )
540 {
541 if ( pIf )
542 throw InvalidBuildFileException (
543 e.location,
544 "<pch> is not a valid sub-element of <if>" );
545 if ( pch )
546 throw InvalidBuildFileException (
547 e.location,
548 "Only one <pch> is valid per module" );
549 pch = new PchFile (
550 e, *this, File ( FixSeparator ( path + CSEP + e.value ), false, "", true ) );
551 subs_invalid = true;
552 }
553 if ( subs_invalid && e.subElements.size() > 0 )
554 throw InvalidBuildFileException (
555 e.location,
556 "<%s> cannot have sub-elements",
557 e.name.c_str() );
558 for ( size_t i = 0; i < e.subElements.size (); i++ )
559 ProcessXMLSubElement ( *e.subElements[i], subpath, pIf );
560 }
561
562 ModuleType
563 Module::GetModuleType ( const string& location, const XMLAttribute& attribute )
564 {
565 if ( attribute.value == "buildtool" )
566 return BuildTool;
567 if ( attribute.value == "staticlibrary" )
568 return StaticLibrary;
569 if ( attribute.value == "objectlibrary" )
570 return ObjectLibrary;
571 if ( attribute.value == "kernel" )
572 return Kernel;
573 if ( attribute.value == "kernelmodedll" )
574 return KernelModeDLL;
575 if ( attribute.value == "kernelmodedriver" )
576 return KernelModeDriver;
577 if ( attribute.value == "nativedll" )
578 return NativeDLL;
579 if ( attribute.value == "nativecui" )
580 return NativeCUI;
581 if ( attribute.value == "win32dll" )
582 return Win32DLL;
583 if ( attribute.value == "win32cui" )
584 return Win32CUI;
585 if ( attribute.value == "win32gui" )
586 return Win32GUI;
587 if ( attribute.value == "bootloader" )
588 return BootLoader;
589 if ( attribute.value == "bootsector" )
590 return BootSector;
591 if ( attribute.value == "iso" )
592 return Iso;
593 if ( attribute.value == "liveiso" )
594 return LiveIso;
595 if ( attribute.value == "test" )
596 return Test;
597 if ( attribute.value == "rpcserver" )
598 return RpcServer;
599 if ( attribute.value == "rpcclient" )
600 return RpcClient;
601 if ( attribute.value == "alias" )
602 return Alias;
603 throw InvalidAttributeValueException ( location,
604 attribute.name,
605 attribute.value );
606 }
607
608 string
609 Module::GetDefaultModuleExtension () const
610 {
611 switch (type)
612 {
613 case BuildTool:
614 return EXEPOSTFIX;
615 case StaticLibrary:
616 return ".a";
617 case ObjectLibrary:
618 return ".o";
619 case Kernel:
620 case NativeCUI:
621 case Win32CUI:
622 case Win32GUI:
623 return ".exe";
624 case KernelModeDLL:
625 case NativeDLL:
626 case Win32DLL:
627 return ".dll";
628 case KernelModeDriver:
629 case BootLoader:
630 return ".sys";
631 case BootSector:
632 return ".o";
633 case Iso:
634 case LiveIso:
635 return ".iso";
636 case Test:
637 return ".exe";
638 case RpcServer:
639 return ".o";
640 case RpcClient:
641 return ".o";
642 case Alias:
643 return "";
644 }
645 throw InvalidOperationException ( __FILE__,
646 __LINE__ );
647 }
648
649 string
650 Module::GetDefaultModuleEntrypoint () const
651 {
652 switch ( type )
653 {
654 case Kernel:
655 return "_NtProcessStartup";
656 case KernelModeDLL:
657 return "_DriverEntry@8";
658 case NativeDLL:
659 return "_DllMainCRTStartup@12";
660 case NativeCUI:
661 return "_NtProcessStartup@4";
662 case Win32DLL:
663 return "_DllMain@12";
664 case Win32CUI:
665 case Test:
666 return "_mainCRTStartup";
667 case Win32GUI:
668 return "_WinMainCRTStartup";
669 case KernelModeDriver:
670 return "_DriverEntry@8";
671 case BuildTool:
672 case StaticLibrary:
673 case ObjectLibrary:
674 case BootLoader:
675 case BootSector:
676 case Iso:
677 case LiveIso:
678 case RpcServer:
679 case RpcClient:
680 case Alias:
681 return "";
682 }
683 throw InvalidOperationException ( __FILE__,
684 __LINE__ );
685 }
686
687 string
688 Module::GetDefaultModuleBaseaddress () const
689 {
690 switch ( type )
691 {
692 case Kernel:
693 return "0x80000000";
694 case Win32DLL:
695 return "0x10000000";
696 case NativeDLL:
697 case NativeCUI:
698 case Win32CUI:
699 case Test:
700 return "0x00400000";
701 case Win32GUI:
702 return "0x00400000";
703 case KernelModeDLL:
704 case KernelModeDriver:
705 return "0x00010000";
706 case BuildTool:
707 case StaticLibrary:
708 case ObjectLibrary:
709 case BootLoader:
710 case BootSector:
711 case Iso:
712 case LiveIso:
713 case RpcServer:
714 case RpcClient:
715 case Alias:
716 return "";
717 }
718 throw InvalidOperationException ( __FILE__,
719 __LINE__ );
720 }
721
722 bool
723 Module::HasImportLibrary () const
724 {
725 return importLibrary != NULL;
726 }
727
728 bool
729 Module::IsDLL () const
730 {
731 switch ( type )
732 {
733 case Kernel:
734 case KernelModeDLL:
735 case NativeDLL:
736 case Win32DLL:
737 case KernelModeDriver:
738 return true;
739 case NativeCUI:
740 case Win32CUI:
741 case Test:
742 case Win32GUI:
743 case BuildTool:
744 case StaticLibrary:
745 case ObjectLibrary:
746 case BootLoader:
747 case BootSector:
748 case Iso:
749 case LiveIso:
750 case RpcServer:
751 case RpcClient:
752 case Alias:
753 return false;
754 }
755 throw InvalidOperationException ( __FILE__,
756 __LINE__ );
757 }
758
759 bool
760 Module::GenerateInOutputTree () const
761 {
762 switch ( type )
763 {
764 case Kernel:
765 case KernelModeDLL:
766 case NativeDLL:
767 case Win32DLL:
768 case KernelModeDriver:
769 case NativeCUI:
770 case Win32CUI:
771 case Test:
772 case Win32GUI:
773 case BuildTool:
774 case BootLoader:
775 case BootSector:
776 case Iso:
777 case LiveIso:
778 return true;
779 case StaticLibrary:
780 case ObjectLibrary:
781 case RpcServer:
782 case RpcClient:
783 case Alias:
784 return false;
785 }
786 throw InvalidOperationException ( __FILE__,
787 __LINE__ );
788 }
789
790 string
791 Module::GetTargetName () const
792 {
793 return name + extension;
794 }
795
796 string
797 Module::GetDependencyPath () const
798 {
799 if ( HasImportLibrary () )
800 return ReplaceExtension ( GetPathWithPrefix ( "lib" ), ".a" );
801 else
802 return GetPath();
803 }
804
805 string
806 Module::GetBasePath () const
807 {
808 return path;
809 }
810
811 string
812 Module::GetPath () const
813 {
814 if ( path.length() > 0 )
815 return path + CSEP + GetTargetName ();
816 else
817 return GetTargetName ();
818 }
819
820 string
821 Module::GetPathWithPrefix ( const string& prefix ) const
822 {
823 return path + CSEP + prefix + GetTargetName ();
824 }
825
826 string
827 Module::GetInvocationTarget ( const int index ) const
828 {
829 return ssprintf ( "%s_invoke_%d",
830 name.c_str (),
831 index );
832 }
833
834 bool
835 Module::HasFileWithExtension (
836 const IfableData& data,
837 const std::string& extension ) const
838 {
839 size_t i;
840 for ( i = 0; i < data.files.size (); i++ )
841 {
842 File& file = *data.files[i];
843 string file_ext = GetExtension ( file.name );
844 if ( !stricmp ( file_ext.c_str (), extension.c_str () ) )
845 return true;
846 }
847 for ( i = 0; i < data.ifs.size (); i++ )
848 {
849 if ( HasFileWithExtension ( data.ifs[i]->data, extension ) )
850 return true;
851 }
852 return false;
853 }
854
855 void
856 Module::InvokeModule () const
857 {
858 for ( size_t i = 0; i < invocations.size (); i++ )
859 {
860 Invoke& invoke = *invocations[i];
861 string command = invoke.invokeModule->GetPath () + " " + invoke.GetParameters ();
862 printf ( "Executing '%s'\n\n", command.c_str () );
863 int exitcode = system ( command.c_str () );
864 if ( exitcode != 0 )
865 throw InvocationFailedException ( command,
866 exitcode );
867 }
868 }
869
870
871 File::File ( const string& _name, bool _first,
872 std::string _switches,
873 bool _isPreCompiledHeader )
874 : name(_name),
875 first(_first),
876 switches(_switches),
877 isPreCompiledHeader(_isPreCompiledHeader)
878 {
879 }
880
881 void
882 File::ProcessXML()
883 {
884 }
885
886 bool
887 File::IsGeneratedFile () const
888 {
889 string extension = GetExtension ( name );
890 return ( extension == ".spec" || extension == ".SPEC" );
891 }
892
893
894 Library::Library ( const XMLElement& _node,
895 const Module& _module,
896 const string& _name )
897 : node(_node),
898 module(_module),
899 name(_name),
900 importedModule(_module.project.LocateModule(_name))
901 {
902 if ( module.name == name )
903 throw InvalidBuildFileException (
904 node.location,
905 "module '%s' cannot link against itself",
906 name.c_str() );
907 if ( !importedModule )
908 throw InvalidBuildFileException (
909 node.location,
910 "module '%s' trying to import non-existant module '%s'",
911 module.name.c_str(),
912 name.c_str() );
913 }
914
915 void
916 Library::ProcessXML()
917 {
918 if ( !module.project.LocateModule ( name ) )
919 throw InvalidBuildFileException (
920 node.location,
921 "module '%s' is trying to link against non-existant module '%s'",
922 module.name.c_str(),
923 name.c_str() );
924 }
925
926
927 Invoke::Invoke ( const XMLElement& _node,
928 const Module& _module )
929 : node (_node),
930 module (_module)
931 {
932 }
933
934 void
935 Invoke::ProcessXML()
936 {
937 const XMLAttribute* att = node.GetAttribute ( "module", false );
938 if (att == NULL)
939 invokeModule = &module;
940 else
941 {
942 invokeModule = module.project.LocateModule ( att->value );
943 if ( invokeModule == NULL )
944 throw InvalidBuildFileException (
945 node.location,
946 "module '%s' is trying to invoke non-existant module '%s'",
947 module.name.c_str(),
948 att->value.c_str() );
949 }
950
951 for ( size_t i = 0; i < node.subElements.size (); i++ )
952 ProcessXMLSubElement ( *node.subElements[i] );
953 }
954
955 void
956 Invoke::ProcessXMLSubElement ( const XMLElement& e )
957 {
958 bool subs_invalid = false;
959 if ( e.name == "input" )
960 {
961 for ( size_t i = 0; i < e.subElements.size (); i++ )
962 ProcessXMLSubElementInput ( *e.subElements[i] );
963 }
964 else if ( e.name == "output" )
965 {
966 for ( size_t i = 0; i < e.subElements.size (); i++ )
967 ProcessXMLSubElementOutput ( *e.subElements[i] );
968 }
969 if ( subs_invalid && e.subElements.size() > 0 )
970 throw InvalidBuildFileException ( e.location,
971 "<%s> cannot have sub-elements",
972 e.name.c_str() );
973 }
974
975 void
976 Invoke::ProcessXMLSubElementInput ( const XMLElement& e )
977 {
978 bool subs_invalid = false;
979 if ( e.name == "inputfile" && e.value.size () > 0 )
980 {
981 input.push_back ( new InvokeFile ( e, FixSeparator ( module.path + CSEP + e.value ) ) );
982 subs_invalid = true;
983 }
984 if ( subs_invalid && e.subElements.size() > 0 )
985 throw InvalidBuildFileException ( e.location,
986 "<%s> cannot have sub-elements",
987 e.name.c_str() );
988 }
989
990 void
991 Invoke::ProcessXMLSubElementOutput ( const XMLElement& e )
992 {
993 bool subs_invalid = false;
994 if ( e.name == "outputfile" && e.value.size () > 0 )
995 {
996 output.push_back ( new InvokeFile ( e, FixSeparator ( module.path + CSEP + e.value ) ) );
997 subs_invalid = true;
998 }
999 if ( subs_invalid && e.subElements.size() > 0 )
1000 throw InvalidBuildFileException (
1001 e.location,
1002 "<%s> cannot have sub-elements",
1003 e.name.c_str() );
1004 }
1005
1006 void
1007 Invoke::GetTargets ( string_list& targets ) const
1008 {
1009 for ( size_t i = 0; i < output.size (); i++ )
1010 {
1011 InvokeFile& file = *output[i];
1012 targets.push_back ( NormalizeFilename ( file.name ) );
1013 }
1014 }
1015
1016 string
1017 Invoke::GetParameters () const
1018 {
1019 string parameters ( "" );
1020 size_t i;
1021 for ( i = 0; i < output.size (); i++ )
1022 {
1023 if ( parameters.length () > 0)
1024 parameters += " ";
1025 InvokeFile& invokeFile = *output[i];
1026 if ( invokeFile.switches.length () > 0 )
1027 {
1028 parameters += invokeFile.switches + " ";
1029 }
1030 parameters += invokeFile.name;
1031 }
1032
1033 for ( i = 0; i < input.size (); i++ )
1034 {
1035 if ( parameters.length () > 0 )
1036 parameters += " ";
1037 InvokeFile& invokeFile = *input[i];
1038 if ( invokeFile.switches.length () > 0 )
1039 {
1040 parameters += invokeFile.switches;
1041 parameters += " ";
1042 }
1043 parameters += invokeFile.name ;
1044 }
1045
1046 return parameters;
1047 }
1048
1049
1050 InvokeFile::InvokeFile ( const XMLElement& _node,
1051 const string& _name )
1052 : node (_node),
1053 name (_name)
1054 {
1055 const XMLAttribute* att = _node.GetAttribute ( "switches", false );
1056 if (att != NULL)
1057 switches = att->value;
1058 else
1059 switches = "";
1060 }
1061
1062 void
1063 InvokeFile::ProcessXML()
1064 {
1065 }
1066
1067
1068 Dependency::Dependency ( const XMLElement& _node,
1069 const Module& _module )
1070 : node (_node),
1071 module (_module),
1072 dependencyModule (NULL)
1073 {
1074 }
1075
1076 void
1077 Dependency::ProcessXML()
1078 {
1079 dependencyModule = module.project.LocateModule ( node.value );
1080 if ( dependencyModule == NULL )
1081 throw InvalidBuildFileException ( node.location,
1082 "module '%s' depend on non-existant module '%s'",
1083 module.name.c_str(),
1084 node.value.c_str() );
1085 }
1086
1087
1088 ImportLibrary::ImportLibrary ( const XMLElement& _node,
1089 const Module& _module )
1090 : node (_node),
1091 module (_module)
1092 {
1093 const XMLAttribute* att = _node.GetAttribute ( "basename", false );
1094 if (att != NULL)
1095 basename = att->value;
1096 else
1097 basename = module.name;
1098
1099 att = _node.GetAttribute ( "definition", true );
1100 assert (att);
1101 definition = FixSeparator(att->value);
1102 }
1103
1104
1105 If::If ( const XMLElement& node_,
1106 const Project& project_,
1107 const Module* module_,
1108 const bool negated_ )
1109 : node(node_), project(project_), module(module_), negated(negated_)
1110 {
1111 const XMLAttribute* att;
1112
1113 att = node.GetAttribute ( "property", true );
1114 assert(att);
1115 property = att->value;
1116
1117 att = node.GetAttribute ( "value", true );
1118 assert(att);
1119 value = att->value;
1120 }
1121
1122 If::~If ()
1123 {
1124 }
1125
1126 void
1127 If::ProcessXML()
1128 {
1129 }
1130
1131
1132 Property::Property ( const XMLElement& node_,
1133 const Project& project_,
1134 const Module* module_ )
1135 : node(node_), project(project_), module(module_)
1136 {
1137 const XMLAttribute* att;
1138
1139 att = node.GetAttribute ( "name", true );
1140 assert(att);
1141 name = att->value;
1142
1143 att = node.GetAttribute ( "value", true );
1144 assert(att);
1145 value = att->value;
1146 }
1147
1148 void
1149 Property::ProcessXML()
1150 {
1151 }
1152
1153
1154 PchFile::PchFile (
1155 const XMLElement& node_,
1156 const Module& module_,
1157 const File file_ )
1158 : node(node_), module(module_), file(file_)
1159 {
1160 }
1161
1162 void
1163 PchFile::ProcessXML()
1164 {
1165 }