Browse Source

Merge pull request #65745 from akien-mga/scons-production-lto-earlier

Refactor handling of `production` flag and per-platform LTO defaults
Rémi Verschelde 2 years ago
parent
commit
aa553f4030

+ 34 - 37
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("float", "Floating-point precision", "32", ("32", "64")))
 opts.Add(EnumVariable("optimize", "Optimization type", "speed", ("speed", "size", "none")))
 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("production", "Set defaults to build Godot for use in production", False))
-opts.Add(EnumVariable("lto", "Link-time optimization (for production buids)", "none", ("none", "thin", "full")))
+opts.Add(EnumVariable("lto", "Link-time optimization (for production buids)", "none", ("none", "auto", "thin", "full")))
 
 
 # Components
 # Components
 opts.Add(BoolVariable("deprecated", "Enable compatibility code for deprecated and removed features", True))
 opts.Add(BoolVariable("deprecated", "Enable compatibility code for deprecated and removed features", True))
@@ -460,36 +460,17 @@ if selected_platform in platform_list:
     env["LINKFLAGS"] = ""
     env["LINKFLAGS"] = ""
     env.Append(LINKFLAGS=str(LINKFLAGS).split())
     env.Append(LINKFLAGS=str(LINKFLAGS).split())
 
 
-    # Platform specific flags
+    # Platform specific flags.
+    # These can sometimes override default options.
     flag_list = platform_flags[selected_platform]
     flag_list = platform_flags[selected_platform]
     for f in flag_list:
     for f in flag_list:
         if not (f[0] in ARGUMENTS) or ARGUMENTS[f[0]] == "auto":  # Allow command line to override platform flags
         if not (f[0] in ARGUMENTS) or ARGUMENTS[f[0]] == "auto":  # Allow command line to override platform flags
             env[f[0]] = f[1]
             env[f[0]] = f[1]
 
 
-    # Must happen after the flags' definition, so that they can be used by platform detect
-    detect.configure(env)
-
-    print(
-        'Building for platform "%s", architecture "%s", %s, target "%s".'
-        % (selected_platform, env["arch"], "editor" if env["tools"] else "template", env["target"])
-    )
-
-    # Set our C and C++ standard requirements.
-    # C++17 is required as we need guaranteed copy elision as per GH-36436.
-    # Prepending to make it possible to override.
-    # This needs to come after `configure`, otherwise we don't have env.msvc.
-    if not env.msvc:
-        # Specifying GNU extensions support explicitly, which are supported by
-        # both GCC and Clang. Both currently default to gnu11 and gnu++14.
-        env.Prepend(CFLAGS=["-std=gnu11"])
-        env.Prepend(CXXFLAGS=["-std=gnu++17"])
-    else:
-        # MSVC doesn't have clear C standard support, /std only covers C++.
-        # 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.
+    # 'dev' and 'production' are aliases to set default options if they haven't been
+    # set manually by the user.
+    # These need to be checked *after* platform specific flags so that different
+    # default values can be set (e.g. to keep LTO off for `production` on some platforms).
     if env["dev"]:
     if env["dev"]:
         env["verbose"] = methods.get_cmdline_bool("verbose", True)
         env["verbose"] = methods.get_cmdline_bool("verbose", True)
         env["warnings"] = ARGUMENTS.get("warnings", "extra")
         env["warnings"] = ARGUMENTS.get("warnings", "extra")
@@ -498,27 +479,43 @@ if selected_platform in platform_list:
             env["tests"] = methods.get_cmdline_bool("tests", True)
             env["tests"] = methods.get_cmdline_bool("tests", True)
     if env["production"]:
     if env["production"]:
         env["use_static_cpp"] = methods.get_cmdline_bool("use_static_cpp", True)
         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)
         env["debug_symbols"] = methods.get_cmdline_bool("debug_symbols", False)
