Introduce "generator expressions" to add_test()
[cmake.git] / Source / cmparseMSBuildXML.py
blobfece9a6d60c7eb7fd3afbe557f32e3c40f5eb4dd
1 # This python script parses the spec files from MSBuild to create
2 # mappings from compiler options to IDE XML specifications. For
3 # more information see here:
5 # http://blogs.msdn.com/vcblog/archive/2008/12/16/msbuild-task.aspx
6 # cl.xml
8 # BoolProperty <Name>true|false</Name>
9 # simple example:
10 # <BoolProperty ReverseSwitch="Oy-" Name="OmitFramePointers"
11 # Category="Optimization" Switch="Oy">
12 # <BoolProperty.DisplayName> <BoolProperty.Description>
13 # <CLCompile>
14 # <OmitFramePointers>true</OmitFramePointers>
15 # </ClCompile>
17 # argument means it might be this: /MP3
18 # example with argument:
19 # <BoolProperty Name="MultiProcessorCompilation" Category="General" Switch="MP">
20 # <BoolProperty.DisplayName>
21 # <sys:String>Multi-processor Compilation</sys:String>
22 # </BoolProperty.DisplayName>
23 # <BoolProperty.Description>
24 # <sys:String>Multi-processor Compilation</sys:String>
25 # </BoolProperty.Description>
26 # <Argument Property="ProcessorNumber" IsRequired="false" />
27 # </BoolProperty>
28 # <CLCompile>
29 # <MultiProcessorCompilation>true</MultiProcessorCompilation>
30 # <ProcessorNumber>4</ProcessorNumber>
31 # </ClCompile>
32 # IntProperty
33 # not used AFIT
34 # <IntProperty Name="ProcessorNumber" Category="General" Visible="false">
37 # per config options example
38 # <EnableFiberSafeOptimizations Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</EnableFiberSafeOptimizations>
40 # EnumProperty
41 # <EnumProperty Name="Optimization" Category="Optimization">
42 # <EnumProperty.DisplayName>
43 # <sys:String>Optimization</sys:String>
44 # </EnumProperty.DisplayName>
45 # <EnumProperty.Description>
46 # <sys:String>Select option for code optimization; choose Custom to use specific optimization options. (/Od, /O1, /O2, /Ox)</sys:String>
47 # </EnumProperty.Description>
48 # <EnumValue Name="MaxSpeed" Switch="O2">
49 # <EnumValue.DisplayName>
50 # <sys:String>Maximize Speed</sys:String>
51 # </EnumValue.DisplayName>
52 # <EnumValue.Description>
53 # <sys:String>Equivalent to /Og /Oi /Ot /Oy /Ob2 /Gs /GF /Gy</sys:String>
54 # </EnumValue.Description>
55 # </EnumValue>
56 # <EnumValue Name="MinSpace" Switch="O1">
57 # <EnumValue.DisplayName>
58 # <sys:String>Minimize Size</sys:String>
59 # </EnumValue.DisplayName>
60 # <EnumValue.Description>
61 # <sys:String>Equivalent to /Og /Os /Oy /Ob2 /Gs /GF /Gy</sys:String>
62 # </EnumValue.Description>
63 # </EnumValue>
64 # example for O2 would be this:
65 # <Optimization>MaxSpeed</Optimization>
66 # example for O1 would be this:
67 # <Optimization>MinSpace</Optimization>
69 # StringListProperty
70 # <StringListProperty Name="PreprocessorDefinitions" Category="Preprocessor" Switch="D ">
71 # <StringListProperty.DisplayName>
72 # <sys:String>Preprocessor Definitions</sys:String>
73 # </StringListProperty.DisplayName>
74 # <StringListProperty.Description>
75 # <sys:String>Defines a preprocessing symbols for your source file.</sys:String>
76 # </StringListProperty.Description>
77 # </StringListProperty>
79 # <StringListProperty Subtype="folder" Name="AdditionalIncludeDirectories" Category="General" Switch="I">
80 # <StringListProperty.DisplayName>
81 # <sys:String>Additional Include Directories</sys:String>
82 # </StringListProperty.DisplayName>
83 # <StringListProperty.Description>
84 # <sys:String>Specifies one or more directories to add to the include path; separate with semi-colons if more than one. (/I[path])</sys:String>
85 # </StringListProperty.Description>
86 # </StringListProperty>
87 # StringProperty
89 # Example add bill include:
91 # <AdditionalIncludeDirectories>..\..\..\..\..\..\bill;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
94 import sys
95 from xml.dom.minidom import parse, parseString
97 def getText(node):
98 nodelist = node.childNodes
99 rc = ""
100 for child in nodelist:
101 if child.nodeType == child.TEXT_NODE:
102 rc = rc + child.data
103 return rc
105 def print_tree(document, spaces=""):
106 for i in range(len(document.childNodes)):
107 if document.childNodes[i].nodeType == document.childNodes[i].ELEMENT_NODE:
108 print spaces+str(document.childNodes[i].nodeName )
109 print_tree(document.childNodes[i],spaces+"----")
110 pass
112 ###########################################################################################
113 #Data structure that stores a property of MSBuild
114 class Property:
115 #type = type of MSBuild property (ex. if the property is EnumProperty type should be "Enum")
116 #attributeNames = a list of any attributes that this property could have (ex. if this was a EnumProperty it should be ["Name","Category"])
117 #document = the dom file that's root node is the Property node (ex. if you were parsing a BoolProperty the root node should be something like <BoolProperty Name="RegisterOutput" Category="General" IncludeInCommandLine="false">
118 def __init__(self,type,attributeNames,document=None):
119 self.suffix_type = "Property"
120 self.prefix_type = type
121 self.attributeNames = attributeNames
122 self.attributes = {}
123 self.DisplayName = ""
124 self.Description = ""
125 self.argumentProperty = ""
126 self.argumentIsRequired = ""
127 self.values = []
128 if document is not None:
129 self.populate(document)
130 pass
132 #document = the dom file that's root node is the Property node (ex. if you were parsing a BoolProperty the root node should be something like <BoolProperty Name="RegisterOutput" Category="General" IncludeInCommandLine="false">
133 #spaces = do not use
134 def populate(self,document, spaces = ""):
135 if document.nodeName == self.prefix_type+self.suffix_type:
136 for i in self.attributeNames:
137 self.attributes[i] = document.getAttribute(i)
138 for i in range(len(document.childNodes)):
139 child = document.childNodes[i]
140 if child.nodeType == child.ELEMENT_NODE:
141 if child.nodeName == self.prefix_type+self.suffix_type+".DisplayName":
142 self.DisplayName = getText(child.childNodes[1])
143 if child.nodeName == self.prefix_type+self.suffix_type+".Description":
144 self.Description = getText(child.childNodes[1])
145 if child.nodeName == "Argument":
146 self.argumentProperty = child.getAttribute("Property")
147 self.argumentIsRequired = child.getAttribute("IsRequired")
148 if child.nodeName == self.prefix_type+"Value":
149 va = Property(self.prefix_type,["Name","Switch"])
150 va.suffix_type = "Value"
151 va.populate(child)
152 self.values.append(va)
153 self.populate(child,spaces+"----")
154 pass
156 #toString function
157 def __str__(self):
158 toReturn = self.prefix_type+self.suffix_type+":"
159 for i in self.attributeNames:
160 toReturn += "\n "+i+": "+self.attributes[i]
161 if self.argumentProperty != "":
162 toReturn += "\n Argument:\n Property: "+self.argumentProperty+"\n IsRequired: "+self.argumentIsRequired
163 for i in self.values:
164 toReturn+="\n "+str(i).replace("\n","\n ")
165 return toReturn
166 ###########################################################################################
168 ###########################################################################################
169 #Class that populates itself from an MSBuild file and outputs it in CMake
170 #format
172 class MSBuildToCMake:
173 #document = the entire MSBuild xml file
174 def __init__(self,document=None):
175 self.enumProperties = []
176 self.stringProperties = []
177 self.stringListProperties = []
178 self.boolProperties = []
179 self.intProperties = []
180 if document!=None :
181 self.populate(document)
182 pass
184 #document = the entire MSBuild xml file
185 #spaces = don't use
186 #To add a new property (if they exist) copy and paste this code and fill in appropriate places
188 #if child.nodeName == "<Name>Property":
189 # self.<Name>Properties.append(Property("<Name>",[<List of attributes>],child))
191 #Replace <Name> with the name of the new property (ex. if property is StringProperty replace <Name> with String)
192 #Replace <List of attributes> with a list of attributes in your property's root node
193 #in the __init__ function add the line self.<Name>Properties = []
195 #That is all that is required to add new properties
197 def populate(self,document, spaces=""):
198 for i in range(len(document.childNodes)):
199 child = document.childNodes[i]
200 if child.nodeType == child.ELEMENT_NODE:
201 if child.nodeName == "EnumProperty":
202 self.enumProperties.append(Property("Enum",["Name","Category"],child))
203 if child.nodeName == "StringProperty":
204 self.stringProperties.append(Property("String",["Name","Subtype","Separator","Category","Visible","IncludeInCommandLine","Switch","ReadOnly"],child))
205 if child.nodeName == "StringListProperty":
206 self.stringListProperties.append(Property("StringList",["Name","Category","Switch","Subtype"],child))
207 if child.nodeName == "BoolProperty":
208 self.boolProperties.append(Property("Bool",["ReverseSwitch","Name","Category","Switch","SwitchPrefix","IncludeInCommandLine"],child))
209 if child.nodeName == "IntProperty":
210 self.intProperties.append(Property("Int",["Name","Category","Visible"],child))
211 self.populate(child,spaces+"----")
212 pass
214 #outputs information that CMake needs to know about MSBuild xml files
215 def toCMake(self):
216 toReturn = "static cmVS7FlagTable cmVS10CxxTable[] =\n{\n"
217 toReturn += "\n //Enum Properties\n"
218 for i in self.enumProperties:
219 for j in i.values:
220 toReturn+=" {\""+i.attributes["Name"]+"\", \""+j.attributes["Switch"]+"\",\n \""+j.DisplayName+"\", \""+j.attributes["Name"]+"\", 0},\n"
221 toReturn += "\n"
224 toReturn += "\n //Bool Properties\n"
225 for i in self.boolProperties:
226 if i.argumentProperty == "":
227 if i.attributes["ReverseSwitch"] != "":
228 toReturn += " {\""+i.attributes["Name"]+"\", \""+i.attributes["ReverseSwitch"]+"\", \"\", \"false\", 0},\n"
229 if i.attributes["Switch"] != "":
230 toReturn += " {\""+i.attributes["Name"]+"\", \""+i.attributes["Switch"]+"\", \"\", \"true\", 0},\n"
232 toReturn += "\n //Bool Properties With Argument\n"
233 for i in self.boolProperties:
234 if i.argumentProperty != "":
235 if i.attributes["ReverseSwitch"] != "":
236 toReturn += " {\""+i.attributes["Name"]+"\", \""+i.attributes["ReverseSwitch"]+"\", \"\", \"false\", cmVS7FlagTable::Continue},\n"
237 toReturn += " {\""+i.attributes["Name"]+"\", \""+i.attributes["ReverseSwitch"]+"\", \""+i.DisplayName+"\", \"\",\n cmVS7FlagTable::UserValueRequired},\n"
238 if i.attributes["Switch"] != "":
239 toReturn += " {\""+i.attributes["Name"]+"\", \""+i.attributes["Switch"]+"\", \"\", \"true\", cmVS7FlagTable::Continue},\n"
240 toReturn += " {\""+i.argumentProperty+"\", \""+i.attributes["Switch"]+"\", \""+i.DisplayName+"\", \"\",\n cmVS7FlagTable::UserValueRequired},\n"
242 toReturn += "\n //String List Properties\n"
243 for i in self.stringListProperties:
244 if i.attributes["Switch"] == "":
245 toReturn += " // Skip [" + i.attributes["Name"] + "] - no command line Switch.\n";
246 else:
247 toReturn +=" {\""+i.attributes["Name"]+"\", \""+i.attributes["Switch"]+"\",\n \""+i.DisplayName+"\",\n \"\", cmVS7FlagTable::UserValue | cmVS7FlagTable::SemicolonAppendable},\n"
249 toReturn += " {0,0,0,0,0}\n};"
250 return toReturn
251 pass
253 #toString function
254 def __str__(self):
255 toReturn = ""
256 allList = [self.enumProperties,self.stringProperties,self.stringListProperties,self.boolProperties,self.intProperties]
257 for p in allList:
258 for i in p:
259 toReturn += "==================================================\n"+str(i).replace("\n","\n ")+"\n==================================================\n"
261 return toReturn
262 ###########################################################################################
264 ###########################################################################################
265 # main function
266 def main(argv):
267 xml_file = None
268 help = """
269 Please specify an input xml file with -x
271 Exiting...
272 Have a nice day :)"""
273 for i in range(0,len(argv)):
274 if argv[i] == "-x":
275 xml_file = argv[i+1]
276 if argv[i] == "-h":
277 print help
278 sys.exit(0)
279 pass
280 if xml_file == None:
281 print help
282 sys.exit(1)
284 f = open(xml_file,"r")
285 xml_str = f.read()
286 xml_dom = parseString(xml_str)
288 convertor = MSBuildToCMake(xml_dom)
289 print convertor.toCMake()
291 xml_dom.unlink()
292 ###########################################################################################
293 # main entry point
294 if __name__ == "__main__":
295 main(sys.argv)
297 sys.exit(0)