Imported Upstream version 2.8.9
[platform/upstream/cmake.git] / Source / cmparseMSBuildXML.py
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:
4
5 #  http://blogs.msdn.com/vcblog/archive/2008/12/16/msbuild-task.aspx
6 #  "${PROGRAMFILES}/MSBuild/Microsoft.Cpp/v4.0/1033/cl.xml"
7 #  "${PROGRAMFILES}/MSBuild/Microsoft.Cpp/v4.0/1033/lib.xml"
8 #  "${PROGRAMFILES}/MSBuild/Microsoft.Cpp/v4.0/1033/link.xml"
9 #
10 #  BoolProperty  <Name>true|false</Name>
11 #   simple example:
12 #     <BoolProperty ReverseSwitch="Oy-" Name="OmitFramePointers" 
13 #      Category="Optimization" Switch="Oy">
14 #   <BoolProperty.DisplayName>  <BoolProperty.Description>
15 # <CLCompile>
16 #     <OmitFramePointers>true</OmitFramePointers>
17 #  </ClCompile>
18 #
19 #  argument means it might be this: /MP3
20 #   example with argument:
21 #   <BoolProperty Name="MultiProcessorCompilation" Category="General" Switch="MP">
22 #      <BoolProperty.DisplayName>
23 #        <sys:String>Multi-processor Compilation</sys:String>
24 #      </BoolProperty.DisplayName>
25 #      <BoolProperty.Description>
26 #        <sys:String>Multi-processor Compilation</sys:String>
27 #      </BoolProperty.Description>
28 #      <Argument Property="ProcessorNumber" IsRequired="false" />
29 #    </BoolProperty>
30 # <CLCompile>
31 #   <MultiProcessorCompilation>true</MultiProcessorCompilation>
32 #   <ProcessorNumber>4</ProcessorNumber>
33 #  </ClCompile>
34 #  IntProperty
35 #     not used AFIT
36 #  <IntProperty Name="ProcessorNumber" Category="General" Visible="false">
37
38
39 #  per config options example
40 #    <EnableFiberSafeOptimizations Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</EnableFiberSafeOptimizations>
41 #
42 #  EnumProperty
43 #   <EnumProperty Name="Optimization" Category="Optimization">
44 #      <EnumProperty.DisplayName>
45 #       <sys:String>Optimization</sys:String>
46 #     </EnumProperty.DisplayName>
47 #     <EnumProperty.Description>
48 #       <sys:String>Select option for code optimization; choose Custom to use specific optimization options.     (/Od, /O1, /O2, /Ox)</sys:String>
49 #     </EnumProperty.Description>
50 #      <EnumValue Name="MaxSpeed" Switch="O2">
51 #       <EnumValue.DisplayName>
52 #         <sys:String>Maximize Speed</sys:String>
53 #       </EnumValue.DisplayName>
54 #       <EnumValue.Description>
55 #         <sys:String>Equivalent to /Og /Oi /Ot /Oy /Ob2 /Gs /GF /Gy</sys:String>
56 #       </EnumValue.Description>
57 #     </EnumValue>
58 #     <EnumValue Name="MinSpace" Switch="O1">
59 #       <EnumValue.DisplayName>
60 #         <sys:String>Minimize Size</sys:String>
61 #       </EnumValue.DisplayName>
62 #       <EnumValue.Description>
63 #         <sys:String>Equivalent to /Og /Os /Oy /Ob2 /Gs /GF /Gy</sys:String>
64 #       </EnumValue.Description>
65 #     </EnumValue>
66 #     example for O2 would be this:
67 #     <Optimization>MaxSpeed</Optimization>
68 #     example for O1 would be this:
69 #     <Optimization>MinSpace</Optimization>
70
71 #  StringListProperty
72 #   <StringListProperty Name="PreprocessorDefinitions" Category="Preprocessor" Switch="D ">
73 #     <StringListProperty.DisplayName>
74 #       <sys:String>Preprocessor Definitions</sys:String>
75 #     </StringListProperty.DisplayName>
76 #     <StringListProperty.Description>
77 #       <sys:String>Defines a preprocessing symbols for your source file.</sys:String>
78 #     </StringListProperty.Description>
79 #   </StringListProperty>
80
81 #   <StringListProperty Subtype="folder" Name="AdditionalIncludeDirectories" Category="General" Switch="I">
82 #     <StringListProperty.DisplayName>
83 #       <sys:String>Additional Include Directories</sys:String>
84 #     </StringListProperty.DisplayName>
85 #     <StringListProperty.Description>
86 #       <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>
87 #     </StringListProperty.Description>
88 #   </StringListProperty>
89 #  StringProperty
90
91 # Example add bill include:
92
93 #   <AdditionalIncludeDirectories>..\..\..\..\..\..\bill;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
94
95
96 import sys
97 from xml.dom.minidom import parse, parseString
98
99 def getText(node):
100     nodelist = node.childNodes
101     rc = ""
102     for child in nodelist:
103         if child.nodeType == child.TEXT_NODE:
104             rc = rc + child.data
105     return rc
106
107 def print_tree(document, spaces=""):
108   for i in range(len(document.childNodes)):
109     if document.childNodes[i].nodeType == document.childNodes[i].ELEMENT_NODE:
110       print spaces+str(document.childNodes[i].nodeName )
111     print_tree(document.childNodes[i],spaces+"----")
112   pass
113
114 ###########################################################################################
115 #Data structure that stores a property of MSBuild
116 class Property:
117   #type = type of MSBuild property (ex. if the property is EnumProperty type should be "Enum")
118   #attributeNames = a list of any attributes that this property could have (ex. if this was a EnumProperty it should be ["Name","Category"])
119   #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">
120   def __init__(self,type,attributeNames,document=None):
121     self.suffix_type = "Property"
122     self.prefix_type = type
123     self.attributeNames = attributeNames
124     self.attributes = {}
125     self.DisplayName = ""
126     self.Description = ""
127     self.argumentProperty = ""
128     self.argumentIsRequired = ""
129     self.values = []
130     if document is not None:
131       self.populate(document)
132     pass
133    
134   #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">
135   #spaces = do not use
136   def populate(self,document, spaces = ""):
137     if document.nodeName == self.prefix_type+self.suffix_type:
138       for i in self.attributeNames:
139         self.attributes[i] = document.getAttribute(i)
140     for i in range(len(document.childNodes)):
141       child = document.childNodes[i]
142       if child.nodeType == child.ELEMENT_NODE:
143         if child.nodeName == self.prefix_type+self.suffix_type+".DisplayName":
144           self.DisplayName = getText(child.childNodes[1])
145         if child.nodeName == self.prefix_type+self.suffix_type+".Description":
146           self.Description = getText(child.childNodes[1])
147         if child.nodeName == "Argument":
148           self.argumentProperty = child.getAttribute("Property")
149           self.argumentIsRequired = child.getAttribute("IsRequired")
150         if child.nodeName == self.prefix_type+"Value":
151           va = Property(self.prefix_type,["Name","Switch"])
152           va.suffix_type = "Value"
153           va.populate(child)
154           self.values.append(va)
155       self.populate(child,spaces+"----")
156       pass
157
158   #toString function 
159   def __str__(self):
160     toReturn = self.prefix_type+self.suffix_type+":" 
161     for i in self.attributeNames:
162       toReturn += "\n    "+i+": "+self.attributes[i]
163     if self.argumentProperty != "":
164       toReturn += "\n    Argument:\n        Property: "+self.argumentProperty+"\n        IsRequired: "+self.argumentIsRequired
165     for i in self.values:
166         toReturn+="\n    "+str(i).replace("\n","\n    ")
167     return toReturn
168 ###########################################################################################
169
170 ###########################################################################################
171 #Class that populates itself from an MSBuild file and outputs it in CMake
172 #format
173
174 class MSBuildToCMake:
175   #document = the entire MSBuild xml file
176   def __init__(self,document=None):
177     self.enumProperties = []
178     self.stringProperties = []
179     self.stringListProperties = []
180     self.boolProperties = []
181     self.intProperties = []
182     if document!=None :
183       self.populate(document)
184     pass
185
186   #document = the entire MSBuild xml file
187   #spaces = don't use
188   #To add a new property (if they exist) copy and paste this code and fill in appropriate places
189   #
190   #if child.nodeName == "<Name>Property":
191   #        self.<Name>Properties.append(Property("<Name>",[<List of attributes>],child))
192   #
193   #Replace <Name> with the name of the new property (ex. if property is StringProperty replace <Name> with String)
194   #Replace <List of attributes> with a list of attributes in your property's root node 
195   #in the __init__ function add the line self.<Name>Properties = []
196   #
197   #That is all that is required to add new properties
198   #
199   def populate(self,document, spaces=""):
200     for i in range(len(document.childNodes)):
201       child = document.childNodes[i]
202       if child.nodeType == child.ELEMENT_NODE:
203         if child.nodeName == "EnumProperty":
204           self.enumProperties.append(Property("Enum",["Name","Category"],child))
205         if child.nodeName == "StringProperty":
206           self.stringProperties.append(Property("String",["Name","Subtype","Separator","Category","Visible","IncludeInCommandLine","Switch","ReadOnly"],child))
207         if child.nodeName == "StringListProperty":
208            self.stringListProperties.append(Property("StringList",["Name","Category","Switch","Subtype"],child))
209         if child.nodeName == "BoolProperty":
210            self.boolProperties.append(Property("Bool",["ReverseSwitch","Name","Category","Switch","SwitchPrefix","IncludeInCommandLine"],child))
211         if child.nodeName == "IntProperty":
212            self.intProperties.append(Property("Int",["Name","Category","Visible"],child))
213       self.populate(child,spaces+"----")
214     pass
215
216   #outputs information that CMake needs to know about MSBuild xml files
217   def toCMake(self):
218     toReturn = "static cmVS7FlagTable cmVS10CxxTable[] =\n{\n"
219     toReturn += "\n  //Enum Properties\n"
220     lastProp = {}
221     for i in self.enumProperties:
222       if i.attributes["Name"] == "CompileAsManaged":
223         #write these out after the rest of the enumProperties
224         lastProp = i
225         continue
226       for j in i.values:
227         #hardcore Brad King's manual fixes for cmVS10CLFlagTable.h
228         if i.attributes["Name"] == "PrecompiledHeader" and j.attributes["Switch"] != "":
229           toReturn+="  {\""+i.attributes["Name"]+"\", \""+j.attributes["Switch"]+"\",\n   \""+j.DisplayName+"\", \""+j.attributes["Name"]+"\",\n   cmVS7FlagTable::UserValueIgnored | cmVS7FlagTable::Continue},\n"
230         else:
231           #default (normal, non-hardcoded) case
232           toReturn+="  {\""+i.attributes["Name"]+"\", \""+j.attributes["Switch"]+"\",\n   \""+j.DisplayName+"\", \""+j.attributes["Name"]+"\", 0},\n"
233       toReturn += "\n"
234
235     if lastProp != {}:
236       for j in lastProp.values:
237           toReturn+="  {\""+lastProp.attributes["Name"]+"\", \""+j.attributes["Switch"]+"\",\n   \""+j.DisplayName+"\", \""+j.attributes["Name"]+"\", 0},\n"
238       toReturn += "\n"
239     
240     toReturn += "\n  //Bool Properties\n"
241     for i in self.boolProperties:
242       if i.argumentProperty == "":
243         if i.attributes["ReverseSwitch"] != "":
244           toReturn += "  {\""+i.attributes["Name"]+"\", \""+i.attributes["ReverseSwitch"]+"\", \"\", \"false\", 0},\n"
245         if i.attributes["Switch"] != "":
246           toReturn += "  {\""+i.attributes["Name"]+"\", \""+i.attributes["Switch"]+"\", \"\", \"true\", 0},\n"
247
248     toReturn += "\n  //Bool Properties With Argument\n"
249     for i in self.boolProperties:
250       if i.argumentProperty != "":
251         if i.attributes["ReverseSwitch"] != "":
252           toReturn += "  {\""+i.attributes["Name"]+"\", \""+i.attributes["ReverseSwitch"]+"\", \"\", \"false\",\n   cmVS7FlagTable::UserValueIgnored | cmVS7FlagTable::Continue},\n"
253           toReturn += "  {\""+i.attributes["Name"]+"\", \""+i.attributes["ReverseSwitch"]+"\", \""+i.DisplayName+"\", \"\",\n   cmVS7FlagTable::UserValueRequired},\n"
254         if i.attributes["Switch"] != "":
255           toReturn += "  {\""+i.attributes["Name"]+"\", \""+i.attributes["Switch"]+"\", \"\", \"true\",\n   cmVS7FlagTable::UserValueIgnored | cmVS7FlagTable::Continue},\n"
256           toReturn += "  {\""+i.argumentProperty+"\", \""+i.attributes["Switch"]+"\", \""+i.DisplayName+"\", \"\",\n   cmVS7FlagTable::UserValueRequired},\n"
257     
258     toReturn += "\n  //String List Properties\n"
259     for i in self.stringListProperties:
260       if i.attributes["Switch"] == "":
261         toReturn += "  // Skip [" + i.attributes["Name"] + "] - no command line Switch.\n";
262       else:
263         toReturn +="  {\""+i.attributes["Name"]+"\", \""+i.attributes["Switch"]+"\",\n   \""+i.DisplayName+"\",\n   \"\", cmVS7FlagTable::UserValue | cmVS7FlagTable::SemicolonAppendable},\n"
264
265     toReturn += "\n  //String Properties\n"
266     for i in self.stringProperties:
267       if i.attributes["Switch"] == "":
268         if i.attributes["Name"] == "PrecompiledHeaderFile":
269           #more hardcoding
270           toReturn += "  {\"PrecompiledHeaderFile\", \"Yc\",\n"
271           toReturn += "   \"Precompiled Header Name\",\n"
272           toReturn += "   \"\", cmVS7FlagTable::UserValueRequired},\n"
273           toReturn += "  {\"PrecompiledHeaderFile\", \"Yu\",\n"
274           toReturn += "   \"Precompiled Header Name\",\n"
275           toReturn += "   \"\", cmVS7FlagTable::UserValueRequired},\n"
276         else:
277           toReturn += "  // Skip [" + i.attributes["Name"] + "] - no command line Switch.\n";
278       else:
279         toReturn +="  {\""+i.attributes["Name"]+"\", \""+i.attributes["Switch"]+i.attributes["Separator"]+"\",\n   \""+i.DisplayName+"\",\n   \"\", cmVS7FlagTable::UserValue},\n"
280
281     toReturn += "  {0,0,0,0,0}\n};"
282     return toReturn
283     pass
284
285   #toString function
286   def __str__(self):
287     toReturn = ""
288     allList = [self.enumProperties,self.stringProperties,self.stringListProperties,self.boolProperties,self.intProperties]
289     for p in allList:
290       for i in p:
291         toReturn += "==================================================\n"+str(i).replace("\n","\n    ")+"\n==================================================\n"
292     
293     return toReturn
294 ###########################################################################################
295
296 ###########################################################################################
297 # main function
298 def main(argv):
299   xml_file = None
300   help = """
301   Please specify an input xml file with -x
302
303   Exiting...
304   Have a nice day :)"""
305   for i in range(0,len(argv)):
306     if argv[i] == "-x":
307       xml_file = argv[i+1]
308     if argv[i] == "-h": 
309       print help 
310       sys.exit(0)
311     pass
312   if xml_file == None:
313     print help
314     sys.exit(1)
315
316   f = open(xml_file,"r")
317   xml_str = f.read()
318   xml_dom = parseString(xml_str)
319
320   convertor = MSBuildToCMake(xml_dom)
321   print convertor.toCMake()
322
323   xml_dom.unlink()
324 ###########################################################################################
325 # main entry point
326 if __name__ == "__main__":
327   main(sys.argv)
328
329 sys.exit(0)