PageRenderTime 28ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/Core/Dependencies/Havok/Tools/Serialize/reflectionDatabase/gccxmlGenerateDB.py

https://bitbucket.org/barakianc/nvidia-physx-and-apex-in-gge
Python | 437 lines | 411 code | 10 blank | 16 comment | 13 complexity | a82f18db9a62fd9ba270f5cd3f0e0c3e MD5 | raw file
  1. #
  2. # Confidential Information of Telekinesys Research Limited (t/a Havok). Not for disclosure or distribution without Havok's
  3. # prior written consent. This software contains code, techniques and know-how which is confidential and proprietary to Havok.
  4. # Level 2 and Level 3 source code contains trade secrets of Havok. Havok Software (C) Copyright 1999-2011 Telekinesys Research Limited t/a Havok. All Rights Reserved. Use of this software is subject to the terms of an end user license agreement.
  5. #
  6. #!/usr/bin/env python
  7. # Generate a project database using gccxml. This should not be called directly.
  8. # generateDB is the external interface. This examines the project database corresponding
  9. # to the specified project, creating or updating it if necessary and returning the
  10. # resulting (up-to-date) database object
  11. #
  12. # This manages all of the interface to gccxml. headerToDomXML parses the resulting data
  13. # structures using the pygccxml bindings
  14. from __future__ import with_statement
  15. import reflectionDatabase
  16. import headerToDomXML
  17. import string
  18. import sys
  19. import os
  20. import re
  21. import util
  22. import cPickle
  23. ## GCCXML settings file, customise gccxml behaviour in this file
  24. import gccxmlSettings
  25. try:
  26. for p in gccxmlSettings.HK_PYGCCXML_PATHADD:
  27. sys.path.append(p)
  28. except (NameError, AttributeError):
  29. pass
  30. BASE_DIR = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir)
  31. def handleErrorIfThisIsRelease(msg):
  32. if 'HAVOK_SDKS_DIR' not in os.environ:
  33. import checkPrerequisites
  34. print msg
  35. checkPrerequisites.checkPrerequisites()
  36. sys.exit(-1)
  37. # If we don't have the HAVOK_SDKS_DIR it must be in the system path
  38. try:
  39. sys.path.append(os.path.join(os.environ['HAVOK_SDKS_DIR'], "win32", "gccXML", "pygccxml-1.0.0"))
  40. except KeyError:
  41. pass
  42. try:
  43. from pygccxml import parser as pygccxmlParser, declarations as pygccxmlDecls
  44. except:
  45. handleErrorIfThisIsRelease('Error locating PyGCCXML, launching prerequisites checker...\n')
  46. raise RuntimeError, """Unable to load pygccxml module. Ensure that it is visible to
  47. the default python installation. See Common Havok Components > Serialization for more information"""
  48. ## Find the gccxml executable
  49. def findGCCXML():
  50. gccxmlExe = 'gccxml'
  51. exeExtention = {'nt': '.exe',
  52. 'posix': ''}
  53. gccxmlExe += exeExtention[os.name]
  54. gccxmlFile = None
  55. try:
  56. gccxmlFile = gccxmlSettings.HK_GCCXML_PATH
  57. except (NameError, AttributeError):
  58. pass
  59. if not gccxmlFile:
  60. #check for the GCCXML_ROOT env var
  61. gccxml_root_env_var = 'GCCXML_ROOT'
  62. if gccxml_root_env_var in os.environ:
  63. gccxml_root = os.path.join(os.environ[gccxml_root_env_var].replace('"',''), '..', '..')
  64. possibleGccxmlExe = os.path.join(gccxml_root, 'bin', gccxmlExe)
  65. gccxmlFile = possibleGccxmlExe
  66. if not gccxmlFile and os.name == "nt": #try HAVOK_SDKS_DIR if GCCXML_ROOT didnt work out, and we're on windows
  67. havok_sdks_dir_env_var = 'HAVOK_SDKS_DIR'
  68. if havok_sdks_dir_env_var in os.environ:
  69. gccxml_root = os.path.join(os.environ[havok_sdks_dir_env_var], "win32", "gccXML")
  70. gccxmlFile = os.path.join(gccxml_root, "gccxml.exe")
  71. if not gccxmlFile:
  72. #Search the PATH
  73. for path in os.environ['PATH'].split(os.path.pathsep):
  74. possible_gccxmlExe = os.path.join(path, gccxmlExe)
  75. if os.path.exists(possible_gccxmlExe):
  76. gccxmlFile = possible_gccxmlExe
  77. if gccxmlFile and os.path.exists(gccxmlFile):
  78. return gccxmlFile
  79. raise RuntimeError, """Unable to find gccxml.exe -- check Tools/Serialization/reflectionDatabase/gccxmlSettings.py
  80. See Common Havok Components > Serialization for more information"""
  81. env_re = re.compile(r'\$\(([\w\d_]+)\)')
  82. ## Resolve any environment variables in a given string
  83. ## Any substring of the form $(NAME) is replaced with the
  84. ## environment variable NAME
  85. def resolveEnv(path):
  86. ret = env_re.search(path)
  87. while ret:
  88. try:
  89. path = path.replace('$(' + ret.groups()[0] + ')', os.environ[ret.groups()[0]])
  90. except KeyError:
  91. path = path.replace('$(' + ret.groups()[0] + ')', "")
  92. ret = env_re.search(path)
  93. return path
  94. def generateCachedDecls(ns):
  95. localDicts = {"": {}}
  96. globalDict = localDicts[""]
  97. fnameCache = {}
  98. for d in ns.decls():
  99. try:
  100. globalDict[d.name].append(d)
  101. except KeyError:
  102. globalDict[d.name] = [d]
  103. if d.location:
  104. try:
  105. fname = fnameCache[d.location.file_name]
  106. except KeyError:
  107. fname = os.path.normpath(os.path.abspath(d.location.file_name)).lower()
  108. fnameCache[d.location.file_name] = fname
  109. try:
  110. localDicts[fname].append(d)
  111. except KeyError:
  112. localDicts[fname] = [d]
  113. return localDicts
  114. ## Generate the configuration for gccxml to parse a given project.
  115. ## When run on an internal Havok build this reads "global_settings.build"
  116. ## and the project's "settings.build" file. It saves the required settings
  117. ## (include directories and files) into a cached settings file called
  118. ## "reflectionSettings.cache". Build settings are then extracted from
  119. ## these caches, as unlike the "settings.build" files they're shipped in
  120. ## Havok's SDK packages.
  121. def generateConfig(project_dir, DBObject, compilerOverride = None):
  122. standardCFlags = r"-include " + os.path.abspath(os.path.join( os.path.dirname(__file__), "tweakdefines.h")) + " -include " + os.path.abspath(os.path.join( BASE_DIR, "Source", "Common", "Base", "hkBase.h"))
  123. standardIncludePaths = [os.path.dirname(__file__), os.path.join( BASE_DIR, "Source"), BASE_DIR]
  124. DBObject.project_dir = project_dir
  125. settings_build = os.path.join(project_dir, "settings.build")
  126. requiredSettings = ['addIncludePath', 'setPchFile', 'setPrefix'] # The only settings.build settings the reflection system uses.
  127. cachedSettingsFile = os.path.join(project_dir, "reflectionSettings.cache")
  128. # Read the settings.build file (if it exists) and create a cached settings file.
  129. if os.path.exists(settings_build) and os.path.isfile(settings_build):
  130. sys.path.append(os.path.join(BASE_DIR, "Build", "ReleaseSystem"))
  131. import BuildSystem.Modules
  132. with open(settings_build, "r") as project:
  133. settingsGlobals = {}
  134. globalSettingsFile = os.path.join(BASE_DIR, "Build", "ReleaseSystem", "global_settings.build")
  135. if os.path.exists(globalSettingsFile):
  136. for line in open(globalSettingsFile).readlines():
  137. line=line.strip()
  138. if line!='':
  139. (key, value) = map(string.strip, line.split('='))
  140. value = resolveEnv(value)
  141. settingsGlobals[key] = value
  142. settingsDict = BuildSystem.Modules.evalSettings(settings_build, "win32", "msvc", "9", "ai+animation+behavior+cloth+destruction+physics", "", False, "win32", settingsGlobals)
  143. prunedSettingsDict = {}
  144. for x in settingsDict:
  145. if x in requiredSettings:
  146. prunedSettingsDict[x] = settingsDict[x]
  147. import pprint
  148. #open( cachedSettingsFile, 'w' ).write( pprint.pformat(prunedSettingsDict) )
  149. util.writeIfDifferent(pprint.pformat(prunedSettingsDict), cachedSettingsFile)
  150. # The cached file will be present in internal Havok modules or modules in Havok sdk packages.
  151. if os.path.exists( cachedSettingsFile ):
  152. # We need to explicitly remove all '\r' characters otherwise eval fails on linux.
  153. DBObject.settings = eval(open(cachedSettingsFile).read().replace('\r\n','\n' ))
  154. # Update standardCFlags and standardIncludePaths using the information in the cached settings file.
  155. try:
  156. for path in DBObject.settings['addIncludePath']:
  157. path = resolveEnv(path)
  158. relativeDirName = os.path.join( BASE_DIR, path)
  159. dirName = path
  160. if os.path.exists(relativeDirName) and os.path.isdir(relativeDirName):
  161. standardIncludePaths.append(relativeDirName)
  162. if os.path.exists(dirName) and os.path.isdir(dirName):
  163. standardIncludePaths.append(dirName)
  164. except (KeyError, TypeError):
  165. pass
  166. try:
  167. for f in DBObject.settings['setPchFile']:
  168. standardCFlags += r" -include %s" % f
  169. except (KeyError, TypeError):
  170. pass
  171. # Find gccxml executable
  172. try:
  173. gccxml_path = findGCCXML()
  174. except:
  175. handleErrorIfThisIsRelease('Error locating gccxml, launching prerequisites checker...\n')
  176. raise
  177. # Compiler should be set but set it if it isn't
  178. if compilerOverride:
  179. HK_GCCXML_COMPILER = compilerOverride
  180. else:
  181. try:
  182. HK_GCCXML_COMPILER = gccxmlSettings.HK_GCCXML_COMPILER
  183. except (NameError, AttributeError):
  184. HK_GCCXML_COMPILER="cl"
  185. try:
  186. parseConfig = pygccxmlParser.config_t(gccxml_path=gccxml_path,
  187. include_paths=standardIncludePaths,
  188. working_directory=os.path.dirname(__file__),
  189. cflags=(standardCFlags),
  190. compiler=HK_GCCXML_COMPILER)
  191. except RuntimeError:
  192. handleErrorIfThisIsRelease('Error locating gccxml, launching prerequisites checker...\n')
  193. sys.stderr.write("Error running GCCXML -- check the system is setup correctly.\nGCCXML must be present in the ThirdParty/sdks directory\n")
  194. raise
  195. return parseConfig
  196. def getProjectDBFile(project_dir):
  197. return os.path.join(project_dir, "reflections.db")
  198. # Platform is treated differently if there is a reflection declaration
  199. # With NO reflection declaration, allow only of platforms is 'ALL'
  200. # WITH reflection declaration, allow if platform is not 'NONE' or 'SPU'
  201. def allowPlatform(platformString, hasReflection):
  202. if not platformString:
  203. return True
  204. platforms = set(platformString.split(' '))
  205. if 'NONE' in platforms:
  206. return False
  207. if 'ALL' in platforms:
  208. return True
  209. if 'SPU' in platforms:
  210. return False
  211. return hasReflection
  212. def allowProduct(product, tkbms=None):
  213. if tkbms:
  214. if not product or product == 'ALL':
  215. return True
  216. tkbmsList = tkbms.split("+")
  217. productList = product.split("+")
  218. # If any of the tags in product are not in tkbms, fail
  219. return not any([tag not in tkbmsList for tag in productList])
  220. else:
  221. return product != 'NONE'
  222. excludeFiles = ["tests.h", "alltests.h", "hkpps2solverinternal.h", "hkpsolverinternal.h", "hkppspgccsolverinternal.h", "hkpfpusolverinternal.h",
  223. "hkpps2jacobianbuilder.h", "hkpps3jacobianbuilder.h", "hkppspgccjacobianbuilder.h", "hkpfpujacobianbuilder.h", "hkpjacobianbuilder.h",
  224. "hkppspsolverexport.h", "hkpfpusolverexport.h", "hkcompatversions.h", "hkhavokversionclasses.h", "hkmemoryclasses.h",
  225. "hkgptriangulatordebugger.h", "hkfinishloadedobjectflag.h", "hkworldstresstesttestfunctions.h", "bdferror.h", "hkbaseext.h",
  226. "hkpseudopad.h", "hkmxvector4.h", "hkvector4mxvector4.h", "hkpcollisionqueryutil.h"]
  227. excludeFileEndings = ["demovariants.h", "spu.h"]
  228. excludeFileStarts = ["wii", "hkspu", "hkps3", "hkbsd"]
  229. def allowFileName(filename, dirname, hasReflection):
  230. filename = filename.lower()
  231. dirname = dirname.lower()
  232. # Exclude various platform-specific files that aren't useful to the
  233. # reflection and tracking system
  234. if filename in excludeFiles:
  235. return False
  236. for e in excludeFileEndings:
  237. if filename.endswith(e):
  238. return False
  239. for e in excludeFileStarts:
  240. if filename.startswith(e):
  241. return False
  242. if os.path.basename(dirname) in ["classlist", "classes"]:
  243. return False
  244. if re.search(r'\b((demo[\\/]graphics[\\/]common[\\/]font|demo[\\/]graphics[\\/]common[\\/]texture)|base[\\/]system[\\/]stopwatch|base[\\/]system[\\/]io[\\/](directory|filesystem|reader)|demo[\\/]demos[\\/]resources|spu|wii)\b', dirname):
  245. return False
  246. if re.search(r'\bcontenttools\b', dirname) and not hasReflection:
  247. return False
  248. if re.search(r'\bbase[\\/]system[\\/]io[\\/]writer\b', dirname) and filename != 'hkstreamwriter.h':
  249. return False
  250. if re.search(r'\bdemo[\\/]demos\b', dirname) and filename == 'main.h':
  251. return False
  252. # If it's not in source or demos, or it is in contenttools, we are only interested in reflected files
  253. # This is from memoryTracker.py -- it should match the condition there
  254. if not re.search(r'\b(source|demos)\b', dirname) or re.search(r'\bcontenttools\b', dirname):
  255. return hasReflection
  256. return True
  257. def filterProjectFiles(filename, build_tkbms=None):
  258. if not filename.endswith(".h"):
  259. return None
  260. (dirname, justfilename) = os.path.split(filename)
  261. fileText = open(filename).read()
  262. tkbms = util.extract_tkbms(fileText)
  263. hasReflection = util.hasReflectionDeclaration(fileText)
  264. if not allowProduct(tkbms.get("product", ""), build_tkbms) or not allowPlatform(tkbms.get("platform",""), hasReflection) or not allowFileName(justfilename, dirname, hasReflection):
  265. return None
  266. ## if not util.hasReflectionDeclaration(fileText):
  267. ## return None
  268. filehash = reflectionDatabase.hashFileContents(fileText)
  269. return (filename, filehash)
  270. ## Find all of the files with reflection declarations from a given starting point.
  271. def findProjectFiles(project_dir, tkbms = None):
  272. fileList = set()
  273. for dirname, subdirs, files in os.walk(project_dir):
  274. headers = filter(None, [filterProjectFiles(fname, tkbms) for fname in [reflectionDatabase.standardFileName(os.path.join(dirname,f)) for f in files]])
  275. fileList.update(headers)
  276. return sorted(fileList)
  277. def createDB(project_dir, options, DBObject, filesToUpdate):
  278. reflections_db = getProjectDBFile(project_dir)
  279. try:
  280. if not DBObject:
  281. DBObject = reflectionDatabase.createReflectionDatabase()
  282. if options and options.compiler_msvc8:
  283. compilerOverride = "msvc8"
  284. elif options and options.compiler_msvc9:
  285. compilerOverride = "msvc9"
  286. elif options and options.compiler_gcc:
  287. compilerOverride = "gcc"
  288. else:
  289. compilerOverride = None
  290. projectConfig = generateConfig(project_dir, DBObject, compilerOverride)
  291. # If there are no reflected files, just return an empty object
  292. if filesToUpdate and len(zip(*filesToUpdate)):
  293. projectParser = headerToDomXML.hkXMLParser()
  294. try:
  295. decls = pygccxmlParser.parse(zip(*filesToUpdate)[0], compilation_mode = pygccxmlParser.COMPILATION_MODE.ALL_AT_ONCE, config = projectConfig)
  296. except (RuntimeError, IOError, OSError, pygccxmlParser.source_reader.gccxml_runtime_error_t), error:
  297. print "%s: error creating reflection file : %s" % (project_dir, error)
  298. handleErrorIfThisIsRelease('Error in pygccxml, launching prerequisites checker...\n')
  299. raise
  300. ns = pygccxmlDecls.get_global_namespace(decls)
  301. dicts = generateCachedDecls(ns)
  302. for (headerfile, filehash) in filesToUpdate:
  303. try:
  304. projectClass = projectParser.parseFileGeneric(headerfile, ns, dicts)
  305. projectClass.origfilename = reflectionDatabase.standardFileName(headerfile)
  306. DBObject.Documents[projectClass.origfilename] = projectClass
  307. DBObject.contentsHash[projectClass.origfilename] = filehash
  308. DBObject.new = True
  309. except (RuntimeError, IOError, OSError), error:
  310. print "%s: error creating reflection file : %s" % (headerfile, error)
  311. raise
  312. util.writeDatabase(DBObject, reflections_db)
  313. except:
  314. if os.path.exists(reflections_db):
  315. os.remove(reflections_db)
  316. raise
  317. return DBObject
  318. def checkProjectDependencies(project_dir, reflections_db, DBObject, havokProducts=None):
  319. try:
  320. projectFileList = findProjectFiles(project_dir, havokProducts) # (filename, filehash) tuples
  321. if not DBObject:
  322. return projectFileList
  323. if not len(zip(projectFileList)):
  324. return []
  325. # If any of the files in the DB no longer exist or are no longer in the database, remove them
  326. for removedFile in [x.origfilename for x in DBObject.getDocuments() if x.origfilename not in zip(*projectFileList)[0]]:
  327. del DBObject.Documents[removedFile]
  328. del DBObject.contentsHash[removedFile]
  329. DBObject.new = True
  330. # Check the file hash for each included file. Note this is based on the file contents NOT the mtime
  331. return [(fname, fhash) for (fname, fhash) in projectFileList if DBObject.contentsHash.get(fname, "") != fhash]
  332. except OSError:
  333. # If any file does not exist or is not readable, fail the dependency
  334. return projectFileList
  335. def generateDB(project_dir, options):
  336. """Create if necessary and return a project information database"""
  337. project_dir = reflectionDatabase.standardFileName(project_dir)
  338. if not os.path.exists(os.path.join(project_dir, "settings.build")) and not os.path.exists(os.path.join(project_dir, "reflectionSettings.cache")):
  339. # This is a customer build
  340. setattr(options, "customer_build", True)
  341. reflections_db = getProjectDBFile(project_dir)
  342. try:
  343. DBObject = util.readDatabase(reflections_db)
  344. except cPickle.PickleError:
  345. DBObject = None
  346. # Else the exception will be passed on
  347. # Check that the objects look the same
  348. localDB = reflectionDatabase.createReflectionDatabase()
  349. if not DBObject or sorted(localDB.__dict__.keys()) != sorted(DBObject.__dict__.keys()) or localDB.version != DBObject.version:
  350. DBObject = None
  351. filesToUpdate = checkProjectDependencies(project_dir, reflections_db, DBObject, (options and options.havok_products))
  352. if (not DBObject) or (options and options.force_rebuild):
  353. filesToUpdate = findProjectFiles(project_dir, (options and options.havok_products))
  354. if (not DBObject) or (options and options.force_rebuild) or len(filesToUpdate):
  355. return createDB(project_dir, options, DBObject, filesToUpdate)
  356. return DBObject
  357. #
  358. # Havok SDK - NO SOURCE PC DOWNLOAD, BUILD(#20110914)
  359. #
  360. # Confidential Information of Havok. (C) Copyright 1999-2011
  361. # Telekinesys Research Limited t/a Havok. All Rights Reserved. The Havok
  362. # Logo, and the Havok buzzsaw logo are trademarks of Havok. Title, ownership
  363. # rights, and intellectual property rights in the Havok software remain in
  364. # Havok and/or its suppliers.
  365. #
  366. # Use of this software for evaluation purposes is subject to and indicates
  367. # acceptance of the End User licence Agreement for this product. A copy of
  368. # the license is included with this software and is also available at www.havok.com/tryhavok.
  369. #
  370. #