2 * Copyright (C) 2005 Casper S. Hornstrup
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 FixSeparator ( const string
& s
)
30 char* p
= strchr ( &s2
[0], CBAD_SEP
);
34 p
= strchr ( p
, CBAD_SEP
);
41 const string
& filename
,
42 const string
& newExtension
)
44 size_t index
= filename
.find_last_of ( '/' );
45 if ( index
== string::npos
)
47 size_t index2
= filename
.find_last_of ( '\\' );
48 if ( index2
!= string::npos
&& index2
> index
)
50 string tmp
= filename
.substr( index
/*, filename.size() - index*/ );
51 size_t ext_index
= tmp
.find_last_of( '.' );
52 if ( ext_index
!= string::npos
)
53 return filename
.substr ( 0, index
+ ext_index
) + newExtension
;
54 return filename
+ newExtension
;
59 const string
& location
,
61 const string
& att_value
)
63 if ( !att_value
.size() )
64 throw InvalidBuildFileException (
66 "<directory> tag has empty 'name' attribute" );
67 if ( strpbrk ( att_value
.c_str (), "/\\?*:<>|" ) )
68 throw InvalidBuildFileException (
70 "<directory> tag has invalid characters in 'name' attribute" );
73 return FixSeparator(path
+ CSEP
+ att_value
);
77 GetExtension ( const string
& filename
)
79 size_t index
= filename
.find_last_of ( '/' );
80 if (index
== string::npos
) index
= 0;
81 string tmp
= filename
.substr( index
, filename
.size() - index
);
82 size_t ext_index
= tmp
.find_last_of( '.' );
83 if (ext_index
!= string::npos
)
84 return filename
.substr ( index
+ ext_index
, filename
.size() );
89 GetDirectory ( const string
& filename
)
91 size_t index
= filename
.find_last_of ( CSEP
);
92 if ( index
== string::npos
)
95 return filename
.substr ( 0, index
);
99 GetFilename ( const string
& filename
)
101 size_t index
= filename
.find_last_of ( CSEP
);
102 if ( index
== string::npos
)
105 return filename
.substr ( index
+ 1, filename
.length () - index
);
109 NormalizeFilename ( const string
& filename
)
111 if ( filename
== "" )
114 string normalizedPath
= path
.Fixup ( filename
, true );
115 string relativeNormalizedPath
= path
.RelativeFromWorkingDirectory ( normalizedPath
);
116 return FixSeparator ( relativeNormalizedPath
);
120 GetBooleanValue ( const string
& value
)
128 IfableData::~IfableData()
131 for ( i
= 0; i
< files
.size(); i
++ )
133 for ( i
= 0; i
< includes
.size(); i
++ )
135 for ( i
= 0; i
< defines
.size(); i
++ )
137 for ( i
= 0; i
< libraries
.size(); i
++ )
139 for ( i
= 0; i
< properties
.size(); i
++ )
140 delete properties
[i
];
141 for ( i
= 0; i
< compilerFlags
.size(); i
++ )
142 delete compilerFlags
[i
];
143 for ( i
= 0; i
< ifs
.size(); i
++ )
147 void IfableData::ProcessXML ()
150 for ( i
= 0; i
< files
.size (); i
++ )
151 files
[i
]->ProcessXML ();
152 for ( i
= 0; i
< includes
.size (); i
++ )
153 includes
[i
]->ProcessXML ();
154 for ( i
= 0; i
< defines
.size (); i
++ )
155 defines
[i
]->ProcessXML ();
156 for ( i
= 0; i
< libraries
.size (); i
++ )
157 libraries
[i
]->ProcessXML ();
158 for ( i
= 0; i
< properties
.size(); i
++ )
159 properties
[i
]->ProcessXML ();
160 for ( i
= 0; i
< compilerFlags
.size(); i
++ )
161 compilerFlags
[i
]->ProcessXML ();
162 for ( i
= 0; i
< ifs
.size (); i
++ )
163 ifs
[i
]->ProcessXML ();
166 Module::Module ( const Project
& project
,
167 const XMLElement
& moduleNode
,
168 const string
& modulePath
)
171 importLibrary (NULL
),
177 if ( node
.name
!= "module" )
178 throw InvalidOperationException ( __FILE__
,
180 "Module created with non-<module> node" );
182 xmlbuildFile
= Path::RelativeFromWorkingDirectory ( moduleNode
.xmlFile
->filename () );
184 path
= FixSeparator ( modulePath
);
188 const XMLAttribute
* att
= moduleNode
.GetAttribute ( "if", false );
190 enabled
= GetBooleanValue ( project
.ResolveProperties ( att
->value
) );
192 att
= moduleNode
.GetAttribute ( "ifnot", false );
194 enabled
= !GetBooleanValue ( project
.ResolveProperties ( att
->value
) );
196 att
= moduleNode
.GetAttribute ( "name", true );
200 att
= moduleNode
.GetAttribute ( "type", true );
202 type
= GetModuleType ( node
.location
, *att
);
204 att
= moduleNode
.GetAttribute ( "extension", false );
206 extension
= att
->value
;
208 extension
= GetDefaultModuleExtension ();
210 att
= moduleNode
.GetAttribute ( "entrypoint", false );
212 entrypoint
= att
->value
;
214 entrypoint
= GetDefaultModuleEntrypoint ();
216 att
= moduleNode
.GetAttribute ( "baseaddress", false );
218 baseaddress
= att
->value
;
220 baseaddress
= GetDefaultModuleBaseaddress ();
222 att
= moduleNode
.GetAttribute ( "mangledsymbols", false );
225 const char* p
= att
->value
.c_str();
226 if ( !stricmp ( p
, "true" ) || !stricmp ( p
, "yes" ) )
227 mangledSymbols
= true;
228 else if ( !stricmp ( p
, "false" ) || !stricmp ( p
, "no" ) )
229 mangledSymbols
= false;
232 throw InvalidAttributeValueException (
239 mangledSymbols
= false;
241 att
= moduleNode
.GetAttribute ( "host", false );
244 const char* p
= att
->value
.c_str();
245 if ( !stricmp ( p
, "true" ) || !stricmp ( p
, "yes" ) )
247 else if ( !stricmp ( p
, "false" ) || !stricmp ( p
, "no" ) )
251 throw InvalidAttributeValueException (
258 att
= moduleNode
.GetAttribute ( "prefix", false );
262 att
= moduleNode
.GetAttribute ( "installbase", false );
264 installBase
= att
->value
;
268 att
= moduleNode
.GetAttribute ( "installname", false );
270 installName
= att
->value
;
274 att
= moduleNode
.GetAttribute ( "usewrc", false );
276 useWRC
= att
->value
== "true";
280 att
= moduleNode
.GetAttribute ( "warnings", false );
282 enableWarnings
= att
->value
== "true";
284 enableWarnings
= false;
290 for ( i
= 0; i
< invocations
.size(); i
++ )
291 delete invocations
[i
];
292 for ( i
= 0; i
< dependencies
.size(); i
++ )
293 delete dependencies
[i
];
294 for ( i
= 0; i
< compilerFlags
.size(); i
++ )
295 delete compilerFlags
[i
];
296 for ( i
= 0; i
< linkerFlags
.size(); i
++ )
297 delete linkerFlags
[i
];
298 for ( i
= 0; i
< stubbedComponents
.size(); i
++ )
299 delete stubbedComponents
[i
];
308 for ( i
= 0; i
< node
.subElements
.size(); i
++ )
309 ProcessXMLSubElement ( *node
.subElements
[i
], path
);
310 for ( i
= 0; i
< invocations
.size(); i
++ )
311 invocations
[i
]->ProcessXML ();
312 for ( i
= 0; i
< dependencies
.size(); i
++ )
313 dependencies
[i
]->ProcessXML ();
314 for ( i
= 0; i
< compilerFlags
.size(); i
++ )
315 compilerFlags
[i
]->ProcessXML();
316 for ( i
= 0; i
< linkerFlags
.size(); i
++ )
317 linkerFlags
[i
]->ProcessXML();
318 for ( i
= 0; i
< stubbedComponents
.size(); i
++ )
319 stubbedComponents
[i
]->ProcessXML();
320 non_if_data
.ProcessXML();
326 Module::ProcessXMLSubElement ( const XMLElement
& e
,
330 bool subs_invalid
= false;
331 string
subpath ( path
);
332 if ( e
.name
== "file" && e
.value
.size () > 0 )
335 const XMLAttribute
* att
= e
.GetAttribute ( "first", false );
338 if ( !stricmp ( att
->value
.c_str(), "true" ) )
340 else if ( stricmp ( att
->value
.c_str(), "false" ) )
341 throw InvalidBuildFileException (
343 "attribute 'first' of <file> element can only be 'true' or 'false'" );
345 string switches
= "";
346 att
= e
.GetAttribute ( "switches", false );
348 switches
= att
->value
;
351 // check for c++ file
352 string ext
= GetExtension ( e
.value
);
353 if ( !stricmp ( ext
.c_str(), ".cpp" ) )
355 else if ( !stricmp ( ext
.c_str(), ".cc" ) )
357 else if ( !stricmp ( ext
.c_str(), ".cxx" ) )
360 File
* pFile
= new File ( FixSeparator ( path
+ CSEP
+ e
.value
),
365 pIf
->data
.files
.push_back ( pFile
);
367 non_if_data
.files
.push_back ( pFile
);
370 else if ( e
.name
== "library" && e
.value
.size () )
372 Library
* pLibrary
= new Library ( e
, *this, e
.value
);
374 pIf
->data
.libraries
.push_back ( pLibrary
);
376 non_if_data
.libraries
.push_back ( pLibrary
);
379 else if ( e
.name
== "directory" )
381 const XMLAttribute
* att
= e
.GetAttribute ( "name", true );
383 subpath
= GetSubPath ( e
.location
, path
, att
->value
);
385 else if ( e
.name
== "include" )
387 Include
* include
= new Include ( project
, this, &e
);
389 pIf
->data
.includes
.push_back ( include
);
391 non_if_data
.includes
.push_back ( include
);
394 else if ( e
.name
== "define" )
396 Define
* pDefine
= new Define ( project
, this, e
);
398 pIf
->data
.defines
.push_back ( pDefine
);
400 non_if_data
.defines
.push_back ( pDefine
);
403 else if ( e
.name
== "invoke" )
406 throw InvalidBuildFileException (
408 "<invoke> is not a valid sub-element of <if>" );
409 invocations
.push_back ( new Invoke ( e
, *this ) );
410 subs_invalid
= false;
412 else if ( e
.name
== "dependency" )
415 throw InvalidBuildFileException (
417 "<dependency> is not a valid sub-element of <if>" );
418 dependencies
.push_back ( new Dependency ( e
, *this ) );
421 else if ( e
.name
== "importlibrary" )
424 throw InvalidBuildFileException (
426 "<importlibrary> is not a valid sub-element of <if>" );
428 throw InvalidBuildFileException (
430 "Only one <importlibrary> is valid per module" );
431 importLibrary
= new ImportLibrary ( e
, *this );
434 else if ( e
.name
== "if" )
437 pIf
= new If ( e
, project
, this );
439 pOldIf
->data
.ifs
.push_back ( pIf
);
441 non_if_data
.ifs
.push_back ( pIf
);
442 subs_invalid
= false;
444 else if ( e
.name
== "ifnot" )
447 pIf
= new If ( e
, project
, this, true );
449 pOldIf
->data
.ifs
.push_back ( pIf
);
451 non_if_data
.ifs
.push_back ( pIf
);
452 subs_invalid
= false;
454 else if ( e
.name
== "compilerflag" )
456 CompilerFlag
* pCompilerFlag
= new CompilerFlag ( project
, this, e
);
458 pIf
->data
.compilerFlags
.push_back ( pCompilerFlag
);
460 non_if_data
.compilerFlags
.push_back ( pCompilerFlag
);
463 else if ( e
.name
== "linkerflag" )
465 linkerFlags
.push_back ( new LinkerFlag ( project
, this, e
) );
468 else if ( e
.name
== "component" )
470 stubbedComponents
.push_back ( new StubbedComponent ( this, e
) );
471 subs_invalid
= false;
473 else if ( e
.name
== "property" )
475 throw InvalidBuildFileException (
477 "<property> is not a valid sub-element of <module>" );
479 else if ( e
.name
== "bootstrap" )
481 bootstrap
= new Bootstrap ( project
, this, e
);
484 else if ( e
.name
== "pch" )
487 throw InvalidBuildFileException (
489 "<pch> is not a valid sub-element of <if>" );
491 throw InvalidBuildFileException (
493 "Only one <pch> is valid per module" );
495 e
, *this, File ( FixSeparator ( path
+ CSEP
+ e
.value
), false, "", true ) );
498 if ( subs_invalid
&& e
.subElements
.size() > 0 )
499 throw InvalidBuildFileException (
501 "<%s> cannot have sub-elements",
503 for ( size_t i
= 0; i
< e
.subElements
.size (); i
++ )
504 ProcessXMLSubElement ( *e
.subElements
[i
], subpath
, pIf
);
508 Module::GetModuleType ( const string
& location
, const XMLAttribute
& attribute
)
510 if ( attribute
.value
== "buildtool" )
512 if ( attribute
.value
== "staticlibrary" )
513 return StaticLibrary
;
514 if ( attribute
.value
== "objectlibrary" )
515 return ObjectLibrary
;
516 if ( attribute
.value
== "kernel" )
518 if ( attribute
.value
== "kernelmodedll" )
519 return KernelModeDLL
;
520 if ( attribute
.value
== "kernelmodedriver" )
521 return KernelModeDriver
;
522 if ( attribute
.value
== "nativedll" )
524 if ( attribute
.value
== "nativecui" )
526 if ( attribute
.value
== "win32dll" )
528 if ( attribute
.value
== "win32cui" )
530 if ( attribute
.value
== "win32gui" )
532 if ( attribute
.value
== "bootloader" )
534 if ( attribute
.value
== "bootsector" )
536 if ( attribute
.value
== "iso" )
538 if ( attribute
.value
== "liveiso" )
540 if ( attribute
.value
== "test" )
542 if ( attribute
.value
== "rpcserver" )
544 if ( attribute
.value
== "rpcclient" )
546 throw InvalidAttributeValueException ( location
,
552 Module::GetDefaultModuleExtension () const
571 case KernelModeDriver
:
586 throw InvalidOperationException ( __FILE__
,
591 Module::GetDefaultModuleEntrypoint () const
596 return "_NtProcessStartup";
598 return "_DriverEntry@8";
600 return "_DllMainCRTStartup@12";
602 return "_NtProcessStartup@4";
604 return "_DllMain@12";
607 return "_mainCRTStartup";
609 return "_WinMainCRTStartup";
610 case KernelModeDriver
:
611 return "_DriverEntry@8";
623 throw InvalidOperationException ( __FILE__
,
628 Module::GetDefaultModuleBaseaddress () const
644 case KernelModeDriver
:
657 throw InvalidOperationException ( __FILE__
,
662 Module::HasImportLibrary () const
664 return importLibrary
!= NULL
;
668 Module::IsDLL () const
676 case KernelModeDriver
:
693 throw InvalidOperationException ( __FILE__
,
698 Module::GenerateInOutputTree () const
706 case KernelModeDriver
:
723 throw InvalidOperationException ( __FILE__
,
728 Module::GetTargetName () const
730 return name
+ extension
;
734 Module::GetDependencyPath () const
736 if ( HasImportLibrary () )
737 return ReplaceExtension ( GetPathWithPrefix ( "lib" ), ".a" );
743 Module::GetBasePath () const
749 Module::GetPath () const
751 if ( path
.length() > 0 )
752 return path
+ CSEP
+ GetTargetName ();
754 return GetTargetName ();
758 Module::GetPathWithPrefix ( const string
& prefix
) const
760 return path
+ CSEP
+ prefix
+ GetTargetName ();
764 Module::GetInvocationTarget ( const int index
) const
766 return ssprintf ( "%s_invoke_%d",
772 Module::HasFileWithExtension (
773 const IfableData
& data
,
774 const std::string
& extension
) const
777 for ( i
= 0; i
< data
.files
.size (); i
++ )
779 File
& file
= *data
.files
[i
];
780 string file_ext
= GetExtension ( file
.name
);
781 if ( !stricmp ( file_ext
.c_str (), extension
.c_str () ) )
784 for ( i
= 0; i
< data
.ifs
.size (); i
++ )
786 if ( HasFileWithExtension ( data
.ifs
[i
]->data
, extension
) )
793 Module::InvokeModule () const
795 for ( size_t i
= 0; i
< invocations
.size (); i
++ )
797 Invoke
& invoke
= *invocations
[i
];
798 string command
= invoke
.invokeModule
->GetPath () + " " + invoke
.GetParameters ();
799 printf ( "Executing '%s'\n\n", command
.c_str () );
800 int exitcode
= system ( command
.c_str () );
802 throw InvocationFailedException ( command
,
808 File::File ( const string
& _name
, bool _first
,
809 std::string _switches
,
810 bool _isPreCompiledHeader
)
814 isPreCompiledHeader(_isPreCompiledHeader
)
824 File::IsGeneratedFile () const
826 string extension
= GetExtension ( name
);
827 return ( extension
== ".spec" || extension
== ".SPEC" );
831 Library::Library ( const XMLElement
& _node
,
832 const Module
& _module
,
833 const string
& _name
)
837 importedModule(_module
.project
.LocateModule(_name
))
839 if ( module
.name
== name
)
840 throw InvalidBuildFileException (
842 "module '%s' cannot link against itself",
844 if ( !importedModule
)
845 throw InvalidBuildFileException (
847 "module '%s' trying to import non-existant module '%s'",
853 Library::ProcessXML()
855 if ( !module
.project
.LocateModule ( name
) )
856 throw InvalidBuildFileException (
858 "module '%s' is trying to link against non-existant module '%s'",
864 Invoke::Invoke ( const XMLElement
& _node
,
865 const Module
& _module
)
874 const XMLAttribute
* att
= node
.GetAttribute ( "module", false );
876 invokeModule
= &module
;
879 invokeModule
= module
.project
.LocateModule ( att
->value
);
880 if ( invokeModule
== NULL
)
881 throw InvalidBuildFileException (
883 "module '%s' is trying to invoke non-existant module '%s'",
885 att
->value
.c_str() );
888 for ( size_t i
= 0; i
< node
.subElements
.size (); i
++ )
889 ProcessXMLSubElement ( *node
.subElements
[i
] );
893 Invoke::ProcessXMLSubElement ( const XMLElement
& e
)
895 bool subs_invalid
= false;
896 if ( e
.name
== "input" )
898 for ( size_t i
= 0; i
< e
.subElements
.size (); i
++ )
899 ProcessXMLSubElementInput ( *e
.subElements
[i
] );
901 else if ( e
.name
== "output" )
903 for ( size_t i
= 0; i
< e
.subElements
.size (); i
++ )
904 ProcessXMLSubElementOutput ( *e
.subElements
[i
] );
906 if ( subs_invalid
&& e
.subElements
.size() > 0 )
907 throw InvalidBuildFileException ( e
.location
,
908 "<%s> cannot have sub-elements",
913 Invoke::ProcessXMLSubElementInput ( const XMLElement
& e
)
915 bool subs_invalid
= false;
916 if ( e
.name
== "inputfile" && e
.value
.size () > 0 )
918 input
.push_back ( new InvokeFile ( e
, FixSeparator ( module
.path
+ CSEP
+ e
.value
) ) );
921 if ( subs_invalid
&& e
.subElements
.size() > 0 )
922 throw InvalidBuildFileException ( e
.location
,
923 "<%s> cannot have sub-elements",
928 Invoke::ProcessXMLSubElementOutput ( const XMLElement
& e
)
930 bool subs_invalid
= false;
931 if ( e
.name
== "outputfile" && e
.value
.size () > 0 )
933 output
.push_back ( new InvokeFile ( e
, FixSeparator ( module
.path
+ CSEP
+ e
.value
) ) );
936 if ( subs_invalid
&& e
.subElements
.size() > 0 )
937 throw InvalidBuildFileException (
939 "<%s> cannot have sub-elements",
944 Invoke::GetTargets ( string_list
& targets
) const
946 for ( size_t i
= 0; i
< output
.size (); i
++ )
948 InvokeFile
& file
= *output
[i
];
949 targets
.push_back ( NormalizeFilename ( file
.name
) );
954 Invoke::GetParameters () const
956 string
parameters ( "" );
958 for ( i
= 0; i
< output
.size (); i
++ )
960 if ( parameters
.length () > 0)
962 InvokeFile
& invokeFile
= *output
[i
];
963 if ( invokeFile
.switches
.length () > 0 )
965 parameters
+= invokeFile
.switches
+ " ";
967 parameters
+= invokeFile
.name
;
970 for ( i
= 0; i
< input
.size (); i
++ )
972 if ( parameters
.length () > 0 )
974 InvokeFile
& invokeFile
= *input
[i
];
975 if ( invokeFile
.switches
.length () > 0 )
977 parameters
+= invokeFile
.switches
;
980 parameters
+= invokeFile
.name
;
987 InvokeFile::InvokeFile ( const XMLElement
& _node
,
988 const string
& _name
)
992 const XMLAttribute
* att
= _node
.GetAttribute ( "switches", false );
994 switches
= att
->value
;
1000 InvokeFile::ProcessXML()
1005 Dependency::Dependency ( const XMLElement
& _node
,
1006 const Module
& _module
)
1009 dependencyModule (NULL
)
1014 Dependency::ProcessXML()
1016 dependencyModule
= module
.project
.LocateModule ( node
.value
);
1017 if ( dependencyModule
== NULL
)
1018 throw InvalidBuildFileException ( node
.location
,
1019 "module '%s' depend on non-existant module '%s'",
1020 module
.name
.c_str(),
1021 node
.value
.c_str() );
1025 ImportLibrary::ImportLibrary ( const XMLElement
& _node
,
1026 const Module
& _module
)
1030 const XMLAttribute
* att
= _node
.GetAttribute ( "basename", false );
1032 basename
= att
->value
;
1034 basename
= module
.name
;
1036 att
= _node
.GetAttribute ( "definition", true );
1038 definition
= FixSeparator(att
->value
);
1042 If::If ( const XMLElement
& node_
,
1043 const Project
& project_
,
1044 const Module
* module_
,
1045 const bool negated_
)
1046 : node(node_
), project(project_
), module(module_
), negated(negated_
)
1048 const XMLAttribute
* att
;
1050 att
= node
.GetAttribute ( "property", true );
1052 property
= att
->value
;
1054 att
= node
.GetAttribute ( "value", true );
1069 Property::Property ( const XMLElement
& node_
,
1070 const Project
& project_
,
1071 const Module
* module_
)
1072 : node(node_
), project(project_
), module(module_
)
1074 const XMLAttribute
* att
;
1076 att
= node
.GetAttribute ( "name", true );
1080 att
= node
.GetAttribute ( "value", true );
1086 Property::ProcessXML()
1092 const XMLElement
& node_
,
1093 const Module
& module_
,
1095 : node(node_
), module(module_
), file(file_
)
1100 PchFile::ProcessXML()