123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951 |
- import os
- import re
- import subprocess
- import sys
- from typing import TYPE_CHECKING
- import methods
- from methods import print_error, print_warning
- from platform_methods import detect_arch, validate_arch
- if TYPE_CHECKING:
- from SCons.Script.SConscript import SConsEnvironment
- # To match other platforms
- STACK_SIZE = 8388608
- STACK_SIZE_SANITIZERS = 30 * 1024 * 1024
- def get_name():
- return "Windows"
- def try_cmd(test, prefix, arch, check_clang=False):
- archs = ["x86_64", "x86_32", "arm64", "arm32"]
- if arch:
- archs = [arch]
- for a in archs:
- try:
- out = subprocess.Popen(
- get_mingw_bin_prefix(prefix, a) + test,
- shell=True,
- stderr=subprocess.PIPE,
- stdout=subprocess.PIPE,
- )
- outs, errs = out.communicate()
- if out.returncode == 0:
- if check_clang and not outs.startswith(b"clang"):
- return False
- return True
- except Exception:
- pass
- return False
- def can_build():
- if os.name == "nt":
- # Building natively on Windows
- # If VCINSTALLDIR is set in the OS environ, use traditional Godot logic to set up MSVC
- if os.getenv("VCINSTALLDIR"): # MSVC, manual setup
- return True
- # Otherwise, let SCons find MSVC if installed, or else MinGW.
- # Since we're just returning True here, if there's no compiler
- # installed, we'll get errors when it tries to build with the
- # null compiler.
- return True
- if os.name == "posix":
- # Cross-compiling with MinGW-w64 (old MinGW32 is not supported)
- prefix = os.getenv("MINGW_PREFIX", "")
- if try_cmd("gcc --version", prefix, "") or try_cmd("clang --version", prefix, ""):
- return True
- return False
- def get_mingw_bin_prefix(prefix, arch):
- bin_prefix = (os.path.normpath(os.path.join(prefix, "bin")) + os.sep) if prefix else ""
- ARCH_PREFIXES = {
- "x86_64": "x86_64-w64-mingw32-",
- "x86_32": "i686-w64-mingw32-",
- "arm32": "armv7-w64-mingw32-",
- "arm64": "aarch64-w64-mingw32-",
- }
- arch_prefix = ARCH_PREFIXES[arch] if arch else ""
- return bin_prefix + arch_prefix
- def get_detected(env: "SConsEnvironment", tool: str) -> str:
- checks = [
- get_mingw_bin_prefix(env["mingw_prefix"], env["arch"]) + tool,
- get_mingw_bin_prefix(env["mingw_prefix"], "") + tool,
- ]
- return str(env.Detect(checks))
- def detect_build_env_arch():
- msvc_target_aliases = {
- "amd64": "x86_64",
- "i386": "x86_32",
- "i486": "x86_32",
- "i586": "x86_32",
- "i686": "x86_32",
- "x86": "x86_32",
- "x64": "x86_64",
- "x86_64": "x86_64",
- "arm": "arm32",
- "arm64": "arm64",
- "aarch64": "arm64",
- }
- if os.getenv("VCINSTALLDIR") or os.getenv("VCTOOLSINSTALLDIR"):
- if os.getenv("Platform"):
- msvc_arch = os.getenv("Platform").lower()
- if msvc_arch in msvc_target_aliases.keys():
- return msvc_target_aliases[msvc_arch]
- if os.getenv("VSCMD_ARG_TGT_ARCH"):
- msvc_arch = os.getenv("VSCMD_ARG_TGT_ARCH").lower()
- if msvc_arch in msvc_target_aliases.keys():
- return msvc_target_aliases[msvc_arch]
- # Pre VS 2017 checks.
- if os.getenv("VCINSTALLDIR"):
- PATH = os.getenv("PATH").upper()
- VCINSTALLDIR = os.getenv("VCINSTALLDIR").upper()
- path_arch = {
- "BIN\\x86_ARM;": "arm32",
- "BIN\\amd64_ARM;": "arm32",
- "BIN\\x86_ARM64;": "arm64",
- "BIN\\amd64_ARM64;": "arm64",
- "BIN\\x86_amd64;": "a86_64",
- "BIN\\amd64;": "x86_64",
- "BIN\\amd64_x86;": "x86_32",
- "BIN;": "x86_32",
- }
- for path, arch in path_arch.items():
- final_path = VCINSTALLDIR + path
- if final_path in PATH:
- return arch
- # VS 2017 and newer.
- if os.getenv("VCTOOLSINSTALLDIR"):
- host_path_index = os.getenv("PATH").upper().find(os.getenv("VCTOOLSINSTALLDIR").upper() + "BIN\\HOST")
- if host_path_index > -1:
- first_path_arch = os.getenv("PATH")[host_path_index:].split(";")[0].rsplit("\\", 1)[-1].lower()
- if first_path_arch in msvc_target_aliases.keys():
- return msvc_target_aliases[first_path_arch]
- msys_target_aliases = {
- "mingw32": "x86_32",
- "mingw64": "x86_64",
- "ucrt64": "x86_64",
- "clang64": "x86_64",
- "clang32": "x86_32",
- "clangarm64": "arm64",
- }
- if os.getenv("MSYSTEM"):
- msys_arch = os.getenv("MSYSTEM").lower()
- if msys_arch in msys_target_aliases.keys():
- return msys_target_aliases[msys_arch]
- return ""
- def get_opts():
- from SCons.Variables import BoolVariable, EnumVariable
- mingw = os.getenv("MINGW_PREFIX", "")
- # Direct3D 12 SDK dependencies folder.
- d3d12_deps_folder = os.getenv("LOCALAPPDATA")
- if d3d12_deps_folder:
- d3d12_deps_folder = os.path.join(d3d12_deps_folder, "Godot", "build_deps")
- else:
- # Cross-compiling, the deps install script puts things in `bin`.
- # Getting an absolute path to it is a bit hacky in Python.
- try:
- import inspect
- caller_frame = inspect.stack()[1]
- caller_script_dir = os.path.dirname(os.path.abspath(caller_frame[1]))
- d3d12_deps_folder = os.path.join(caller_script_dir, "bin", "build_deps")
- except Exception: # Give up.
- d3d12_deps_folder = ""
- return [
- ("mingw_prefix", "MinGW prefix", mingw),
- # Targeted Windows version: 7 (and later), minimum supported version
- # XP support dropped after EOL due to missing API for IPv6 and other issues
- # Vista support dropped after EOL due to GH-10243
- (
- "target_win_version",
- "Targeted Windows version, >= 0x0601 (Windows 7)",
- "0x0601",
- ),
- EnumVariable("windows_subsystem", "Windows subsystem", "gui", ("gui", "console")),
- (
- "msvc_version",
- "MSVC version to use. Ignored if VCINSTALLDIR is set in shell env.",
- None,
- ),
- BoolVariable("use_mingw", "Use the Mingw compiler, even if MSVC is installed.", False),
- BoolVariable("use_llvm", "Use the LLVM compiler", False),
- BoolVariable("use_static_cpp", "Link MinGW/MSVC C++ runtime libraries statically", True),
- BoolVariable("use_asan", "Use address sanitizer (ASAN)", False),
- BoolVariable("use_ubsan", "Use LLVM compiler undefined behavior sanitizer (UBSAN)", False),
- BoolVariable("debug_crt", "Compile with MSVC's debug CRT (/MDd)", False),
- BoolVariable("incremental_link", "Use MSVC incremental linking. May increase or decrease build times.", False),
- BoolVariable("silence_msvc", "Silence MSVC's cl/link stdout bloat, redirecting any errors to stderr.", True),
- ("angle_libs", "Path to the ANGLE static libraries", ""),
- # Direct3D 12 support.
- (
- "mesa_libs",
- "Path to the MESA/NIR static libraries (required for D3D12)",
- os.path.join(d3d12_deps_folder, "mesa"),
- ),
- (
- "agility_sdk_path",
- "Path to the Agility SDK distribution (optional for D3D12)",
- os.path.join(d3d12_deps_folder, "agility_sdk"),
- ),
- BoolVariable(
- "agility_sdk_multiarch",
- "Whether the Agility SDK DLLs will be stored in arch-specific subdirectories",
- False,
- ),
- BoolVariable("use_pix", "Use PIX (Performance tuning and debugging for DirectX 12) runtime", False),
- (
- "pix_path",
- "Path to the PIX runtime distribution (optional for D3D12)",
- os.path.join(d3d12_deps_folder, "pix"),
- ),
- ]
- def get_doc_classes():
- return [
- "EditorExportPlatformWindows",
- ]
- def get_doc_path():
- return "doc_classes"
- def get_flags():
- arch = detect_build_env_arch() or detect_arch()
- return {
- "arch": arch,
- "supported": ["d3d12", "mono", "xaudio2"],
- }
- def setup_msvc_manual(env: "SConsEnvironment"):
- """Running from VCVARS environment"""
- env_arch = detect_build_env_arch()
- if env["arch"] != env_arch:
- print_error(
- "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"
- "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."
- % (env["arch"], env_arch)
- )
- sys.exit(255)
- print("Using VCVARS-determined MSVC, arch %s" % (env_arch))
- def setup_msvc_auto(env: "SConsEnvironment"):
- """Set up MSVC using SCons's auto-detection logic"""
- # If MSVC_VERSION is set by SCons, we know MSVC is installed.
- # But we may want a different version or target arch.
- # Valid architectures for MSVC's TARGET_ARCH:
- # ['amd64', 'emt64', 'i386', 'i486', 'i586', 'i686', 'ia64', 'itanium', 'x86', 'x86_64', 'arm', 'arm64', 'aarch64']
- # Our x86_64 and arm64 are the same, and we need to map the 32-bit
- # architectures to other names since MSVC isn't as explicit.
- # The rest we don't need to worry about because they are
- # aliases or aren't supported by Godot (itanium & ia64).
- msvc_arch_aliases = {"x86_32": "x86", "arm32": "arm"}
- if env["arch"] in msvc_arch_aliases.keys():
- env["TARGET_ARCH"] = msvc_arch_aliases[env["arch"]]
- else:
- env["TARGET_ARCH"] = env["arch"]
- # The env may have already been set up with default MSVC tools, so
- # reset a few things so we can set it up with the tools we want.
- # (Ideally we'd decide on the tool config before configuring any
- # environment, and just set the env up once, but this function runs
- # on an existing env so this is the simplest way.)
- env["MSVC_SETUP_RUN"] = False # Need to set this to re-run the tool
- env["MSVS_VERSION"] = None
- env["MSVC_VERSION"] = None
- if "msvc_version" in env:
- env["MSVC_VERSION"] = env["msvc_version"]
- env.Tool("msvc")
- env.Tool("mssdk") # we want the MS SDK
- # Re-add potentially overwritten flags.
- env.AppendUnique(CCFLAGS=env.get("ccflags", "").split())
- env.AppendUnique(CXXFLAGS=env.get("cxxflags", "").split())
- env.AppendUnique(CFLAGS=env.get("cflags", "").split())
- env.AppendUnique(RCFLAGS=env.get("rcflags", "").split())
- # Note: actual compiler version can be found in env['MSVC_VERSION'], e.g. "14.1" for VS2015
- print("Using SCons-detected MSVC version %s, arch %s" % (env["MSVC_VERSION"], env["arch"]))
- def setup_mingw(env: "SConsEnvironment"):
- """Set up env for use with mingw"""
- env_arch = detect_build_env_arch()
- if os.getenv("MSYSTEM") == "MSYS":
- print_error(
- "Running from base MSYS2 console/environment, use target specific environment instead (e.g., mingw32, mingw64, clang32, clang64)."
- )
- sys.exit(255)
- if env_arch != "" and env["arch"] != env_arch:
- print_error(
- "Arch argument (%s) is not matching MSYS2 console/environment that is being used to run SCons (%s).\n"
- "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."
- % (env["arch"], env_arch)
- )
- sys.exit(255)
- if not try_cmd("gcc --version", env["mingw_prefix"], env["arch"]) and not try_cmd(
- "clang --version", env["mingw_prefix"], env["arch"]
- ):
- print_error("No valid compilers found, use MINGW_PREFIX environment variable to set MinGW path.")
- sys.exit(255)
- env.Tool("mingw")
- env.AppendUnique(CCFLAGS=env.get("ccflags", "").split())
- env.AppendUnique(RCFLAGS=env.get("rcflags", "").split())
- print("Using MinGW, arch %s" % (env["arch"]))
- def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config):
- """Configure env to work with MSVC"""
- ## Build type
- # TODO: Re-evaluate the need for this / streamline with common config.
- if env["target"] == "template_release":
- env.Append(LINKFLAGS=["/ENTRY:mainCRTStartup"])
- if env["windows_subsystem"] == "gui":
- env.Append(LINKFLAGS=["/SUBSYSTEM:WINDOWS"])
- else:
- env.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"])
- env.AppendUnique(CPPDEFINES=["WINDOWS_SUBSYSTEM_CONSOLE"])
- ## Compile/link flags
- if env["use_llvm"]:
- env["CC"] = "clang-cl"
- env["CXX"] = "clang-cl"
- env["LINK"] = "lld-link"
- env["AR"] = "llvm-lib"
- env.AppendUnique(CPPDEFINES=["R128_STDC_ONLY"])
- env.extra_suffix = ".llvm" + env.extra_suffix
- if env["silence_msvc"] and not env.GetOption("clean"):
- from tempfile import mkstemp
- # Ensure we have a location to write captured output to, in case of false positives.
- capture_path = methods.base_folder_path + "platform/windows/msvc_capture.log"
- with open(capture_path, "wt", encoding="utf-8"):
- pass
- old_spawn = env["SPAWN"]
- re_redirect_stream = re.compile(r"^[12]?>")
- re_cl_capture = re.compile(r"^.+\.(c|cc|cpp|cxx|c[+]{2})$", re.IGNORECASE)
- re_link_capture = re.compile(r'\s{3}\S.+\s(?:"[^"]+.lib"|\S+.lib)\s.+\s(?:"[^"]+.exp"|\S+.exp)')
- def spawn_capture(sh, escape, cmd, args, env):
- # We only care about cl/link, process everything else as normal.
- if args[0] not in ["cl", "link"]:
- return old_spawn(sh, escape, cmd, args, env)
- # Process as normal if the user is manually rerouting output.
- for arg in args:
- if re_redirect_stream.match(arg):
- return old_spawn(sh, escape, cmd, args, env)
- tmp_stdout, tmp_stdout_name = mkstemp()
- os.close(tmp_stdout)
- args.append(f">{tmp_stdout_name}")
- ret = old_spawn(sh, escape, cmd, args, env)
- try:
- with open(tmp_stdout_name, "r", encoding=sys.stdout.encoding, errors="replace") as tmp_stdout:
- lines = tmp_stdout.read().splitlines()
- os.remove(tmp_stdout_name)
- except OSError:
- pass
- # Early process no lines (OSError)
- if not lines:
- return ret
- is_cl = args[0] == "cl"
- content = ""
- caught = False
- for line in lines:
- # These conditions are far from all-encompassing, but are specialized
- # for what can be reasonably expected to show up in the repository.
- if not caught and (is_cl and re_cl_capture.match(line)) or (not is_cl and re_link_capture.match(line)):
- caught = True
- try:
- with open(capture_path, "a", encoding=sys.stdout.encoding) as log:
- log.write(line + "\n")
- except OSError:
- print_warning(f'Failed to log captured line: "{line}".')
- continue
- content += line + "\n"
- # Content remaining assumed to be an error/warning.
- if content:
- sys.stderr.write(content)
- return ret
- env["SPAWN"] = spawn_capture
- if env["debug_crt"]:
- # Always use dynamic runtime, static debug CRT breaks thread_local.
- env.AppendUnique(CCFLAGS=["/MDd"])
- else:
- if env["use_static_cpp"]:
- env.AppendUnique(CCFLAGS=["/MT"])
- else:
- env.AppendUnique(CCFLAGS=["/MD"])
- # MSVC incremental linking is broken and may _increase_ link time (GH-77968).
- if not env["incremental_link"]:
- env.Append(LINKFLAGS=["/INCREMENTAL:NO"])
- if env["arch"] == "x86_32":
- env["x86_libtheora_opt_vc"] = True
- env.Append(CCFLAGS=["/fp:strict"])
- env.AppendUnique(CCFLAGS=["/Gd", "/GR", "/nologo"])
- env.AppendUnique(CCFLAGS=["/utf-8"]) # Force to use Unicode encoding.
- # Once it was thought that only debug builds would be too large,
- # but this has recently stopped being true. See the mingw function
- # for notes on why this shouldn't be enabled for gcc
- env.AppendUnique(CCFLAGS=["/bigobj"])
- if vcvars_msvc_config: # should be automatic if SCons found it
- if os.getenv("WindowsSdkDir") is not None:
- env.Prepend(CPPPATH=[str(os.getenv("WindowsSdkDir")) + "/Include"])
- else:
- print_warning("Missing environment variable: WindowsSdkDir")
- validate_win_version(env)
- env.AppendUnique(
- CPPDEFINES=[
- "WINDOWS_ENABLED",
- "WASAPI_ENABLED",
- "WINMIDI_ENABLED",
- "TYPED_METHOD_BIND",
- "WIN32",
- "WINVER=%s" % env["target_win_version"],
- "_WIN32_WINNT=%s" % env["target_win_version"],
- ]
- )
- env.AppendUnique(CPPDEFINES=["NOMINMAX"]) # disable bogus min/max WinDef.h macros
- if env["arch"] == "x86_64":
- env.AppendUnique(CPPDEFINES=["_WIN64"])
- # Sanitizers
- prebuilt_lib_extra_suffix = ""
- if env["use_asan"]:
- env.extra_suffix += ".san"
- prebuilt_lib_extra_suffix = ".san"
- env.AppendUnique(CPPDEFINES=["SANITIZERS_ENABLED"])
- env.Append(CCFLAGS=["/fsanitize=address"])
- env.Append(LINKFLAGS=["/INFERASANLIBS"])
- ## Libs
- LIBS = [
- "winmm",
- "dsound",
- "kernel32",
- "ole32",
- "oleaut32",
- "sapi",
- "user32",
- "gdi32",
- "IPHLPAPI",
- "Shlwapi",
- "wsock32",
- "Ws2_32",
- "shell32",
- "advapi32",
- "dinput8",
- "dxguid",
- "imm32",
- "bcrypt",
- "Crypt32",
- "Avrt",
- "dwmapi",
- "dwrite",
- "wbemuuid",
- "ntdll",
- ]
- if env.debug_features:
- LIBS += ["psapi", "dbghelp"]
- if env["vulkan"]:
- env.AppendUnique(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
- if not env["use_volk"]:
- LIBS += ["vulkan"]
- if env["d3d12"]:
- check_d3d12_installed(env)
- env.AppendUnique(CPPDEFINES=["D3D12_ENABLED", "RD_ENABLED"])
- LIBS += ["dxgi", "dxguid"]
- LIBS += ["version"] # Mesa dependency.
- # Needed for avoiding C1128.
- if env["target"] == "release_debug":
- env.Append(CXXFLAGS=["/bigobj"])
- # PIX
- if env["arch"] not in ["x86_64", "arm64"] or env["pix_path"] == "" or not os.path.exists(env["pix_path"]):
- env["use_pix"] = False
- if env["use_pix"]:
- arch_subdir = "arm64" if env["arch"] == "arm64" else "x64"
- env.Append(LIBPATH=[env["pix_path"] + "/bin/" + arch_subdir])
- LIBS += ["WinPixEventRuntime"]
- env.Append(LIBPATH=[env["mesa_libs"] + "/bin"])
- LIBS += ["libNIR.windows." + env["arch"] + prebuilt_lib_extra_suffix]
- if env["opengl3"]:
- env.AppendUnique(CPPDEFINES=["GLES3_ENABLED"])
- if env["angle_libs"] != "":
- env.AppendUnique(CPPDEFINES=["EGL_STATIC"])
- env.Append(LIBPATH=[env["angle_libs"]])
- LIBS += [
- "libANGLE.windows." + env["arch"] + prebuilt_lib_extra_suffix,
- "libEGL.windows." + env["arch"] + prebuilt_lib_extra_suffix,
- "libGLES.windows." + env["arch"] + prebuilt_lib_extra_suffix,
- ]
- LIBS += ["dxgi", "d3d9", "d3d11"]
- env.Prepend(CPPPATH=["#thirdparty/angle/include"])
- if env["target"] in ["editor", "template_debug"]:
- LIBS += ["psapi", "dbghelp"]
- if env["use_llvm"]:
- LIBS += [f"clang_rt.builtins-{env['arch']}"]
- env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS])
- if vcvars_msvc_config:
- if os.getenv("WindowsSdkDir") is not None:
- env.Append(LIBPATH=[str(os.getenv("WindowsSdkDir")) + "/Lib"])
- else:
- print_warning("Missing environment variable: WindowsSdkDir")
- ## LTO
- if env["lto"] == "auto": # No LTO by default for MSVC, doesn't help.
- env["lto"] = "none"
- if env["lto"] != "none":
- if env["lto"] == "thin":
- if not env["use_llvm"]:
- print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
- sys.exit(255)
- env.AppendUnique(CCFLAGS=["-flto=thin"])
- elif env["use_llvm"]:
- env.AppendUnique(CCFLAGS=["-flto"])
- else:
- env.AppendUnique(CCFLAGS=["/GL"])
- if env["progress"]:
- env.AppendUnique(LINKFLAGS=["/LTCG:STATUS"])
- else:
- env.AppendUnique(LINKFLAGS=["/LTCG"])
- env.AppendUnique(ARFLAGS=["/LTCG"])
- if vcvars_msvc_config:
- env.Prepend(CPPPATH=[p for p in str(os.getenv("INCLUDE")).split(";")])
- env.Append(LIBPATH=[p for p in str(os.getenv("LIB")).split(";")])
- # Incremental linking fix
- env["BUILDERS"]["ProgramOriginal"] = env["BUILDERS"]["Program"]
- env["BUILDERS"]["Program"] = methods.precious_program
- env.Append(LINKFLAGS=["/NATVIS:platform\\windows\\godot.natvis"])
- if env["use_asan"]:
- env.AppendUnique(LINKFLAGS=["/STACK:" + str(STACK_SIZE_SANITIZERS)])
- else:
- env.AppendUnique(LINKFLAGS=["/STACK:" + str(STACK_SIZE)])
- def get_ar_version(env):
- ret = {
- "major": -1,
- "minor": -1,
- "patch": -1,
- "is_llvm": False,
- }
- try:
- output = (
- subprocess.check_output([env.subst(env["AR"]), "--version"], shell=(os.name == "nt"))
- .strip()
- .decode("utf-8")
- )
- except (subprocess.CalledProcessError, OSError):
- print_warning("Couldn't check version of `ar`.")
- return ret
- match = re.search(r"GNU ar(?: \(GNU Binutils\)| version) (\d+)\.(\d+)(?:\.(\d+))?", output)
- if match:
- ret["major"] = int(match[1])
- ret["minor"] = int(match[2])
- if match[3]:
- ret["patch"] = int(match[3])
- else:
- ret["patch"] = 0
- return ret
- match = re.search(r"LLVM version (\d+)\.(\d+)\.(\d+)", output)
- if match:
- ret["major"] = int(match[1])
- ret["minor"] = int(match[2])
- ret["patch"] = int(match[3])
- ret["is_llvm"] = True
- return ret
- print_warning("Couldn't parse version of `ar`.")
- return ret
- def get_is_ar_thin_supported(env):
- """Check whether `ar --thin` is supported. It is only supported since Binutils 2.38 or LLVM 14."""
- ar_version = get_ar_version(env)
- if ar_version["major"] == -1:
- return False
- if ar_version["is_llvm"]:
- return ar_version["major"] >= 14
- if ar_version["major"] == 2:
- return ar_version["minor"] >= 38
- print_warning("Unknown Binutils `ar` version.")
- return False
- WINPATHSEP_RE = re.compile(r"\\([^\"'\\]|$)")
- def tempfile_arg_esc_func(arg):
- from SCons.Subst import quote_spaces
- arg = quote_spaces(arg)
- # GCC requires double Windows slashes, let's use UNIX separator
- return WINPATHSEP_RE.sub(r"/\1", arg)
- def configure_mingw(env: "SConsEnvironment"):
- # Workaround for MinGW. See:
- # https://www.scons.org/wiki/LongCmdLinesOnWin32
- env.use_windows_spawn_fix()
- # HACK: For some reason, Windows-native shells have their MinGW tools
- # frequently fail as a result of parsing path separators incorrectly.
- # For some other reason, this issue is circumvented entirely if the
- # `mingw_prefix` bin is prepended to PATH.
- if os.sep == "\\":
- env.PrependENVPath("PATH", os.path.join(env["mingw_prefix"], "bin"))
- # In case the command line to AR is too long, use a response file.
- env["ARCOM_ORIG"] = env["ARCOM"]
- env["ARCOM"] = "${TEMPFILE('$ARCOM_ORIG', '$ARCOMSTR')}"
- env["TEMPFILESUFFIX"] = ".rsp"
- if os.name == "nt":
- env["TEMPFILEARGESCFUNC"] = tempfile_arg_esc_func
- ## Build type
- if not env["use_llvm"] and not try_cmd("gcc --version", env["mingw_prefix"], env["arch"]):
- env["use_llvm"] = True
- if env["use_llvm"] and not try_cmd("clang --version", env["mingw_prefix"], env["arch"]):
- env["use_llvm"] = False
- if not env["use_llvm"] and try_cmd("gcc --version", env["mingw_prefix"], env["arch"], True):
- print("Detected GCC to be a wrapper for Clang.")
- env["use_llvm"] = True
- # TODO: Re-evaluate the need for this / streamline with common config.
- if env["target"] == "template_release":
- if env["arch"] != "arm64":
- env.Append(CCFLAGS=["-msse2"])
- elif env.dev_build:
- # Allow big objects. It's supposed not to have drawbacks but seems to break
- # GCC LTO, so enabling for debug builds only (which are not built with LTO
- # and are the only ones with too big objects).
- env.Append(CCFLAGS=["-Wa,-mbig-obj"])
- if env["windows_subsystem"] == "gui":
- env.Append(LINKFLAGS=["-Wl,--subsystem,windows"])
- else:
- env.Append(LINKFLAGS=["-Wl,--subsystem,console"])
- env.AppendUnique(CPPDEFINES=["WINDOWS_SUBSYSTEM_CONSOLE"])
- ## Compiler configuration
- if env["arch"] == "x86_32":
- if env["use_static_cpp"]:
- env.Append(LINKFLAGS=["-static"])
- env.Append(LINKFLAGS=["-static-libgcc"])
- env.Append(LINKFLAGS=["-static-libstdc++"])
- else:
- if env["use_static_cpp"]:
- env.Append(LINKFLAGS=["-static"])
- if env["arch"] in ["x86_32", "x86_64"]:
- env["x86_libtheora_opt_gcc"] = True
- env.Append(CCFLAGS=["-ffp-contract=off"])
- if env["use_llvm"]:
- env["CC"] = get_detected(env, "clang")
- env["CXX"] = get_detected(env, "clang++")
- env["AR"] = get_detected(env, "ar")
- env["RANLIB"] = get_detected(env, "ranlib")
- env.Append(ASFLAGS=["-c"])
- env.extra_suffix = ".llvm" + env.extra_suffix
- else:
- env["CC"] = get_detected(env, "gcc")
- env["CXX"] = get_detected(env, "g++")
- env["AR"] = get_detected(env, "gcc-ar" if os.name != "nt" else "ar")
- env["RANLIB"] = get_detected(env, "gcc-ranlib")
- env["RC"] = get_detected(env, "windres")
- ARCH_TARGETS = {
- "x86_32": "pe-i386",
- "x86_64": "pe-x86-64",
- "arm32": "armv7-w64-mingw32",
- "arm64": "aarch64-w64-mingw32",
- }
- env.AppendUnique(RCFLAGS=f"--target={ARCH_TARGETS[env['arch']]}")
- env["AS"] = get_detected(env, "as")
- env["OBJCOPY"] = get_detected(env, "objcopy")
- env["STRIP"] = get_detected(env, "strip")
- ## LTO
- if env["lto"] == "auto": # Full LTO for production with MinGW.
- env["lto"] = "full"
- if env["lto"] != "none":
- if env["lto"] == "thin":
- if not env["use_llvm"]:
- print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
- sys.exit(255)
- env.Append(CCFLAGS=["-flto=thin"])
- env.Append(LINKFLAGS=["-flto=thin"])
- elif not env["use_llvm"] and env.GetOption("num_jobs") > 1:
- env.Append(CCFLAGS=["-flto"])
- env.Append(LINKFLAGS=["-flto=" + str(env.GetOption("num_jobs"))])
- else:
- env.Append(CCFLAGS=["-flto"])
- env.Append(LINKFLAGS=["-flto"])
- if env["use_asan"]:
- env.Append(LINKFLAGS=["-Wl,--stack," + str(STACK_SIZE_SANITIZERS)])
- else:
- env.Append(LINKFLAGS=["-Wl,--stack," + str(STACK_SIZE)])
- ## Compile flags
- validate_win_version(env)
- if not env["use_llvm"]:
- env.Append(CCFLAGS=["-mwindows"])
- if env["use_asan"] or env["use_ubsan"]:
- if not env["use_llvm"]:
- print("GCC does not support sanitizers on Windows.")
- sys.exit(255)
- if env["arch"] not in ["x86_32", "x86_64"]:
- print("Sanitizers are only supported for x86_32 and x86_64.")
- sys.exit(255)
- env.extra_suffix += ".san"
- env.AppendUnique(CPPDEFINES=["SANITIZERS_ENABLED"])
- san_flags = []
- if env["use_asan"]:
- san_flags.append("-fsanitize=address")
- if env["use_ubsan"]:
- san_flags.append("-fsanitize=undefined")
- # Disable the vptr check since it gets triggered on any COM interface calls.
- san_flags.append("-fno-sanitize=vptr")
- env.Append(CFLAGS=san_flags)
- env.Append(CCFLAGS=san_flags)
- env.Append(LINKFLAGS=san_flags)
- if env["use_llvm"] and os.name == "nt" and methods._colorize:
- env.Append(CCFLAGS=["$(-fansi-escape-codes$)", "$(-fcolor-diagnostics$)"])
- if get_is_ar_thin_supported(env):
- env.Append(ARFLAGS=["--thin"])
- env.Append(CPPDEFINES=["WINDOWS_ENABLED", "WASAPI_ENABLED", "WINMIDI_ENABLED"])
- env.Append(
- CPPDEFINES=[
- ("WINVER", env["target_win_version"]),
- ("_WIN32_WINNT", env["target_win_version"]),
- ]
- )
- env.Append(
- LIBS=[
- "mingw32",
- "dsound",
- "ole32",
- "d3d9",
- "winmm",
- "gdi32",
- "iphlpapi",
- "shlwapi",
- "wsock32",
- "ws2_32",
- "kernel32",
- "oleaut32",
- "sapi",
- "dinput8",
- "dxguid",
- "ksuser",
- "imm32",
- "bcrypt",
- "crypt32",
- "avrt",
- "uuid",
- "dwmapi",
- "dwrite",
- "wbemuuid",
- "ntdll",
- ]
- )
- if env.debug_features:
- env.Append(LIBS=["psapi", "dbghelp"])
- if env["vulkan"]:
- env.Append(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
- if not env["use_volk"]:
- env.Append(LIBS=["vulkan"])
- if env["d3d12"]:
- check_d3d12_installed(env)
- env.AppendUnique(CPPDEFINES=["D3D12_ENABLED", "RD_ENABLED"])
- env.Append(LIBS=["dxgi", "dxguid"])
- # PIX
- if env["arch"] not in ["x86_64", "arm64"] or env["pix_path"] == "" or not os.path.exists(env["pix_path"]):
- env["use_pix"] = False
- if env["use_pix"]:
- arch_subdir = "arm64" if env["arch"] == "arm64" else "x64"
- env.Append(LIBPATH=[env["pix_path"] + "/bin/" + arch_subdir])
- env.Append(LIBS=["WinPixEventRuntime"])
- env.Append(LIBPATH=[env["mesa_libs"] + "/bin"])
- env.Append(LIBS=["libNIR.windows." + env["arch"]])
- env.Append(LIBS=["version"]) # Mesa dependency.
- if env["opengl3"]:
- env.Append(CPPDEFINES=["GLES3_ENABLED"])
- if env["angle_libs"] != "":
- env.AppendUnique(CPPDEFINES=["EGL_STATIC"])
- env.Append(LIBPATH=[env["angle_libs"]])
- env.Append(
- LIBS=[
- "EGL.windows." + env["arch"],
- "GLES.windows." + env["arch"],
- "ANGLE.windows." + env["arch"],
- ]
- )
- env.Append(LIBS=["dxgi", "d3d9", "d3d11"])
- env.Prepend(CPPPATH=["#thirdparty/angle/include"])
- env.Append(CPPDEFINES=["MINGW_ENABLED", ("MINGW_HAS_SECURE_API", 1)])
- def configure(env: "SConsEnvironment"):
- # Validate arch.
- supported_arches = ["x86_32", "x86_64", "arm32", "arm64"]
- validate_arch(env["arch"], get_name(), supported_arches)
- # At this point the env has been set up with basic tools/compilers.
- env.Prepend(CPPPATH=["#platform/windows"])
- if os.name == "nt":
- env["ENV"] = os.environ # this makes build less repeatable, but simplifies some things
- env["ENV"]["TMP"] = os.environ["TMP"]
- # First figure out which compiler, version, and target arch we're using
- if os.getenv("VCINSTALLDIR") and detect_build_env_arch() and not env["use_mingw"]:
- setup_msvc_manual(env)
- env.msvc = True
- vcvars_msvc_config = True
- elif env.get("MSVC_VERSION", "") and not env["use_mingw"]:
- setup_msvc_auto(env)
- env.msvc = True
- vcvars_msvc_config = False
- else:
- setup_mingw(env)
- env.msvc = False
- # Now set compiler/linker flags
- if env.msvc:
- configure_msvc(env, vcvars_msvc_config)
- else: # MinGW
- configure_mingw(env)
- def check_d3d12_installed(env):
- if not os.path.exists(env["mesa_libs"]):
- print_error(
- "The Direct3D 12 rendering driver requires dependencies to be installed.\n"
- "You can install them by running `python misc\\scripts\\install_d3d12_sdk_windows.py`.\n"
- "See the documentation for more information:\n\t"
- "https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_windows.html"
- )
- sys.exit(255)
- def validate_win_version(env):
- if int(env["target_win_version"], 16) < 0x0601:
- print_error("`target_win_version` should be 0x0601 or higher (Windows 7).")
- sys.exit(255)
|