more efficient detection of C++ modules, fixed bug in C++ pch support, always clean...
[reactos.git] / reactos / tools / rbuild / module.cpp
1 #include "pch.h"
2 #include <assert.h>
3
4 #include "rbuild.h"
5
6 using std::string;
7 using std::vector;
8
9 string
10 FixSeparator ( const string& s )
11 {
12 string s2(s);
13 char* p = strchr ( &s2[0], CBAD_SEP );
14 while ( p )
15 {
16 *p++ = CSEP;
17 p = strchr ( p, CBAD_SEP );
18 }
19 return s2;
20 }
21
22 string
23 GetExtension ( const string& filename )
24 {
25 size_t index = filename.find_last_of ( '/' );
26 if (index == string::npos) index = 0;
27 string tmp = filename.substr( index, filename.size() - index );
28 size_t ext_index = tmp.find_last_of( '.' );
29 if (ext_index != string::npos)
30 return filename.substr ( index + ext_index, filename.size() );
31 return "";
32 }
33
34 string
35 GetDirectory ( const string& filename )
36 {
37 size_t index = filename.find_last_of ( CSEP );
38 if ( index == string::npos )
39 return filename;
40 else
41 return filename.substr ( 0, index );
42 }
43
44 string
45 NormalizeFilename ( const string& filename )
46 {
47 Path path;
48 string normalizedPath = path.Fixup ( filename, true );
49 string relativeNormalizedPath = path.RelativeFromWorkingDirectory ( normalizedPath );
50 return FixSeparator ( relativeNormalizedPath );
51 }
52
53 IfableData::~IfableData()
54 {
55 size_t i;
56 for ( i = 0; i < files.size(); i++ )
57 delete files[i];
58 for ( i = 0; i < includes.size(); i++ )
59 delete includes[i];
60 for ( i = 0; i < defines.size(); i++ )
61 delete defines[i];
62 for ( i = 0; i < libraries.size(); i++ )
63 delete libraries[i];
64 for ( i = 0; i < properties.size(); i++ )
65 delete properties[i];
66 for ( i = 0; i < ifs.size(); i++ )
67 delete ifs[i];
68 }
69
70 void IfableData::ProcessXML ()
71 {
72 size_t i;
73 for ( i = 0; i < files.size (); i++ )
74 files[i]->ProcessXML ();
75 for ( i = 0; i < includes.size (); i++ )
76 includes[i]->ProcessXML ();
77 for ( i = 0; i < defines.size (); i++ )
78 defines[i]->ProcessXML ();
79 for ( i = 0; i < libraries.size (); i++ )
80 libraries[i]->ProcessXML ();
81 for ( i = 0; i < properties.size(); i++ )
82 properties[i]->ProcessXML ();
83 for ( i = 0; i < ifs.size (); i++ )
84 ifs[i]->ProcessXML ();
85 }
86
87 Module::Module ( const Project& project,
88 const XMLElement& moduleNode,
89 const string& modulePath )
90 : project (project),
91 node (moduleNode),
92 importLibrary (NULL),
93 bootstrap (NULL),
94 pch (NULL),
95 cplusplus (false)
96 {
97 if ( node.name != "module" )
98 throw Exception ( "internal tool error: Module created with non-<module> node" );
99
100 path = FixSeparator ( modulePath );
101
102 const XMLAttribute* att = moduleNode.GetAttribute ( "name", true );
103 assert(att);
104 name = att->value;
105
106 att = moduleNode.GetAttribute ( "type", true );
107 assert(att);
108 type = GetModuleType ( node.location, *att );
109
110 att = moduleNode.GetAttribute ( "extension", false );
111 if ( att != NULL )
112 extension = att->value;
113 else
114 extension = GetDefaultModuleExtension ();
115
116 att = moduleNode.GetAttribute ( "entrypoint", false );
117 if ( att != NULL )
118 entrypoint = att->value;
119 else
120 entrypoint = GetDefaultModuleEntrypoint ();
121
122 att = moduleNode.GetAttribute ( "baseaddress", false );
123 if ( att != NULL )
124 baseaddress = att->value;
125 else
126 baseaddress = GetDefaultModuleBaseaddress ();
127
128 att = moduleNode.GetAttribute ( "mangledsymbols", false );
129 if ( att != NULL )
130 mangledSymbols = att->value != "false";
131 else
132 mangledSymbols = false;
133 }
134
135 Module::~Module ()
136 {
137 size_t i;
138 for ( i = 0; i < invocations.size(); i++ )
139 delete invocations[i];
140 for ( i = 0; i < dependencies.size(); i++ )
141 delete dependencies[i];
142 for ( i = 0; i < compilerFlags.size(); i++ )
143 delete compilerFlags[i];
144 for ( i = 0; i < linkerFlags.size(); i++ )
145 delete linkerFlags[i];
146 if ( pch )
147 delete pch;
148 }
149
150 void
151 Module::ProcessXML()
152 {
153 size_t i;
154 for ( i = 0; i < node.subElements.size(); i++ )
155 ProcessXMLSubElement ( *node.subElements[i], path );
156 for ( i = 0; i < invocations.size(); i++ )
157 invocations[i]->ProcessXML ();
158 for ( i = 0; i < dependencies.size(); i++ )
159 dependencies[i]->ProcessXML ();
160 for ( i = 0; i < compilerFlags.size(); i++ )
161 compilerFlags[i]->ProcessXML();
162 for ( i = 0; i < linkerFlags.size(); i++ )
163 linkerFlags[i]->ProcessXML();
164 non_if_data.ProcessXML();
165 if ( pch )
166 pch->ProcessXML();
167 }
168
169 void
170 Module::ProcessXMLSubElement ( const XMLElement& e,
171 const string& path,
172 If* pIf /*= NULL*/ )
173 {
174 bool subs_invalid = false;
175 string subpath ( path );
176 if ( e.name == "file" && e.value.size () > 0 )
177 {
178 bool first = false;
179 const XMLAttribute* att = e.GetAttribute ( "first", false );
180 if ( att )
181 {
182 if ( !stricmp ( att->value.c_str(), "true" ) )
183 first = true;
184 else if ( stricmp ( att->value.c_str(), "false" ) )
185 throw InvalidBuildFileException (
186 e.location,
187 "attribute 'first' of <file> element can only be 'true' or 'false'" );
188 }
189 if ( !cplusplus )
190 {
191 // check for c++ file
192 string ext = GetExtension ( e.value );
193 if ( !stricmp ( ext.c_str(), ".cpp" ) )
194 cplusplus = true;
195 else if ( !stricmp ( ext.c_str(), ".cc" ) )
196 cplusplus = true;
197 else if ( !stricmp ( ext.c_str(), ".cxx" ) )
198 cplusplus = true;
199 }
200 File* pFile = new File ( FixSeparator ( path + CSEP + e.value ), first );
201 if ( pIf )
202 pIf->data.files.push_back ( pFile );
203 else
204 non_if_data.files.push_back ( pFile );
205 subs_invalid = true;
206 }
207 else if ( e.name == "library" && e.value.size () )
208 {
209 Library* pLibrary = new Library ( e, *this, e.value );
210 if ( pIf )
211 pIf->data.libraries.push_back ( pLibrary );
212 else
213 non_if_data.libraries.push_back ( pLibrary );
214 subs_invalid = true;
215 }
216 else if ( e.name == "directory" )
217 {
218 const XMLAttribute* att = e.GetAttribute ( "name", true );
219 assert(att);
220 subpath = FixSeparator ( path + CSEP + att->value );
221 }
222 else if ( e.name == "include" )
223 {
224 Include* include = new Include ( project, this, e );
225 if ( pIf )
226 pIf->data.includes.push_back ( include );
227 else
228 non_if_data.includes.push_back ( include );
229 subs_invalid = true;
230 }
231 else if ( e.name == "define" )
232 {
233 Define* pDefine = new Define ( project, this, e );
234 if ( pIf )
235 pIf->data.defines.push_back ( pDefine );
236 else
237 non_if_data.defines.push_back ( pDefine );
238 subs_invalid = true;
239 }
240 else if ( e.name == "invoke" )
241 {
242 if ( pIf )
243 throw InvalidBuildFileException (
244 e.location,
245 "<invoke> is not a valid sub-element of <if>" );
246 invocations.push_back ( new Invoke ( e, *this ) );
247 subs_invalid = false;
248 }
249 else if ( e.name == "dependency" )
250 {
251 if ( pIf )
252 throw InvalidBuildFileException (
253 e.location,
254 "<dependency> is not a valid sub-element of <if>" );
255 dependencies.push_back ( new Dependency ( e, *this ) );
256 subs_invalid = true;
257 }
258 else if ( e.name == "importlibrary" )
259 {
260 if ( pIf )
261 throw InvalidBuildFileException (
262 e.location,
263 "<importlibrary> is not a valid sub-element of <if>" );
264 if ( importLibrary )
265 throw InvalidBuildFileException (
266 e.location,
267 "Only one <importlibrary> is valid per module" );
268 importLibrary = new ImportLibrary ( e, *this );
269 subs_invalid = true;
270 }
271 else if ( e.name == "if" )
272 {
273 If* pOldIf = pIf;
274 pIf = new If ( e, project, this );
275 if ( pOldIf )
276 pOldIf->data.ifs.push_back ( pIf );
277 else
278 non_if_data.ifs.push_back ( pIf );
279 subs_invalid = false;
280 }
281 else if ( e.name == "compilerflag" )
282 {
283 compilerFlags.push_back ( new CompilerFlag ( project, this, e ) );
284 subs_invalid = true;
285 }
286 else if ( e.name == "linkerflag" )
287 {
288 linkerFlags.push_back ( new LinkerFlag ( project, this, e ) );
289 subs_invalid = true;
290 }
291 else if ( e.name == "property" )
292 {
293 throw InvalidBuildFileException (
294 e.location,
295 "<property> is not a valid sub-element of <module>" );
296 }
297 else if ( e.name == "bootstrap" )
298 {
299 bootstrap = new Bootstrap ( project, this, e );
300 subs_invalid = true;
301 }
302 else if ( e.name == "pch" )
303 {
304 if ( pIf )
305 throw InvalidBuildFileException (
306 e.location,
307 "<pch> is not a valid sub-element of <if>" );
308 if ( pch )
309 throw InvalidBuildFileException (
310 e.location,
311 "Only one <pch> is valid per module" );
312 pch = new PchFile (
313 e, *this, FixSeparator ( path + CSEP + e.value ) );
314 subs_invalid = true;
315 }
316 if ( subs_invalid && e.subElements.size() > 0 )
317 throw InvalidBuildFileException (
318 e.location,
319 "<%s> cannot have sub-elements",
320 e.name.c_str() );
321 for ( size_t i = 0; i < e.subElements.size (); i++ )
322 ProcessXMLSubElement ( *e.subElements[i], subpath, pIf );
323 }
324
325 ModuleType
326 Module::GetModuleType ( const string& location, const XMLAttribute& attribute )
327 {
328 if ( attribute.value == "buildtool" )
329 return BuildTool;
330 if ( attribute.value == "staticlibrary" )
331 return StaticLibrary;
332 if ( attribute.value == "objectlibrary" )
333 return ObjectLibrary;
334 if ( attribute.value == "kernel" )
335 return Kernel;
336 if ( attribute.value == "kernelmodedll" )
337 return KernelModeDLL;
338 if ( attribute.value == "kernelmodedriver" )
339 return KernelModeDriver;
340 if ( attribute.value == "nativedll" )
341 return NativeDLL;
342 if ( attribute.value == "nativecui" )
343 return NativeCUI;
344 if ( attribute.value == "win32dll" )
345 return Win32DLL;
346 if ( attribute.value == "win32cui" )
347 return Win32CUI;
348 if ( attribute.value == "win32gui" )
349 return Win32GUI;
350 if ( attribute.value == "bootloader" )
351 return BootLoader;
352 if ( attribute.value == "bootsector" )
353 return BootSector;
354 if ( attribute.value == "iso" )
355 return Iso;
356 throw InvalidAttributeValueException ( location,
357 attribute.name,
358 attribute.value );
359 }
360
361 string
362 Module::GetDefaultModuleExtension () const
363 {
364 switch (type)
365 {
366 case BuildTool:
367 return EXEPOSTFIX;
368 case StaticLibrary:
369 return ".a";
370 case ObjectLibrary:
371 return ".o";
372 case Kernel:
373 case NativeCUI:
374 case Win32CUI:
375 case Win32GUI:
376 return ".exe";
377 case KernelModeDLL:
378 case NativeDLL:
379 case Win32DLL:
380 return ".dll";
381 case KernelModeDriver:
382 case BootLoader:
383 return ".sys";
384 case BootSector:
385 return ".o";
386 case Iso:
387 return ".iso";
388 }
389 throw InvalidOperationException ( __FILE__,
390 __LINE__ );
391 }
392
393 string
394 Module::GetDefaultModuleEntrypoint () const
395 {
396 switch (type)
397 {
398 case Kernel:
399 return "_NtProcessStartup";
400 case KernelModeDLL:
401 return "_DriverEntry@8";
402 case NativeDLL:
403 return "_DllMainCRTStartup@12";
404 case NativeCUI:
405 return "_NtProcessStartup@4";
406 case Win32DLL:
407 return "_DllMain@12";
408 case Win32CUI:
409 return "_mainCRTStartup";
410 case Win32GUI:
411 return "_WinMainCRTStartup";
412 case KernelModeDriver:
413 return "_DriverEntry@8";
414 case BuildTool:
415 case StaticLibrary:
416 case ObjectLibrary:
417 case BootLoader:
418 case BootSector:
419 case Iso:
420 return "";
421 }
422 throw InvalidOperationException ( __FILE__,
423 __LINE__ );
424 }
425
426 string
427 Module::GetDefaultModuleBaseaddress () const
428 {
429 switch (type)
430 {
431 case Kernel:
432 return "0xc0000000";
433 case KernelModeDLL:
434 return "0x10000";
435 case NativeDLL:
436 return "0x10000";
437 case NativeCUI:
438 return "0x10000";
439 case Win32DLL:
440 return "0x10000";
441 case Win32CUI:
442 return "0x00400000";
443 case Win32GUI:
444 return "0x00400000";
445 case KernelModeDriver:
446 return "0x10000";
447 case BuildTool:
448 case StaticLibrary:
449 case ObjectLibrary:
450 case BootLoader:
451 case BootSector:
452 case Iso:
453 return "";
454 }
455 throw InvalidOperationException ( __FILE__,
456 __LINE__ );
457 }
458
459 bool
460 Module::HasImportLibrary () const
461 {
462 return importLibrary != NULL;
463 }
464
465 string
466 Module::GetTargetName () const
467 {
468 return name + extension;
469 }
470
471 string
472 Module::GetDependencyPath () const
473 {
474 if ( HasImportLibrary () )
475 {
476 return ssprintf ( "dk%snkm%slib%slib%s.a",
477 SSEP,
478 SSEP,
479 SSEP,
480 name.c_str () );
481 }
482 else
483 return GetPath();
484 }
485
486 string
487 Module::GetBasePath () const
488 {
489 return path;
490 }
491
492 string
493 Module::GetPath () const
494 {
495 return path + CSEP + GetTargetName ();
496 }
497
498 string
499 Module::GetPathWithPrefix ( const string& prefix ) const
500 {
501 return path + CSEP + prefix + GetTargetName ();
502 }
503
504 string
505 Module::GetTargets () const
506 {
507 if ( invocations.size () > 0 )
508 {
509 string targets ( "" );
510 for ( size_t i = 0; i < invocations.size (); i++ )
511 {
512 Invoke& invoke = *invocations[i];
513 if ( targets.length () > 0 )
514 targets += " ";
515 targets += invoke.GetTargets ();
516 }
517 return targets;
518 }
519 else
520 return GetPath ();
521 }
522
523 string
524 Module::GetInvocationTarget ( const int index ) const
525 {
526 return ssprintf ( "%s_invoke_%d",
527 name.c_str (),
528 index );
529 }
530
531 bool
532 Module::HasFileWithExtension (
533 const IfableData& data,
534 const std::string& extension ) const
535 {
536 size_t i;
537 for ( i = 0; i < data.files.size (); i++ )
538 {
539 File& file = *data.files[i];
540 string file_ext = GetExtension ( file.name );
541 if ( !stricmp ( file_ext.c_str (), extension.c_str () ) )
542 return true;
543 }
544 for ( i = 0; i < data.ifs.size (); i++ )
545 {
546 if ( HasFileWithExtension ( data.ifs[i]->data, extension ) )
547 return true;
548 }
549 return false;
550 }
551
552 void
553 Module::InvokeModule () const
554 {
555 for ( size_t i = 0; i < invocations.size (); i++ )
556 {
557 Invoke& invoke = *invocations[i];
558 string command = invoke.invokeModule->GetPath () + " " + invoke.GetParameters ();
559 printf ( "Executing '%s'\n\n", command.c_str () );
560 int exitcode = system ( command.c_str () );
561 if ( exitcode != 0 )
562 throw InvocationFailedException ( command,
563 exitcode );
564 }
565 }
566
567
568 File::File ( const string& _name, bool _first )
569 : name(_name), first(_first)
570 {
571 }
572
573 void
574 File::ProcessXML()
575 {
576 }
577
578
579 Library::Library ( const XMLElement& _node,
580 const Module& _module,
581 const string& _name )
582 : node(_node),
583 module(_module),
584 name(_name),
585 imported_module(_module.project.LocateModule(_name))
586 {
587 if ( module.name == name )
588 throw InvalidBuildFileException (
589 node.location,
590 "module '%s' cannot link against itself",
591 name.c_str() );
592 if ( !imported_module )
593 throw InvalidBuildFileException (
594 node.location,
595 "module '%s' trying to import non-existant module '%s'",
596 module.name.c_str(),
597 name.c_str() );
598 }
599
600 void
601 Library::ProcessXML()
602 {
603 if ( !module.project.LocateModule ( name ) )
604 throw InvalidBuildFileException (
605 node.location,
606 "module '%s' is trying to link against non-existant module '%s'",
607 module.name.c_str(),
608 name.c_str() );
609 }
610
611
612 Invoke::Invoke ( const XMLElement& _node,
613 const Module& _module )
614 : node (_node),
615 module (_module)
616 {
617 }
618
619 void
620 Invoke::ProcessXML()
621 {
622 const XMLAttribute* att = node.GetAttribute ( "module", false );
623 if (att == NULL)
624 invokeModule = &module;
625 else
626 {
627 invokeModule = module.project.LocateModule ( att->value );
628 if ( invokeModule == NULL )
629 throw InvalidBuildFileException (
630 node.location,
631 "module '%s' is trying to invoke non-existant module '%s'",
632 module.name.c_str(),
633 att->value.c_str() );
634 }
635
636 for ( size_t i = 0; i < node.subElements.size (); i++ )
637 ProcessXMLSubElement ( *node.subElements[i] );
638 }
639
640 void
641 Invoke::ProcessXMLSubElement ( const XMLElement& e )
642 {
643 bool subs_invalid = false;
644 if ( e.name == "input" )
645 {
646 for ( size_t i = 0; i < e.subElements.size (); i++ )
647 ProcessXMLSubElementInput ( *e.subElements[i] );
648 }
649 else if ( e.name == "output" )
650 {
651 for ( size_t i = 0; i < e.subElements.size (); i++ )
652 ProcessXMLSubElementOutput ( *e.subElements[i] );
653 }
654 if ( subs_invalid && e.subElements.size() > 0 )
655 throw InvalidBuildFileException ( e.location,
656 "<%s> cannot have sub-elements",
657 e.name.c_str() );
658 }
659
660 void
661 Invoke::ProcessXMLSubElementInput ( const XMLElement& e )
662 {
663 bool subs_invalid = false;
664 if ( e.name == "inputfile" && e.value.size () > 0 )
665 {
666 input.push_back ( new InvokeFile ( e, FixSeparator ( module.path + CSEP + e.value ) ) );
667 subs_invalid = true;
668 }
669 if ( subs_invalid && e.subElements.size() > 0 )
670 throw InvalidBuildFileException ( e.location,
671 "<%s> cannot have sub-elements",
672 e.name.c_str() );
673 }
674
675 void
676 Invoke::ProcessXMLSubElementOutput ( const XMLElement& e )
677 {
678 bool subs_invalid = false;
679 if ( e.name == "outputfile" && e.value.size () > 0 )
680 {
681 output.push_back ( new InvokeFile ( e, FixSeparator ( module.path + CSEP + e.value ) ) );
682 subs_invalid = true;
683 }
684 if ( subs_invalid && e.subElements.size() > 0 )
685 throw InvalidBuildFileException ( e.location,
686 "<%s> cannot have sub-elements",
687 e.name.c_str() );
688 }
689
690 string
691 Invoke::GetTargets () const
692 {
693 string targets ( "" );
694 for ( size_t i = 0; i < output.size (); i++ )
695 {
696 InvokeFile& file = *output[i];
697 if ( targets.length () > 0 )
698 targets += " ";
699 targets += NormalizeFilename ( file.name );
700 }
701 return targets;
702 }
703
704 string
705 Invoke::GetParameters () const
706 {
707 string parameters ( "" );
708 size_t i;
709 for ( i = 0; i < output.size (); i++ )
710 {
711 if ( parameters.length () > 0)
712 parameters += " ";
713 InvokeFile& invokeFile = *output[i];
714 if ( invokeFile.switches.length () > 0 )
715 {
716 parameters += invokeFile.switches;
717 parameters += " ";
718 }
719 parameters += invokeFile.name;
720 }
721
722 for ( i = 0; i < input.size (); i++ )
723 {
724 if ( parameters.length () > 0 )
725 parameters += " ";
726 InvokeFile& invokeFile = *input[i];
727 if ( invokeFile.switches.length () > 0 )
728 {
729 parameters += invokeFile.switches;
730 parameters += " ";
731 }
732 parameters += invokeFile.name ;
733 }
734
735 return parameters;
736 }
737
738
739 InvokeFile::InvokeFile ( const XMLElement& _node,
740 const string& _name )
741 : node (_node),
742 name (_name)
743 {
744 const XMLAttribute* att = _node.GetAttribute ( "switches", false );
745 if (att != NULL)
746 switches = att->value;
747 else
748 switches = "";
749 }
750
751 void
752 InvokeFile::ProcessXML()
753 {
754 }
755
756
757 Dependency::Dependency ( const XMLElement& _node,
758 const Module& _module )
759 : node (_node),
760 module (_module),
761 dependencyModule (NULL)
762 {
763 }
764
765 void
766 Dependency::ProcessXML()
767 {
768 dependencyModule = module.project.LocateModule ( node.value );
769 if ( dependencyModule == NULL )
770 throw InvalidBuildFileException ( node.location,
771 "module '%s' depend on non-existant module '%s'",
772 module.name.c_str(),
773 node.value.c_str() );
774 }
775
776
777 ImportLibrary::ImportLibrary ( const XMLElement& _node,
778 const Module& _module )
779 : node (_node),
780 module (_module)
781 {
782 const XMLAttribute* att = _node.GetAttribute ( "basename", false );
783 if (att != NULL)
784 basename = att->value;
785 else
786 basename = module.name;
787
788 att = _node.GetAttribute ( "definition", true );
789 assert (att);
790 definition = FixSeparator(att->value);
791 }
792
793
794 If::If ( const XMLElement& node_,
795 const Project& project_,
796 const Module* module_ )
797 : node(node_), project(project_), module(module_)
798 {
799 const XMLAttribute* att;
800
801 att = node.GetAttribute ( "property", true );
802 assert(att);
803 property = att->value;
804
805 att = node.GetAttribute ( "value", true );
806 assert(att);
807 value = att->value;
808 }
809
810 If::~If ()
811 {
812 }
813
814 void
815 If::ProcessXML()
816 {
817 }
818
819
820 Property::Property ( const XMLElement& node_,
821 const Project& project_,
822 const Module* module_ )
823 : node(node_), project(project_), module(module_)
824 {
825 const XMLAttribute* att;
826
827 att = node.GetAttribute ( "name", true );
828 assert(att);
829 name = att->value;
830
831 att = node.GetAttribute ( "value", true );
832 assert(att);
833 value = att->value;
834 }
835
836 void
837 Property::ProcessXML()
838 {
839 }
840
841
842 PchFile::PchFile (
843 const XMLElement& node_,
844 const Module& module_,
845 const string& header_ )
846 : node(node_), module(module_), header(header_)
847 {
848 }
849
850 void
851 PchFile::ProcessXML()
852 {
853 }