Scons - add compiler requirements

This commit is contained in:
lawnjelly 2025-06-18 12:09:12 +01:00
parent 1a7d99e37d
commit 4aa8ecd6be
2 changed files with 207 additions and 0 deletions

View file

@ -469,6 +469,74 @@ if selected_platform in platform_list:
if env["lto"] != "none":
print("Using LTO: " + env["lto"])
# Enforce our minimal compiler version requirements
cc_version = methods.get_compiler_version_ex(env)
cc_version_major = cc_version["major"]
cc_version_minor = cc_version["minor"]
cc_version_metadata1 = cc_version["metadata1"]
if cc_version_major == -1:
print_warning(
"Couldn't detect compiler version, skipping version checks. "
"Build may fail if the compiler doesn't support C++17 fully."
)
elif methods.using_gcc(env):
if cc_version_major < 9:
print_error(
"Detected GCC version older than 9, which does not fully support "
"C++17, or has bugs when compiling Godot. Supported versions are 9 "
"and later. Use a newer GCC version, or Clang 6 or later by passing "
'"use_llvm=yes" to the SCons command line.'
)
Exit(255)
elif cc_version_metadata1 == "win32":
print_error(
"Detected mingw version is not using posix threads. Only posix "
"version of mingw is supported. "
'Use "update-alternatives --config x86_64-w64-mingw32-g++" '
"to switch to posix threads."
)
Exit(255)
elif methods.using_clang(env):
# Apple LLVM versions differ from upstream LLVM version \o/, compare
# in https://en.wikipedia.org/wiki/Xcode#Toolchain_versions
if methods.is_apple_clang(env):
if cc_version_major < 10:
print_error(
"Detected Apple Clang version older than 10, which does not fully "
"support C++17. Supported versions are Apple Clang 10 and later."
)
Exit(255)
else:
if cc_version_major < 6:
print_error(
"Detected Clang version older than 6, which does not fully support "
"C++17. Supported versions are Clang 6 and later."
)
Exit(255)
elif env.msvc:
# Ensure latest minor builds of Visual Studio 2017/2019.
# https://github.com/godotengine/godot/pull/94995#issuecomment-2336464574
if cc_version_major == 16 and cc_version_minor < 11:
print_error(
"Detected Visual Studio 2019 version older than 16.11, which has bugs "
"when compiling Godot. Use a newer VS2019 version, or VS2022."
)
Exit(255)
if cc_version_major == 15 and cc_version_minor < 9:
print_error(
"Detected Visual Studio 2017 version older than 15.9, which has bugs "
"when compiling Godot. Use a newer VS2017 version, or VS2019/VS2022."
)
Exit(255)
if cc_version_major < 15:
print_error(
"Detected Visual Studio 2015 or earlier, which is unsupported in Godot. "
"Supported versions are Visual Studio 2017 and later."
)
Exit(255)
# Set our C and C++ standard requirements.
# Prepending to make it possible to override
# This needs to come after `configure`, otherwise we don't have env.msvc.

View file

