Skip to content

Commit

Permalink
Fix platform selection, let platforms override custom tools, and obey…
Browse files Browse the repository at this point in the history
… user 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`.

Fix the order of initialization, so we can pick up the use_mingw option
from all the different places where it can be set, and use it during
environment initialization.

This also fixes a related issue where, if a platform sets a default target
in `get_flags`, if the platform doesn't support the `editor` target, 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, just long enough
for some wrong variables to be set, and cause the build to fail, or include
bad defines.

Fixes godotengine#60719
  • Loading branch information
shana committed Oct 12, 2023
1 parent 88269cf commit 3011800
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 111 deletions.
262 changes: 151 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,68 @@ 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 +428,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 3011800

Please sign in to comment.