methods.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  1. import os
  2. import re
  3. import glob
  4. import subprocess
  5. def add_source_files(self, sources, files, warn_duplicates=True):
  6. # Convert string to list of absolute paths (including expanding wildcard)
  7. if isinstance(files, (str, bytes)):
  8. # Keep SCons project-absolute path as they are (no wildcard support)
  9. if files.startswith("#"):
  10. if "*" in files:
  11. print("ERROR: Wildcards can't be expanded in SCons project-absolute path: '{}'".format(files))
  12. return
  13. files = [files]
  14. else:
  15. dir_path = self.Dir(".").abspath
  16. files = sorted(glob.glob(dir_path + "/" + files))
  17. # Add each path as compiled Object following environment (self) configuration
  18. for path in files:
  19. obj = self.Object(path)
  20. if obj in sources:
  21. if warn_duplicates:
  22. print('WARNING: Object "{}" already included in environment sources.'.format(obj))
  23. else:
  24. continue
  25. sources.append(obj)
  26. def disable_warnings(self):
  27. # 'self' is the environment
  28. if self.msvc:
  29. # We have to remove existing warning level defines before appending /w,
  30. # otherwise we get: "warning D9025 : overriding '/W3' with '/w'"
  31. warn_flags = ["/Wall", "/W4", "/W3", "/W2", "/W1", "/WX"]
  32. self.Append(CCFLAGS=["/w"])
  33. self.Append(CFLAGS=["/w"])
  34. self.Append(CXXFLAGS=["/w"])
  35. self["CCFLAGS"] = [x for x in self["CCFLAGS"] if not x in warn_flags]
  36. self["CFLAGS"] = [x for x in self["CFLAGS"] if not x in warn_flags]
  37. self["CXXFLAGS"] = [x for x in self["CXXFLAGS"] if not x in warn_flags]
  38. else:
  39. self.Append(CCFLAGS=["-w"])
  40. self.Append(CFLAGS=["-w"])
  41. self.Append(CXXFLAGS=["-w"])
  42. def add_module_version_string(self, s):
  43. self.module_version_string += "." + s
  44. def update_version(module_version_string=""):
  45. build_name = "custom_build"
  46. if os.getenv("BUILD_NAME") != None:
  47. build_name = os.getenv("BUILD_NAME")
  48. print("Using custom build name: " + build_name)
  49. import version
  50. # NOTE: It is safe to generate this file here, since this is still executed serially
  51. f = open("core/version_generated.gen.h", "w")
  52. f.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
  53. f.write("#ifndef VERSION_GENERATED_GEN_H\n")
  54. f.write("#define VERSION_GENERATED_GEN_H\n")
  55. f.write('#define VERSION_SHORT_NAME "' + str(version.short_name) + '"\n')
  56. f.write('#define VERSION_NAME "' + str(version.name) + '"\n')
  57. f.write("#define VERSION_MAJOR " + str(version.major) + "\n")
  58. f.write("#define VERSION_MINOR " + str(version.minor) + "\n")
  59. f.write("#define VERSION_PATCH " + str(version.patch) + "\n")
  60. f.write('#define VERSION_STATUS "' + str(version.status) + '"\n')
  61. f.write('#define VERSION_BUILD "' + str(build_name) + '"\n')
  62. f.write('#define VERSION_MODULE_CONFIG "' + str(version.module_config) + module_version_string + '"\n')
  63. f.write("#define VERSION_YEAR " + str(version.year) + "\n")
  64. f.write('#define VERSION_WEBSITE "' + str(version.website) + '"\n')
  65. f.write("#endif // VERSION_GENERATED_GEN_H\n")
  66. f.close()
  67. # NOTE: It is safe to generate this file here, since this is still executed serially
  68. fhash = open("core/version_hash.gen.h", "w")
  69. fhash.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
  70. fhash.write("#ifndef VERSION_HASH_GEN_H\n")
  71. fhash.write("#define VERSION_HASH_GEN_H\n")
  72. githash = ""
  73. gitfolder = ".git"
  74. if os.path.isfile(".git"):
  75. module_folder = open(".git", "r").readline().strip()
  76. if module_folder.startswith("gitdir: "):
  77. gitfolder = module_folder[8:]
  78. if os.path.isfile(os.path.join(gitfolder, "HEAD")):
  79. head = open(os.path.join(gitfolder, "HEAD"), "r").readline().strip()
  80. if head.startswith("ref: "):
  81. head = os.path.join(gitfolder, head[5:])
  82. if os.path.isfile(head):
  83. githash = open(head, "r").readline().strip()
  84. else:
  85. githash = head
  86. fhash.write('#define VERSION_HASH "' + githash + '"\n')
  87. fhash.write("#endif // VERSION_HASH_GEN_H\n")
  88. fhash.close()
  89. def parse_cg_file(fname, uniforms, sizes, conditionals):
  90. fs = open(fname, "r")
  91. line = fs.readline()
  92. while line:
  93. if re.match(r"^\s*uniform", line):
  94. res = re.match(r"uniform ([\d\w]*) ([\d\w]*)")
  95. type = res.groups(1)
  96. name = res.groups(2)
  97. uniforms.append(name)
  98. if type.find("texobj") != -1:
  99. sizes.append(1)
  100. else:
  101. t = re.match(r"float(\d)x(\d)", type)
  102. if t:
  103. sizes.append(int(t.groups(1)) * int(t.groups(2)))
  104. else:
  105. t = re.match(r"float(\d)", type)
  106. sizes.append(int(t.groups(1)))
  107. if line.find("[branch]") != -1:
  108. conditionals.append(name)
  109. line = fs.readline()
  110. fs.close()
  111. def detect_modules():
  112. module_list = []
  113. includes_cpp = ""
  114. register_cpp = ""
  115. unregister_cpp = ""
  116. preregister_cpp = ""
  117. files = glob.glob("modules/*")
  118. files.sort() # so register_module_types does not change that often, and also plugins are registered in alphabetic order
  119. for x in files:
  120. if not os.path.isdir(x):
  121. continue
  122. if not os.path.exists(x + "/config.py"):
  123. continue
  124. x = x.replace("modules/", "") # rest of world
  125. x = x.replace("modules\\", "") # win32
  126. module_list.append(x)
  127. try:
  128. with open("modules/" + x + "/register_types.h"):
  129. includes_cpp += '#include "modules/' + x + '/register_types.h"\n'
  130. register_cpp += "#ifdef MODULE_" + x.upper() + "_ENABLED\n"
  131. register_cpp += "\tregister_" + x + "_types();\n"
  132. register_cpp += "#endif\n"
  133. preregister_cpp += "#ifdef MODULE_" + x.upper() + "_ENABLED\n"
  134. preregister_cpp += "#ifdef MODULE_" + x.upper() + "_HAS_PREREGISTER\n"
  135. preregister_cpp += "\tpreregister_" + x + "_types();\n"
  136. preregister_cpp += "#endif\n"
  137. preregister_cpp += "#endif\n"
  138. unregister_cpp += "#ifdef MODULE_" + x.upper() + "_ENABLED\n"
  139. unregister_cpp += "\tunregister_" + x + "_types();\n"
  140. unregister_cpp += "#endif\n"
  141. except IOError:
  142. pass
  143. modules_cpp = """// register_module_types.gen.cpp
  144. /* THIS FILE IS GENERATED DO NOT EDIT */
  145. #include "register_module_types.h"
  146. #include "modules/modules_enabled.gen.h"
  147. %s
  148. void preregister_module_types() {
  149. %s
  150. }
  151. void register_module_types() {
  152. %s
  153. }
  154. void unregister_module_types() {
  155. %s
  156. }
  157. """ % (
  158. includes_cpp,
  159. preregister_cpp,
  160. register_cpp,
  161. unregister_cpp,
  162. )
  163. # NOTE: It is safe to generate this file here, since this is still executed serially
  164. with open("modules/register_module_types.gen.cpp", "w") as f:
  165. f.write(modules_cpp)
  166. return module_list
  167. def disable_module(self):
  168. self.disabled_modules.append(self.current_module)
  169. def use_windows_spawn_fix(self, platform=None):
  170. if os.name != "nt":
  171. return # not needed, only for windows
  172. # On Windows, due to the limited command line length, when creating a static library
  173. # from a very high number of objects SCons will invoke "ar" once per object file;
  174. # that makes object files with same names to be overwritten so the last wins and
  175. # the library looses symbols defined by overwritten objects.
  176. # By enabling quick append instead of the default mode (replacing), libraries will
  177. # got built correctly regardless the invocation strategy.
  178. # Furthermore, since SCons will rebuild the library from scratch when an object file
  179. # changes, no multiple versions of the same object file will be present.
  180. self.Replace(ARFLAGS="q")
  181. def mySubProcess(cmdline, env):
  182. startupinfo = subprocess.STARTUPINFO()
  183. startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
  184. proc = subprocess.Popen(
  185. cmdline,
  186. stdin=subprocess.PIPE,
  187. stdout=subprocess.PIPE,
  188. stderr=subprocess.PIPE,
  189. startupinfo=startupinfo,
  190. shell=False,
  191. env=env,
  192. )
  193. _, err = proc.communicate()
  194. rv = proc.wait()
  195. if rv:
  196. print("=====")
  197. print(err)
  198. print("=====")
  199. return rv
  200. def mySpawn(sh, escape, cmd, args, env):
  201. newargs = " ".join(args[1:])
  202. cmdline = cmd + " " + newargs
  203. rv = 0
  204. env = {str(key): str(value) for key, value in iter(env.items())}
  205. if len(cmdline) > 32000 and cmd.endswith("ar"):
  206. cmdline = cmd + " " + args[1] + " " + args[2] + " "
  207. for i in range(3, len(args)):
  208. rv = mySubProcess(cmdline + args[i], env)
  209. if rv:
  210. break
  211. else:
  212. rv = mySubProcess(cmdline, env)
  213. return rv
  214. self["SPAWN"] = mySpawn
  215. def save_active_platforms(apnames, ap):
  216. for x in ap:
  217. names = ["logo"]
  218. if os.path.isfile(x + "/run_icon.png"):
  219. names.append("run_icon")
  220. for name in names:
  221. pngf = open(x + "/" + name + ".png", "rb")
  222. b = pngf.read(1)
  223. str = " /* AUTOGENERATED FILE, DO NOT EDIT */ \n"
  224. str += " static const unsigned char _" + x[9:] + "_" + name + "[]={"
  225. while len(b) == 1:
  226. str += hex(ord(b))
  227. b = pngf.read(1)
  228. if len(b) == 1:
  229. str += ","
  230. str += "};\n"
  231. pngf.close()
  232. # NOTE: It is safe to generate this file here, since this is still executed serially
  233. wf = x + "/" + name + ".gen.h"
  234. with open(wf, "w") as pngw:
  235. pngw.write(str)
  236. def no_verbose(sys, env):
  237. colors = {}
  238. # Colors are disabled in non-TTY environments such as pipes. This means
  239. # that if output is redirected to a file, it will not contain color codes
  240. if sys.stdout.isatty():
  241. colors["cyan"] = "\033[96m"
  242. colors["purple"] = "\033[95m"
  243. colors["blue"] = "\033[94m"
  244. colors["green"] = "\033[92m"
  245. colors["yellow"] = "\033[93m"
  246. colors["red"] = "\033[91m"
  247. colors["end"] = "\033[0m"
  248. else:
  249. colors["cyan"] = ""
  250. colors["purple"] = ""
  251. colors["blue"] = ""
  252. colors["green"] = ""
  253. colors["yellow"] = ""
  254. colors["red"] = ""
  255. colors["end"] = ""
  256. compile_source_message = "{}Compiling {}==> {}$SOURCE{}".format(
  257. colors["blue"], colors["purple"], colors["yellow"], colors["end"]
  258. )
  259. java_compile_source_message = "{}Compiling {}==> {}$SOURCE{}".format(
  260. colors["blue"], colors["purple"], colors["yellow"], colors["end"]
  261. )
  262. compile_shared_source_message = "{}Compiling shared {}==> {}$SOURCE{}".format(
  263. colors["blue"], colors["purple"], colors["yellow"], colors["end"]
  264. )
  265. link_program_message = "{}Linking Program {}==> {}$TARGET{}".format(
  266. colors["red"], colors["purple"], colors["yellow"], colors["end"]
  267. )
  268. link_library_message = "{}Linking Static Library {}==> {}$TARGET{}".format(
  269. colors["red"], colors["purple"], colors["yellow"], colors["end"]
  270. )
  271. ranlib_library_message = "{}Ranlib Library {}==> {}$TARGET{}".format(
  272. colors["red"], colors["purple"], colors["yellow"], colors["end"]
  273. )
  274. link_shared_library_message = "{}Linking Shared Library {}==> {}$TARGET{}".format(
  275. colors["red"], colors["purple"], colors["yellow"], colors["end"]
  276. )
  277. java_library_message = "{}Creating Java Archive {}==> {}$TARGET{}".format(
  278. colors["red"], colors["purple"], colors["yellow"], colors["end"]
  279. )
  280. env.Append(CXXCOMSTR=[compile_source_message])
  281. env.Append(CCCOMSTR=[compile_source_message])
  282. env.Append(SHCCCOMSTR=[compile_shared_source_message])
  283. env.Append(SHCXXCOMSTR=[compile_shared_source_message])
  284. env.Append(ARCOMSTR=[link_library_message])
  285. env.Append(RANLIBCOMSTR=[ranlib_library_message])
  286. env.Append(SHLINKCOMSTR=[link_shared_library_message])
  287. env.Append(LINKCOMSTR=[link_program_message])
  288. env.Append(JARCOMSTR=[java_library_message])
  289. env.Append(JAVACCOMSTR=[java_compile_source_message])
  290. def detect_visual_c_compiler_version(tools_env):
  291. # tools_env is the variable scons uses to call tools that execute tasks, SCons's env['ENV'] that executes tasks...
  292. # (see the SCons documentation for more information on what it does)...
  293. # in order for this function to be well encapsulated i choose to force it to receive SCons's TOOLS env (env['ENV']
  294. # and not scons setup environment (env)... so make sure you call the right environment on it or it will fail to detect
  295. # the proper vc version that will be called
  296. # There is no flag to give to visual c compilers to set the architecture, ie scons bits argument (32,64,ARM etc)
  297. # There are many different cl.exe files that are run, and each one compiles & links to a different architecture
  298. # As far as I know, the only way to figure out what compiler will be run when Scons calls cl.exe via Program()
  299. # is to check the PATH variable and figure out which one will be called first. Code below does that and returns:
  300. # the following string values:
  301. # "" Compiler not detected
  302. # "amd64" Native 64 bit compiler
  303. # "amd64_x86" 64 bit Cross Compiler for 32 bit
  304. # "x86" Native 32 bit compiler
  305. # "x86_amd64" 32 bit Cross Compiler for 64 bit
  306. # There are other architectures, but Godot does not support them currently, so this function does not detect arm/amd64_arm
  307. # and similar architectures/compilers
  308. # Set chosen compiler to "not detected"
  309. vc_chosen_compiler_index = -1
  310. vc_chosen_compiler_str = ""
  311. # Start with Pre VS 2017 checks which uses VCINSTALLDIR:
  312. if "VCINSTALLDIR" in tools_env:
  313. # print("Checking VCINSTALLDIR")
  314. # find() works with -1 so big ifs below are needed... the simplest solution, in fact
  315. # First test if amd64 and amd64_x86 compilers are present in the path
  316. vc_amd64_compiler_detection_index = tools_env["PATH"].find(tools_env["VCINSTALLDIR"] + "BIN\\amd64;")
  317. if vc_amd64_compiler_detection_index > -1:
  318. vc_chosen_compiler_index = vc_amd64_compiler_detection_index
  319. vc_chosen_compiler_str = "amd64"
  320. vc_amd64_x86_compiler_detection_index = tools_env["PATH"].find(tools_env["VCINSTALLDIR"] + "BIN\\amd64_x86;")
  321. if vc_amd64_x86_compiler_detection_index > -1 and (
  322. vc_chosen_compiler_index == -1 or vc_chosen_compiler_index > vc_amd64_x86_compiler_detection_index
  323. ):
  324. vc_chosen_compiler_index = vc_amd64_x86_compiler_detection_index
  325. vc_chosen_compiler_str = "amd64_x86"
  326. # Now check the 32 bit compilers
  327. vc_x86_compiler_detection_index = tools_env["PATH"].find(tools_env["VCINSTALLDIR"] + "BIN;")
  328. if vc_x86_compiler_detection_index > -1 and (
  329. vc_chosen_compiler_index == -1 or vc_chosen_compiler_index > vc_x86_compiler_detection_index
  330. ):
  331. vc_chosen_compiler_index = vc_x86_compiler_detection_index
  332. vc_chosen_compiler_str = "x86"
  333. vc_x86_amd64_compiler_detection_index = tools_env["PATH"].find(tools_env["VCINSTALLDIR"] + "BIN\\x86_amd64;")
  334. if vc_x86_amd64_compiler_detection_index > -1 and (
  335. vc_chosen_compiler_index == -1 or vc_chosen_compiler_index > vc_x86_amd64_compiler_detection_index
  336. ):
  337. vc_chosen_compiler_index = vc_x86_amd64_compiler_detection_index
  338. vc_chosen_compiler_str = "x86_amd64"
  339. # and for VS 2017 and newer we check VCTOOLSINSTALLDIR:
  340. if "VCTOOLSINSTALLDIR" in tools_env:
  341. # Newer versions have a different path available
  342. vc_amd64_compiler_detection_index = (
  343. tools_env["PATH"].upper().find(tools_env["VCTOOLSINSTALLDIR"].upper() + "BIN\\HOSTX64\\X64;")
  344. )
  345. if vc_amd64_compiler_detection_index > -1:
  346. vc_chosen_compiler_index = vc_amd64_compiler_detection_index
  347. vc_chosen_compiler_str = "amd64"
  348. vc_amd64_x86_compiler_detection_index = (
  349. tools_env["PATH"].upper().find(tools_env["VCTOOLSINSTALLDIR"].upper() + "BIN\\HOSTX64\\X86;")
  350. )
  351. if vc_amd64_x86_compiler_detection_index > -1 and (
  352. vc_chosen_compiler_index == -1 or vc_chosen_compiler_index > vc_amd64_x86_compiler_detection_index
  353. ):
  354. vc_chosen_compiler_index = vc_amd64_x86_compiler_detection_index
  355. vc_chosen_compiler_str = "amd64_x86"
  356. vc_x86_compiler_detection_index = (
  357. tools_env["PATH"].upper().find(tools_env["VCTOOLSINSTALLDIR"].upper() + "BIN\\HOSTX86\\X86;")
  358. )
  359. if vc_x86_compiler_detection_index > -1 and (
  360. vc_chosen_compiler_index == -1 or vc_chosen_compiler_index > vc_x86_compiler_detection_index
  361. ):
  362. vc_chosen_compiler_index = vc_x86_compiler_detection_index
  363. vc_chosen_compiler_str = "x86"
  364. vc_x86_amd64_compiler_detection_index = (
  365. tools_env["PATH"].upper().find(tools_env["VCTOOLSINSTALLDIR"].upper() + "BIN\\HOSTX86\\X64;")
  366. )
  367. if vc_x86_amd64_compiler_detection_index > -1 and (
  368. vc_chosen_compiler_index == -1 or vc_chosen_compiler_index > vc_x86_amd64_compiler_detection_index
  369. ):
  370. vc_chosen_compiler_index = vc_x86_amd64_compiler_detection_index
  371. vc_chosen_compiler_str = "x86_amd64"
  372. return vc_chosen_compiler_str
  373. def find_visual_c_batch_file(env):
  374. from SCons.Tool.MSCommon.vc import get_default_version, get_host_target, find_batch_file
  375. version = get_default_version(env)
  376. (host_platform, target_platform, _) = get_host_target(env)
  377. return find_batch_file(env, version, host_platform, target_platform)[0]
  378. def generate_cpp_hint_file(filename):
  379. if os.path.isfile(filename):
  380. # Don't overwrite an existing hint file since the user may have customized it.
  381. pass
  382. else:
  383. try:
  384. with open(filename, "w") as fd:
  385. fd.write("#define GDCLASS(m_class, m_inherits)\n")
  386. except IOError:
  387. print("Could not write cpp.hint file.")
  388. def generate_vs_project(env, num_jobs):
  389. batch_file = find_visual_c_batch_file(env)
  390. if batch_file:
  391. def build_commandline(commands):
  392. common_build_prefix = [
  393. 'cmd /V /C set "plat=$(PlatformTarget)"',
  394. '(if "$(PlatformTarget)"=="x64" (set "plat=x86_amd64"))',
  395. 'set "tools=yes"',
  396. '(if "$(Configuration)"=="release" (set "tools=no"))',
  397. 'call "' + batch_file + '" !plat!',
  398. ]
  399. result = " ^& ".join(common_build_prefix + [commands])
  400. return result
  401. env.AddToVSProject(env.core_sources)
  402. env.AddToVSProject(env.main_sources)
  403. env.AddToVSProject(env.modules_sources)
  404. env.AddToVSProject(env.scene_sources)
  405. env.AddToVSProject(env.servers_sources)
  406. env.AddToVSProject(env.editor_sources)
  407. # windows allows us to have spaces in paths, so we need
  408. # to double quote off the directory. However, the path ends
  409. # in a backslash, so we need to remove this, lest it escape the
  410. # last double quote off, confusing MSBuild
  411. env["MSVSBUILDCOM"] = build_commandline(
  412. "scons --directory=\"$(ProjectDir.TrimEnd('\\'))\" platform=windows progress=no target=$(Configuration) tools=!tools! -j"
  413. + str(num_jobs)
  414. )
  415. env["MSVSREBUILDCOM"] = build_commandline(
  416. "scons --directory=\"$(ProjectDir.TrimEnd('\\'))\" platform=windows progress=no target=$(Configuration) tools=!tools! vsproj=yes -j"
  417. + str(num_jobs)
  418. )
  419. env["MSVSCLEANCOM"] = build_commandline(
  420. "scons --directory=\"$(ProjectDir.TrimEnd('\\'))\" --clean platform=windows progress=no target=$(Configuration) tools=!tools! -j"
  421. + str(num_jobs)
  422. )
  423. # This version information (Win32, x64, Debug, Release, Release_Debug seems to be
  424. # required for Visual Studio to understand that it needs to generate an NMAKE
  425. # project. Do not modify without knowing what you are doing.
  426. debug_variants = ["debug|Win32"] + ["debug|x64"]
  427. release_variants = ["release|Win32"] + ["release|x64"]
  428. release_debug_variants = ["release_debug|Win32"] + ["release_debug|x64"]
  429. variants = debug_variants + release_variants + release_debug_variants
  430. debug_targets = ["bin\\godot.windows.tools.32.exe"] + ["bin\\godot.windows.tools.64.exe"]
  431. release_targets = ["bin\\godot.windows.opt.32.exe"] + ["bin\\godot.windows.opt.64.exe"]
  432. release_debug_targets = ["bin\\godot.windows.opt.tools.32.exe"] + ["bin\\godot.windows.opt.tools.64.exe"]
  433. targets = debug_targets + release_targets + release_debug_targets
  434. if not env.get("MSVS"):
  435. env["MSVS"]["PROJECTSUFFIX"] = ".vcxproj"
  436. env["MSVS"]["SOLUTIONSUFFIX"] = ".sln"
  437. env.MSVSProject(
  438. target=["#godot" + env["MSVSPROJECTSUFFIX"]],
  439. incs=env.vs_incs,
  440. srcs=env.vs_srcs,
  441. runfile=targets,
  442. buildtarget=targets,
  443. auto_build_solution=1,
  444. variant=variants,
  445. )
  446. else:
  447. print("Could not locate Visual Studio batch file to set up the build environment. Not generating VS project.")
  448. def precious_program(env, program, sources, **args):
  449. program = env.ProgramOriginal(program, sources, **args)
  450. env.Precious(program)
  451. return program
  452. def add_shared_library(env, name, sources, **args):
  453. library = env.SharedLibrary(name, sources, **args)
  454. env.NoCache(library)
  455. return library
  456. def add_library(env, name, sources, **args):
  457. library = env.Library(name, sources, **args)
  458. env.NoCache(library)
  459. return library
  460. def add_program(env, name, sources, **args):
  461. program = env.Program(name, sources, **args)
  462. env.NoCache(program)
  463. return program
  464. def CommandNoCache(env, target, sources, command, **args):
  465. result = env.Command(target, sources, command, **args)
  466. env.NoCache(result)
  467. return result
  468. def detect_darwin_sdk_path(platform, env):
  469. sdk_name = ""
  470. if platform == "osx":
  471. sdk_name = "macosx"
  472. var_name = "MACOS_SDK_PATH"
  473. elif platform == "iphone":
  474. sdk_name = "iphoneos"
  475. var_name = "IPHONESDK"
  476. elif platform == "iphonesimulator":
  477. sdk_name = "iphonesimulator"
  478. var_name = "IPHONESDK"
  479. else:
  480. raise Exception("Invalid platform argument passed to detect_darwin_sdk_path")
  481. if not env[var_name]:
  482. try:
  483. sdk_path = subprocess.check_output(["xcrun", "--sdk", sdk_name, "--show-sdk-path"]).strip().decode("utf-8")
  484. if sdk_path:
  485. env[var_name] = sdk_path
  486. except (subprocess.CalledProcessError, OSError):
  487. print("Failed to find SDK path while running xcrun --sdk {} --show-sdk-path.".format(sdk_name))
  488. raise
  489. def is_vanilla_clang(env):
  490. if not using_clang(env):
  491. return False
  492. version = subprocess.check_output([env["CXX"], "--version"]).strip().decode("utf-8")
  493. return not version.startswith("Apple")
  494. def get_compiler_version(env):
  495. """
  496. Returns an array of version numbers as ints: [major, minor, patch].
  497. The return array should have at least two values (major, minor).
  498. """
  499. if not env.msvc:
  500. # Not using -dumpversion as some GCC distros only return major, and
  501. # Clang used to return hardcoded 4.2.1: # https://reviews.llvm.org/D56803
  502. try:
  503. version = subprocess.check_output([env.subst(env["CXX"]), "--version"]).strip().decode("utf-8")
  504. except (subprocess.CalledProcessError, OSError):
  505. print("Couldn't parse CXX environment variable to infer compiler version.")
  506. return None
  507. else: # TODO: Implement for MSVC
  508. return None
  509. match = re.search("[0-9]+\.[0-9.]+", version)
  510. if match is not None:
  511. return list(map(int, match.group().split(".")))
  512. else:
  513. return None
  514. def using_gcc(env):
  515. return "gcc" in os.path.basename(env["CC"])
  516. def using_clang(env):
  517. return "clang" in os.path.basename(env["CC"])