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