+        # LTO "auto" means we handle the preferred option in each platform detect.py.
+        env["lto"] = ARGUMENTS.get("lto", "auto")
         if not env["tools"] and env["target"] == "debug":
         if not env["tools"] and env["target"] == "debug":
             print(
             print(
                 "WARNING: Requested `production` build with `tools=no target=debug`, "
                 "WARNING: Requested `production` build with `tools=no target=debug`, "
                 "this will give you a full debug template (use `target=release_debug` "
                 "this will give you a full debug template (use `target=release_debug` "
                 "for an optimized template with debug features)."
                 "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)
+
+    # Must happen after the flags' definition, as configure is when most flags
+    # are actually handled to change compile options, etc.
+    detect.configure(env)
+
+    # Needs to happen after configure to handle "auto".
     if env["lto"] != "none":
     if env["lto"] != "none":
         print("Using LTO: " + env["lto"])
         print("Using LTO: " + env["lto"])
 
 
+    # Set our C and C++ standard requirements.
+    # C++17 is required as we need guaranteed copy elision as per GH-36436.
+    # Prepending to make it possible to override.
+    # This needs to come after `configure`, otherwise we don't have env.msvc.
+    if not env.msvc:
+        # Specifying GNU extensions support explicitly, which are supported by
+        # both GCC and Clang. Both currently default to gnu11 and gnu++14.
+        env.Prepend(CFLAGS=["-std=gnu11"])
+        env.Prepend(CXXFLAGS=["-std=gnu++17"])
+    else:
+        # MSVC doesn't have clear C standard support, /std only covers C++.
+        # We apply it to CCFLAGS (both C and C++ code) in case it impacts C features.
+        env.Prepend(CCFLAGS=["/std:c++17"])
+
+    print(
+        'Building for platform "%s", architecture "%s", %s, target "%s".'
+        % (selected_platform, env["arch"], "editor" if env["tools"] else "template", env["target"])
+    )
+
     # Enforce our minimal compiler version requirements
     # Enforce our minimal compiler version requirements
     cc_version = methods.get_compiler_version(env) or {
     cc_version = methods.get_compiler_version(env) or {
         "major": None,
         "major": None,

+ 4 - 3
platform/android/detect.py

@@ -47,9 +47,6 @@ def get_flags():
     return [
     return [
         ("arch", "arm64"),  # Default for convenience.
         ("arch", "arm64"),  # Default for convenience.
         ("tools", False),
         ("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"),
     ]
     ]
 
 
 
 
@@ -136,6 +133,10 @@ def configure(env):
         env.Append(CPPFLAGS=["-UNDEBUG"])
         env.Append(CPPFLAGS=["-UNDEBUG"])
 
 
     # LTO
     # LTO
+
+    if env["lto"] == "auto":  # LTO benefits for Android (size, performance) haven't been clearly established yet.
+        env["lto"] = "none"
+
     if env["lto"] != "none":
     if env["lto"] != "none":
         if env["lto"] == "thin":
         if env["lto"] == "thin":
             env.Append(CCFLAGS=["-flto=thin"])
             env.Append(CCFLAGS=["-flto=thin"])

+ 5 - 4
platform/ios/detect.py

@@ -39,9 +39,6 @@ def get_flags():
         ("arch", "arm64"),  # Default for convenience.
         ("arch", "arm64"),  # Default for convenience.
         ("tools", False),
         ("tools", False),
         ("use_volk", 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"),
     ]
     ]
 
 
 
 
@@ -73,7 +70,11 @@ def configure(env):
         env.Append(CCFLAGS=["-gdwarf-2", "-O0"])
         env.Append(CCFLAGS=["-gdwarf-2", "-O0"])
         env.Append(CPPDEFINES=["_DEBUG", ("DEBUG", 1)])
         env.Append(CPPDEFINES=["_DEBUG", ("DEBUG", 1)])
 
 
-    # LTO
+    ## LTO
+
+    if env["lto"] == "auto":  # Disable by default as it makes linking in Xcode very slow.
+        env["lto"] = "none"
+
     if env["lto"] != "none":
     if env["lto"] != "none":
         if env["lto"] == "thin":
         if env["lto"] == "thin":
             env.Append(CCFLAGS=["-flto=thin"])
             env.Append(CCFLAGS=["-flto=thin"])

+ 4 - 0
platform/linuxbsd/detect.py

@@ -171,6 +171,10 @@ def configure(env):
             env.Append(LINKFLAGS=["-fsanitize=memory"])
             env.Append(LINKFLAGS=["-fsanitize=memory"])
 
 
     # LTO
     # LTO
+
+    if env["lto"] == "auto":  # Full LTO for production.
+        env["lto"] = "full"
+
     if env["lto"] != "none":
     if env["lto"] != "none":
         if env["lto"] == "thin":
         if env["lto"] == "thin":
             if not env["use_llvm"]:
             if not env["use_llvm"]:

+ 6 - 3
platform/macos/detect.py

@@ -40,9 +40,6 @@ def get_flags():
     return [
     return [
         ("arch", detect_arch()),
         ("arch", detect_arch()),
         ("use_volk", False),
         ("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"),
     ]
     ]
 
 
 
 
@@ -170,6 +167,10 @@ def configure(env):
         env["AS"] = basecmd + "as"
         env["AS"] = basecmd + "as"
 
 
     # LTO
     # LTO
+
+    if env["lto"] == "auto":  # LTO benefits for macOS (size, performance) haven't been clearly established yet.
+        env["lto"] = "none"
+
     if env["lto"] != "none":
     if env["lto"] != "none":
         if env["lto"] == "thin":
         if env["lto"] == "thin":
             env.Append(CCFLAGS=["-flto=thin"])
             env.Append(CCFLAGS=["-flto=thin"])
@@ -178,6 +179,8 @@ def configure(env):
             env.Append(CCFLAGS=["-flto"])
             env.Append(CCFLAGS=["-flto"])
             env.Append(LINKFLAGS=["-flto"])
             env.Append(LINKFLAGS=["-flto"])
 
 
+    # Sanitizers
+
     if env["use_ubsan"] or env["use_asan"] or env["use_tsan"]:
     if env["use_ubsan"] or env["use_asan"] or env["use_tsan"]:
         env.extra_suffix += ".san"
         env.extra_suffix += ".san"
         env.Append(CCFLAGS=["-DSANITIZERS_ENABLED"])
         env.Append(CCFLAGS=["-DSANITIZERS_ENABLED"])

+ 4 - 0
platform/web/detect.py

@@ -109,6 +109,10 @@ def configure(env):
     env["ENV"] = os.environ
     env["ENV"] = os.environ
 
 
     # LTO
     # LTO
+
+    if env["lto"] == "auto":  # Full LTO for production.
+        env["lto"] = "full"
+
     if env["lto"] != "none":
     if env["lto"] != "none":
         if env["lto"] == "thin":
         if env["lto"] == "thin":
             env.Append(CCFLAGS=["-flto=thin"])
             env.Append(CCFLAGS=["-flto=thin"])

+ 8 - 0
platform/windows/detect.py

@@ -448,6 +448,9 @@ def configure_msvc(env, vcvars_msvc_config):
 
 
     ## LTO
     ## LTO
 
 
+    if env["lto"] == "auto":  # No LTO by default for MSVC, doesn't help.
+        env["lto"] = "none"
+
     if env["lto"] != "none":
     if env["lto"] != "none":
         if env["lto"] == "thin":
         if env["lto"] == "thin":
             print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
             print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
@@ -564,6 +567,11 @@ def configure_mingw(env):
         if try_cmd("gcc-ranlib --version", env["mingw_prefix"], env["arch"]):
         if try_cmd("gcc-ranlib --version", env["mingw_prefix"], env["arch"]):
             env["RANLIB"] = mingw_bin_prefix + "gcc-ranlib"
             env["RANLIB"] = mingw_bin_prefix + "gcc-ranlib"
 
 
+    ## LTO
+
+    if env["lto"] == "auto":  # Full LTO for production with MinGW.
+        env["lto"] = "full"
+
     if env["lto"] != "none":
     if env["lto"] != "none":
         if env["lto"] == "thin":
         if env["lto"] == "thin":
             if not env["use_llvm"]:
             if not env["use_llvm"]: