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