From 3011800535ca5727864cefde310d3f731be72f36 Mon Sep 17 00:00:00 2001 From: Andreia Gaita Date: Fri, 15 Sep 2023 15:30:35 +0200 Subject: [PATCH] Fix platform selection, let platforms override custom tools, and obey 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 #60719 --- SConstruct | 262 +++++++++++++++++++++---------------- platform/android/detect.py | 1 + platform/web/detect.py | 2 + 3 files changed, 154 insertions(+), 111 deletions(-) diff --git a/SConstruct b/SConstruct index df750beae415c..b8e377767303f 100644 --- a/SConstruct +++ b/SConstruct @@ -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 @@ -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/*")): @@ -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=") -# 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 @@ -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=") +# 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() @@ -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 diff --git a/platform/android/detect.py b/platform/android/detect.py index 33c65657899d7..2266815fc3222 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -68,6 +68,7 @@ def get_flags(): return [ ("arch", "arm64"), # Default for convenience. ("target", "template_debug"), + ("custom_tools", "clang,clang++,as,ar,link"), ] diff --git a/platform/web/detect.py b/platform/web/detect.py index 7b2e5646d6688..daaee294b84bb 100644 --- a/platform/web/detect.py +++ b/platform/web/detect.py @@ -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"), ]