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