detect.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859
  1. import os
  2. import re
  3. import subprocess
  4. import sys
  5. from typing import TYPE_CHECKING
  6. import methods
  7. from methods import print_error, print_warning
  8. from platform_methods import detect_arch
  9. if TYPE_CHECKING:
  10. from SCons.Script.SConscript import SConsEnvironment
  11. # To match other platforms
  12. STACK_SIZE = 8388608
  13. def get_name():
  14. return "Windows"
  15. def try_cmd(test, prefix, arch):
  16. if arch:
  17. try:
  18. out = subprocess.Popen(
  19. get_mingw_bin_prefix(prefix, arch) + test,
  20. shell=True,
  21. stderr=subprocess.PIPE,
  22. stdout=subprocess.PIPE,
  23. )
  24. out.communicate()
  25. if out.returncode == 0:
  26. return True
  27. except Exception:
  28. pass
  29. else:
  30. for a in ["x86_64", "x86_32", "arm64", "arm32"]:
  31. try:
  32. out = subprocess.Popen(
  33. get_mingw_bin_prefix(prefix, a) + test,
  34. shell=True,
  35. stderr=subprocess.PIPE,
  36. stdout=subprocess.PIPE,
  37. )
  38. out.communicate()
  39. if out.returncode == 0:
  40. return True
  41. except Exception:
  42. pass
  43. return False
  44. def can_build():
  45. if os.name == "nt":
  46. # Building natively on Windows
  47. # If VCINSTALLDIR is set in the OS environ, use traditional Godot logic to set up MSVC
  48. if os.getenv("VCINSTALLDIR"): # MSVC, manual setup
  49. return True
  50. # Otherwise, let SCons find MSVC if installed, or else MinGW.
  51. # Since we're just returning True here, if there's no compiler
  52. # installed, we'll get errors when it tries to build with the
  53. # null compiler.
  54. return True
  55. if os.name == "posix":
  56. # Cross-compiling with MinGW-w64 (old MinGW32 is not supported)
  57. prefix = os.getenv("MINGW_PREFIX", "")
  58. if try_cmd("gcc --version", prefix, "") or try_cmd("clang --version", prefix, ""):
  59. return True
  60. return False
  61. def get_mingw_bin_prefix(prefix, arch):
  62. if not prefix:
  63. mingw_bin_prefix = ""
  64. elif prefix[-1] != "/":
  65. mingw_bin_prefix = prefix + "/bin/"
  66. else:
  67. mingw_bin_prefix = prefix + "bin/"
  68. if arch == "x86_64":
  69. mingw_bin_prefix += "x86_64-w64-mingw32-"
  70. elif arch == "x86_32":
  71. mingw_bin_prefix += "i686-w64-mingw32-"
  72. elif arch == "arm32":
  73. mingw_bin_prefix += "armv7-w64-mingw32-"
  74. elif arch == "arm64":
  75. mingw_bin_prefix += "aarch64-w64-mingw32-"
  76. return mingw_bin_prefix
  77. def detect_build_env_arch():
  78. msvc_target_aliases = {
  79. "amd64": "x86_64",
  80. "i386": "x86_32",
  81. "i486": "x86_32",
  82. "i586": "x86_32",
  83. "i686": "x86_32",
  84. "x86": "x86_32",
  85. "x64": "x86_64",
  86. "x86_64": "x86_64",
  87. "arm": "arm32",
  88. "arm64": "arm64",
  89. "aarch64": "arm64",
  90. }
  91. if os.getenv("VCINSTALLDIR") or os.getenv("VCTOOLSINSTALLDIR"):
  92. if os.getenv("Platform"):
  93. msvc_arch = os.getenv("Platform").lower()
  94. if msvc_arch in msvc_target_aliases.keys():
  95. return msvc_target_aliases[msvc_arch]
  96. if os.getenv("VSCMD_ARG_TGT_ARCH"):
  97. msvc_arch = os.getenv("VSCMD_ARG_TGT_ARCH").lower()
  98. if msvc_arch in msvc_target_aliases.keys():
  99. return msvc_target_aliases[msvc_arch]
  100. # Pre VS 2017 checks.
  101. if os.getenv("VCINSTALLDIR"):
  102. PATH = os.getenv("PATH").upper()
  103. VCINSTALLDIR = os.getenv("VCINSTALLDIR").upper()
  104. path_arch = {
  105. "BIN\\x86_ARM;": "arm32",
  106. "BIN\\amd64_ARM;": "arm32",
  107. "BIN\\x86_ARM64;": "arm64",
  108. "BIN\\amd64_ARM64;": "arm64",
  109. "BIN\\x86_amd64;": "a86_64",
  110. "BIN\\amd64;": "x86_64",
  111. "BIN\\amd64_x86;": "x86_32",
  112. "BIN;": "x86_32",
  113. }
  114. for path, arch in path_arch.items():
  115. final_path = VCINSTALLDIR + path
  116. if final_path in PATH:
  117. return arch
  118. # VS 2017 and newer.
  119. if os.getenv("VCTOOLSINSTALLDIR"):
  120. host_path_index = os.getenv("PATH").upper().find(os.getenv("VCTOOLSINSTALLDIR").upper() + "BIN\\HOST")
  121. if host_path_index > -1:
  122. first_path_arch = os.getenv("PATH")[host_path_index:].split(";")[0].rsplit("\\", 1)[-1].lower()
  123. if first_path_arch in msvc_target_aliases.keys():
  124. return msvc_target_aliases[first_path_arch]
  125. msys_target_aliases = {
  126. "mingw32": "x86_32",
  127. "mingw64": "x86_64",
  128. "ucrt64": "x86_64",
  129. "clang64": "x86_64",
  130. "clang32": "x86_32",
  131. "clangarm64": "arm64",
  132. }
  133. if os.getenv("MSYSTEM"):
  134. msys_arch = os.getenv("MSYSTEM").lower()
  135. if msys_arch in msys_target_aliases.keys():
  136. return msys_target_aliases[msys_arch]
  137. return ""
  138. def get_opts():
  139. from SCons.Variables import BoolVariable, EnumVariable
  140. mingw = os.getenv("MINGW_PREFIX", "")
  141. # Direct3D 12 SDK dependencies folder.
  142. d3d12_deps_folder = os.getenv("LOCALAPPDATA")
  143. if d3d12_deps_folder:
  144. d3d12_deps_folder = os.path.join(d3d12_deps_folder, "Godot", "build_deps")
  145. else:
  146. # Cross-compiling, the deps install script puts things in `bin`.
  147. # Getting an absolute path to it is a bit hacky in Python.
  148. try:
  149. import inspect
  150. caller_frame = inspect.stack()[1]
  151. caller_script_dir = os.path.dirname(os.path.abspath(caller_frame[1]))
  152. d3d12_deps_folder = os.path.join(caller_script_dir, "bin", "build_deps")
  153. except Exception: # Give up.
  154. d3d12_deps_folder = ""
  155. return [
  156. ("mingw_prefix", "MinGW prefix", mingw),
  157. # Targeted Windows version: 7 (and later), minimum supported version
  158. # XP support dropped after EOL due to missing API for IPv6 and other issues
  159. # Vista support dropped after EOL due to GH-10243
  160. (
  161. "target_win_version",
  162. "Targeted Windows version, >= 0x0601 (Windows 7)",
  163. "0x0601",
  164. ),
  165. EnumVariable("windows_subsystem", "Windows subsystem", "gui", ("gui", "console")),
  166. (
  167. "msvc_version",
  168. "MSVC version to use. Ignored if VCINSTALLDIR is set in shell env.",
  169. None,
  170. ),
  171. BoolVariable("use_mingw", "Use the Mingw compiler, even if MSVC is installed.", False),
  172. BoolVariable("use_llvm", "Use the LLVM compiler", False),
  173. BoolVariable("use_static_cpp", "Link MinGW/MSVC C++ runtime libraries statically", True),
  174. BoolVariable("use_asan", "Use address sanitizer (ASAN)", False),
  175. BoolVariable("debug_crt", "Compile with MSVC's debug CRT (/MDd)", False),
  176. BoolVariable("incremental_link", "Use MSVC incremental linking. May increase or decrease build times.", False),
  177. BoolVariable("silence_msvc", "Silence MSVC's cl/link stdout bloat, redirecting any errors to stderr.", True),
  178. ("angle_libs", "Path to the ANGLE static libraries", ""),
  179. # Direct3D 12 support.
  180. (
  181. "mesa_libs",
  182. "Path to the MESA/NIR static libraries (required for D3D12)",
  183. os.path.join(d3d12_deps_folder, "mesa"),
  184. ),
  185. (
  186. "dxc_path",
  187. "Path to the DirectX Shader Compiler distribution (required for D3D12)",
  188. os.path.join(d3d12_deps_folder, "dxc"),
  189. ),
  190. (
  191. "agility_sdk_path",
  192. "Path to the Agility SDK distribution (optional for D3D12)",
  193. os.path.join(d3d12_deps_folder, "agility_sdk"),
  194. ),
  195. BoolVariable(
  196. "agility_sdk_multiarch",
  197. "Whether the Agility SDK DLLs will be stored in arch-specific subdirectories",
  198. False,
  199. ),
  200. BoolVariable("use_pix", "Use PIX (Performance tuning and debugging for DirectX 12) runtime", False),
  201. (
  202. "pix_path",
  203. "Path to the PIX runtime distribution (optional for D3D12)",
  204. os.path.join(d3d12_deps_folder, "pix"),
  205. ),
  206. ]
  207. def get_doc_classes():
  208. return [
  209. "EditorExportPlatformWindows",
  210. ]
  211. def get_doc_path():
  212. return "doc_classes"
  213. def get_flags():
  214. arch = detect_build_env_arch() or detect_arch()
  215. return {
  216. "arch": arch,
  217. "supported": ["mono"],
  218. }
  219. def build_res_file(target, source, env: "SConsEnvironment"):
  220. arch_aliases = {
  221. "x86_32": "pe-i386",
  222. "x86_64": "pe-x86-64",
  223. "arm32": "armv7-w64-mingw32",
  224. "arm64": "aarch64-w64-mingw32",
  225. }
  226. cmdbase = "windres --include-dir . --target=" + arch_aliases[env["arch"]]
  227. mingw_bin_prefix = get_mingw_bin_prefix(env["mingw_prefix"], env["arch"])
  228. for x in range(len(source)):
  229. ok = True
  230. # Try prefixed executable (MinGW on Linux).
  231. cmd = mingw_bin_prefix + cmdbase + " -i " + str(source[x]) + " -o " + str(target[x])
  232. try:
  233. out = subprocess.Popen(cmd, shell=True, stderr=subprocess.PIPE).communicate()
  234. if len(out[1]):
  235. ok = False
  236. except Exception:
  237. ok = False
  238. # Try generic executable (MSYS2).
  239. if not ok:
  240. cmd = cmdbase + " -i " + str(source[x]) + " -o " + str(target[x])
  241. try:
  242. out = subprocess.Popen(cmd, shell=True, stderr=subprocess.PIPE).communicate()
  243. if len(out[1]):
  244. return -1
  245. except Exception:
  246. return -1
  247. return 0
  248. def setup_msvc_manual(env: "SConsEnvironment"):
  249. """Running from VCVARS environment"""
  250. env_arch = detect_build_env_arch()
  251. if env["arch"] != env_arch:
  252. print_error(
  253. "Arch argument (%s) is not matching Native/Cross Compile Tools Prompt/Developer Console (or Visual Studio settings) that is being used to run SCons (%s).\n"
  254. "Run SCons again without arch argument (example: scons p=windows) and SCons will attempt to detect what MSVC compiler will be executed and inform you."
  255. % (env["arch"], env_arch)
  256. )
  257. sys.exit(255)
  258. print("Using VCVARS-determined MSVC, arch %s" % (env_arch))
  259. # FIXME: Likely overwrites command-line options for the msvc compiler. See #91883.
  260. def setup_msvc_auto(env: "SConsEnvironment"):
  261. """Set up MSVC using SCons's auto-detection logic"""
  262. # If MSVC_VERSION is set by SCons, we know MSVC is installed.
  263. # But we may want a different version or target arch.
  264. # Valid architectures for MSVC's TARGET_ARCH:
  265. # ['amd64', 'emt64', 'i386', 'i486', 'i586', 'i686', 'ia64', 'itanium', 'x86', 'x86_64', 'arm', 'arm64', 'aarch64']
  266. # Our x86_64 and arm64 are the same, and we need to map the 32-bit
  267. # architectures to other names since MSVC isn't as explicit.
  268. # The rest we don't need to worry about because they are
  269. # aliases or aren't supported by Godot (itanium & ia64).
  270. msvc_arch_aliases = {"x86_32": "x86", "arm32": "arm"}
  271. if env["arch"] in msvc_arch_aliases.keys():
  272. env["TARGET_ARCH"] = msvc_arch_aliases[env["arch"]]
  273. else:
  274. env["TARGET_ARCH"] = env["arch"]
  275. # The env may have already been set up with default MSVC tools, so
  276. # reset a few things so we can set it up with the tools we want.
  277. # (Ideally we'd decide on the tool config before configuring any
  278. # environment, and just set the env up once, but this function runs
  279. # on an existing env so this is the simplest way.)
  280. env["MSVC_SETUP_RUN"] = False # Need to set this to re-run the tool
  281. env["MSVS_VERSION"] = None
  282. env["MSVC_VERSION"] = None
  283. if "msvc_version" in env:
  284. env["MSVC_VERSION"] = env["msvc_version"]
  285. env.Tool("msvc")
  286. env.Tool("mssdk") # we want the MS SDK
  287. # Note: actual compiler version can be found in env['MSVC_VERSION'], e.g. "14.1" for VS2015
  288. print("Using SCons-detected MSVC version %s, arch %s" % (env["MSVC_VERSION"], env["arch"]))
  289. def setup_mingw(env: "SConsEnvironment"):
  290. """Set up env for use with mingw"""
  291. env_arch = detect_build_env_arch()
  292. if os.getenv("MSYSTEM") == "MSYS":
  293. print_error(
  294. "Running from base MSYS2 console/environment, use target specific environment instead (e.g., mingw32, mingw64, clang32, clang64)."
  295. )
  296. sys.exit(255)
  297. if env_arch != "" and env["arch"] != env_arch:
  298. print_error(
  299. "Arch argument (%s) is not matching MSYS2 console/environment that is being used to run SCons (%s).\n"
  300. "Run SCons again without arch argument (example: scons p=windows) and SCons will attempt to detect what MSYS2 compiler will be executed and inform you."
  301. % (env["arch"], env_arch)
  302. )
  303. sys.exit(255)
  304. if not try_cmd("gcc --version", env["mingw_prefix"], env["arch"]) and not try_cmd(
  305. "clang --version", env["mingw_prefix"], env["arch"]
  306. ):
  307. print_error("No valid compilers found, use MINGW_PREFIX environment variable to set MinGW path.")
  308. sys.exit(255)
  309. print("Using MinGW, arch %s" % (env["arch"]))
  310. def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config):
  311. """Configure env to work with MSVC"""
  312. ## Build type
  313. # TODO: Re-evaluate the need for this / streamline with common config.
  314. if env["target"] == "template_release":
  315. env.Append(LINKFLAGS=["/ENTRY:mainCRTStartup"])
  316. if env["windows_subsystem"] == "gui":
  317. env.Append(LINKFLAGS=["/SUBSYSTEM:WINDOWS"])
  318. else:
  319. env.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"])
  320. env.AppendUnique(CPPDEFINES=["WINDOWS_SUBSYSTEM_CONSOLE"])
  321. ## Compile/link flags
  322. env["MAXLINELENGTH"] = 8192 # Windows Vista and beyond, so always applicable.
  323. if env["silence_msvc"] and not env.GetOption("clean"):
  324. from tempfile import mkstemp
  325. # Ensure we have a location to write captured output to, in case of false positives.
  326. capture_path = methods.base_folder_path + "platform/windows/msvc_capture.log"
  327. with open(capture_path, "wt", encoding="utf-8"):
  328. pass
  329. old_spawn = env["SPAWN"]
  330. re_redirect_stream = re.compile(r"^[12]?>")
  331. re_cl_capture = re.compile(r"^.+\.(c|cc|cpp|cxx|c[+]{2})$", re.IGNORECASE)
  332. re_link_capture = re.compile(r'\s{3}\S.+\s(?:"[^"]+.lib"|\S+.lib)\s.+\s(?:"[^"]+.exp"|\S+.exp)')
  333. def spawn_capture(sh, escape, cmd, args, env):
  334. # We only care about cl/link, process everything else as normal.
  335. if args[0] not in ["cl", "link"]:
  336. return old_spawn(sh, escape, cmd, args, env)
  337. # Process as normal if the user is manually rerouting output.
  338. for arg in args:
  339. if re_redirect_stream.match(arg):
  340. return old_spawn(sh, escape, cmd, args, env)
  341. tmp_stdout, tmp_stdout_name = mkstemp()
  342. os.close(tmp_stdout)
  343. args.append(f">{tmp_stdout_name}")
  344. ret = old_spawn(sh, escape, cmd, args, env)
  345. try:
  346. with open(tmp_stdout_name, "r", encoding=sys.stdout.encoding, errors="replace") as tmp_stdout:
  347. lines = tmp_stdout.read().splitlines()
  348. os.remove(tmp_stdout_name)
  349. except OSError:
  350. pass
  351. # Early process no lines (OSError)
  352. if not lines:
  353. return ret
  354. is_cl = args[0] == "cl"
  355. content = ""
  356. caught = False
  357. for line in lines:
  358. # These conditions are far from all-encompassing, but are specialized
  359. # for what can be reasonably expected to show up in the repository.
  360. if not caught and (is_cl and re_cl_capture.match(line)) or (not is_cl and re_link_capture.match(line)):
  361. caught = True
  362. try:
  363. with open(capture_path, "a", encoding=sys.stdout.encoding) as log:
  364. log.write(line + "\n")
  365. except OSError:
  366. print_warning(f'Failed to log captured line: "{line}".')
  367. continue
  368. content += line + "\n"
  369. # Content remaining assumed to be an error/warning.
  370. if content:
  371. sys.stderr.write(content)
  372. return ret
  373. env["SPAWN"] = spawn_capture
  374. if env["debug_crt"]:
  375. # Always use dynamic runtime, static debug CRT breaks thread_local.
  376. env.AppendUnique(CCFLAGS=["/MDd"])
  377. else:
  378. if env["use_static_cpp"]:
  379. env.AppendUnique(CCFLAGS=["/MT"])
  380. else:
  381. env.AppendUnique(CCFLAGS=["/MD"])
  382. # MSVC incremental linking is broken and may _increase_ link time (GH-77968).
  383. if not env["incremental_link"]:
  384. env.Append(LINKFLAGS=["/INCREMENTAL:NO"])
  385. if env["arch"] == "x86_32":
  386. env["x86_libtheora_opt_vc"] = True
  387. env.AppendUnique(CCFLAGS=["/Gd", "/GR", "/nologo"])
  388. env.AppendUnique(CCFLAGS=["/utf-8"]) # Force to use Unicode encoding.
  389. env.AppendUnique(CXXFLAGS=["/TP"]) # assume all sources are C++
  390. # Once it was thought that only debug builds would be too large,
  391. # but this has recently stopped being true. See the mingw function
  392. # for notes on why this shouldn't be enabled for gcc
  393. env.AppendUnique(CCFLAGS=["/bigobj"])
  394. if vcvars_msvc_config: # should be automatic if SCons found it
  395. if os.getenv("WindowsSdkDir") is not None:
  396. env.Prepend(CPPPATH=[str(os.getenv("WindowsSdkDir")) + "/Include"])
  397. else:
  398. print_warning("Missing environment variable: WindowsSdkDir")
  399. if int(env["target_win_version"], 16) < 0x0601:
  400. print_error("`target_win_version` should be 0x0601 or higher (Windows 7).")
  401. sys.exit(255)
  402. env.AppendUnique(
  403. CPPDEFINES=[
  404. "WINDOWS_ENABLED",
  405. "WASAPI_ENABLED",
  406. "WINMIDI_ENABLED",
  407. "TYPED_METHOD_BIND",
  408. "WIN32",
  409. "WINVER=%s" % env["target_win_version"],
  410. "_WIN32_WINNT=%s" % env["target_win_version"],
  411. ]
  412. )
  413. env.AppendUnique(CPPDEFINES=["NOMINMAX"]) # disable bogus min/max WinDef.h macros
  414. if env["arch"] == "x86_64":
  415. env.AppendUnique(CPPDEFINES=["_WIN64"])
  416. # Sanitizers
  417. prebuilt_lib_extra_suffix = ""
  418. if env["use_asan"]:
  419. env.extra_suffix += ".san"
  420. prebuilt_lib_extra_suffix = ".san"
  421. env.Append(CCFLAGS=["/fsanitize=address"])
  422. env.Append(LINKFLAGS=["/INFERASANLIBS"])
  423. ## Libs
  424. LIBS = [
  425. "winmm",
  426. "dsound",
  427. "kernel32",
  428. "ole32",
  429. "oleaut32",
  430. "sapi",
  431. "user32",
  432. "gdi32",
  433. "IPHLPAPI",
  434. "Shlwapi",
  435. "wsock32",
  436. "Ws2_32",
  437. "shell32",
  438. "advapi32",
  439. "dinput8",
  440. "dxguid",
  441. "imm32",
  442. "bcrypt",
  443. "Crypt32",
  444. "Avrt",
  445. "dwmapi",
  446. "dwrite",
  447. "wbemuuid",
  448. "ntdll",
  449. ]
  450. if env.debug_features:
  451. LIBS += ["psapi", "dbghelp"]
  452. if env["vulkan"]:
  453. env.AppendUnique(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
  454. if not env["use_volk"]:
  455. LIBS += ["vulkan"]
  456. if env["d3d12"]:
  457. # Check whether we have d3d12 dependencies installed.
  458. if not os.path.exists(env["mesa_libs"]):
  459. print_error(
  460. "The Direct3D 12 rendering driver requires dependencies to be installed.\n"
  461. "You can install them by running `python misc\\scripts\\install_d3d12_sdk_windows.py`.\n"
  462. "See the documentation for more information:\n\t"
  463. "https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_windows.html"
  464. )
  465. sys.exit(255)
  466. env.AppendUnique(CPPDEFINES=["D3D12_ENABLED", "RD_ENABLED"])
  467. LIBS += ["dxgi", "dxguid"]
  468. LIBS += ["version"] # Mesa dependency.
  469. # Needed for avoiding C1128.
  470. if env["target"] == "release_debug":
  471. env.Append(CXXFLAGS=["/bigobj"])
  472. # PIX
  473. if env["arch"] not in ["x86_64", "arm64"] or env["pix_path"] == "" or not os.path.exists(env["pix_path"]):
  474. env["use_pix"] = False
  475. if env["use_pix"]:
  476. arch_subdir = "arm64" if env["arch"] == "arm64" else "x64"
  477. env.Append(LIBPATH=[env["pix_path"] + "/bin/" + arch_subdir])
  478. LIBS += ["WinPixEventRuntime"]
  479. env.Append(LIBPATH=[env["mesa_libs"] + "/bin"])
  480. LIBS += ["libNIR.windows." + env["arch"] + prebuilt_lib_extra_suffix]
  481. if env["opengl3"]:
  482. env.AppendUnique(CPPDEFINES=["GLES3_ENABLED"])
  483. if env["angle_libs"] != "":
  484. env.AppendUnique(CPPDEFINES=["EGL_STATIC"])
  485. env.Append(LIBPATH=[env["angle_libs"]])
  486. LIBS += [
  487. "libANGLE.windows." + env["arch"] + prebuilt_lib_extra_suffix,
  488. "libEGL.windows." + env["arch"] + prebuilt_lib_extra_suffix,
  489. "libGLES.windows." + env["arch"] + prebuilt_lib_extra_suffix,
  490. ]
  491. LIBS += ["dxgi", "d3d9", "d3d11"]
  492. env.Prepend(CPPPATH=["#thirdparty/angle/include"])
  493. if env["target"] in ["editor", "template_debug"]:
  494. LIBS += ["psapi", "dbghelp"]
  495. env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS])
  496. if vcvars_msvc_config:
  497. if os.getenv("WindowsSdkDir") is not None:
  498. env.Append(LIBPATH=[str(os.getenv("WindowsSdkDir")) + "/Lib"])
  499. else:
  500. print_warning("Missing environment variable: WindowsSdkDir")
  501. ## LTO
  502. if env["lto"] == "auto": # No LTO by default for MSVC, doesn't help.
  503. env["lto"] = "none"
  504. if env["lto"] != "none":
  505. if env["lto"] == "thin":
  506. print_error("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
  507. sys.exit(255)
  508. env.AppendUnique(CCFLAGS=["/GL"])
  509. env.AppendUnique(ARFLAGS=["/LTCG"])
  510. if env["progress"]:
  511. env.AppendUnique(LINKFLAGS=["/LTCG:STATUS"])
  512. else:
  513. env.AppendUnique(LINKFLAGS=["/LTCG"])
  514. if vcvars_msvc_config:
  515. env.Prepend(CPPPATH=[p for p in str(os.getenv("INCLUDE")).split(";")])
  516. env.Append(LIBPATH=[p for p in str(os.getenv("LIB")).split(";")])
  517. # Incremental linking fix
  518. env["BUILDERS"]["ProgramOriginal"] = env["BUILDERS"]["Program"]
  519. env["BUILDERS"]["Program"] = methods.precious_program
  520. env.Append(LINKFLAGS=["/NATVIS:platform\\windows\\godot.natvis"])
  521. env.AppendUnique(LINKFLAGS=["/STACK:" + str(STACK_SIZE)])
  522. def configure_mingw(env: "SConsEnvironment"):
  523. # Workaround for MinGW. See:
  524. # https://www.scons.org/wiki/LongCmdLinesOnWin32
  525. env.use_windows_spawn_fix()
  526. ## Build type
  527. if not env["use_llvm"] and not try_cmd("gcc --version", env["mingw_prefix"], env["arch"]):
  528. env["use_llvm"] = True
  529. if env["use_llvm"] and not try_cmd("clang --version", env["mingw_prefix"], env["arch"]):
  530. env["use_llvm"] = False
  531. # TODO: Re-evaluate the need for this / streamline with common config.
  532. if env["target"] == "template_release":
  533. env.Append(CCFLAGS=["-msse2"])
  534. elif env.dev_build:
  535. # Allow big objects. It's supposed not to have drawbacks but seems to break
  536. # GCC LTO, so enabling for debug builds only (which are not built with LTO
  537. # and are the only ones with too big objects).
  538. env.Append(CCFLAGS=["-Wa,-mbig-obj"])
  539. if env["windows_subsystem"] == "gui":
  540. env.Append(LINKFLAGS=["-Wl,--subsystem,windows"])
  541. else:
  542. env.Append(LINKFLAGS=["-Wl,--subsystem,console"])
  543. env.AppendUnique(CPPDEFINES=["WINDOWS_SUBSYSTEM_CONSOLE"])
  544. ## Compiler configuration
  545. if os.name != "nt":
  546. env["PROGSUFFIX"] = env["PROGSUFFIX"] + ".exe" # for linux cross-compilation
  547. if env["arch"] == "x86_32":
  548. if env["use_static_cpp"]:
  549. env.Append(LINKFLAGS=["-static"])
  550. env.Append(LINKFLAGS=["-static-libgcc"])
  551. env.Append(LINKFLAGS=["-static-libstdc++"])
  552. else:
  553. if env["use_static_cpp"]:
  554. env.Append(LINKFLAGS=["-static"])
  555. if env["arch"] in ["x86_32", "x86_64"]:
  556. env["x86_libtheora_opt_gcc"] = True
  557. mingw_bin_prefix = get_mingw_bin_prefix(env["mingw_prefix"], env["arch"])
  558. if env["use_llvm"]:
  559. env["CC"] = mingw_bin_prefix + "clang"
  560. env["CXX"] = mingw_bin_prefix + "clang++"
  561. if try_cmd("as --version", env["mingw_prefix"], env["arch"]):
  562. env["AS"] = mingw_bin_prefix + "as"
  563. env.Append(ASFLAGS=["-c"])
  564. if try_cmd("ar --version", env["mingw_prefix"], env["arch"]):
  565. env["AR"] = mingw_bin_prefix + "ar"
  566. if try_cmd("ranlib --version", env["mingw_prefix"], env["arch"]):
  567. env["RANLIB"] = mingw_bin_prefix + "ranlib"
  568. env.extra_suffix = ".llvm" + env.extra_suffix
  569. else:
  570. env["CC"] = mingw_bin_prefix + "gcc"
  571. env["CXX"] = mingw_bin_prefix + "g++"
  572. if try_cmd("as --version", env["mingw_prefix"], env["arch"]):
  573. env["AS"] = mingw_bin_prefix + "as"
  574. if try_cmd("gcc-ar --version", env["mingw_prefix"], env["arch"]):
  575. env["AR"] = mingw_bin_prefix + "gcc-ar"
  576. if try_cmd("gcc-ranlib --version", env["mingw_prefix"], env["arch"]):
  577. env["RANLIB"] = mingw_bin_prefix + "gcc-ranlib"
  578. ## LTO
  579. if env["lto"] == "auto": # Full LTO for production with MinGW.
  580. env["lto"] = "full"
  581. if env["lto"] != "none":
  582. if env["lto"] == "thin":
  583. if not env["use_llvm"]:
  584. print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
  585. sys.exit(255)
  586. env.Append(CCFLAGS=["-flto=thin"])
  587. env.Append(LINKFLAGS=["-flto=thin"])
  588. elif not env["use_llvm"] and env.GetOption("num_jobs") > 1:
  589. env.Append(CCFLAGS=["-flto"])
  590. env.Append(LINKFLAGS=["-flto=" + str(env.GetOption("num_jobs"))])
  591. else:
  592. env.Append(CCFLAGS=["-flto"])
  593. env.Append(LINKFLAGS=["-flto"])
  594. env.Append(LINKFLAGS=["-Wl,--stack," + str(STACK_SIZE)])
  595. ## Compile flags
  596. if int(env["target_win_version"], 16) < 0x0601:
  597. print_error("`target_win_version` should be 0x0601 or higher (Windows 7).")
  598. sys.exit(255)
  599. if not env["use_llvm"]:
  600. env.Append(CCFLAGS=["-mwindows"])
  601. env.Append(CPPDEFINES=["WINDOWS_ENABLED", "WASAPI_ENABLED", "WINMIDI_ENABLED"])
  602. env.Append(
  603. CPPDEFINES=[
  604. ("WINVER", env["target_win_version"]),
  605. ("_WIN32_WINNT", env["target_win_version"]),
  606. ]
  607. )
  608. env.Append(
  609. LIBS=[
  610. "mingw32",
  611. "dsound",
  612. "ole32",
  613. "d3d9",
  614. "winmm",
  615. "gdi32",
  616. "iphlpapi",
  617. "shlwapi",
  618. "wsock32",
  619. "ws2_32",
  620. "kernel32",
  621. "oleaut32",
  622. "sapi",
  623. "dinput8",
  624. "dxguid",
  625. "ksuser",
  626. "imm32",
  627. "bcrypt",
  628. "crypt32",
  629. "avrt",
  630. "uuid",
  631. "dwmapi",
  632. "dwrite",
  633. "wbemuuid",
  634. "ntdll",
  635. ]
  636. )
  637. if env.debug_features:
  638. env.Append(LIBS=["psapi", "dbghelp"])
  639. if env["vulkan"]:
  640. env.Append(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
  641. if not env["use_volk"]:
  642. env.Append(LIBS=["vulkan"])
  643. if env["d3d12"]:
  644. # Check whether we have d3d12 dependencies installed.
  645. if not os.path.exists(env["mesa_libs"]):
  646. print_error(
  647. "The Direct3D 12 rendering driver requires dependencies to be installed.\n"
  648. "You can install them by running `python misc\\scripts\\install_d3d12_sdk_windows.py`.\n"
  649. "See the documentation for more information:\n\t"
  650. "https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_windows.html"
  651. )
  652. sys.exit(255)
  653. env.AppendUnique(CPPDEFINES=["D3D12_ENABLED", "RD_ENABLED"])
  654. env.Append(LIBS=["dxgi", "dxguid"])
  655. # PIX
  656. if env["arch"] not in ["x86_64", "arm64"] or env["pix_path"] == "" or not os.path.exists(env["pix_path"]):
  657. env["use_pix"] = False
  658. if env["use_pix"]:
  659. arch_subdir = "arm64" if env["arch"] == "arm64" else "x64"
  660. env.Append(LIBPATH=[env["pix_path"] + "/bin/" + arch_subdir])
  661. env.Append(LIBS=["WinPixEventRuntime"])
  662. env.Append(LIBPATH=[env["mesa_libs"] + "/bin"])
  663. env.Append(LIBS=["libNIR.windows." + env["arch"]])
  664. env.Append(LIBS=["version"]) # Mesa dependency.
  665. if env["opengl3"]:
  666. env.Append(CPPDEFINES=["GLES3_ENABLED"])
  667. if env["angle_libs"] != "":
  668. env.AppendUnique(CPPDEFINES=["EGL_STATIC"])
  669. env.Append(LIBPATH=[env["angle_libs"]])
  670. env.Append(
  671. LIBS=[
  672. "EGL.windows." + env["arch"],
  673. "GLES.windows." + env["arch"],
  674. "ANGLE.windows." + env["arch"],
  675. ]
  676. )
  677. env.Append(LIBS=["dxgi", "d3d9", "d3d11"])
  678. env.Prepend(CPPPATH=["#thirdparty/angle/include"])
  679. env.Append(CPPDEFINES=["MINGW_ENABLED", ("MINGW_HAS_SECURE_API", 1)])
  680. # resrc
  681. env.Append(BUILDERS={"RES": env.Builder(action=build_res_file, suffix=".o", src_suffix=".rc")})
  682. def configure(env: "SConsEnvironment"):
  683. # Validate arch.
  684. supported_arches = ["x86_32", "x86_64", "arm32", "arm64"]
  685. if env["arch"] not in supported_arches:
  686. print_error(
  687. 'Unsupported CPU architecture "%s" for Windows. Supported architectures are: %s.'
  688. % (env["arch"], ", ".join(supported_arches))
  689. )
  690. sys.exit(255)
  691. # At this point the env has been set up with basic tools/compilers.
  692. env.Prepend(CPPPATH=["#platform/windows"])
  693. if os.name == "nt":
  694. env["ENV"] = os.environ # this makes build less repeatable, but simplifies some things
  695. env["ENV"]["TMP"] = os.environ["TMP"]
  696. # First figure out which compiler, version, and target arch we're using
  697. if os.getenv("VCINSTALLDIR") and detect_build_env_arch() and not env["use_mingw"]:
  698. setup_msvc_manual(env)
  699. env.msvc = True
  700. vcvars_msvc_config = True
  701. elif env.get("MSVC_VERSION", "") and not env["use_mingw"]:
  702. setup_msvc_auto(env)
  703. env.msvc = True
  704. vcvars_msvc_config = False
  705. else:
  706. setup_mingw(env)
  707. env.msvc = False
  708. # Now set compiler/linker flags
  709. if env.msvc:
  710. configure_msvc(env, vcvars_msvc_config)
  711. else: # MinGW
  712. configure_mingw(env)