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