SCons: Add emitter to declutter build objects

This commit is contained in:
Thaddeus Crews 2025-01-13 11:13:17 -06:00
parent 0028fd625e
commit 10ed66f28c
No known key found for this signature in database
GPG Key ID: 62181B86FE9E5D84
7 changed files with 50 additions and 11 deletions

View File

@ -14,6 +14,7 @@ from importlib.util import module_from_spec, spec_from_file_location
from types import ModuleType from types import ModuleType
from SCons import __version__ as scons_raw_version from SCons import __version__ as scons_raw_version
from SCons.Builder import ListEmitter
# Explicitly resolve the helper modules, this is done to avoid clash with # Explicitly resolve the helper modules, this is done to avoid clash with
# modules of the same name that might be randomly added (e.g. someone adding # modules of the same name that might be randomly added (e.g. someone adding
@ -236,6 +237,13 @@ opts.Add(BoolVariable("engine_update_check", "Enable engine update checks in the
opts.Add(BoolVariable("steamapi", "Enable minimal SteamAPI integration for usage time tracking (editor only)", False)) opts.Add(BoolVariable("steamapi", "Enable minimal SteamAPI integration for usage time tracking (editor only)", False))
opts.Add("cache_path", "Path to a directory where SCons cache files will be stored. No value disables the cache.", "") opts.Add("cache_path", "Path to a directory where SCons cache files will be stored. No value disables the cache.", "")
opts.Add("cache_limit", "Max size (in GiB) for the SCons cache. 0 means no limit.", "0") opts.Add("cache_limit", "Max size (in GiB) for the SCons cache. 0 means no limit.", "0")
opts.Add(
BoolVariable(
"redirect_build_objects",
"Enable redirecting built objects/libraries to `bin/obj/` to declutter the repository.",
True,
)
)
# Thirdparty libraries # Thirdparty libraries
opts.Add(BoolVariable("builtin_brotli", "Use the built-in Brotli library", True)) opts.Add(BoolVariable("builtin_brotli", "Use the built-in Brotli library", True))
@ -1052,6 +1060,14 @@ if env["ninja"]:
if env["threads"]: if env["threads"]:
env.Append(CPPDEFINES=["THREADS_ENABLED"]) env.Append(CPPDEFINES=["THREADS_ENABLED"])
# Ensure build objects are put in their own folder if `redirect_build_objects` is enabled.
env.Prepend(LIBEMITTER=[methods.redirect_emitter])
env.Prepend(SHLIBEMITTER=[methods.redirect_emitter])
for key in (emitters := env.StaticObject.builder.emitter):
emitters[key] = ListEmitter([methods.redirect_emitter] + env.Flatten(emitters[key]))
for key in (emitters := env.SharedObject.builder.emitter):
emitters[key] = ListEmitter([methods.redirect_emitter] + env.Flatten(emitters[key]))
# Build subdirs, the build order is dependent on link order. # Build subdirs, the build order is dependent on link order.
Export("env") Export("env")

View File

@ -16,8 +16,7 @@ from typing import Generator, List, Optional, Union, cast
from misc.utility.color import print_error, print_info, print_warning from misc.utility.color import print_error, print_info, print_warning
# Get the "Godot" folder name ahead of time # Get the "Godot" folder name ahead of time
base_folder_path = str(os.path.abspath(Path(__file__).parent)) + "/" base_folder = Path(__file__).resolve().parent
base_folder_only = os.path.basename(os.path.normpath(base_folder_path))
compiler_version_cache = None compiler_version_cache = None
@ -83,6 +82,29 @@ def add_source_files(self, sources, files, allow_gen=False):
return True return True
def redirect_emitter(target, source, env):
"""
Emitter to automatically redirect object/library build files to the `bin/obj` directory,
retaining subfolder structure. External build files will attempt to retain subfolder
structure relative to their environment's parent directory, sorted under `bin/obj/external`.
If `redirect_build_objects` is `False`, or an external build file isn't relative to the
passed environment, this emitter does nothing.
"""
if not env["redirect_build_objects"]:
return target, source
redirected_targets = []
for item in target:
if base_folder in (path := Path(item.get_abspath()).resolve()).parents:
item = env.File(f"#bin/obj/{path.relative_to(base_folder)}")
elif (alt_base := Path(env.Dir(".").get_abspath()).resolve().parent) in path.parents:
item = env.File(f"#bin/obj/external/{path.relative_to(alt_base)}")
else:
print_warning(f'Failed to redirect "{path}"')
redirected_targets.append(item)
return redirected_targets, source
def disable_warnings(self): def disable_warnings(self):
# 'self' is the environment # 'self' is the environment
if self.msvc and not using_clang(self): if self.msvc and not using_clang(self):
@ -150,7 +172,7 @@ def get_version_info(module_version_string="", silent=False):
def get_git_info(): def get_git_info():
os.chdir(base_folder_path) os.chdir(base_folder)
# Parse Git hash if we're in a Git repo. # Parse Git hash if we're in a Git repo.
git_hash = "" git_hash = ""
@ -775,7 +797,7 @@ def show_progress(env):
if env["ninja"]: if env["ninja"]:
return return
NODE_COUNT_FILENAME = f"{base_folder_path}.scons_node_count" NODE_COUNT_FILENAME = base_folder / ".scons_node_count"
class ShowProgress: class ShowProgress:
def __init__(self): def __init__(self):

View File

@ -101,7 +101,7 @@ for name, path in env.module_list.items():
if env["tests"]: if env["tests"]:
def modules_tests_builder(target, source, env): def modules_tests_builder(target, source, env):
headers = sorted([os.path.relpath(src.path, methods.base_folder_path).replace("\\", "/") for src in source]) headers = sorted([os.path.relpath(src.path, methods.base_folder).replace("\\", "/") for src in source])
with methods.generated_wrapper(str(target[0])) as file: with methods.generated_wrapper(str(target[0])) as file:
for header in headers: for header in headers:
file.write(f'#include "{header}"\n') file.write(f'#include "{header}"\n')

View File

@ -44,7 +44,7 @@ env_thirdparty.disable_warnings()
thirdparty_obj = env_thirdparty.SharedObject("#thirdparty/misc/ifaddrs-android.cc") thirdparty_obj = env_thirdparty.SharedObject("#thirdparty/misc/ifaddrs-android.cc")
android_objects.append(thirdparty_obj) android_objects.append(thirdparty_obj)
lib = env_android.add_shared_library("#bin/libgodot", [android_objects], SHLIBSUFFIX=env["SHLIBSUFFIX"]) lib = env_android.add_shared_library("libgodot", android_objects)
# Needed to force rebuilding the platform files when the thirdparty code is updated. # Needed to force rebuilding the platform files when the thirdparty code is updated.
env.Depends(lib, thirdparty_obj) env.Depends(lib, thirdparty_obj)
@ -78,9 +78,7 @@ if lib_arch_dir != "":
lib_tools_dir = "" lib_tools_dir = ""
out_dir = "#platform/android/java/lib/libs/" + lib_tools_dir + lib_type_dir + "/" + lib_arch_dir out_dir = "#platform/android/java/lib/libs/" + lib_tools_dir + lib_type_dir + "/" + lib_arch_dir
env_android.Command( env_android.Command(out_dir + "/libgodot_android.so", lib, Move("$TARGET", "$SOURCE"))
out_dir + "/libgodot_android.so", "#bin/libgodot" + env["SHLIBSUFFIX"], Move("$TARGET", "$SOURCE")
)
stl_lib_path = ( stl_lib_path = (
str(env["ANDROID_NDK_ROOT"]) + "/sources/cxx-stl/llvm-libc++/libs/" + lib_arch_dir + "/libc++_shared.so" str(env["ANDROID_NDK_ROOT"]) + "/sources/cxx-stl/llvm-libc++/libs/" + lib_arch_dir + "/libc++_shared.so"

View File

@ -98,7 +98,7 @@ def library_emitter(target, source, env):
def configure(env: "SConsEnvironment"): def configure(env: "SConsEnvironment"):
env.Append(LIBEMITTER=library_emitter) env.Append(LIBEMITTER=[library_emitter])
# Validate arch. # Validate arch.
supported_arches = ["wasm32"] supported_arches = ["wasm32"]

View File

@ -8,6 +8,8 @@ from pathlib import Path
import platform_windows_builders import platform_windows_builders
from methods import redirect_emitter
sources = [] sources = []
common_win = [ common_win = [
@ -49,6 +51,7 @@ def arrange_program_clean(prog):
Clean(prog, extra_files_to_clean) Clean(prog, extra_files_to_clean)
env["BUILDERS"]["RES"].emitter = redirect_emitter
res_file = "godot_res.rc" res_file = "godot_res.rc"
res_target = "godot_res" + env["OBJSUFFIX"] res_target = "godot_res" + env["OBJSUFFIX"]
res_obj = env.RES(res_target, res_file) res_obj = env.RES(res_target, res_file)

View File

@ -278,7 +278,7 @@ def configure_msvc(env: "SConsEnvironment"):
from tempfile import mkstemp from tempfile import mkstemp
# Ensure we have a location to write captured output to, in case of false positives. # Ensure we have a location to write captured output to, in case of false positives.
capture_path = methods.base_folder_path + "platform/windows/msvc_capture.log" capture_path = methods.base_folder / "platform" / "windows" / "msvc_capture.log"
with open(capture_path, "wt", encoding="utf-8"): with open(capture_path, "wt", encoding="utf-8"):
pass pass