From 04d79ba3b704e08fcec90489ef39c5d072bc32e5 Mon Sep 17 00:00:00 2001 From: dementive <87823030+dementive@users.noreply.github.com> Date: Mon, 8 Sep 2025 16:37:31 -0500 Subject: [PATCH] Add missing scons flags --- tools/common_compiler_flags.py | 26 ++++++ tools/godotcpp.py | 14 +++ tools/linux.py | 165 ++++++++++++++++++++++++++++++++- 3 files changed, 204 insertions(+), 1 deletion(-) diff --git a/tools/common_compiler_flags.py b/tools/common_compiler_flags.py index 05f95d7a3..1e0f47124 100644 --- a/tools/common_compiler_flags.py +++ b/tools/common_compiler_flags.py @@ -30,6 +30,32 @@ def generate(env): if env["lto"] != "none": print("Using LTO: " + env["lto"]) + # Environment flags + env.Append(CPPDEFINES=env.get("cppdefines", "").split()) + env.Append(CCFLAGS=env.get("ccflags", "").split()) + env.Append(CXXFLAGS=env.get("cxxflags", "").split()) + env.Append(CFLAGS=env.get("cflags", "").split()) + env.Append(LINKFLAGS=env.get("linkflags", "").split()) + env.Append(ASFLAGS=env.get("asflags", "").split()) + env.Append(ARFLAGS=env.get("arflags", "").split()) + env.Append(RCFLAGS=env.get("rcflags", "").split()) + + # Prepend compiler launchers + if "c_compiler_launcher" in env: + env["CC"] = " ".join([env["c_compiler_launcher"], env["CC"]]) + + if "cpp_compiler_launcher" in env: + env["CXX"] = " ".join([env["cpp_compiler_launcher"], env["CXX"]]) + + # SCons speed optimization controlled by the `fast_unsafe` option, which provide + # more than 10 s speed up for incremental rebuilds. + # Unsafe as they reduce the certainty of rebuilding all changed files, so it's + # enabled by default for `debug` builds, and can be overridden from command line. + # Ref: https://github.com/SCons/scons/wiki/GoFastButton + if env.get("fast_unsafe", False): + env.SetOption("implicit_cache", 1) + env.SetOption("max_drift", 60) + # Require C++17 if env.get("is_msvc", False): env.Append(CXXFLAGS=["/std:c++17"]) diff --git a/tools/godotcpp.py b/tools/godotcpp.py index 7be96247b..29bdf15a7 100644 --- a/tools/godotcpp.py +++ b/tools/godotcpp.py @@ -241,6 +241,20 @@ def options(opts, env): ) ) + opts.Add("cppdefines", "Custom defines for the pre-processor") + opts.Add("ccflags", "Custom flags for both the C and C++ compilers") + opts.Add("cxxflags", "Custom flags for the C++ compiler") + opts.Add("cflags", "Custom flags for the C compiler") + opts.Add("linkflags", "Custom flags for the linker") + opts.Add("asflags", "Custom flags for the assembler") + opts.Add("arflags", "Custom flags for the archive tool") + opts.Add("rcflags", "Custom flags for Windows resource compiler") + + opts.Add("c_compiler_launcher", "C compiler launcher (e.g. `ccache`)") + opts.Add("cpp_compiler_launcher", "C++ compiler launcher (e.g. `ccache`)") + + opts.Add(BoolVariable("fast_unsafe", "Enable unsafe options for faster rebuilds", False)) + # Editor and template_debug are compatible (i.e. you can use the same binary for Godot editor builds and Godot debug templates). # Godot release templates are only compatible with "template_release" builds. # For this reason, we default to template_debug builds, unlike Godot which defaults to editor builds. diff --git a/tools/linux.py b/tools/linux.py index ae8019877..e8af05d02 100644 --- a/tools/linux.py +++ b/tools/linux.py @@ -1,17 +1,152 @@ +import os +import re +import subprocess +import sys + import common_compiler_flags from SCons.Tool import clang, clangxx -from SCons.Variables import BoolVariable +from SCons.Variables import BoolVariable, EnumVariable + +compiler_version_cache = None + + +def get_compiler_version(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 env["use_llvm"]: + 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("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("Couldn't parse CXX environment variable to infer compiler version.") + return update_compiler_version_cache(ret) + + match = re.search( + r"(?:(?<=version )|(?<=\) )|(?<=^))" + r"(?P\d+)" + r"(?:\.(?P\d*))?" + r"(?:\.(?P\d*))?" + r"(?:-(?P[0-9a-zA-Z-]*))?" + r"(?:\+(?P[0-9a-zA-Z-]*))?" + r"(?: (?P[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\d+)" + r"(?:\.(?P\d*))?" + r"(?:\.(?P\d*))?" + r"(?:\.(?P\d*))?" + r"(?:\.(?P\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 options(opts): opts.Add(BoolVariable("use_llvm", "Use the LLVM compiler - only effective when targeting Linux", False)) opts.Add(BoolVariable("use_static_cpp", "Link libgcc and libstdc++ statically for better portability", True)) + opts.Add( + EnumVariable("linker", "Linker program", "default", ["default", "bfd", "gold", "lld", "mold"], ignorecase=2), + ) def exists(env): return True +def using_gcc(env) -> bool: + return not env["use_llvm"] + + def generate(env): if env["use_llvm"]: clang.generate(env) @@ -20,6 +155,34 @@ def generate(env): # Required for extensions to truly unload. env.Append(CXXFLAGS=["-fno-gnu-unique"]) + if env["linker"] != "default": + print("Using linker program: " + env["linker"]) + if env["linker"] == "mold" and using_gcc(env): # GCC < 12.1 doesn't support -fuse-ld=mold. + cc_version = get_compiler_version(env) + cc_semver = (cc_version["major"], cc_version["minor"]) + if cc_semver < (12, 1): + found_wrapper = False + for path in ["/usr/libexec", "/usr/local/libexec", "/usr/lib", "/usr/local/lib"]: + if os.path.isfile(path + "/mold/ld"): + env.Append(LINKFLAGS=["-B" + path + "/mold"]) + found_wrapper = True + break + if not found_wrapper: + for path in os.environ["PATH"].split(os.pathsep): + if os.path.isfile(path + "/ld.mold"): + env.Append(LINKFLAGS=["-B" + path]) + found_wrapper = True + break + if not found_wrapper: + print( + "Couldn't locate mold installation path. Make sure it's installed in /usr, /usr/local or in PATH environment variable." + ) + sys.exit(255) + else: + env.Append(LINKFLAGS=["-fuse-ld=mold"]) + else: + env.Append(LINKFLAGS=["-fuse-ld=%s" % env["linker"]]) + env.Append(CCFLAGS=["-fPIC", "-Wwrite-strings"]) env.Append(LINKFLAGS=["-Wl,-R,'$$ORIGIN'"])