project_info.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. #!/usr/bin/env python3
  2. # ***** BEGIN GPL LICENSE BLOCK *****
  3. #
  4. # This program is free software; you can redistribute it and/or
  5. # modify it under the terms of the GNU General Public License
  6. # as published by the Free Software Foundation; either version 2
  7. # of the License, or (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software Foundation,
  16. # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  17. #
  18. # Contributor(s): Campbell Barton, M.G. Kishalmi
  19. #
  20. # ***** END GPL LICENSE BLOCK *****
  21. # <pep8 compliant>
  22. """
  23. Module for accessing project file data for Blender.
  24. Before use, call init(cmake_build_dir).
  25. """
  26. __all__ = (
  27. "SIMPLE_PROJECTFILE",
  28. "SOURCE_DIR",
  29. "CMAKE_DIR",
  30. "PROJECT_DIR",
  31. "source_list",
  32. "is_project_file",
  33. "is_c_header",
  34. "is_py",
  35. "cmake_advanced_info",
  36. "cmake_compiler_defines",
  37. "project_name_get",
  38. "init",
  39. )
  40. import sys
  41. if not sys.version.startswith("3"):
  42. print("\nPython3.x needed, found %s.\nAborting!\n" %
  43. sys.version.partition(" ")[0])
  44. sys.exit(1)
  45. import os
  46. from os.path import join, dirname, normpath, abspath, splitext, exists
  47. SOURCE_DIR = join(dirname(__file__), "..", "..")
  48. SOURCE_DIR = normpath(SOURCE_DIR)
  49. SOURCE_DIR = abspath(SOURCE_DIR)
  50. SIMPLE_PROJECTFILE = False
  51. # must initialize from 'init'
  52. CMAKE_DIR = None
  53. def init(cmake_path):
  54. global CMAKE_DIR, PROJECT_DIR
  55. # get cmake path
  56. cmake_path = cmake_path or ""
  57. if (not cmake_path) or (not exists(join(cmake_path, "CMakeCache.txt"))):
  58. cmake_path = os.getcwd()
  59. if not exists(join(cmake_path, "CMakeCache.txt")):
  60. print("CMakeCache.txt not found in %r or %r\n"
  61. " Pass CMake build dir as an argument, or run from that dir, aborting" %
  62. (cmake_path, os.getcwd()))
  63. return False
  64. PROJECT_DIR = CMAKE_DIR = cmake_path
  65. return True
  66. def source_list(path, filename_check=None):
  67. for dirpath, dirnames, filenames in os.walk(path):
  68. # skip '.git'
  69. dirnames[:] = [d for d in dirnames if not d.startswith(".")]
  70. for filename in filenames:
  71. filepath = join(dirpath, filename)
  72. if filename_check is None or filename_check(filepath):
  73. yield filepath
  74. # extension checking
  75. def is_cmake(filename):
  76. ext = splitext(filename)[1]
  77. return (ext == ".cmake") or (filename.endswith("CMakeLists.txt"))
  78. def is_c_header(filename):
  79. ext = splitext(filename)[1]
  80. return (ext in {".h", ".hpp", ".hxx", ".hh"})
  81. def is_py(filename):
  82. ext = splitext(filename)[1]
  83. return (ext == ".py")
  84. def is_glsl(filename):
  85. ext = splitext(filename)[1]
  86. return (ext == ".glsl")
  87. def is_c(filename):
  88. ext = splitext(filename)[1]
  89. return (ext in {".c", ".cpp", ".cxx", ".m", ".mm", ".rc", ".cc", ".inl", ".osl"})
  90. def is_c_any(filename):
  91. return is_c(filename) or is_c_header(filename)
  92. def is_svn_file(filename):
  93. dn, fn = os.path.split(filename)
  94. filename_svn = join(dn, ".svn", "text-base", "%s.svn-base" % fn)
  95. return exists(filename_svn)
  96. def is_project_file(filename):
  97. return (is_c_any(filename) or is_cmake(filename) or is_glsl(filename)) # and is_svn_file(filename)
  98. def cmake_advanced_info():
  99. """ Extract includes and defines from cmake.
  100. """
  101. make_exe = cmake_cache_var("CMAKE_MAKE_PROGRAM")
  102. make_exe_basename = os.path.basename(make_exe)
  103. def create_eclipse_project():
  104. print("CMAKE_DIR %r" % CMAKE_DIR)
  105. if sys.platform == "win32":
  106. raise Exception("Error: win32 is not supported")
  107. else:
  108. if make_exe_basename.startswith(("make", "gmake")):
  109. cmd = 'cmake "%s" -G"Eclipse CDT4 - Unix Makefiles"' % CMAKE_DIR
  110. elif make_exe_basename.startswith("ninja"):
  111. cmd = 'cmake "%s" -G"Eclipse CDT4 - Ninja"' % CMAKE_DIR
  112. else:
  113. raise Exception("Unknown make program %r" % make_exe)
  114. os.system(cmd)
  115. return join(CMAKE_DIR, ".cproject")
  116. includes = []
  117. defines = []
  118. project_path = create_eclipse_project()
  119. if not exists(project_path):
  120. print("Generating Eclipse Prokect File Failed: %r not found" % project_path)
  121. return None, None
  122. from xml.dom.minidom import parse
  123. tree = parse(project_path)
  124. # to check on nicer xml
  125. # f = open(".cproject_pretty", 'w')
  126. # f.write(tree.toprettyxml(indent=" ", newl=""))
  127. ELEMENT_NODE = tree.ELEMENT_NODE
  128. cproject, = tree.getElementsByTagName("cproject")
  129. for storage in cproject.childNodes:
  130. if storage.nodeType != ELEMENT_NODE:
  131. continue
  132. if storage.attributes["moduleId"].value == "org.eclipse.cdt.core.settings":
  133. cconfig = storage.getElementsByTagName("cconfiguration")[0]
  134. for substorage in cconfig.childNodes:
  135. if substorage.nodeType != ELEMENT_NODE:
  136. continue
  137. moduleId = substorage.attributes["moduleId"].value
  138. # org.eclipse.cdt.core.settings
  139. # org.eclipse.cdt.core.language.mapping
  140. # org.eclipse.cdt.core.externalSettings
  141. # org.eclipse.cdt.core.pathentry
  142. # org.eclipse.cdt.make.core.buildtargets
  143. if moduleId == "org.eclipse.cdt.core.pathentry":
  144. for path in substorage.childNodes:
  145. if path.nodeType != ELEMENT_NODE:
  146. continue
  147. kind = path.attributes["kind"].value
  148. if kind == "mac":
  149. # <pathentry kind="mac" name="PREFIX" path="" value="&quot;/opt/blender25&quot;"/>
  150. defines.append((path.attributes["name"].value, path.attributes["value"].value))
  151. elif kind == "inc":
  152. # <pathentry include="/data/src/blender/blender/source/blender/editors/include" kind="inc" path="" system="true"/>
  153. includes.append(path.attributes["include"].value)
  154. else:
  155. pass
  156. return includes, defines
  157. def cmake_cache_var(var):
  158. cache_file = open(join(CMAKE_DIR, "CMakeCache.txt"), encoding='utf-8')
  159. lines = [
  160. l_strip for l in cache_file
  161. for l_strip in (l.strip(),)
  162. if l_strip
  163. if not l_strip.startswith(("//", "#"))
  164. ]
  165. cache_file.close()
  166. for l in lines:
  167. if l.split(":")[0] == var:
  168. return l.split("=", 1)[-1]
  169. return None
  170. def cmake_compiler_defines():
  171. compiler = cmake_cache_var("CMAKE_C_COMPILER") # could do CXX too
  172. if compiler is None:
  173. print("Couldn't find the compiler, os defines will be omitted...")
  174. return
  175. import tempfile
  176. temp_c = tempfile.mkstemp(suffix=".c")[1]
  177. temp_def = tempfile.mkstemp(suffix=".def")[1]
  178. os.system("%s -dM -E %s > %s" % (compiler, temp_c, temp_def))
  179. temp_def_file = open(temp_def)
  180. lines = [l.strip() for l in temp_def_file if l.strip()]
  181. temp_def_file.close()
  182. os.remove(temp_c)
  183. os.remove(temp_def)
  184. return lines
  185. def project_name_get():
  186. return cmake_cache_var("CMAKE_PROJECT_NAME")