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