[SCons] Split `targets.py`, apply flags from tools

Split `targets` tool logic, moving all the compiler-specific flags to a
new `common_compiler_flags.py` file, and everything else (CPPDEFINES,
optimize option logic, dev build logic, etc) to the `godotcpp` tool.

The default tools now apply the common compiler flags by importing the
file and explicitly calling `configure`.

(cherry picked from commit 16df4bff30)
pull/1410/head
Fabio Alessandrelli 2024-02-14 15:32:27 +01:00 committed by David Snopek
parent 1b8cfaab71
commit 1517a24f72
9 changed files with 177 additions and 177 deletions

View File

@ -1,6 +1,7 @@
import os import os
import sys import sys
import my_spawn import my_spawn
import common_compiler_flags
from SCons.Script import ARGUMENTS from SCons.Script import ARGUMENTS
@ -118,3 +119,5 @@ def generate(env):
env.Append(LINKFLAGS=["--target=" + arch_info["target"] + env["android_api_level"], "-march=" + arch_info["march"]]) env.Append(LINKFLAGS=["--target=" + arch_info["target"] + env["android_api_level"], "-march=" + arch_info["march"]])
env.Append(CPPDEFINES=["ANDROID_ENABLED", "UNIX_ENABLED"]) env.Append(CPPDEFINES=["ANDROID_ENABLED", "UNIX_ENABLED"])
common_compiler_flags.generate(env)

View File

@ -0,0 +1,94 @@
import os
import subprocess
import sys
def using_clang(env):
return "clang" in os.path.basename(env["CC"])
def is_vanilla_clang(env):
if not using_clang(env):
return False
try:
version = subprocess.check_output([env.subst(env["CXX"]), "--version"]).strip().decode("utf-8")
except (subprocess.CalledProcessError, OSError):
print("Couldn't parse CXX environment variable to infer compiler version.")
return False
return not version.startswith("Apple")
def exists(env):
return True
def generate(env):
# Require C++17
if env.get("is_msvc", False):
env.Append(CXXFLAGS=["/std:c++17"])
else:
env.Append(CXXFLAGS=["-std=c++17"])
# Disable exception handling. Godot doesn't use exceptions anywhere, and this
# saves around 20% of binary size and very significant build time.
if env["disable_exceptions"]:
if env.get("is_msvc", False):
env.Append(CPPDEFINES=[("_HAS_EXCEPTIONS", 0)])
else:
env.Append(CXXFLAGS=["-fno-exceptions"])
elif env.get("is_msvc", False):
env.Append(CXXFLAGS=["/EHsc"])
if not env.get("is_msvc", False):
if env["symbols_visibility"] == "visible":
env.Append(CCFLAGS=["-fvisibility=default"])
env.Append(LINKFLAGS=["-fvisibility=default"])
elif env["symbols_visibility"] == "hidden":
env.Append(CCFLAGS=["-fvisibility=hidden"])
env.Append(LINKFLAGS=["-fvisibility=hidden"])
# Set optimize and debug_symbols flags.
# "custom" means do nothing and let users set their own optimization flags.
if env.get("is_msvc", False):
if env["debug_symbols"]:
env.Append(CCFLAGS=["/Zi", "/FS"])
env.Append(LINKFLAGS=["/DEBUG:FULL"])
if env["optimize"] == "speed":
env.Append(CCFLAGS=["/O2"])
env.Append(LINKFLAGS=["/OPT:REF"])
elif env["optimize"] == "speed_trace":
env.Append(CCFLAGS=["/O2"])
env.Append(LINKFLAGS=["/OPT:REF", "/OPT:NOICF"])
elif env["optimize"] == "size":
env.Append(CCFLAGS=["/O1"])
env.Append(LINKFLAGS=["/OPT:REF"])
elif env["optimize"] == "debug" or env["optimize"] == "none":
env.Append(CCFLAGS=["/Od"])
else:
if env["debug_symbols"]:
# Adding dwarf-4 explicitly makes stacktraces work with clang builds,
# otherwise addr2line doesn't understand them.
env.Append(CCFLAGS=["-gdwarf-4"])
if env.dev_build:
env.Append(CCFLAGS=["-g3"])
else:
env.Append(CCFLAGS=["-g2"])
else:
if using_clang(env) and not is_vanilla_clang(env):
# Apple Clang, its linker doesn't like -s.
env.Append(LINKFLAGS=["-Wl,-S", "-Wl,-x", "-Wl,-dead_strip"])
else:
env.Append(LINKFLAGS=["-s"])
if env["optimize"] == "speed":
env.Append(CCFLAGS=["-O3"])
# `-O2` is friendlier to debuggers than `-O3`, leading to better crash backtraces.
elif env["optimize"] == "speed_trace":
env.Append(CCFLAGS=["-O2"])
elif env["optimize"] == "size":
env.Append(CCFLAGS=["-Os"])
elif env["optimize"] == "debug":
env.Append(CCFLAGS=["-Og"])
elif env["optimize"] == "none":
env.Append(CCFLAGS=["-O0"])

