Skip to content

Commit

Permalink
Fix platform/target selection, let platforms override custom tools, o…
Browse files Browse the repository at this point in the history
…bey overrides of use_mingw

Let the platforms optionally override the default scons environment tools
via a `custom_tools` flag, so the default SConstruct only needs to set the
defaults and not worry about each individual platform requirements.
Users can also override the tools via `custom_tools`.

Add a temporary SCons environment to only process `use_mingw`, `platform`,
`target` and `custom_tools` options from all the different places where
they can be set, and then use those values to initialize the actual
environment that we use to build, as well as determine which builder scripts
to load based on the target.

This fixes a related issue where, if a platform sets a default target
in `get_flags` different from editor, and the user doesn't pass in the
target in the command line, the `editor` target would be briefly selected
during a portion of SConstruct execution, long enough to set the
env.editor_build to true, causing editor files and defines to be set
for non-editor builds.

Fixes godotengine#60719
  • Loading branch information
shana committed Oct 12, 2023
1 parent 88269cf commit 331d1a9
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 111 deletions.
264 changes: 153 additions & 111 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,6 @@ import gles3_builders
import scu_builders
from platform_methods import architectures, architecture_aliases, generate_export_icons

if ARGUMENTS.get("target", "editor") == "editor":
_helper_module("editor.editor_builders", "editor/editor_builders.py")
_helper_module("editor.template_builders", "editor/template_builders.py")

# Scan possible build platforms

platform_list = [] # list of platforms
Expand All @@ -71,6 +67,8 @@ platform_doc_class_path = {}
platform_exporters = []
platform_apis = []

default_tools = ["default"]

time_at_start = time.time()

for x in sorted(glob.glob("platform/*")):
Expand Down Expand Up @@ -98,73 +96,119 @@ for x in sorted(glob.glob("platform/*")):
if os.path.exists(x + "/api/api.cpp"):
platform_apis.append(platform_name)
if detect.can_build():
x = x.replace("platform/", "") # rest of world
x = x.replace("platform\\", "") # win32
platform_list += [x]
platform_opts[x] = detect.get_opts()
platform_flags[x] = detect.get_flags()
platform_list += [platform_name]
platform_opts[platform_name] = detect.get_opts()
platform_flags[platform_name] = detect.get_flags()
sys.path.remove(tmppath)
sys.modules.pop("detect")

custom_tools = ["default"]

platform_arg = ARGUMENTS.get("platform", ARGUMENTS.get("p", False))
# Environment initialization options.
customs = ["custom.py"]

if platform_arg == "android":
custom_tools = ["clang", "clang++", "as", "ar", "link"]
elif platform_arg == "web":
# Use generic POSIX build toolchain for Emscripten.
custom_tools = ["cc", "c++", "ar", "link", "textfile", "zip"]
elif os.name == "nt" and methods.get_cmdline_bool("use_mingw", False):
custom_tools = ["mingw"]
profile = ARGUMENTS.get("profile", "")
if profile:
if os.path.isfile(profile):
customs.append(profile)
elif os.path.isfile(profile + ".py"):
customs.append(profile + ".py")

# We let SCons build its default ENV as it includes OS-specific things which we don't
# want to have to pull in manually.
# Then we prepend PATH to make it take precedence, while preserving SCons' own entries.
env_base = Environment(tools=custom_tools)
env_base.PrependENVPath("PATH", os.getenv("PATH"))
env_base.PrependENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH"))
if "TERM" in os.environ: # Used for colored output.
env_base["ENV"]["TERM"] = os.environ["TERM"]

env_base.disabled_modules = []
env_base.module_version_string = ""
env_base.msvc = False
opts = Variables(customs, ARGUMENTS)
opts.Add("platform", "Target platform (%s)" % ("|".join(platform_list),), "")
opts.Add("p", "Platform (alias for 'platform')", "")
opts.Add(EnumVariable("target", "Compilation target", "editor", ("editor", "template_release", "template_debug")))
opts.Add("custom_tools", "Scons Environment toolset override", "")

env_base.__class__.disable_module = methods.disable_module
if os.name == "nt":
opts.Add(BoolVariable("use_mingw", "Use the Mingw compiler, even if MSVC is installed.", False)),

env_base.__class__.add_module_version_string = methods.add_module_version_string
# We let SCons build a default environment so we can read environment-specific options
# from user and platform locations.
# We will then reinitialize the environment with the correct information.
env_base = Environment(tools=default_tools)
opts.Update(env_base)

env_base.__class__.add_source_files = methods.add_source_files
env_base.__class__.use_windows_spawn_fix = methods.use_windows_spawn_fix
# Platform selection.

