Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions tools/common_compiler_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"])
Expand Down
14 changes: 14 additions & 0 deletions tools/godotcpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
165 changes: 164 additions & 1 deletion tools/linux.py
Original file line number Diff line number Diff line change
@@ -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<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 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)
Expand All @@ -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'"])

Expand Down
Loading