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("optimize", "Optimization type", "speed", ("speed", "size", "none")))
 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
 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.Append(LINKFLAGS=str(LINKFLAGS).split())
 
-    # Platform specific flags
+    # Platform specific flags.
+    # These can sometimes override default options.
     flag_list = platform_flags[selected_platform]
     for f in flag_list:
         if not (f[0] in ARGUMENTS) or ARGUMENTS[f[0]] == "auto":  # Allow command line to override platform flags
             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"]:
         env["verbose"] = methods.get_cmdline_bool("verbose", True)
         env["warnings"] = ARGUMENTS.get("warnings", "extra")
@@ -498,27 +479,43 @@ if selected_platform in platform_list:
             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)
+        # 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":
             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)
+
+    # 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":
         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
     cc_version = methods.get_compiler_version(env) or {
         "major": None,

+ 4 - 3
platform/android/detect.py

@@ -47,9 +47,6 @@ 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"),
     ]
 
 
@@ -136,6 +133,10 @@ def configure(env):
         env.Append(CPPFLAGS=["-UNDEBUG"])
 
     # 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"] == "thin":
             env.Append(CCFLAGS=["-flto=thin"])

+ 5 - 4
platform/ios/detect.py

@@ -39,9 +39,6 @@ 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"),
     ]
 
 
@@ -73,7 +70,11 @@ def configure(env):
         env.Append(CCFLAGS=["-gdwarf-2", "-O0"])
         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"] == "thin":
             env.Append(CCFLAGS=["-flto=thin"])

+ 4 - 0
platform/linuxbsd/detect.py

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

+ 6 - 3
platform/macos/detect.py

@@ -40,9 +40,6 @@ 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"),
     ]
 
 
@@ -170,6 +167,10 @@ def configure(env):
         env["AS"] = basecmd + "as"
 
     # 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"] == "thin":
             env.Append(CCFLAGS=["-flto=thin"])
@@ -178,6 +179,8 @@ def configure(env):
             env.Append(CCFLAGS=["-flto"])
             env.Append(LINKFLAGS=["-flto"])
 
+    # Sanitizers
+
     if env["use_ubsan"] or env["use_asan"] or env["use_tsan"]:
         env.extra_suffix += ".san"
         env.Append(CCFLAGS=["-DSANITIZERS_ENABLED"])

+ 4 - 0
platform/web/detect.py

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

+ 8 - 0
platform/windows/detect.py

@@ -448,6 +448,9 @@ def configure_msvc(env, vcvars_msvc_config):
 
     ## LTO
 
+    if env["lto"] == "auto":  # No LTO by default for MSVC, doesn't help.
+        env["lto"] = "none"
+
     if env["lto"] != "none":
         if env["lto"] == "thin":
             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"]):
             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"] == "thin":
             if not env["use_llvm"]: