Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/platform/windows/detect.py
21023 views
1
import os
2
import re
3
import subprocess
4
import sys
5
from typing import TYPE_CHECKING
6
7
import methods
8
from methods import print_error, print_warning
9
from platform_methods import detect_arch, validate_arch
10
11
if TYPE_CHECKING:
12
from SCons.Script.SConscript import SConsEnvironment
13
14
# To match other platforms
15
STACK_SIZE = 8388608
16
STACK_SIZE_SANITIZERS = 30 * 1024 * 1024
17
18
19
def get_name():
20
return "Windows"
21
22
23
def try_cmd(test, prefix, arch, check_clang=False):
24
archs = ["x86_64", "x86_32", "arm64", "arm32"]
25
if arch:
26
archs = [arch]
27
if os.name == "nt":
28
archs += [""]
29
30
for a in archs:
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
outs, errs = out.communicate()
39
if out.returncode == 0:
40
if check_clang and not outs.startswith(b"clang"):
41
return False
42
return True
43
except Exception:
44
pass
45
46
return False
47
48
49
def can_build():
50
if os.name == "nt":
51
# Building natively on Windows
52
return True
53
54
if os.name == "posix":
55
# Cross-compiling with MinGW-w64 (old MinGW32 is not supported)
56
prefix = os.getenv("MINGW_PREFIX", "")
57
58
if try_cmd("gcc --version", prefix, "") or try_cmd("clang --version", prefix, ""):
59
return True
60
61
return False
62
63
64
def get_mingw_bin_prefix(prefix, arch):
65
bin_prefix = (os.path.normpath(os.path.join(prefix, "bin")) + os.sep) if prefix else ""
66
ARCH_PREFIXES = {
67
"x86_64": "x86_64-w64-mingw32-",
68
"x86_32": "i686-w64-mingw32-",
69
"arm32": "armv7-w64-mingw32-",
70
"arm64": "aarch64-w64-mingw32-",
71
}
72
arch_prefix = ARCH_PREFIXES[arch] if arch else ""
73
return bin_prefix + arch_prefix
74
75
76
def get_detected(env: "SConsEnvironment", tool: str) -> str:
77
checks = [
78
get_mingw_bin_prefix(env["mingw_prefix"], env["arch"]) + tool,
79
get_mingw_bin_prefix(env["mingw_prefix"], "") + tool,
80
]
81
return str(env.Detect(checks))
82
83
84
def detect_build_env_arch():
85
msvc_target_aliases = {
86
"amd64": "x86_64",
87
"i386": "x86_32",
88
"i486": "x86_32",
89
"i586": "x86_32",
90
"i686": "x86_32",
91
"x86": "x86_32",
92
"x64": "x86_64",
93
"x86_64": "x86_64",
94
"arm": "arm32",
95
"arm64": "arm64",
96
"aarch64": "arm64",
97
}
98
if os.getenv("VCINSTALLDIR") or os.getenv("VCTOOLSINSTALLDIR"):
99
if os.getenv("Platform"):
100
msvc_arch = os.getenv("Platform").lower()
101
if msvc_arch in msvc_target_aliases.keys():
102
return msvc_target_aliases[msvc_arch]
103
104
if os.getenv("VSCMD_ARG_TGT_ARCH"):
105
msvc_arch = os.getenv("VSCMD_ARG_TGT_ARCH").lower()
106
if msvc_arch in msvc_target_aliases.keys():
107
return msvc_target_aliases[msvc_arch]
108
109
# Pre VS 2017 checks.
110
if os.getenv("VCINSTALLDIR"):
111
PATH = os.getenv("PATH").upper()
112
VCINSTALLDIR = os.getenv("VCINSTALLDIR").upper()
113
path_arch = {
114
"BIN\\x86_ARM;": "arm32",
115
"BIN\\amd64_ARM;": "arm32",
116
"BIN\\x86_ARM64;": "arm64",
117
"BIN\\amd64_ARM64;": "arm64",
118
"BIN\\x86_amd64;": "a86_64",
119
"BIN\\amd64;": "x86_64",
120
"BIN\\amd64_x86;": "x86_32",
121
"BIN;": "x86_32",
122
}
123
for path, arch in path_arch.items():
124
final_path = VCINSTALLDIR + path
125
if final_path in PATH:
126
return arch
127
128
# VS 2017 and newer.
129
if os.getenv("VCTOOLSINSTALLDIR"):
130
host_path_index = os.getenv("PATH").upper().find(os.getenv("VCTOOLSINSTALLDIR").upper() + "BIN\\HOST")
131
if host_path_index > -1:
132
first_path_arch = os.getenv("PATH")[host_path_index:].split(";")[0].rsplit("\\", 1)[-1].lower()
133
if first_path_arch in msvc_target_aliases.keys():
134
return msvc_target_aliases[first_path_arch]
135
136
msys_target_aliases = {
137
"mingw32": "x86_32",
138
"mingw64": "x86_64",
139
"ucrt64": "x86_64",
140
"clang64": "x86_64",
141
"clang32": "x86_32",
142
"clangarm64": "arm64",
143
}
144
if os.getenv("MSYSTEM"):
145
msys_arch = os.getenv("MSYSTEM").lower()
146
if msys_arch in msys_target_aliases.keys():
147
return msys_target_aliases[msys_arch]
148
149
return ""
150
151
152
def get_tools(env: "SConsEnvironment"):
153
from SCons.Tool.MSCommon import msvc_exists
154
155
if os.name != "nt" or env.get("use_mingw") or not msvc_exists():
156
return ["mingw"]
157
else:
158
msvc_arch_aliases = {"x86_32": "x86", "arm32": "arm"}
159
env["TARGET_ARCH"] = msvc_arch_aliases.get(env["arch"], env["arch"])
160
env["MSVC_VERSION"] = env["MSVS_VERSION"] = env.get("msvc_version")
161
return ["msvc", "mslink", "mslib"]
162
163
164
def get_opts():
165
from SCons.Variables import BoolVariable, EnumVariable
166
167
mingw = os.getenv("MINGW_PREFIX", "")
168
169
# Direct3D 12 SDK dependencies folder.
170
d3d12_deps_folder = os.getenv("LOCALAPPDATA")
171
if d3d12_deps_folder:
172
d3d12_deps_folder = os.path.join(d3d12_deps_folder, "Godot", "build_deps")
173
else:
174
# Cross-compiling, the deps install script puts things in `bin`.
175
# Getting an absolute path to it is a bit hacky in Python.
176
try:
177
import inspect
178
179
caller_frame = inspect.stack()[1]
180
caller_script_dir = os.path.dirname(os.path.abspath(caller_frame[1]))
181
d3d12_deps_folder = os.path.join(caller_script_dir, "bin", "build_deps")
182
except Exception: # Give up.
183
d3d12_deps_folder = ""
184
185
return [
186
("mingw_prefix", "MinGW prefix", mingw),
187
EnumVariable("windows_subsystem", "Windows subsystem", "gui", ["gui", "console"], ignorecase=2),
188
("msvc_version", "MSVC version to use. Handled automatically by SCons if omitted.", ""),
189
BoolVariable("use_mingw", "Use the Mingw compiler, even if MSVC is installed.", False),
190
BoolVariable("use_llvm", "Use the LLVM compiler", False),
191
BoolVariable("use_static_cpp", "Link MinGW/MSVC C++ runtime libraries statically", True),
192
BoolVariable("use_asan", "Use address sanitizer (ASAN)", False),
193
BoolVariable("use_ubsan", "Use LLVM compiler undefined behavior sanitizer (UBSAN)", False),
194
BoolVariable("debug_crt", "Compile with MSVC's debug CRT (/MDd)", False),
195
BoolVariable("incremental_link", "Use MSVC incremental linking. May increase or decrease build times.", False),
196
BoolVariable("silence_msvc", "Silence MSVC's cl/link stdout bloat, redirecting any errors to stderr.", True),
197
("angle_libs", "Path to the ANGLE static libraries", ""),
198
# Direct3D 12 support.
199
(
200
"mesa_libs",
201
"Path to the MESA/NIR static libraries (required for D3D12)",
202
os.path.join(d3d12_deps_folder, "mesa"),
203
),
204
(
205
"agility_sdk_path",
206
"Path to the Agility SDK distribution (optional for D3D12)",
207
os.path.join(d3d12_deps_folder, "agility_sdk"),
208
),
209
BoolVariable(
210
"agility_sdk_multiarch",
211
"Whether the Agility SDK DLLs will be stored in arch-specific subdirectories",
212
False,
213
),
214
BoolVariable("use_pix", "Use PIX (Performance tuning and debugging for DirectX 12) runtime", False),
215
(
216
"pix_path",
217
"Path to the PIX runtime distribution (optional for D3D12)",
218
os.path.join(d3d12_deps_folder, "pix"),
219
),
220
]
221
222
223
def get_doc_classes():
224
return [
225
"EditorExportPlatformWindows",
226
]
227
228
229
def get_doc_path():
230
return "doc_classes"
231
232
233
def get_flags():
234
arch = detect_build_env_arch() or detect_arch()
235
236
return {
237
"arch": arch,
238
"d3d12": True,
239
"supported": ["d3d12", "dcomp", "library", "mono", "xaudio2"],
240
}
241
242
243
def configure_msvc(env: "SConsEnvironment"):
244
"""Configure env to work with MSVC"""
245
246
## Build type
247
248
# TODO: Re-evaluate the need for this / streamline with common config.
249
if env["target"] == "template_release" and env["library_type"] == "executable":
250
env.Append(LINKFLAGS=["/ENTRY:mainCRTStartup"])
251
252
if env["windows_subsystem"] == "gui":
253
env.Append(LINKFLAGS=["/SUBSYSTEM:WINDOWS"])
254
else:
255
env.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"])
256
env.AppendUnique(CPPDEFINES=["WINDOWS_SUBSYSTEM_CONSOLE"])
257
258
## Compile/link flags
259
260
if env["use_llvm"]:
261
env["CC"] = "clang-cl"
262
env["CXX"] = "clang-cl"
263
env["LINK"] = "lld-link"
264
env["AR"] = "llvm-lib"
265
266
env.AppendUnique(CPPDEFINES=["R128_STDC_ONLY"])
267
env.extra_suffix = ".llvm" + env.extra_suffix
268
269
# Ensure intellisense tools like `compile_commands.json` play nice with MSVC syntax.
270
env["CPPDEFPREFIX"] = "-D"
271
env["INCPREFIX"] = "-I"
272
env.AppendUnique(CPPDEFINES=[("alloca", "_alloca")])
273
274
if env["silence_msvc"] and not env.GetOption("clean"):
275
from tempfile import mkstemp
276
277
# Ensure we have a location to write captured output to, in case of false positives.
278
capture_path = methods.base_folder / "platform" / "windows" / "msvc_capture.log"
279
with open(capture_path, "wt", encoding="utf-8"):
280
pass
281
282
old_spawn = env["SPAWN"]
283
re_redirect_stream = re.compile(r"^[12]?>")
284
re_cl_capture = re.compile(r"^.+\.(c|cc|cpp|cxx|c[+]{2})$", re.IGNORECASE)
285
re_link_capture = re.compile(r'\s{3}\S.+\s(?:"[^"]+.lib"|\S+.lib)\s.+\s(?:"[^"]+.exp"|\S+.exp)')
286
287
def spawn_capture(sh, escape, cmd, args, env):
288
# We only care about cl/link, process everything else as normal.
289
if args[0] not in ["cl", "link"]:
290
return old_spawn(sh, escape, cmd, args, env)
291
292
# Process as normal if the user is manually rerouting output.
293
for arg in args:
294
if re_redirect_stream.match(arg):
295
return old_spawn(sh, escape, cmd, args, env)
296
297
tmp_stdout, tmp_stdout_name = mkstemp()
298
os.close(tmp_stdout)
299
args.append(f">{tmp_stdout_name}")
300
ret = old_spawn(sh, escape, cmd, args, env)
301
302
try:
303
with open(tmp_stdout_name, "r", encoding=sys.stdout.encoding, errors="replace") as tmp_stdout:
304
lines = tmp_stdout.read().splitlines()
305
os.remove(tmp_stdout_name)
306
except OSError:
307
pass
308
309
# Early process no lines (OSError)
310
if not lines:
311
return ret
312
313
is_cl = args[0] == "cl"
314
content = ""
315
caught = False
316
for line in lines:
317
# These conditions are far from all-encompassing, but are specialized
318
# for what can be reasonably expected to show up in the repository.
319
if not caught and (is_cl and re_cl_capture.match(line)) or (not is_cl and re_link_capture.match(line)):
320
caught = True
321
try:
322
with open(capture_path, "a", encoding=sys.stdout.encoding, errors="replace") as log:
323
log.write(line + "\n")
324
except OSError:
325
print_warning(f'Failed to log captured line: "{line}".')
326
continue
327
content += line + "\n"
328
# Content remaining assumed to be an error/warning.
329
if content:
330
sys.stderr.write(content)
331
332
return ret
333
334
env["SPAWN"] = spawn_capture
335
336
if env["debug_crt"]:
337
# Always use dynamic runtime, static debug CRT breaks thread_local.
338
env.AppendUnique(CCFLAGS=["/MDd"])
339
else:
340
if env["use_static_cpp"]:
341
env.AppendUnique(CCFLAGS=["/MT"])
342
else:
343
env.AppendUnique(CCFLAGS=["/MD"])
344
345
# MSVC incremental linking is broken and may _increase_ link time (GH-77968).
346
if not env["incremental_link"]:
347
env.Append(LINKFLAGS=["/INCREMENTAL:NO"])
348
349
if env["arch"] == "x86_32":
350
env["x86_libtheora_opt_vc"] = True
351
352
env.Append(CCFLAGS=["/fp:strict"])
353
354
env.AppendUnique(CCFLAGS=["/Gd", "/GR", "/nologo"])
355
env.AppendUnique(CCFLAGS=["/utf-8"]) # Force to use Unicode encoding.
356
env.AppendUnique(CCFLAGS=["/bigobj"]) # Support big objects.
357
358
env.AppendUnique(
359
CPPDEFINES=[
360
"WINDOWS_ENABLED",
361
"WASAPI_ENABLED",
362
"WINMIDI_ENABLED",
363
"TYPED_METHOD_BIND",
364
"WIN32",
365
("WINVER", "0x0A00"),
366
("_WIN32_WINNT", "0x0A00"),
367
]
368
)
369
env.AppendUnique(CPPDEFINES=["NOMINMAX"]) # disable bogus min/max WinDef.h macros
370
if env["arch"] == "x86_64":
371
env.AppendUnique(CPPDEFINES=["_WIN64"])
372
373
# Sanitizers
374
prebuilt_lib_extra_suffix = ""
375
if env["use_asan"]:
376
env.extra_suffix += ".san"
377
prebuilt_lib_extra_suffix = ".san"
378
env.AppendUnique(CPPDEFINES=["SANITIZERS_ENABLED"])
379
env.Append(CCFLAGS=["/fsanitize=address"])
380
env.Append(LINKFLAGS=["/INFERASANLIBS"])
381
382
## Libs
383
384
LIBS = [
385
"winmm",
386
"dsound",
387
"kernel32",
388
"ole32",
389
"oleaut32",
390
"sapi",
391
"user32",
392
"gdi32",
393
"IPHLPAPI",
394
"Shlwapi",
395
"Shcore",
396
"wsock32",
397
"Ws2_32",
398
"shell32",
399
"advapi32",
400
"dinput8",
401
"dxguid",
402
"imm32",
403
"bcrypt",
404
"Crypt32",
405
"Avrt",
406
"dwmapi",
407
"dwrite",
408
"wbemuuid",
409
"ntdll",
410
"hid",
411
]
412
413
if env.debug_features:
414
LIBS += ["psapi", "dbghelp"]
415
416
if env["accesskit"]:
417
if env["accesskit_sdk_path"] != "":
418
env.Prepend(CPPPATH=[env["accesskit_sdk_path"] + "/include"])
419
if env["arch"] == "arm64":
420
env.Append(LIBPATH=[env["accesskit_sdk_path"] + "/lib/windows/arm64/msvc/static"])
421
elif env["arch"] == "x86_64":
422
env.Append(LIBPATH=[env["accesskit_sdk_path"] + "/lib/windows/x86_64/msvc/static"])
423
elif env["arch"] == "x86_32":
424
env.Append(LIBPATH=[env["accesskit_sdk_path"] + "/lib/windows/x86/msvc/static"])
425
LIBS += [
426
"accesskit",
427
"uiautomationcore",
428
"runtimeobject",
429
"propsys",
430
"oleaut32",
431
"user32",
432
"userenv",
433
"ntdll",
434
]
435
else:
436
env.Append(CPPDEFINES=["ACCESSKIT_DYNAMIC"])
437
env.Append(CPPDEFINES=["ACCESSKIT_ENABLED"])
438
439
if env["vulkan"]:
440
env.AppendUnique(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
441
if not env["use_volk"]:
442
LIBS += ["vulkan"]
443
444
if env["sdl"]:
445
env.Append(CPPDEFINES=["SDL_ENABLED"])
446
447
if env["d3d12"]:
448
check_d3d12_installed(env, env["arch"] + "-msvc")
449
450
env.AppendUnique(CPPDEFINES=["D3D12_ENABLED", "RD_ENABLED"])
451
LIBS += ["dxgi", "dxguid"]
452
LIBS += ["version"] # Mesa dependency.
453
454
# PIX
455
if env["arch"] not in ["x86_64", "arm64"] or env["pix_path"] == "" or not os.path.exists(env["pix_path"]):
456
env["use_pix"] = False
457
458
if env["use_pix"]:
459
arch_subdir = "arm64" if env["arch"] == "arm64" else "x64"
460
461
env.Append(LIBPATH=[env["pix_path"] + "/bin/" + arch_subdir])
462
LIBS += ["WinPixEventRuntime"]
463
464
if os.path.exists(env["mesa_libs"] + "-" + env["arch"] + "-msvc"):
465
env.Append(LIBPATH=[env["mesa_libs"] + "-" + env["arch"] + "-msvc/bin"])
466
else:
467
env.Append(LIBPATH=[env["mesa_libs"] + "/bin"])
468
LIBS += ["libNIR.windows." + env["arch"] + prebuilt_lib_extra_suffix]
469
470
if env["opengl3"]:
471
env.AppendUnique(CPPDEFINES=["GLES3_ENABLED"])
472
if env["angle_libs"] != "":
473
env.AppendUnique(CPPDEFINES=["EGL_STATIC"])
474
env.Append(LIBPATH=[env["angle_libs"]])
475
LIBS += [
476
"libANGLE.windows." + env["arch"] + prebuilt_lib_extra_suffix,
477
"libEGL.windows." + env["arch"] + prebuilt_lib_extra_suffix,
478
"libGLES.windows." + env["arch"] + prebuilt_lib_extra_suffix,
479
]
480
LIBS += ["dxgi", "d3d9", "d3d11"]
481
env.Prepend(CPPPATH=["#thirdparty/angle/include"])
482
483
if env["target"] in ["editor", "template_debug"]:
484
LIBS += ["psapi", "dbghelp"]
485
486
if env["use_llvm"]:
487
LIBS += [f"clang_rt.builtins-{env['arch']}"]
488
489
env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS])
490
491
## LTO
492
493
if env["lto"] == "auto": # No LTO by default for MSVC, doesn't help.
494
env["lto"] = "none"
495
496
if env["lto"] != "none":
497
if env["lto"] == "thin":
498
if not env["use_llvm"]:
499
print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
500
sys.exit(255)
501
502
env.AppendUnique(CCFLAGS=["-flto=thin"])
503
elif env["use_llvm"]:
504
env.AppendUnique(CCFLAGS=["-flto"])
505
else:
506
env.AppendUnique(CCFLAGS=["/GL"])
507
if env["progress"]:
508
env.AppendUnique(LINKFLAGS=["/LTCG:STATUS"])
509
else:
510
env.AppendUnique(LINKFLAGS=["/LTCG"])
511
env.AppendUnique(ARFLAGS=["/LTCG"])
512
513
env.Append(LINKFLAGS=["/NATVIS:platform\\windows\\godot.natvis"])
514
515
if env["use_asan"]:
516
env.AppendUnique(LINKFLAGS=["/STACK:" + str(STACK_SIZE_SANITIZERS)])
517
else:
518
env.AppendUnique(LINKFLAGS=["/STACK:" + str(STACK_SIZE)])
519
520
521
def get_ar_version(env):
522
ret = {
523
"major": -1,
524
"minor": -1,
525
"patch": -1,
526
"is_llvm": False,
527
}
528
try:
529
output = (
530
subprocess
531
.check_output([env.subst(env["AR"]), "--version"], shell=(os.name == "nt"))
532
.strip()
533
.decode("utf-8")
534
)
535
except (subprocess.CalledProcessError, OSError):
536
print_warning("Couldn't check version of `ar`.")
537
return ret
538
539
match = re.search(r"GNU ar(?: \(GNU Binutils\)| version) (\d+)\.(\d+)(?:\.(\d+))?", output)
540
if match:
541
ret["major"] = int(match[1])
542
ret["minor"] = int(match[2])
543
if match[3]:
544
ret["patch"] = int(match[3])
545
else:
546
ret["patch"] = 0
547
return ret
548
549
match = re.search(r"LLVM version (\d+)\.(\d+)\.(\d+)", output)
550
if match:
551
ret["major"] = int(match[1])
552
ret["minor"] = int(match[2])
553
ret["patch"] = int(match[3])
554
ret["is_llvm"] = True
555
return ret
556
557
print_warning("Couldn't parse version of `ar`.")
558
return ret
559
560
561
def get_is_ar_thin_supported(env):
562
"""Check whether `ar --thin` is supported. It is only supported since Binutils 2.38 or LLVM 14."""
563
ar_version = get_ar_version(env)
564
if ar_version["major"] == -1:
565
return False
566
567
if ar_version["is_llvm"]:
568
return ar_version["major"] >= 14
569
570
if ar_version["major"] == 2:
571
return ar_version["minor"] >= 38
572
573
print_warning("Unknown Binutils `ar` version.")
574
return False
575
576
577
WINPATHSEP_RE = re.compile(r"\\([^\"'\\]|$)")
578
579
580
def tempfile_arg_esc_func(arg):
581
from SCons.Subst import quote_spaces
582
583
arg = quote_spaces(arg)
584
# GCC requires double Windows slashes, let's use UNIX separator
585
return WINPATHSEP_RE.sub(r"/\1", arg)
586
587
588
def configure_mingw(env: "SConsEnvironment"):
589
if os.getenv("MSYSTEM") == "MSYS":
590
print_error(
591
"Running from base MSYS2 console/environment, use target specific environment instead (e.g., mingw32, mingw64, clang32, clang64)."
592
)
593
sys.exit(255)
594
595
if (env_arch := detect_build_env_arch()) and env["arch"] != env_arch:
596
print_error(
597
f"Arch argument ({env['arch']}) is not matching MSYS2 console/environment that is being used to run SCons ({env_arch}).\n"
598
"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."
599
)
600
sys.exit(255)
601
602
if not try_cmd("gcc --version", env["mingw_prefix"], env["arch"]) and not try_cmd(
603
"clang --version", env["mingw_prefix"], env["arch"]
604
):
605
print_error("No valid compilers found, use MINGW_PREFIX environment variable to set MinGW path.")
606
sys.exit(255)
607
608
# Workaround for MinGW. See:
609
# https://www.scons.org/wiki/LongCmdLinesOnWin32
610
env.use_windows_spawn_fix()
611
612
# HACK: For some reason, Windows-native shells have their MinGW tools
613
# frequently fail as a result of parsing path separators incorrectly.
614
# For some other reason, this issue is circumvented entirely if the
615
# `mingw_prefix` bin is prepended to PATH.
616
if os.sep == "\\":
617
env.PrependENVPath("PATH", os.path.join(env["mingw_prefix"], "bin"))
618
619
# In case the command line to AR is too long, use a response file.
620
env["ARCOM_ORIG"] = env["ARCOM"]
621
env["ARCOM"] = "${TEMPFILE('$ARCOM_ORIG', '$ARCOMSTR')}"
622
env["TEMPFILESUFFIX"] = ".rsp"
623
if os.name == "nt":
624
env["TEMPFILEARGESCFUNC"] = tempfile_arg_esc_func
625
626
## Build type
627
628
if not env["use_llvm"] and not try_cmd("gcc --version", env["mingw_prefix"], env["arch"]):
629
env["use_llvm"] = True
630
631
if env["use_llvm"] and not try_cmd("clang --version", env["mingw_prefix"], env["arch"]):
632
env["use_llvm"] = False
633
634
if not env["use_llvm"] and try_cmd("gcc --version", env["mingw_prefix"], env["arch"], True):
635
print("Detected GCC to be a wrapper for Clang.")
636
env["use_llvm"] = True
637
638
if env["windows_subsystem"] == "gui":
639
env.Append(LINKFLAGS=["-Wl,--subsystem,windows"])
640
else:
641
env.Append(LINKFLAGS=["-Wl,--subsystem,console"])
642
env.AppendUnique(CPPDEFINES=["WINDOWS_SUBSYSTEM_CONSOLE"])
643
644
## Compiler configuration
645
646
if env["arch"] == "x86_32":
647
if env["use_static_cpp"]:
648
env.Append(LINKFLAGS=["-static"])
649
env.Append(LINKFLAGS=["-static-libgcc"])
650
env.Append(LINKFLAGS=["-static-libstdc++"])
651
else:
652
if env["use_static_cpp"]:
653
env.Append(LINKFLAGS=["-static"])
654
655
# NOTE: Big objects have historically broken LTO on mingw-gcc specifically. While that no
656
# longer appears to be the case, this notice is retained for posterity.
657
env.AppendUnique(CCFLAGS=["-Wa,-mbig-obj"]) # Support big objects.
658
659
if env["arch"] == "x86_32":
660
env["x86_libtheora_opt_gcc"] = True
661
662
env.Append(CCFLAGS=["-ffp-contract=off"])
663
664
if env["use_llvm"]:
665
env["CC"] = get_detected(env, "clang")
666
env["CXX"] = get_detected(env, "clang++")
667
env["AR"] = get_detected(env, "ar")
668
env["RANLIB"] = get_detected(env, "ranlib")
669
env["AS"] = get_detected(env, "clang")
670
env.Append(ASFLAGS=["-c"])
671
env.extra_suffix = ".llvm" + env.extra_suffix
672
else:
673
env["CC"] = get_detected(env, "gcc")
674
env["CXX"] = get_detected(env, "g++")
675
env["AR"] = get_detected(env, "gcc-ar" if os.name != "nt" else "ar")
676
env["RANLIB"] = get_detected(env, "gcc-ranlib")
677
env["AS"] = get_detected(env, "gcc")
678
env.Append(ASFLAGS=["-c"])
679
680
env["RC"] = get_detected(env, "windres")
681
ARCH_TARGETS = {
682
"x86_32": "pe-i386",
683
"x86_64": "pe-x86-64",
684
"arm32": "armv7-w64-mingw32",
685
"arm64": "aarch64-w64-mingw32",
686
}
687
env.AppendUnique(RCFLAGS=f"--target={ARCH_TARGETS[env['arch']]}")
688
689
env["OBJCOPY"] = get_detected(env, "objcopy")
690
env["STRIP"] = get_detected(env, "strip")
691
692
## LTO
693
694
if env["lto"] == "auto": # Enable LTO for production with MinGW.
695
env["lto"] = "thin" if env["use_llvm"] else "full"
696
697
if env["lto"] != "none":
698
if env["lto"] == "thin":
699
if not env["use_llvm"]:
700
print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
701
sys.exit(255)
702
env.Append(CCFLAGS=["-flto=thin"])
703
env.Append(LINKFLAGS=["-flto=thin"])
704
elif not env["use_llvm"] and env.GetOption("num_jobs") > 1:
705
env.Append(CCFLAGS=["-flto"])
706
env.Append(LINKFLAGS=["-flto=" + str(env.GetOption("num_jobs"))])
707
else:
708
env.Append(CCFLAGS=["-flto"])
709
env.Append(LINKFLAGS=["-flto"])
710
if not env["use_llvm"]:
711
# For mingw-gcc LTO, disable linker plugin and enable whole program to work around GH-102867.
712
env.Append(CCFLAGS=["-fno-use-linker-plugin", "-fwhole-program"])
713
env.Append(LINKFLAGS=["-fno-use-linker-plugin", "-fwhole-program"])
714
715
if env["use_asan"]:
716
env.Append(LINKFLAGS=["-Wl,--stack," + str(STACK_SIZE_SANITIZERS)])
717
else:
718
env.Append(LINKFLAGS=["-Wl,--stack," + str(STACK_SIZE)])
719
720
## Compile flags
721
722
if not env["use_llvm"]:
723
env.Append(CCFLAGS=["-mwindows"])
724
725
if env["use_asan"] or env["use_ubsan"]:
726
if not env["use_llvm"]:
727
print("GCC does not support sanitizers on Windows.")
728
sys.exit(255)
729
if env["arch"] not in ["x86_32", "x86_64"]:
730
print("Sanitizers are only supported for x86_32 and x86_64.")
731
sys.exit(255)
732
733
env.extra_suffix += ".san"
734
env.AppendUnique(CPPDEFINES=["SANITIZERS_ENABLED"])
735
san_flags = []
736
if env["use_asan"]:
737
san_flags.append("-fsanitize=address")
738
if env["use_ubsan"]:
739
san_flags.append("-fsanitize=undefined")
740
# Disable the vptr check since it gets triggered on any COM interface calls.
741
san_flags.append("-fno-sanitize=vptr")
742
env.Append(CFLAGS=san_flags)
743
env.Append(CCFLAGS=san_flags)
744
env.Append(LINKFLAGS=san_flags)
745
746
if get_is_ar_thin_supported(env):
747
env.Append(ARFLAGS=["--thin"])
748
749
env.Append(CPPDEFINES=["WINDOWS_ENABLED", "WASAPI_ENABLED", "WINMIDI_ENABLED"])
750
env.Append(
751
CPPDEFINES=[
752
("WINVER", "0x0A00"),
753
("_WIN32_WINNT", "0x0A00"),
754
]
755
)
756
env.Append(
757
LIBS=[
758
"mingw32",
759
"dsound",
760
"ole32",
761
"d3d9",
762
"winmm",
763
"gdi32",
764
"iphlpapi",
765
"shell32",
766
"shlwapi",
767
"shcore",
768
"wsock32",
769
"ws2_32",
770
"kernel32",
771
"oleaut32",
772
"sapi",
773
"dinput8",
774
"dxguid",
775
"ksuser",
776
"imm32",
777
"bcrypt",
778
"crypt32",
779
"avrt",
780
"uuid",
781
"dwmapi",
782
"dwrite",
783
"wbemuuid",
784
"ntdll",
785
"hid",
786
]
787
)
788
789
if env["accesskit"]:
790
if env["accesskit_sdk_path"] != "":
791
env.Prepend(CPPPATH=[env["accesskit_sdk_path"] + "/include"])
792
if env["use_llvm"]:
793
if env["arch"] == "arm64":
794
env.Append(LIBPATH=[env["accesskit_sdk_path"] + "/lib/windows/arm64/mingw-llvm/static/"])
795
elif env["arch"] == "x86_64":
796
env.Append(LIBPATH=[env["accesskit_sdk_path"] + "/lib/windows/x86_64/mingw-llvm/static/"])
797
elif env["arch"] == "x86_32":
798
env.Append(LIBPATH=[env["accesskit_sdk_path"] + "/lib/windows/x86/mingw-llvm/static/"])
799
else:
800
if env["arch"] == "x86_64":
801
env.Append(LIBPATH=[env["accesskit_sdk_path"] + "/lib/windows/x86_64/mingw/static/"])
802
elif env["arch"] == "x86_32":
803
env.Append(LIBPATH=[env["accesskit_sdk_path"] + "/lib/windows/x86/mingw/static/"])
804
env.Append(LIBPATH=["#bin/obj/platform/windows"])
805
env.Append(
806
LIBS=[
807
"accesskit",
808
"uiautomationcore." + env["arch"],
809
"runtimeobject",
810
"propsys",
811
"oleaut32",
812
"user32",
813
"userenv",
814
"ntdll",
815
]
816
)
817
else:
818
env.Append(CPPDEFINES=["ACCESSKIT_DYNAMIC"])
819
env.Append(LIBPATH=["#platform/windows"])
820
env.Append(CPPDEFINES=["ACCESSKIT_ENABLED"])
821
822
if env.debug_features:
823
env.Append(LIBS=["psapi", "dbghelp"])
824
825
if env["vulkan"]:
826
env.Append(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
827
if not env["use_volk"]:
828
env.Append(LIBS=["vulkan"])
829
830
if env["sdl"]:
831
env.Append(CPPDEFINES=["SDL_ENABLED"])
832
833
if env["d3d12"]:
834
if env["use_llvm"]:
835
check_d3d12_installed(env, env["arch"] + "-llvm")
836
else:
837
check_d3d12_installed(env, env["arch"] + "-gcc")
838
839
env.AppendUnique(CPPDEFINES=["D3D12_ENABLED", "RD_ENABLED"])
840
env.Append(LIBS=["dxgi", "dxguid"])
841
842
# PIX
843
if env["arch"] not in ["x86_64", "arm64"] or env["pix_path"] == "" or not os.path.exists(env["pix_path"]):
844
env["use_pix"] = False
845
846
if env["use_pix"]:
847
arch_subdir = "arm64" if env["arch"] == "arm64" else "x64"
848
849
env.Append(LIBPATH=[env["pix_path"] + "/bin/" + arch_subdir])
850
env.Append(LIBS=["WinPixEventRuntime"])
851
852
if env["use_llvm"] and os.path.exists(env["mesa_libs"] + "-" + env["arch"] + "-llvm"):
853
env.Append(LIBPATH=[env["mesa_libs"] + "-" + env["arch"] + "-llvm/bin"])
854
elif not env["use_llvm"] and os.path.exists(env["mesa_libs"] + "-" + env["arch"] + "-gcc"):
855
env.Append(LIBPATH=[env["mesa_libs"] + "-" + env["arch"] + "-gcc/bin"])
856
else:
857
env.Append(LIBPATH=[env["mesa_libs"] + "/bin"])
858
env.Append(LIBS=["libNIR.windows." + env["arch"]])
859
env.Append(LIBS=["version"]) # Mesa dependency.
860
861
if env["opengl3"]:
862
env.Append(CPPDEFINES=["GLES3_ENABLED"])
863
if env["angle_libs"] != "":
864
env.AppendUnique(CPPDEFINES=["EGL_STATIC"])
865
env.Append(LIBPATH=[env["angle_libs"]])
866
env.Append(
867
LIBS=[
868
"EGL.windows." + env["arch"],
869
"GLES.windows." + env["arch"],
870
"ANGLE.windows." + env["arch"],
871
]
872
)
873
env.Append(LIBS=["dxgi", "d3d9", "d3d11"])
874
env.Prepend(CPPPATH=["#thirdparty/angle/include"])
875
876
env.Append(CPPDEFINES=["MINGW_ENABLED", ("MINGW_HAS_SECURE_API", 1)])
877
878
# dlltool
879
env["DEF"] = get_detected(env, "dlltool")
880
env["DEFCOM"] = "$DEF $DEFFLAGS -d $SOURCE -l $TARGET"
881
env["DEFCOMSTR"] = "$CXXCOMSTR"
882
env["DEFPREFIX"] = "$LIBPREFIX"
883
env["DEFSUFFIX"] = ".${__env__['arch']}$LIBSUFFIX"
884
env["DEFSRCSUFFIX"] = ".${__env__['arch']}.def"
885
DEF_ALIASES = {
886
"x86_32": "i386",
887
"x86_64": "i386:x86-64",
888
"arm32": "arm",
889
"arm64": "arm64",
890
}
891
env.Append(DEFFLAGS=["-m", DEF_ALIASES[env["arch"]]])
892
if env["arch"] == "x86_32":
893
env.Append(DEFFLAGS=["-k"])
894
else:
895
env.Append(DEFFLAGS=["--no-leading-underscore"])
896
897
env.Append(
898
BUILDERS={
899
"DEFLIB": env.Builder(
900
action=env.Run("$DEFCOM", "$DEFCOMSTR"),
901
prefix="$DEFPREFIX",
902
suffix="$DEFSUFFIX",
903
src_suffix="$DEFSRCSUFFIX",
904
emitter=methods.redirect_emitter,
905
)
906
}
907
)
908
909
910
def configure(env: "SConsEnvironment"):
911
# Validate arch.
912
supported_arches = ["x86_32", "x86_64", "arm32", "arm64"]
913
validate_arch(env["arch"], get_name(), supported_arches)
914
915
# At this point the env has been set up with basic tools/compilers.
916
env.Prepend(CPPPATH=["#platform/windows"])
917
918
env.msvc = "mingw" not in env["TOOLS"]
919
if env.msvc:
920
configure_msvc(env)
921
else:
922
configure_mingw(env)
923
924
925
def check_d3d12_installed(env, suffix):
926
if not os.path.exists(env["mesa_libs"]) and not os.path.exists(env["mesa_libs"] + "-" + suffix):
927
print_error(
928
"The Direct3D 12 rendering driver requires dependencies to be installed.\n"
929
"You can install them by running `python misc\\scripts\\install_d3d12_sdk_windows.py`.\n"
930
"See the documentation for more information:\n"
931
"\thttps://docs.godotengine.org/en/latest/engine_details/development/compiling/compiling_for_windows.html\n"
932
"Alternatively, disable this driver by compiling with `d3d12=no` explicitly."
933
)
934
sys.exit(255)
935
936