Copy msiexec from trunk
[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 {
533 if ( module.name == name )
534 throw InvalidBuildFileException (
535 node.location,
536 "module '%s' cannot link against itself",
537 name.c_str() );
538 }
539
540 void
541 Library::ProcessXML()
542 {
543 if ( !module.project.LocateModule ( name ) )
544 throw InvalidBuildFileException (
545 node.location,
546 "module '%s' is trying to link against non-existant module '%s'",
547 module.name.c_str(),
548 name.c_str() );
549 }
550
551
552 Invoke::Invoke ( const XMLElement& _node,
553 const Module& _module )
554 : node (_node),
555 module (_module)
556 {
557 }
558
559 void
560 Invoke::ProcessXML()
561 {
562 const XMLAttribute* att = node.GetAttribute ( "module", false );
563 if (att == NULL)
564 invokeModule = &module;
565 else
566 {
567 invokeModule = module.project.LocateModule ( att->value );
568 if ( invokeModule == NULL )
569 throw InvalidBuildFileException (
570 node.location,
571 "module '%s' is trying to invoke non-existant module '%s'",
572 module.name.c_str(),
573 att->value.c_str() );
574 }
575
576 for ( size_t i = 0; i < node.subElements.size (); i++ )
577 ProcessXMLSubElement ( *node.subElements[i] );
578 }
579
580 void
581 Invoke::ProcessXMLSubElement ( const XMLElement& e )
582 {
583 bool subs_invalid = false;
584 if ( e.name == "input" )
585 {
586 for ( size_t i = 0; i < e.subElements.size (); i++ )
587 ProcessXMLSubElementInput ( *e.subElements[i] );
588 }
589 else if ( e.name == "output" )
590 {
591 for ( size_t i = 0; i < e.subElements.size (); i++ )
592 ProcessXMLSubElementOutput ( *e.subElements[i] );
593 }
594 if ( subs_invalid && e.subElements.size() > 0 )
595 throw InvalidBuildFileException ( e.location,
596 "<%s> cannot have sub-elements",
597 e.name.c_str() );
598 }
599
600 void
601 Invoke::ProcessXMLSubElementInput ( const XMLElement& e )
602 {
603 bool subs_invalid = false;
604 if ( e.name == "inputfile" && e.value.size () > 0 )
605 {
606 input.push_back ( new InvokeFile ( e, FixSeparator ( module.path + CSEP + e.value ) ) );
607 subs_invalid = true;
608 }
609 if ( subs_invalid && e.subElements.size() > 0 )
610 throw InvalidBuildFileException ( e.location,
611 "<%s> cannot have sub-elements",
612 e.name.c_str() );
613 }
614
615 void
616 Invoke::ProcessXMLSubElementOutput ( const XMLElement& e )
617 {
618 bool subs_invalid = false;
619 if ( e.name == "outputfile" && e.value.size () > 0 )
620 {
621 output.push_back ( new InvokeFile ( e, FixSeparator ( module.path + CSEP + e.value ) ) );
622 subs_invalid = true;
623 }
624 if ( subs_invalid && e.subElements.size() > 0 )
625 throw InvalidBuildFileException ( e.location,
626 "<%s> cannot have sub-elements",
627 e.name.c_str() );
628 }
629
630 string
631 Invoke::GetTargets () const
632 {
633 string targets ( "" );
634 for ( size_t i = 0; i < output.size (); i++ )
635 {
636 InvokeFile& file = *output[i];
637 if ( targets.length () > 0 )
638 targets += " ";
639 targets += NormalizeFilename ( file.name );
640 }
641 return targets;
642 }
643
644 string
645 Invoke::GetParameters () const
646 {
647 string parameters ( "" );
648 size_t i;
649 for ( i = 0; i < output.size (); i++ )
650 {
651 if ( parameters.length () > 0)
652 parameters += " ";
653 InvokeFile& invokeFile = *output[i];
654 if ( invokeFile.switches.length () > 0 )
655 {
656 parameters += invokeFile.switches;
657 parameters += " ";
658 }
659 parameters += invokeFile.name;
660 }
661
662 for ( i = 0; i < input.size (); i++ )
663 {
664 if ( parameters.length () > 0 )
665 parameters += " ";
666 InvokeFile& invokeFile = *input[i];
667 if ( invokeFile.switches.length () > 0 )
668 {
669 parameters += invokeFile.switches;
670 parameters += " ";
671 }
672 parameters += invokeFile.name ;
673 }
674
675 return parameters;
676 }
677
678
679 InvokeFile::InvokeFile ( const XMLElement& _node,
680 const string& _name )
681 : node (_node),
682 name (_name)
683 {
684 const XMLAttribute* att = _node.GetAttribute ( "switches", false );
685 if (att != NULL)
686 switches = att->value;
687 else
688 switches = "";
689 }
690
691 void
692 InvokeFile::ProcessXML()
693 {
694 }
695
696
697 Dependency::Dependency ( const XMLElement& _node,
698 const Module& _module )
699 : node (_node),
700 module (_module),
701 dependencyModule (NULL)
702 {
703 }
704
705 void
706 Dependency::ProcessXML()
707 {
708 dependencyModule = module.project.LocateModule ( node.value );
709 if ( dependencyModule == NULL )
710 throw InvalidBuildFileException ( node.location,
711 "module '%s' depend on non-existant module '%s'",
712 module.name.c_str(),
713 node.value.c_str() );
714 }
715
716
717 ImportLibrary::ImportLibrary ( const XMLElement& _node,
718 const Module& _module )
719 : node (_node),
720 module (_module)
721 {
722 const XMLAttribute* att = _node.GetAttribute ( "basename", false );
723 if (att != NULL)
724 basename = att->value;
725 else
726 basename = module.name;
727
728 att = _node.GetAttribute ( "definition", true );
729 assert (att);
730 definition = FixSeparator(att->value);
731 }
732
733
734 If::If ( const XMLElement& node_,
735 const Project& project_,
736 const Module* module_ )
737 : node(node_), project(project_), module(module_)
738 {
739 const XMLAttribute* att;
740
741 att = node.GetAttribute ( "property", true );
742 assert(att);
743 property = att->value;
744
745 att = node.GetAttribute ( "value", true );
746 assert(att);
747 value = att->value;
748 }
749
750 If::~If ()
751 {
752 size_t i;
753 for ( i = 0; i < files.size(); i++ )
754 delete files[i];
755 for ( i = 0; i < includes.size(); i++ )
756 delete includes[i];
757 for ( i = 0; i < defines.size(); i++ )
758 delete defines[i];
759 for ( i = 0; i < ifs.size(); i++ )
760 delete ifs[i];
761 }
762
763 void
764 If::ProcessXML()
765 {
766 }
767
768
769 Property::Property ( const XMLElement& node_,
770 const Project& project_,
771 const Module* module_ )
772 : node(node_), project(project_), module(module_)
773 {
774 const XMLAttribute* att;
775
776 att = node.GetAttribute ( "name", true );
777 assert(att);
778 name = att->value;
779
780 att = node.GetAttribute ( "value", true );
781 assert(att);
782 value = att->value;
783 }
784
785 void
786 Property::ProcessXML()
787 {
788 }