createvmap.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. #
  2. # Copyright (c) 2007-2011 Nokia Corporation and/or its subsidiary(-ies).
  3. # All rights reserved.
  4. # This component and the accompanying materials are made available
  5. # under the terms of the License "Eclipse Public License v1.0"
  6. # which accompanies this distribution, and is available
  7. # at the URL "http://www.eclipse.org/legal/epl-v10.html".
  8. #
  9. # Initial Contributors:
  10. # Nokia Corporation - initial contribution.
  11. #
  12. # Contributors:
  13. #
  14. # Description:
  15. #
  16. # Python Script to create the vmap file for Binary Variation support in SBSv2
  17. import sys
  18. import os
  19. import re
  20. import subprocess
  21. import tempfile
  22. import traceback
  23. from optparse import OptionParser
  24. # Need to find the raptor utilities.
  25. sys.path.append(os.path.join(os.environ['SBS_HOME']))
  26. from raptor.utilities import expand_command_options
  27. # the script will exit with 0 if there are no errors
  28. global exitCode
  29. exitCode = 0
  30. # are we running on Windows?
  31. onWindows = sys.platform.lower().startswith("win")
  32. # error messages go to stderr
  33. def error(format, *extras):
  34. sys.stderr.write("createvmap: error: " + (format % extras) + "\n")
  35. global exitCode
  36. exitCode = 1
  37. # warning messages go to stderr
  38. def warning(format, *extras):
  39. sys.stderr.write("createvmap: warning: " + (format % extras) + "\n")
  40. # debug messages go to stderr
  41. global printDebug
  42. #
  43. def debug(format, *extras):
  44. if printDebug:
  45. sys.stderr.write("createvmap: " + (format % extras) + "\n")
  46. # Return a dictionary with the feature names and values from the preinclude file, by running cpp over the source
  47. def getVmapMacros(aPreInclude, aPreprocessedFile=None, aCPP="cpp", aDefines="", aIncludes = ""):
  48. validmacros = {}
  49. # Run the pre-processor
  50. command = aCPP + " -include " + os.path.abspath(aPreInclude) + " -dU " + aDefines + aIncludes
  51. # Feed in the file to stdin, because we must set the stdin to something
  52. # other than the parent stdin anyway as that may not exist - for example
  53. # when using Talon.
  54. infile = open(aPreprocessedFile, "r")
  55. if onWindows:
  56. p = subprocess.Popen(command, bufsize=65535,
  57. stdin=infile,
  58. stdout=subprocess.PIPE,
  59. stderr=sys.stderr,
  60. universal_newlines=True)
  61. else:
  62. p = subprocess.Popen(command, bufsize=65535,
  63. stdin=infile,
  64. stdout=subprocess.PIPE,
  65. stderr=sys.stderr,
  66. close_fds=True, shell=True)
  67. stream = p.stdout
  68. # Parse the pre-processor output to look for -
  69. # lines "#define NAME VALUE" and "#undef NAME"
  70. defineRE = re.compile('^#define (?P<FEATURENAME>\w+)(\s+(?P<VALUE>\w+))?')
  71. undefRE = re.compile('^#undef (?P<FEATURENAME>\w+)')
  72. data = " "
  73. while data:
  74. data = stream.readline()
  75. if not type(data) is str:
  76. data = data.decode()
  77. definedmacro = defineRE.match(data)
  78. if definedmacro:
  79. name = definedmacro.group('FEATURENAME')
  80. value = definedmacro.group('VALUE')
  81. if value:
  82. validmacros[name] = value
  83. else:
  84. validmacros[name] = "defined"
  85. else:
  86. undefinedmacro = undefRE.match(data)
  87. if undefinedmacro:
  88. validmacros[undefinedmacro.group('FEATURENAME')] = "undefined"
  89. if p.wait() != 0:
  90. error("in command '%s'", command)
  91. infile.close()
  92. return validmacros
  93. # Extract the features from a featurelist file
  94. def getFeatures(aFeatureList):
  95. features = set()
  96. for f in aFeatureList:
  97. try:
  98. file = open(os.path.abspath(f),'r')
  99. for data in file.readlines():
  100. data = data.strip()
  101. features.add(data)
  102. file.close()
  103. except IOError:
  104. error("Feature list file %s not found", f)
  105. return sorted(list(features))
  106. # Returns a dictionary of the features to be put in the vmap file
  107. def getVariationFeatures(aFeatureList = [] ,aPreinclude = None,aPreprocessedFile = None,aCPP = "cpp",aDefines="",aIncludes = ""):
  108. variation_features = {'FEATURENAME':[],'VALUE':[]}
  109. macros = getVmapMacros(aPreinclude,aPreprocessedFile,aCPP,aDefines,aIncludes)
  110. # Co-relate the macros obtained from the pre-processor to the featurelist
  111. for f in aFeatureList:
  112. if f in macros:
  113. variation_features['FEATURENAME'].append(f)
  114. variation_features['VALUE'].append(macros[f])
  115. return variation_features
  116. # Write to the vmap file, with the supplied dictionary containing the features
  117. # The vmap path will be created if it doesn't exist
  118. def createVmapFile(aMacroDictionary,aOutputfile):
  119. if not os.path.exists(os.path.dirname(aOutputfile)):
  120. os.makedirs(os.path.dirname(aOutputfile))
  121. try:
  122. vmapfile = open(aOutputfile,'w')
  123. except IOError:
  124. error("Cannot write to " + aOutputfile)
  125. i = 0
  126. while i < len(aMacroDictionary['FEATURENAME']):
  127. vmapfile.write(aMacroDictionary['FEATURENAME'][i]+"="+aMacroDictionary['VALUE'][i]+"\n")
  128. i += 1
  129. vmapfile.close()
  130. def check_exists(thing, filenames):
  131. if not filenames:
  132. error("No %s specified", thing)
  133. return
  134. if not isinstance(filenames, list):
  135. # we just have a single string
  136. filenames = [filenames]
  137. for filename in filenames:
  138. if not os.path.exists(filename):
  139. error("The %s '%s' does not exist", thing, filename)
  140. # Main function, creates the vmap file
  141. def main():
  142. try:
  143. global exitCode, printDebug
  144. # any exceptions make us traceback and exit
  145. parser = OptionParser(prog = "createvmap.py")
  146. parser.add_option("-c","--cpploc",action="store",dest="cpplocation",help="Full path of the preprocessor")
  147. parser.add_option("-d","--debug",action="store_true",default=False,dest="debug",help="Turn debug information on")
  148. parser.add_option("-D","--define",action="append",dest="defines",help="Macro definition")
  149. parser.add_option("-f","--featurelist",action="append",dest="featurelistfile",help="List of featureslist files")
  150. parser.add_option("-o","--output",action="store",dest="outputvmapfile",help="Output VMAP file name")
  151. parser.add_option("-p","--preinclude",action="store",dest="preinclude",help="Pre-include file ")
  152. parser.add_option("-s","--source",action="append",dest="sourcefiles",help="List of source files")
  153. parser.add_option("-u","--userinc",action="append",dest="user_include",help="User Include Folders")
  154. parser.add_option("-x","--systeminc",action="append",dest="system_include",help="System Include Folders")
  155. # The following allows the use of the --command option.
  156. # The add_option() is redundant since --command is
  157. # expanded well before it can take effect but it does
  158. # allow us to print out a useful help message.
  159. parser.add_option("--command",action="store",
  160. dest="preinclude",
  161. help="""Specify a command file with more commandline options
  162. in it (for very large components)""")
  163. expanded_args = expand_command_options(sys.argv[1:])
  164. (options, leftover_args) = parser.parse_args(expanded_args)
  165. if leftover_args:
  166. for invalids in leftover_args:
  167. warning("Unknown parameter '%s'" % invalids)
  168. printDebug = options.debug
  169. debug("Source Files -> %s", options.sourcefiles)
  170. debug("Macro defines -> %s", options.defines)
  171. debug("Features Files -> %s", options.featurelistfile)
  172. debug("Pre-Include File -> %s", options.preinclude)
  173. debug("User Includes -> %s", options.user_include)
  174. debug("System Includes -> %s", options.system_include)
  175. debug("CPP Location -> %s", options.cpplocation)
  176. debug("VMAP Output name -> %s", options.outputvmapfile)
  177. featurelist = []
  178. definelist = ""
  179. user_includeslist = ""
  180. system_includeslist = ""
  181. includeslist = ""
  182. # Some error checking code
  183. if not options.outputvmapfile:
  184. error("No output vmap file name supplied")
  185. # Source files must be supplied
  186. check_exists("source file", options.sourcefiles)
  187. # A valid preinclude file must be supplied
  188. check_exists("pre-include file", options.preinclude)
  189. # Some feature lists are required
  190. check_exists("feature list", options.featurelistfile)
  191. # A cpp tool is required
  192. check_exists("cpp tool", options.cpplocation)
  193. # if an essential option was missing then we should stop now
  194. if exitCode != 0:
  195. sys.exit(exitCode)
  196. # macro definitions
  197. if options.defines:
  198. for macro in options.defines:
  199. definelist += " -D" + macro.replace('__SBS__QUOTE__', '\\"')
  200. # Note that we have to use -isystem for user includes and system
  201. # includes to match what happens in the compiler. Long story.
  202. # Add each source directory as a user-include, so that our temporary
  203. # concatenated source file can find includes that were next to the
  204. # original source files.
  205. # Check that all the specified source files exist
  206. # and collect a set of all the source directories
  207. sourcedirs = set()
  208. for src in options.sourcefiles:
  209. sourcedirs.add(os.path.dirname(src))
  210. for srcdir in sourcedirs:
  211. user_includeslist += " -isystem " + srcdir
  212. # Convert the include list to a string to be passed to cpp
  213. if options.user_include:
  214. for userinc in options.user_include:
  215. user_includeslist += " -isystem " + userinc
  216. if options.system_include:
  217. for sysinc in options.system_include:
  218. system_includeslist += " -isystem " + sysinc
  219. includeslist = user_includeslist + system_includeslist
  220. # Get a list of all the features, from all the featurelist files
  221. featurelist = getFeatures(options.featurelistfile)
  222. # concatenate the source files together into a temporary file
  223. try:
  224. (tempfd, tempname) = tempfile.mkstemp()
  225. temp = os.fdopen(tempfd, "w")
  226. for src in options.sourcefiles:
  227. sfile = open(src, "r")
  228. for sline in sfile:
  229. temp.write(sline)
  230. sfile.close()
  231. temp.close()
  232. except Exception as e:
  233. error("Could not write source files into temporary file %s : %s" % (tempname, str(e)))
  234. return 1
  235. debug("Temporary file name : " + tempname)
  236. # extract the macros from the concatenated source files
  237. macro_dictionary = getVariationFeatures(featurelist,
  238. options.preinclude,
  239. tempname,
  240. options.cpplocation,
  241. definelist,
  242. includeslist)
  243. debug("Macros extracted:")
  244. for key,values in macro_dictionary.items():
  245. debug(key + " " + str(values))
  246. # if there were no macros then the vmap file will be empty...
  247. if not macro_dictionary['FEATURENAME']:
  248. warning("No feature macros were found in the source")
  249. # Get rid of the temporary file
  250. try:
  251. os.remove(tempname)
  252. except:
  253. error("Could not delete temporary %s" % tempname)
  254. createVmapFile(macro_dictionary, options.outputvmapfile)
  255. # exit with 0 if OK
  256. return exitCode
  257. except Exception as ex:
  258. traceback.print_exc()
  259. return 1
  260. if __name__ == "__main__":
  261. sys.exit(main())