summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRémi Verschelde <rverschelde@gmail.com>2022-07-21 15:15:54 +0200
committerRémi Verschelde <rverschelde@gmail.com>2022-09-08 10:00:02 +0200
commitc2c659db326591519d451d368c4e33c78bb9c1fa (patch)
tree3c93bbc143612aa2d5487b9c0918b74629bdd714
parent69233093d7e6479b5130bf2c39cbf464a6809c1b (diff)
SCons: Refactor LTO options with `lto=<none|thin|full>`
Adds support for LTO on macOS and Android. We don't have much experience with LTO on these platforms so for now we keep it disabled by default even when `production=yes` is set. Similarly for iOS where we ship object files for the user to link in Xcode so LTO makes builds extremely slow to link. `production=yes` defaults to full LTO. ThinLTO is much faster for LLVM-based compilers but seems to produce bigger binaries (at least for the Web platform).
-rw-r--r--SConstruct62
-rw-r--r--platform/android/detect.py12
-rw-r--r--platform/ios/detect.py14
-rw-r--r--platform/linuxbsd/detect.py16
-rw-r--r--platform/macos/detect.py12
-rw-r--r--platform/web/detect.py14
-rw-r--r--platform/windows/detect.py24
7 files changed, 94 insertions, 60 deletions
diff --git a/SConstruct b/SConstruct
index ce586010f4..f7504f72e1 100644
--- a/SConstruct
+++ b/SConstruct
@@ -170,7 +170,7 @@ opts.Add(EnumVariable("arch", "CPU architecture", "auto", ["auto"] + architectur
opts.Add(EnumVariable("float", "Floating-point precision", "32", ("32", "64")))
opts.Add(EnumVariable("optimize", "Optimization type", "speed", ("speed", "size", "none")))
opts.Add(BoolVariable("production", "Set defaults to build Godot for use in production", False))
-opts.Add(BoolVariable("use_lto", "Use link-time optimization", False))
+opts.Add(EnumVariable("lto", "Link-time optimization (for production buids)", "none", ("none", "thin", "full")))
# Components
opts.Add(BoolVariable("deprecated", "Enable compatibility code for deprecated and removed features", True))
@@ -438,35 +438,6 @@ if selected_platform in platform_list:
)
env.SetOption("num_jobs", safer_cpu_count)
- # 'dev' and 'production' are aliases to set default options if they haven't been set
- # manually by the user.
- if env["dev"]:
- env["verbose"] = methods.get_cmdline_bool("verbose", True)
- env["warnings"] = ARGUMENTS.get("warnings", "extra")
- env["werror"] = methods.get_cmdline_bool("werror", True)
- if env["tools"]:
- env["tests"] = methods.get_cmdline_bool("tests", True)
- if env["production"]:
- env["use_static_cpp"] = methods.get_cmdline_bool("use_static_cpp", True)
- env["use_lto"] = methods.get_cmdline_bool("use_lto", True)
- env["debug_symbols"] = methods.get_cmdline_bool("debug_symbols", False)
- if not env["tools"] and env["target"] == "debug":
- print(
- "WARNING: Requested `production` build with `tools=no target=debug`, "
- "this will give you a full debug template (use `target=release_debug` "
- "for an optimized template with debug features)."
- )
- if env.msvc:
- print(
- "WARNING: For `production` Windows builds, you should use MinGW with GCC "
- "or Clang instead of Visual Studio, as they can better optimize the "
- "GDScript VM in a very significant way. MSVC LTO also doesn't work "
- "reliably for our use case."
- "If you want to use MSVC nevertheless for production builds, set "
- "`debug_symbols=no use_lto=no` instead of the `production=yes` option."
- )
- Exit(255)
-
env.extra_suffix = ""
if env["extra_suffix"] != "":
@@ -517,6 +488,37 @@ if selected_platform in platform_list:
# We apply it to CCFLAGS (both C and C++ code) in case it impacts C features.
env.Prepend(CCFLAGS=["/std:c++17"])
+ # 'dev' and 'production' are aliases to set default options if they haven't been set
+ # manually by the user.
+ if env["dev"]:
+ env["verbose"] = methods.get_cmdline_bool("verbose", True)
+ env["warnings"] = ARGUMENTS.get("warnings", "extra")
+ env["werror"] = methods.get_cmdline_bool("werror", True)
+ if env["tools"]:
+ env["tests"] = methods.get_cmdline_bool("tests", True)
+ if env["production"]:
+ env["use_static_cpp"] = methods.get_cmdline_bool("use_static_cpp", True)
+ env["lto"] = ARGUMENTS.get("lto", "full")
+ env["debug_symbols"] = methods.get_cmdline_bool("debug_symbols", False)
+ if not env["tools"] and env["target"] == "debug":
+ print(
+ "WARNING: Requested `production` build with `tools=no target=debug`, "
+ "this will give you a full debug template (use `target=release_debug` "
+ "for an optimized template with debug features)."
+ )
+ if env.msvc:
+ print(
+ "WARNING: For `production` Windows builds, you should use MinGW with GCC "
+ "or Clang instead of Visual Studio, as they can better optimize the "
+ "GDScript VM in a very significant way. MSVC LTO also doesn't work "
+ "reliably for our use case."
+ "If you want to use MSVC nevertheless for production builds, set "
+ "`debug_symbols=no lto=none` instead of the `production=yes` option."
+ )
+ Exit(255)
+ if env["lto"] != "none":
+ print("Using LTO: " + env["lto"])
+
# Enforce our minimal compiler version requirements
cc_version = methods.get_compiler_version(env) or {
"major": None,
diff --git a/platform/android/detect.py b/platform/android/detect.py
index ad63821162..1d9bcdd932 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -47,6 +47,9 @@ def get_flags():
return [
("arch", "arm64"), # Default for convenience.
("tools", False),
+ # Benefits of LTO for Android (size, performance) haven't been clearly established yet.
+ # So for now we override the default value which may be set when using `production=yes`.
+ ("lto", "none"),
]
@@ -132,6 +135,15 @@ def configure(env):
env.Append(CPPDEFINES=["_DEBUG"])
env.Append(CPPFLAGS=["-UNDEBUG"])
+ # LTO
+ if env["lto"] != "none":
+ if env["lto"] == "thin":
+ env.Append(CCFLAGS=["-flto=thin"])
+ env.Append(LINKFLAGS=["-flto=thin"])
+ else:
+ env.Append(CCFLAGS=["-flto"])
+ env.Append(LINKFLAGS=["-flto"])
+
# Compiler configuration
env["SHLIBSUFFIX"] = ".so"
diff --git a/platform/ios/detect.py b/platform/ios/detect.py
index 1a8d24d12d..3cfb25cf61 100644
--- a/platform/ios/detect.py
+++ b/platform/ios/detect.py
@@ -39,6 +39,9 @@ def get_flags():
("arch", "arm64"), # Default for convenience.
("tools", False),
("use_volk", False),
+ # Disable by default even if production is set, as it makes linking in Xcode
+ # on exports very slow and that's not what most users expect.
+ ("lto", "none"),
]
@@ -70,9 +73,14 @@ def configure(env):
env.Append(CCFLAGS=["-gdwarf-2", "-O0"])
env.Append(CPPDEFINES=["_DEBUG", ("DEBUG", 1)])
- if env["use_lto"]:
- env.Append(CCFLAGS=["-flto"])
- env.Append(LINKFLAGS=["-flto"])
+ # LTO
+ if env["lto"] != "none":
+ if env["lto"] == "thin":
+ env.Append(CCFLAGS=["-flto=thin"])
+ env.Append(LINKFLAGS=["-flto=thin"])
+ else:
+ env.Append(CCFLAGS=["-flto"])
+ env.Append(LINKFLAGS=["-flto"])
## Compiler configuration
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index f5f7e65417..36644d5f29 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -31,7 +31,6 @@ def get_opts():
return [
EnumVariable("linker", "Linker program", "default", ("default", "bfd", "gold", "lld", "mold")),
BoolVariable("use_llvm", "Use the LLVM compiler", False),
- BoolVariable("use_thinlto", "Use ThinLTO (LLVM only, requires linker=lld, implies use_lto=yes)", False),
BoolVariable("use_static_cpp", "Link libgcc and libstdc++ statically for better portability", True),
BoolVariable("use_coverage", "Test Godot coverage", False),
BoolVariable("use_ubsan", "Use LLVM/GCC compiler undefined behavior sanitizer (UBSAN)", False),
@@ -129,13 +128,6 @@ def configure(env):
else:
env.Append(LINKFLAGS=["-fuse-ld=%s" % env["linker"]])
- if env["use_thinlto"]:
- if not env["use_llvm"] or env["linker"] != "lld":
- print("ThinLTO is only compatible with LLVM and the LLD linker, use `use_llvm=yes linker=lld`.")
- sys.exit(255)
- else:
- env["use_lto"] = True # ThinLTO implies LTO
-
if env["use_coverage"]:
env.Append(CCFLAGS=["-ftest-coverage", "-fprofile-arcs"])
env.Append(LINKFLAGS=["-ftest-coverage", "-fprofile-arcs"])
@@ -178,8 +170,12 @@ def configure(env):
env.Append(CCFLAGS=["-fsanitize-recover=memory"])
env.Append(LINKFLAGS=["-fsanitize=memory"])
- if env["use_lto"]:
- if env["use_thinlto"]:
+ # LTO
+ 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:
diff --git a/platform/macos/detect.py b/platform/macos/detect.py
index bd89e8d59f..bcf4776609 100644
--- a/platform/macos/detect.py
+++ b/platform/macos/detect.py
@@ -40,6 +40,9 @@ def get_flags():
return [
("arch", detect_arch()),
("use_volk", False),
+ # Benefits of LTO for macOS (size, performance) haven't been clearly established yet.
+ # So for now we override the default value which may be set when using `production=yes`.
+ ("lto", "none"),
]
@@ -166,6 +169,15 @@ def configure(env):
env["RANLIB"] = basecmd + "ranlib"
env["AS"] = basecmd + "as"
+ # LTO
+ if env["lto"] != "none":
+ if env["lto"] == "thin":
+ env.Append(CCFLAGS=["-flto=thin"])
+ env.Append(LINKFLAGS=["-flto=thin"])
+ else:
+ env.Append(CCFLAGS=["-flto"])
+ env.Append(LINKFLAGS=["-flto"])
+
if env["use_ubsan"] or env["use_asan"] or env["use_tsan"]:
env.extra_suffix += ".san"
env.Append(CCFLAGS=["-DSANITIZERS_ENABLED"])
diff --git a/platform/web/detect.py b/platform/web/detect.py
index b1c1dd48a9..e055af8400 100644
--- a/platform/web/detect.py
+++ b/platform/web/detect.py
@@ -31,7 +31,6 @@ def get_opts():
return [
("initial_memory", "Initial WASM memory (in MiB)", 32),
BoolVariable("use_assertions", "Use Emscripten runtime assertions", False),
- BoolVariable("use_thinlto", "Use ThinLTO", False),
BoolVariable("use_ubsan", "Use Emscripten undefined behavior sanitizer (UBSAN)", False),
BoolVariable("use_asan", "Use Emscripten address sanitizer (ASAN)", False),
BoolVariable("use_lsan", "Use Emscripten leak sanitizer (LSAN)", False),
@@ -110,12 +109,13 @@ def configure(env):
env["ENV"] = os.environ
# LTO
- if env["use_thinlto"]:
- env.Append(CCFLAGS=["-flto=thin"])
- env.Append(LINKFLAGS=["-flto=thin"])
- elif env["use_lto"]:
- env.Append(CCFLAGS=["-flto=full"])
- env.Append(LINKFLAGS=["-flto=full"])
+ if env["lto"] != "none":
+ if env["lto"] == "thin":
+ env.Append(CCFLAGS=["-flto=thin"])
+ env.Append(LINKFLAGS=["-flto=thin"])
+ else:
+ env.Append(CCFLAGS=["-flto"])
+ env.Append(LINKFLAGS=["-flto"])
# Sanitizers
if env["use_ubsan"]:
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 5607eab342..e6e1874fc0 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -191,7 +191,6 @@ def get_opts():
),
BoolVariable("use_mingw", "Use the Mingw compiler, even if MSVC is installed.", False),
BoolVariable("use_llvm", "Use the LLVM compiler", False),
- BoolVariable("use_thinlto", "Use ThinLTO", False),
BoolVariable("use_static_cpp", "Link MinGW/MSVC C++ runtime libraries statically", True),
BoolVariable("use_asan", "Use address sanitizer (ASAN)", False),
]
@@ -449,7 +448,10 @@ def configure_msvc(env, vcvars_msvc_config):
## LTO
- if env["use_lto"]:
+ if env["lto"] != "none":
+ if env["lto"] == "thin":
+ print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
+ sys.exit(255)
env.AppendUnique(CCFLAGS=["/GL"])
env.AppendUnique(ARFLAGS=["/LTCG"])
if env["progress"]:
@@ -562,17 +564,19 @@ def configure_mingw(env):
if try_cmd("gcc-ranlib --version", env["mingw_prefix"], env["arch"]):
env["RANLIB"] = mingw_bin_prefix + "gcc-ranlib"
- if env["use_lto"]:
- if not env["use_llvm"] and env.GetOption("num_jobs") > 1:
+ 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:
- if env["use_thinlto"]:
- env.Append(CCFLAGS=["-flto=thin"])
- env.Append(LINKFLAGS=["-flto=thin"])
- else:
- env.Append(CCFLAGS=["-flto"])
- env.Append(LINKFLAGS=["-flto"])
+ env.Append(CCFLAGS=["-flto"])
+ env.Append(LINKFLAGS=["-flto"])
env.Append(LINKFLAGS=["-Wl,--stack," + str(STACK_SIZE)])