createvmap.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. #
  2. # Copyright (c) 2007-2010 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'],"python"))
  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. definedmacro = defineRE.match(data)
  76. if definedmacro:
  77. name = definedmacro.group('FEATURENAME')
  78. value = definedmacro.group('VALUE')
  79. if value:
  80. validmacros[name] = value
  81. else:
  82. validmacros[name] = "defined"
  83. else:
  84. undefinedmacro = undefRE.match(data)
  85. if undefinedmacro:
  86. validmacros[undefinedmacro.group('FEATURENAME')] = "undefined"
  87. if p.wait() != 0:
  88. error("in command '%s'", command)
  89. infile.close()
  90. return validmacros
  91. # Extract the features from a featurelist file
  92. def getFeatures(aFeatureList):
  93. features = set()
  94. for f in aFeatureList:
  95. try:
  96. file = open(os.path.abspath(f),'r')
  97. for data in file.readlines():
  98. data = data.strip()
  99. features.add(data)
  100. file.close()
  101. except IOError:
  102. error("Feature list file %s not found", f)
  103. return sorted(list(features))
  104. # Returns a dictionary of the features to be put in the vmap file
  105. def getVariationFeatures(aFeatureList = [] ,aPreinclude = None,aPreprocessedFile = None,aCPP = "cpp",aDefines="",aIncludes = ""):
  106. variation_features = {'FEATURENAME':[],'VALUE':[]}
  107. macros = getVmapMacros(aPreinclude,aPreprocessedFile,aCPP,aDefines,aIncludes)
  108. # Co-relate the macros obtained from the pre-processor to the featurelist
  109. for f in aFeatureList:
  110. if f in macros:
  111. variation_features['FEATURENAME'].append(f)
  112. variation_features['VALUE'].append(macros[f])
  113. return variation_features
  114. # Write to the vmap file, with the supplied dictionary containing the features
  115. # The vmap path will be created if it doesn't exist
  116. def createVmapFile(aMacroDictionary,aOutputfile):
  117. if not os.path.exists(os.path.dirname(aOutputfile)):
  118. os.makedirs(os.path.dirname(aOutputfile))
  119. try:
  120. vmapfile = open(aOutputfile,'w')
  121. except IOError:
  122. error("Cannot write to " + aOutputfile)
  123. i = 0
  124. while i < len(aMacroDictionary['FEATURENAME']):
  125. vmapfile.write(aMacroDictionary['FEATURENAME'][i]+"="+aMacroDictionary['VALUE'][i]+"\n")
  126. i += 1
  127. vmapfile.close()
  128. def check_exists(thing, filenames):
  129. if not filenames:
  130. error("No %s specified", thing)
  131. return
  132. if not isinstance(filenames, list):
  133. # we just have a single string
  134. filenames = [filenames]
  135. for filename in filenames:
  136. if not os.path.exists(filename):
  137. error("The %s '%s' does not exist", thing, filename)
  138. # Main function, creates the vmap file
  139. def main():
  140. try:
  141. global exitCode, printDebug
  142. # any exceptions make us traceback and exit
  143. parser = OptionParser(prog = "createvmap.py")
  144. parser.add_option("-c","--cpploc",action="store",dest="cpplocation",help="Full path of the preprocessor")
  145. parser.add_option("-d","--debug",action="store_true",default=False,dest="debug",help="Turn debug information on")
  146. parser.add_option("-D","--define",action="append",dest="defines",help="Macro definition")
  147. parser.add_option("-f","--featurelist",action="append",dest="featurelistfile",help="List of featureslist files")
  148. parser.add_option("-o","--output",action="store",dest="outputvmapfile",help="Output VMAP file name")
  149. parser.add_option("-p","--preinclude",action="store",dest="preinclude",help="Pre-include file ")
  150. parser.add_option("-s","--source",action="append",dest="sourcefiles",help="List of source files")
  151. parser.add_option("-u","--userinc",action="append",dest="user_include",help="User Include Folders")
  152. parser.add_option("-x","--systeminc",action="append",dest="system_include",help="System Include Folders")
  153. # The following allows the use of the --command option.
  154. # The add_option() is redundant since --command is
  155. # expanded well before it can take effect but it does
  156. # allow us to print out a useful help message.
  157. parser.add_option("--command",action="store",
  158. dest="preinclude",
  159. help="""Specify a command file with more commandline options
  160. in it (for very large components)""")
  161. expanded_args = expand_command_options(sys.argv[1:])
  162. (options, leftover_args) = parser.parse_args(expanded_args)
  163. if leftover_args:
  164. for invalids in leftover_args:
  165. warning("Unknown parameter '%s'" % invalids)
  166. printDebug = options.debug
  167. debug("Source Files -> %s", options.sourcefiles)
  168. debug("Macro defines -> %s", options.defines)
  169. debug("Features Files -> %s", options.featurelistfile)
  170. debug("Pre-Include File -> %s", options.preinclude)
  171. debug("User Includes -> %s", options.user_include)
  172. debug("System Includes -> %s", options.system_include)
  173. debug("CPP Location -> %s", options.cpplocation)
  174. debug("VMAP Output name -> %s", options.outputvmapfile)
  175. featurelist = []
  176. definelist = ""
  177. user_includeslist = ""
  178. system_includeslist = ""
  179. includeslist = ""
  180. # Some error checking code
  181. if not options.outputvmapfile:
  182. error("No output vmap file name supplied")
  183. # Source files must be supplied
  184. check_exists("source file", options.sourcefiles)
  185. # A valid preinclude file must be supplied
  186. check_exists("pre-include file", options.preinclude)
  187. # Some feature lists are required
  188. check_exists("feature list", options.featurelistfile)
  189. # A cpp tool is required
  190. check_exists("cpp tool", options.cpplocation)
  191. # if an essential option was missing then we should stop now
  192. if exitCode != 0:
  193. sys.exit(exitCode)
  194. # macro definitions
  195. if options.defines:
  196. for macro in options.defines:
  197. definelist += " -D" + macro.replace('__SBS__QUOTE__', '\\"')
  198. # Note that we have to use -isystem for user includes and system
  199. # includes to match what happens in the compiler. Long story.
  200. # Add each source directory as a user-include, so that our temporary
  201. # concatenated source file can find includes that were next to the
  202. # original source files.
  203. # Check that all the specified source files exist
  204. # and collect a set of all the source directories
  205. sourcedirs = set()
  206. for src in options.sourcefiles:
  207. sourcedirs.add(os.path.dirname(src))
  208. for srcdir in sourcedirs:
  209. user_includeslist += " -isystem " + srcdir
  210. # Convert the include list to a string to be passed to cpp
  211. if options.user_include:
  212. for userinc in options.user_include:
  213. user_includeslist += " -isystem " + userinc
  214. if options.system_include:
  215. for sysinc in options.system_include:
  216. system_includeslist += " -isystem " + sysinc
  217. includeslist = user_includeslist + system_includeslist
  218. # Get a list of all the features, from all the featurelist files
  219. featurelist = getFeatures(options.featurelistfile)
  220. # concatenate the source files together into a temporary file
  221. try:
  222. (tempfd, tempname) = tempfile.mkstemp()
  223. temp = os.fdopen(tempfd, "w")
  224. for src in options.sourcefiles:
  225. sfile = open(src, "r")
  226. for sline in sfile:
  227. temp.write(sline)
  228. sfile.close()
  229. temp.close()
  230. except Exception,e:
  231. error("Could not write source files into temporary file %s : %s" % (tempname, str(e)))
  232. return 1
  233. debug("Temporary file name : " + tempname)
  234. # extract the macros from the concatenated source files
  235. macro_dictionary = getVariationFeatures(featurelist,
  236. options.preinclude,
  237. tempname,
  238. options.cpplocation,
  239. definelist,
  240. includeslist)
  241. debug("Macros extracted:")
  242. for key,values in macro_dictionary.iteritems():
  243. debug(key + " " + str(values))
  244. # if there were no macros then the vmap file will be empty...
  245. if not macro_dictionary['FEATURENAME']:
  246. warning("No feature macros were found in the source")
  247. # Get rid of the temporary file
  248. try:
  249. os.remove(tempname)
  250. except:
  251. error("Could not delete temporary %s" % tempname)
  252. createVmapFile(macro_dictionary, options.outputvmapfile)
  253. # exit with 0 if OK
  254. return exitCode
  255. except Exception,ex:
  256. traceback.print_exc()
  257. return 1
  258. if __name__ == "__main__":
  259. sys.exit(main())