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