View File

@ -1,9 +1,12 @@
import os, sys, platform import os, sys, platform
from SCons.Variables import EnumVariable, PathVariable, BoolVariable from SCons.Variables import EnumVariable, PathVariable, BoolVariable
from SCons.Variables.BoolVariable import _text2bool
from SCons.Tool import Tool from SCons.Tool import Tool
from SCons.Builder import Builder from SCons.Builder import Builder
from SCons.Errors import UserError from SCons.Errors import UserError
from SCons.Script import ARGUMENTS
from binding_generator import scons_generate_bindings, scons_emit_files from binding_generator import scons_generate_bindings, scons_emit_files
@ -14,6 +17,17 @@ def add_sources(sources, dir, extension):
sources.append(dir + "/" + f) sources.append(dir + "/" + f)
def get_cmdline_bool(option, default):
"""We use `ARGUMENTS.get()` to check if options were manually overridden on the command line,
and SCons' _text2bool helper to convert them to booleans, otherwise they're handled as strings.
"""
cmdline_val = ARGUMENTS.get(option)
if cmdline_val is not None:
return _text2bool(cmdline_val)
else:
return default
def normalize_path(val, env): def normalize_path(val, env):
return val if os.path.isabs(val) else os.path.join(env.Dir("#").abspath, val) return val if os.path.isabs(val) else os.path.join(env.Dir("#").abspath, val)
@ -230,16 +244,23 @@ def options(opts, env):
) )
) )
opts.Add(
EnumVariable(
"optimize",
"The desired optimization flags",
"speed_trace",
("none", "custom", "debug", "speed", "speed_trace", "size"),
)
)
opts.Add(BoolVariable("debug_symbols", "Build with debugging symbols", True))
opts.Add(BoolVariable("dev_build", "Developer build with dev-only debugging code (DEV_ENABLED)", False))
# Add platform options (custom tools can override platforms) # Add platform options (custom tools can override platforms)
for pl in sorted(set(platforms + custom_platforms)): for pl in sorted(set(platforms + custom_platforms)):
tool = Tool(pl, toolpath=get_platform_tools_paths(env)) tool = Tool(pl, toolpath=get_platform_tools_paths(env))
if hasattr(tool, "options"): if hasattr(tool, "options"):
tool.options(opts) tool.options(opts)
# Targets flags tool (optimizations, debug symbols)
target_tool = Tool("targets", toolpath=["tools"])
target_tool.options(opts)
def generate(env): def generate(env):
# Default num_jobs to local cpu count if not user specified. # Default num_jobs to local cpu count if not user specified.
@ -286,10 +307,21 @@ def generate(env):
print("Building for architecture " + env["arch"] + " on platform " + env["platform"]) print("Building for architecture " + env["arch"] + " on platform " + env["platform"])
if env.get("use_hot_reload") is None: # These defaults may be needed by platform tools
env["use_hot_reload"] = env["target"] != "template_release" env.use_hot_reload = env.get("use_hot_reload", env["target"] != "template_release")
if env["use_hot_reload"]: env.editor_build = env["target"] == "editor"
env.Append(CPPDEFINES=["HOT_RELOAD_ENABLED"]) env.dev_build = env["dev_build"]
env.debug_features = env["target"] in ["editor", "template_debug"]
if env.dev_build:
opt_level = "none"
elif env.debug_features:
opt_level = "speed_trace"
else: # Release
opt_level = "speed"
env["optimize"] = ARGUMENTS.get("optimize", opt_level)
env["debug_symbols"] = get_cmdline_bool("debug_symbols", env.dev_build)
tool = Tool(env["platform"], toolpath=get_platform_tools_paths(env)) tool = Tool(env["platform"], toolpath=get_platform_tools_paths(env))
@ -297,32 +329,34 @@ def generate(env):
raise ValueError("Required toolchain not found for platform " + env["platform"]) raise ValueError("Required toolchain not found for platform " + env["platform"])
tool.generate(env) tool.generate(env)
target_tool = Tool("targets", toolpath=["tools"])
target_tool.generate(env)
# Disable exception handling. Godot doesn't use exceptions anywhere, and this if env.use_hot_reload:
# saves around 20% of binary size and very significant build time. env.Append(CPPDEFINES=["HOT_RELOAD_ENABLED"])
if env["disable_exceptions"]:
if env.get("is_msvc", False):
env.Append(CPPDEFINES=[("_HAS_EXCEPTIONS", 0)])
else:
env.Append(CXXFLAGS=["-fno-exceptions"])
elif env.get("is_msvc", False):
env.Append(CXXFLAGS=["/EHsc"])
if not env.get("is_msvc", False): if env.editor_build:
if env["symbols_visibility"] == "visible": env.Append(CPPDEFINES=["TOOLS_ENABLED"])
env.Append(CCFLAGS=["-fvisibility=default"])
env.Append(LINKFLAGS=["-fvisibility=default"])
elif env["symbols_visibility"] == "hidden":
env.Append(CCFLAGS=["-fvisibility=hidden"])
env.Append(LINKFLAGS=["-fvisibility=hidden"])
# Require C++17 # Configuration of build targets:
if env.get("is_msvc", False): # - Editor or template
env.Append(CXXFLAGS=["/std:c++17"]) # - Debug features (DEBUG_ENABLED code)
# - Dev only code (DEV_ENABLED code)
# - Optimization level
# - Debug symbols for crash traces / debuggers
# Keep this configuration in sync with SConstruct in upstream Godot.
if env.debug_features:
# DEBUG_ENABLED enables debugging *features* and debug-only code, which is intended
# to give *users* extra debugging information for their game development.
env.Append(CPPDEFINES=["DEBUG_ENABLED"])
# In upstream Godot this is added in typedefs.h when DEBUG_ENABLED is set.
env.Append(CPPDEFINES=["DEBUG_METHODS_ENABLED"])
if env.dev_build:
# DEV_ENABLED enables *engine developer* code which should only be compiled for those
# working on the engine itself.
env.Append(CPPDEFINES=["DEV_ENABLED"])
else: else:
env.Append(CXXFLAGS=["-std=c++17"]) # Disable assert() for production targets (only used in thirdparty code).
env.Append(CPPDEFINES=["NDEBUG"])
if env["precision"] == "double": if env["precision"] == "double":
env.Append(CPPDEFINES=["REAL_T_IS_DOUBLE"]) env.Append(CPPDEFINES=["REAL_T_IS_DOUBLE"])

