cmparseMSBuildXML.py 15 KB

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