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