project_info.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  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. # ***** END GPL LICENSE BLOCK *****
  19. # <pep8 compliant>
  20. """
  21. Module for accessing project file data for Blender.
  22. Before use, call init(cmake_build_dir).
  23. """
  24. # TODO: Use CMAKE_EXPORT_COMPILE_COMMANDS (compile_commands.json)
  25. # Instead of Eclipse project format.
  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 sys.version_info.major < 3:
  42. print("\nPython3.x needed, found %s.\nAborting!\n" %
  43. sys.version.partition(" ")[0])
  44. sys.exit(1)
  45. import subprocess
  46. import os
  47. from os.path import (
  48. abspath,
  49. dirname,
  50. exists,
  51. join,
  52. normpath,
  53. splitext,
  54. )
  55. SOURCE_DIR = join(dirname(__file__), "..", "..")
  56. SOURCE_DIR = normpath(SOURCE_DIR)
  57. SOURCE_DIR = abspath(SOURCE_DIR)
  58. SIMPLE_PROJECTFILE = False
  59. # must initialize from 'init'
  60. CMAKE_DIR = None
  61. def init(cmake_path):
  62. global CMAKE_DIR, PROJECT_DIR
  63. # get cmake path
  64. cmake_path = cmake_path or ""
  65. if (not cmake_path) or (not exists(join(cmake_path, "CMakeCache.txt"))):
  66. cmake_path = os.getcwd()
  67. if not exists(join(cmake_path, "CMakeCache.txt")):
  68. print("CMakeCache.txt not found in %r or %r\n"
  69. " Pass CMake build dir as an argument, or run from that dir, aborting" %
  70. (cmake_path, os.getcwd()))
  71. return False
  72. PROJECT_DIR = CMAKE_DIR = cmake_path
  73. return True
  74. def source_list(path, filename_check=None):
  75. for dirpath, dirnames, filenames in os.walk(path):
  76. # skip '.git'
  77. dirnames[:] = [d for d in dirnames if not d.startswith(".")]
  78. for filename in filenames:
  79. filepath = join(dirpath, filename)
  80. if filename_check is None or filename_check(filepath):
  81. yield filepath
  82. # extension checking
  83. def is_cmake(filename):
  84. ext = splitext(filename)[1]
  85. return (ext == ".cmake") or (filename.endswith("CMakeLists.txt"))
  86. def is_c_header(filename):
  87. ext = splitext(filename)[1]
  88. return (ext in {".h", ".hpp", ".hxx", ".hh"})
  89. def is_py(filename):
  90. ext = splitext(filename)[1]
  91. return (ext == ".py")
  92. def is_glsl(filename):
  93. ext = splitext(filename)[1]
  94. return (ext == ".glsl")
  95. def is_c(filename):
  96. ext = splitext(filename)[1]
  97. return (ext in {".c", ".cpp", ".cxx", ".m", ".mm", ".rc", ".cc", ".inl", ".osl"})
  98. def is_c_any(filename):
  99. return is_c(filename) or is_c_header(filename)
  100. def is_svn_file(filename):
  101. dn, fn = os.path.split(filename)
  102. filename_svn = join(dn, ".svn", "text-base", "%s.svn-base" % fn)
  103. return exists(filename_svn)
  104. def is_project_file(filename):
  105. return (is_c_any(filename) or is_cmake(filename) or is_glsl(filename)) # and is_svn_file(filename)
  106. def cmake_advanced_info():
  107. """ Extract includes and defines from cmake.
  108. """
  109. make_exe = cmake_cache_var("CMAKE_MAKE_PROGRAM")
  110. make_exe_basename = os.path.basename(make_exe)
  111. def create_eclipse_project():
  112. print("CMAKE_DIR %r" % CMAKE_DIR)
  113. if sys.platform == "win32":
  114. raise Exception("Error: win32 is not supported")
  115. else:
  116. if make_exe_basename.startswith(("make", "gmake")):
  117. cmd = ("cmake", CMAKE_DIR, "-GEclipse CDT4 - Unix Makefiles")
  118. elif make_exe_basename.startswith("ninja"):
  119. cmd = ("cmake", CMAKE_DIR, "-GEclipse CDT4 - Ninja")
  120. else:
  121. raise Exception("Unknown make program %r" % make_exe)
  122. subprocess.check_call(cmd)
  123. return join(CMAKE_DIR, ".cproject")
  124. includes = []
  125. defines = []
  126. project_path = create_eclipse_project()
  127. if not exists(project_path):
  128. print("Generating Eclipse Prokect File Failed: %r not found" % project_path)
  129. return None, None
  130. from xml.dom.minidom import parse
  131. tree = parse(project_path)
  132. # to check on nicer xml
  133. # f = open(".cproject_pretty", 'w')
  134. # f.write(tree.toprettyxml(indent=" ", newl=""))
  135. ELEMENT_NODE = tree.ELEMENT_NODE
  136. cproject, = tree.getElementsByTagName("cproject")
  137. for storage in cproject.childNodes:
  138. if storage.nodeType != ELEMENT_NODE:
  139. continue
  140. if storage.attributes["moduleId"].value == "org.eclipse.cdt.core.settings":
  141. cconfig = storage.getElementsByTagName("cconfiguration")[0]
  142. for substorage in cconfig.childNodes:
  143. if substorage.nodeType != ELEMENT_NODE:
  144. continue
  145. moduleId = substorage.attributes["moduleId"].value
  146. # org.eclipse.cdt.core.settings
  147. # org.eclipse.cdt.core.language.mapping
  148. # org.eclipse.cdt.core.externalSettings
  149. # org.eclipse.cdt.core.pathentry
  150. # org.eclipse.cdt.make.core.buildtargets
  151. if moduleId == "org.eclipse.cdt.core.pathentry":
  152. for path in substorage.childNodes:
  153. if path.nodeType != ELEMENT_NODE:
  154. continue
  155. kind = path.attributes["kind"].value
  156. if kind == "mac":
  157. # <pathentry kind="mac" name="PREFIX" path="" value="&quot;/opt/blender25&quot;"/>
  158. defines.append((path.attributes["name"].value, path.attributes["value"].value))
  159. elif kind == "inc":
  160. # <pathentry include="/data/src/blender/blender/source/blender/editors/include" kind="inc" path="" system="true"/>
  161. includes.append(path.attributes["include"].value)
  162. else:
  163. pass
  164. return includes, defines
  165. def cmake_cache_var(var):
  166. cache_file = open(join(CMAKE_DIR, "CMakeCache.txt"), encoding='utf-8')
  167. lines = [
  168. l_strip for l in cache_file
  169. for l_strip in (l.strip(),)
  170. if l_strip
  171. if not l_strip.startswith(("//", "#"))
  172. ]
  173. cache_file.close()
  174. for l in lines:
  175. if l.split(":")[0] == var:
  176. return l.split("=", 1)[-1]
  177. return None
  178. def cmake_compiler_defines():
  179. compiler = cmake_cache_var("CMAKE_C_COMPILER") # could do CXX too
  180. if compiler is None:
  181. print("Couldn't find the compiler, os defines will be omitted...")
  182. return
  183. import tempfile
  184. temp_c = tempfile.mkstemp(suffix=".c")[1]
  185. temp_def = tempfile.mkstemp(suffix=".def")[1]
  186. os.system("%s -dM -E %s > %s" % (compiler, temp_c, temp_def))
  187. temp_def_file = open(temp_def)
  188. lines = [l.strip() for l in temp_def_file if l.strip()]
  189. temp_def_file.close()
  190. os.remove(temp_c)
  191. os.remove(temp_def)
  192. return lines
  193. def project_name_get():
  194. return cmake_cache_var("CMAKE_PROJECT_NAME")