View File

@ -1,6 +1,7 @@
import os import os
import sys import sys
import subprocess import subprocess
import common_compiler_flags
from SCons.Variables import * from SCons.Variables import *
if sys.version_info < (3,): if sys.version_info < (3,):
@ -104,3 +105,5 @@ def generate(env):
env.Append(LINKFLAGS=["-isysroot", env["IOS_SDK_PATH"], "-F" + env["IOS_SDK_PATH"]]) env.Append(LINKFLAGS=["-isysroot", env["IOS_SDK_PATH"], "-F" + env["IOS_SDK_PATH"]])
env.Append(CPPDEFINES=["IOS_ENABLED", "UNIX_ENABLED"]) env.Append(CPPDEFINES=["IOS_ENABLED", "UNIX_ENABLED"])
common_compiler_flags.generate(env)

View File

@ -1,3 +1,4 @@
import common_compiler_flags
from SCons.Variables import * from SCons.Variables import *
from SCons.Tool import clang, clangxx from SCons.Tool import clang, clangxx
@ -14,7 +15,7 @@ def generate(env):
if env["use_llvm"]: if env["use_llvm"]:
clang.generate(env) clang.generate(env)
clangxx.generate(env) clangxx.generate(env)
elif env["use_hot_reload"]: elif env.use_hot_reload:
# Required for extensions to truly unload. # Required for extensions to truly unload.
env.Append(CXXFLAGS=["-fno-gnu-unique"]) env.Append(CXXFLAGS=["-fno-gnu-unique"])
@ -37,3 +38,5 @@ def generate(env):
env.Append(LINKFLAGS=["-march=rv64gc"]) env.Append(LINKFLAGS=["-march=rv64gc"])
env.Append(CPPDEFINES=["LINUX_ENABLED", "UNIX_ENABLED"]) env.Append(CPPDEFINES=["LINUX_ENABLED", "UNIX_ENABLED"])
common_compiler_flags.generate(env)