env_base.__class__.add_shared_library = methods.add_shared_library
env_base.__class__.add_library = methods.add_library
env_base.__class__.add_program = methods.add_program
env_base.__class__.CommandNoCache = methods.CommandNoCache
env_base.__class__.Run = methods.Run
env_base.__class__.disable_warnings = methods.disable_warnings
env_base.__class__.force_optimization_on_debug = methods.force_optimization_on_debug
env_base.__class__.module_add_dependencies = methods.module_add_dependencies
env_base.__class__.module_check_dependencies = methods.module_check_dependencies
selected_platform = ""

env_base["x86_libtheora_opt_gcc"] = False
env_base["x86_libtheora_opt_vc"] = False
if env_base["platform"] != "":
selected_platform = env_base["platform"]
elif env_base["p"] != "":
selected_platform = env_base["p"]
else:
# Missing `platform` argument, try to detect platform automatically
if (
sys.platform.startswith("linux")
or sys.platform.startswith("dragonfly")
or sys.platform.startswith("freebsd")
or sys.platform.startswith("netbsd")
or sys.platform.startswith("openbsd")
):
selected_platform = "linuxbsd"
elif sys.platform == "darwin":
selected_platform = "macos"
elif sys.platform == "win32":
selected_platform = "windows"
else:
print("Could not detect platform automatically. Supported platforms:")
for x in platform_list:
print("\t" + x)
print("\nPlease run SCons again and select a valid platform: platform=<string>")

# avoid issues when building with different versions of python out of the same directory
env_base.SConsignFile(".sconsign{0}.dblite".format(pickle.HIGHEST_PROTOCOL))
if selected_platform != "":
print("Automatically detected platform: " + selected_platform)

# Build options
if selected_platform in ["macos", "osx"]:
if selected_platform == "osx":
# Deprecated alias kept for compatibility.
print('Platform "osx" has been renamed to "macos" in Godot 4. Building for platform "macos".')
# Alias for convenience.
selected_platform = "macos"

customs = ["custom.py"]
if selected_platform in ["ios", "iphone"]:
if selected_platform == "iphone":
# Deprecated alias kept for compatibility.
print('Platform "iphone" has been renamed to "ios" in Godot 4. Building for platform "ios".')
# Alias for convenience.
selected_platform = "ios"

profile = ARGUMENTS.get("profile", "")
if profile:
if os.path.isfile(profile):
customs.append(profile)
elif os.path.isfile(profile + ".py"):
customs.append(profile + ".py")
if selected_platform in ["linux", "bsd", "x11"]:
if selected_platform == "x11":
# Deprecated alias kept for compatibility.
print('Platform "x11" has been renamed to "linuxbsd" in Godot 4. Building for platform "linuxbsd".')
# Alias for convenience.
selected_platform = "linuxbsd"

# Platform specific flags.
if selected_platform in platform_flags:
platform_dict = dict(platform_flags[selected_platform])
for option in opts.options:
# Use the platform flag value if it's not set in the environment already (i.e. the user hasn't overridden it).
if env_base[option.key] == option.default and option.key in platform_dict:
env_base[option.key] = platform_dict[option.key]

selected_target = env_base["target"]

# Determine the Scons toolset.
custom_tools = default_tools

# Platform or user is overriding the default toolset.
if env_base["custom_tools"] != "":
custom_tools = env_base["custom_tools"].split(",")

# Check for the use_mingw flag.
if os.name == "nt" and env_base["use_mingw"] and "mingw" not in custom_tools:
custom_tools = ["mingw"]

if selected_target == "editor":
_helper_module("editor.editor_builders", "editor/editor_builders.py")
_helper_module("editor.template_builders", "editor/template_builders.py")

# Prepare the real environment options list, now that we know what platform and toolset we're going to use.

# Build options
opts = Variables(customs, ARGUMENTS)

# Target build options
Expand Down Expand Up @@ -262,74 +306,70 @@ opts.Add("CFLAGS", "Custom flags for the C compiler")
opts.Add("CXXFLAGS", "Custom flags for the C++ compiler")
opts.Add("LINKFLAGS", "Custom flags for the linker")

# Update the environment to have all above options defined
# in following code (especially platform and custom_modules).
print(
'Initializing SCons environment for "'
+ selected_platform
+ '" with the following tools: "'
+ " ".join(custom_tools)
+ '".'
)

# We let SCons build its default ENV as it includes OS-specific things which we don't
# want to have to pull in manually.
# This lets us read the environment, command line, user and platform options and flags.
# Then we prepend PATH to make it take precedence, while preserving SCons' own entries.
env_base = Environment(tools=custom_tools)

# Add platform-specific options.
if selected_platform in platform_opts:
for opt in platform_opts[selected_platform]:
opts.Add(opt)

# Update the environment with all general and platform-specific options.
opts.Update(env_base)

# Platform selection: validate input, and add options.
# Make sure to update this to the found, valid platform as it's used through the buildsystem as the reference.
# It should always be re-set after calling `opts.Update()` otherwise it uses the original input value.
env_base["platform"] = selected_platform