@ -19,6 +19,9 @@ from os.path import normpath, basename
# Get the "Godot" folder name ahead of time
base_folder_path = str(os.path.abspath(Path(__file__).parent)) + "/"
base_folder_only = os.path.basename(os.path.normpath(base_folder_path))
compiler_version_cache = None
# Listing all the folders we have converted
# for SCU in scu_builders.py
_scu_folders = set()
@ -1068,6 +1071,21 @@ def detect_darwin_sdk_path(platform, env):
raise
def is_apple_clang(env):
import shlex
if env["platform"] not in ["macos", "ios"]:
return False
if not using_clang(env):
return False
try:
version = subprocess.check_output(shlex.split(env.subst(env["CXX"])) + ["--version"]).strip().decode("utf-8")
except (subprocess.CalledProcessError, OSError):
print_warning("Couldn't parse CXX environment variable to infer compiler version.")
return False
return version.startswith("Apple")
def get_compiler_version(env):
"""
Returns an array of version numbers as ints: [major, minor, patch].
@ -1090,6 +1108,127 @@ def get_compiler_version(env):
return None
def get_compiler_version_ex(env):
"""
Returns a dictionary with various version information:
- major, minor, patch: Version following semantic versioning system
- metadata1, metadata2: Extra information
- date: Date of the build
"""
global compiler_version_cache
if compiler_version_cache is not None:
return compiler_version_cache
import shlex
ret = {
"major": -1,
"minor": -1,
"patch": -1,
"metadata1": "",
"metadata2": "",
"date": "",
"apple_major": -1,
"apple_minor": -1,
"apple_patch1": -1,
"apple_patch2": -1,
"apple_patch3": -1,
}
if env.msvc and not using_clang(env):
try:
# FIXME: `-latest` works for most cases, but there are edge-cases where this would
# benefit from a more nuanced search.
# https://github.com/godotengine/godot/pull/91069#issuecomment-2358956731
# https://github.com/godotengine/godot/pull/91069#issuecomment-2380836341
args = [
env["VSWHERE"],
"-latest",
"-prerelease",
"-products",
"*",
"-requires",
"Microsoft.Component.MSBuild",
"-utf8",
]
version = subprocess.check_output(args, encoding="utf-8").strip()
for line in version.splitlines():
split = line.split(":", 1)
if split[0] == "catalog_productDisplayVersion":
sem_ver = split[1].split(".")
ret["major"] = int(sem_ver[0])
ret["minor"] = int(sem_ver[1])
ret["patch"] = int(sem_ver[2].split()[0])
# Could potentially add section for determining preview version, but
# that can wait until metadata is actually used for something.
if split[0] == "catalog_buildVersion":
ret["metadata1"] = split[1]
except (subprocess.CalledProcessError, OSError):
print_warning("Couldn't find vswhere to determine compiler version.")
return update_compiler_version_cache(ret)
# Not using -dumpversion as some GCC distros only return major, and
# Clang used to return hardcoded 4.2.1: # https://reviews.llvm.org/D56803
try:
version = subprocess.check_output(
shlex.split(env.subst(env["CXX"]), posix=False) + ["--version"], shell=(os.name == "nt"), encoding="utf-8"
).strip()
except (subprocess.CalledProcessError, OSError):
print_warning("Couldn't parse CXX environment variable to infer compiler version.")
return update_compiler_version_cache(ret)
match = re.search(
r"(?:(?<=version )|(?<=\) )|(?<=^))"
r"(?P<major>\d+)"
r"(?:\.(?P<minor>\d*))?"
r"(?:\.(?P<patch>\d*))?"
r"(?:-(?P<metadata1>[0-9a-zA-Z-]*))?"
r"(?:\+(?P<metadata2>[0-9a-zA-Z-]*))?"
r"(?: (?P<date>[0-9]{8}|[0-9]{6})(?![0-9a-zA-Z]))?",
version,
)
if match is not None:
for key, value in match.groupdict().items():
if value is not None:
ret[key] = value
match_apple = re.search(
r"(?:(?<=clang-)|(?<=\) )|(?<=^))"
r"(?P<apple_major>\d+)"
r"(?:\.(?P<apple_minor>\d*))?"
r"(?:\.(?P<apple_patch1>\d*))?"
r"(?:\.(?P<apple_patch2>\d*))?"
r"(?:\.(?P<apple_patch3>\d*))?",
version,
)
if match_apple is not None:
for key, value in match_apple.groupdict().items():
if value is not None:
ret[key] = value
# Transform semantic versioning to integers
for key in [
"major",
"minor",
"patch",
"apple_major",
"apple_minor",
"apple_patch1",
"apple_patch2",
"apple_patch3",
]:
ret[key] = int(ret[key] or -1)
return update_compiler_version_cache(ret)
def update_compiler_version_cache(value):
global compiler_version_cache
compiler_version_cache = value
return value
def is_vanilla_clang(env):
if not using_clang(env):
return False