View File

@ -1,5 +1,6 @@
import os import os
import sys import sys
import common_compiler_flags
def has_osxcross(): def has_osxcross():
@ -70,3 +71,5 @@ def generate(env):
) )
env.Append(CPPDEFINES=["MACOS_ENABLED", "UNIX_ENABLED"]) env.Append(CPPDEFINES=["MACOS_ENABLED", "UNIX_ENABLED"])
common_compiler_flags.generate(env)

View File

@ -1,144 +0,0 @@
import os
import subprocess
import sys
from SCons.Script import ARGUMENTS
from SCons.Variables import *
from SCons.Variables.BoolVariable import _text2bool
# Helper methods
def get_cmdline_bool(option, default):
"""We use `ARGUMENTS.get()` to check if options were manually overridden on the command line,
and SCons' _text2bool helper to convert them to booleans, otherwise they're handled as strings.
"""
cmdline_val = ARGUMENTS.get(option)
if cmdline_val is not None:
return _text2bool(cmdline_val)
else:
return default
def using_clang(env):
return "clang" in os.path.basename(env["CC"])
def is_vanilla_clang(env):
if not using_clang(env):
return False
try:
version = subprocess.check_output([env.subst(env["CXX"]), "--version"]).strip().decode("utf-8")
except (subprocess.CalledProcessError, OSError):
print("Couldn't parse CXX environment variable to infer compiler version.")
return False
return not version.startswith("Apple")
# Main tool definition
def options(opts):
opts.Add(
EnumVariable(
"optimize",
"The desired optimization flags",
"speed_trace",
("none", "custom", "debug", "speed", "speed_trace", "size"),
)
)
opts.Add(BoolVariable("debug_symbols", "Build with debugging symbols", True))
opts.Add(BoolVariable("dev_build", "Developer build with dev-only debugging code (DEV_ENABLED)", False))
def exists(env):
return True
def generate(env):
# Configuration of build targets:
# - Editor or template
# - Debug features (DEBUG_ENABLED code)
# - Dev only code (DEV_ENABLED code)
# - Optimization level
# - Debug symbols for crash traces / debuggers
# Keep this configuration in sync with SConstruct in upstream Godot.
env.editor_build = env["target"] == "editor"
env.dev_build = env["dev_build"]
env.debug_features = env["target"] in ["editor", "template_debug"]
if env.dev_build:
opt_level = "none"
elif env.debug_features:
opt_level = "speed_trace"
else: # Release
opt_level = "speed"
env["optimize"] = ARGUMENTS.get("optimize", opt_level)
env["debug_symbols"] = get_cmdline_bool("debug_symbols", env.dev_build)
if env.editor_build:
env.Append(CPPDEFINES=["TOOLS_ENABLED"])
if env.debug_features:
# DEBUG_ENABLED enables debugging *features* and debug-only code, which is intended
# to give *users* extra debugging information for their game development.
env.Append(CPPDEFINES=["DEBUG_ENABLED"])
# In upstream Godot this is added in typedefs.h when DEBUG_ENABLED is set.
env.Append(CPPDEFINES=["DEBUG_METHODS_ENABLED"])
if env.dev_build:
# DEV_ENABLED enables *engine developer* code which should only be compiled for those
# working on the engine itself.
env.Append(CPPDEFINES=["DEV_ENABLED"])
else:
# Disable assert() for production targets (only used in thirdparty code).
env.Append(CPPDEFINES=["NDEBUG"])
# Set optimize and debug_symbols flags.
# "custom" means do nothing and let users set their own optimization flags.
if env.get("is_msvc", False):
if env["debug_symbols"]:
env.Append(CCFLAGS=["/Zi", "/FS"])
env.Append(LINKFLAGS=["/DEBUG:FULL"])
if env["optimize"] == "speed":
env.Append(CCFLAGS=["/O2"])
env.Append(LINKFLAGS=["/OPT:REF"])
elif env["optimize"] == "speed_trace":
env.Append(CCFLAGS=["/O2"])
env.Append(LINKFLAGS=["/OPT:REF", "/OPT:NOICF"])
elif env["optimize"] == "size":
env.Append(CCFLAGS=["/O1"])
env.Append(LINKFLAGS=["/OPT:REF"])
elif env["optimize"] == "debug" or env["optimize"] == "none":
env.Append(CCFLAGS=["/Od"])
else:
if env["debug_symbols"]:
# Adding dwarf-4 explicitly makes stacktraces work with clang builds,
# otherwise addr2line doesn't understand them.
env.Append(CCFLAGS=["-gdwarf-4"])
if env.dev_build:
env.Append(CCFLAGS=["-g3"])
else:
env.Append(CCFLAGS=["-g2"])
else:
if using_clang(env) and not is_vanilla_clang(env):
# Apple Clang, its linker doesn't like -s.
env.Append(LINKFLAGS=["-Wl,-S", "-Wl,-x", "-Wl,-dead_strip"])
else:
env.Append(LINKFLAGS=["-s"])
if env["optimize"] == "speed":
env.Append(CCFLAGS=["-O3"])
# `-O2` is friendlier to debuggers than `-O3`, leading to better crash backtraces.
elif env["optimize"] == "speed_trace":
env.Append(CCFLAGS=["-O2"])
elif env["optimize"] == "size":
env.Append(CCFLAGS=["-Os"])
elif env["optimize"] == "debug":
env.Append(CCFLAGS=["-Og"])
elif env["optimize"] == "none":
env.Append(CCFLAGS=["-O0"])

View File

@ -1,4 +1,5 @@
import os import os
import common_compiler_flags
from SCons.Util import WhereIs from SCons.Util import WhereIs
@ -42,3 +43,5 @@ def generate(env):
env.Append(LINKFLAGS=["-s", "SIDE_MODULE=1"]) env.Append(LINKFLAGS=["-s", "SIDE_MODULE=1"])
env.Append(CPPDEFINES=["WEB_ENABLED", "UNIX_ENABLED"]) env.Append(CPPDEFINES=["WEB_ENABLED", "UNIX_ENABLED"])
common_compiler_flags.generate(env)

View File

@ -1,7 +1,6 @@
import sys import sys
import my_spawn import my_spawn
import common_compiler_flags
from SCons.Tool import msvc, mingw from SCons.Tool import msvc, mingw
from SCons.Variables import * from SCons.Variables import *
@ -90,3 +89,5 @@ def generate(env):
) )
env.Append(CPPDEFINES=["WINDOWS_ENABLED"]) env.Append(CPPDEFINES=["WINDOWS_ENABLED"])
common_compiler_flags.generate(env)