selected_platform = ""
# Make sure the target we've already parsed is set in this environment.
# The logic for merging platform flags into this environment happens later, and until then, we need
# to make sure this one is set properly
env_base["target"] = selected_target

if env_base["platform"] != "":
selected_platform = env_base["platform"]
elif env_base["p"] != "":
selected_platform = env_base["p"]
else:
# Missing `platform` argument, try to detect platform automatically
if (
sys.platform.startswith("linux")
or sys.platform.startswith("dragonfly")
or sys.platform.startswith("freebsd")
or sys.platform.startswith("netbsd")
or sys.platform.startswith("openbsd")
):
selected_platform = "linuxbsd"
elif sys.platform == "darwin":
selected_platform = "macos"
elif sys.platform == "win32":
selected_platform = "windows"
else:
print("Could not detect platform automatically. Supported platforms:")
for x in platform_list:
print("\t" + x)
print("\nPlease run SCons again and select a valid platform: platform=<string>")
# Now we're ready to set our PATHs
env_base.PrependENVPath("PATH", os.getenv("PATH"))
env_base.PrependENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH"))
if "TERM" in os.environ: # Used for colored output.
env_base["ENV"]["TERM"] = os.environ["TERM"]

if selected_platform != "":
print("Automatically detected platform: " + selected_platform)
env_base.disabled_modules = []
env_base.module_version_string = ""
env_base.msvc = False

if selected_platform in ["macos", "osx"]:
if selected_platform == "osx":
# Deprecated alias kept for compatibility.
print('Platform "osx" has been renamed to "macos" in Godot 4. Building for platform "macos".')
# Alias for convenience.
selected_platform = "macos"
env_base.__class__.disable_module = methods.disable_module

if selected_platform in ["ios", "iphone"]:
if selected_platform == "iphone":
# Deprecated alias kept for compatibility.
print('Platform "iphone" has been renamed to "ios" in Godot 4. Building for platform "ios".')
# Alias for convenience.
selected_platform = "ios"
env_base.__class__.add_module_version_string = methods.add_module_version_string

if selected_platform in ["linux", "bsd", "x11"]:
if selected_platform == "x11":
# Deprecated alias kept for compatibility.
print('Platform "x11" has been renamed to "linuxbsd" in Godot 4. Building for platform "linuxbsd".')
# Alias for convenience.
selected_platform = "linuxbsd"
env_base.__class__.add_source_files = methods.add_source_files
env_base.__class__.use_windows_spawn_fix = methods.use_windows_spawn_fix

# Make sure to update this to the found, valid platform as it's used through the buildsystem as the reference.
# It should always be re-set after calling `opts.Update()` otherwise it uses the original input value.
env_base["platform"] = selected_platform
env_base.__class__.add_shared_library = methods.add_shared_library
env_base.__class__.add_library = methods.add_library
env_base.__class__.add_program = methods.add_program
env_base.__class__.CommandNoCache = methods.CommandNoCache
env_base.__class__.Run = methods.Run
env_base.__class__.disable_warnings = methods.disable_warnings
env_base.__class__.force_optimization_on_debug = methods.force_optimization_on_debug
env_base.__class__.module_add_dependencies = methods.module_add_dependencies
env_base.__class__.module_check_dependencies = methods.module_check_dependencies

# Add platform-specific options.
if selected_platform in platform_opts:
for opt in platform_opts[selected_platform]:
opts.Add(opt)
env_base["x86_libtheora_opt_gcc"] = False
env_base["x86_libtheora_opt_vc"] = False

# avoid issues when building with different versions of python out of the same directory
env_base.SConsignFile(".sconsign{0}.dblite".format(pickle.HIGHEST_PROTOCOL))

# Update the environment to take platform-specific options into account.
opts.Update(env_base)
env_base["platform"] = selected_platform # Must always be re-set after calling opts.Update().

# Detect modules.
modules_detected = OrderedDict()
Expand Down Expand Up @@ -390,6 +430,8 @@ methods.write_modules(modules_detected)
# Update the environment again after all the module options are added.
opts.Update(env_base)
env_base["platform"] = selected_platform # Must always be re-set after calling opts.Update().
env_base["target"] = selected_target # Make sure this is set until the Platform specific flags section below.

Help(opts.GenerateHelpText(env_base))

# add default include paths
Expand Down
1 change: 1 addition & 0 deletions platform/android/detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def get_flags():
return [
("arch", "arm64"), # Default for convenience.
("target", "template_debug"),
("custom_tools", "clang,clang++,as,ar,link"),
]


Expand Down
2 changes: 2 additions & 0 deletions platform/web/detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ def get_flags():
# 100 KiB over -Os, which does not justify the negative impact on
# run-time performance.
("optimize", "size"),
# Use generic POSIX build toolchain for Emscripten.
("custom_tools", "cc,c++,ar,link,textfile,zip"),
]


Expand Down

0 comments on commit 331d1a9

Please sign in to comment.