From 714958738a9398ddebd57acaaec7ff1fe1b80765 Mon Sep 17 00:00:00 2001 From: Justin Nolan Date: Mon, 11 May 2026 19:04:45 +0200 Subject: [PATCH] Adds support for out of source builds Added Variant-dir-aware glob Added deterministic platform option order Added deterministic build module resolution --- .gitignore | 3 +++ SConstruct | 45 ++++++++++++++++++++++++++++++--------------- deps/SCsub | 38 ++++++++++++++++++++++++-------------- scripts | 2 +- 4 files changed, 58 insertions(+), 30 deletions(-) diff --git a/.gitignore b/.gitignore index 8bc6c82..742dcbd 100644 --- a/.gitignore +++ b/.gitignore @@ -78,3 +78,6 @@ tests/bin/* !game/common/map/*.obj compile_commands.json + +# Out-of-source build directory +build/ \ No newline at end of file diff --git a/SConstruct b/SConstruct index 61670d1..1700397 100644 --- a/SConstruct +++ b/SConstruct @@ -28,6 +28,26 @@ opts.Add( env.FinalizeOptions() +suffix = ".{}.{}".format(env["platform"], env["target"]) +if env.dev_build: + suffix += ".dev" +if env["precision"] == "double": + suffix += ".double" +suffix += "." + env["arch"] +if env["platform"] == "windows": + if env.get("debug_crt", False): + suffix += ".mdd" + elif env.get("use_static_cpp", False): + suffix += ".mt" + else: + suffix += ".md" +if env.get("use_asan", False): + suffix += ".san" +env["suffix"] = suffix + +build_dir = env.Dir("build/" + suffix.lstrip(".")).abspath.replace("\\", "/") +env["build_dir"] = build_dir + env.exposed_includes = [] SConscript("deps/SCsub", "env") @@ -45,20 +65,13 @@ env.openvic_dataloader = {} # tweak this if you want to use different folders, or more folders, to store your source code in. source_path = "src/openvic-dataloader" include_path = "include" -env.Append(CPPPATH=[[env.Dir(p) for p in [source_path, include_path]]]) -sources = env.GlobRecursive("*.cpp", [source_path]) +# Mirror the dataloader source tree into the per-config build dir. +dataloader_variant = build_dir + "/" + source_path +env.VariantDir(dataloader_variant, source_path, duplicate=True) +env.Append(CPPPATH=[[env.Dir(p) for p in [include_path, dataloader_variant]]]) +sources = env.GlobRecursive("*.cpp", [dataloader_variant]) env.dataloader_sources = sources -suffix = ".{}.{}".format(env["platform"], env["target"]) -if env.dev_build: - suffix += ".dev" -if env["precision"] == "double": - suffix += ".double" -suffix += "." + env["arch"] - -# Expose it when included from another project -env["suffix"] = suffix - library = None env["OBJSUFFIX"] = suffix + env["OBJSUFFIX"] library_name = "libopenvic-dataloader{}{}".format(suffix, env["LIBSUFFIX"]) @@ -94,10 +107,12 @@ env["PROGSUFFIX"] = suffix + env["PROGSUFFIX"] if env["build_ovdl_headless"]: headless_name = "openvic-dataloader" headless_env = env.Clone() - headless_path = ["src/headless"] + headless_src = "src/headless" + headless_variant = build_dir + "/" + headless_src + headless_env.VariantDir(headless_variant, headless_src, duplicate=True) headless_env.Append(CPPDEFINES=["OPENVIC_DATALOADER_HEADLESS"]) - headless_env.Append(CPPPATH=[headless_env.Dir(headless_path)]) - headless_env.headless_sources = env.GlobRecursive("*.cpp", headless_path) + headless_env.Append(CPPPATH=[headless_env.Dir(headless_variant)]) + headless_env.headless_sources = env.GlobRecursive("*.cpp", [headless_variant]) if not env["build_ovdl_library"]: headless_env.headless_sources += sources headless_program = headless_env.Program( diff --git a/deps/SCsub b/deps/SCsub index 74857cb..8d50a22 100644 --- a/deps/SCsub +++ b/deps/SCsub @@ -1,5 +1,4 @@ #!/usr/bin/env python -import os Import("env") @@ -35,22 +34,25 @@ def build_lexy(env): include_path = "lexy/include" source_path = "lexy/src" lexy_env.Append(CPPPATH=[[lexy_env.Dir(p) for p in [source_path, include_path]]]) - sources = env.GlobRecursive("*.cpp", [source_path]) + # Mirror lexy's source tree into the per-config build dir. + build_root = (env.get("build_dir") or env.Dir("#").abspath).replace("\\", "/") + lexy_out = build_root + "/lexy/" + source_path + lexy_env.VariantDir(lexy_out, source_path, duplicate=True) + sources = env.GlobRecursive("*.cpp", [lexy_out]) env.lexy_sources = sources - library_name = "liblexy_file" + env["LIBSUFFIX"] - library = lexy_env.StaticLibrary(target=os.path.join(source_path, library_name), source=sources) + library_name = "liblexy_file" + env.get("suffix", "") + env["LIBSUFFIX"] + library = lexy_env.StaticLibrary(target=lexy_out + "/" + library_name, source=sources) Default(library) include_dir = lexy_env.Dir(include_path) - source_dir = lexy_env.Dir(source_path) env.Append(CPPPATH=[include_dir]) if env.get("is_msvc", False): env.Append(CXXFLAGS=["/external:I", include_dir, "/external:W0"]) else: env.Append(CXXFLAGS=["-isystem", include_dir]) env.Append(CXXFLAGS=[""]) - env.Append(LIBPATH=[source_dir]) + env.Append(LIBPATH=[lexy_env.Dir(lexy_out)]) env.Prepend(LIBS=[library_name]) @@ -121,11 +123,15 @@ def build_fmt(env): source_path = "fmt/src" paths = [include_path, source_path] fmt_env.Append(CPPPATH=[[fmt_env.Dir(p) for p in paths]]) - sources = env.GlobRecursive("*.cc", paths, os.path.join(source_path, "fmt.cc")) + # Mirror fmt's source tree into the per-config build dir. + build_root = (env.get("build_dir") or env.Dir("#").abspath).replace("\\", "/") + fmt_out = build_root + "/fmt/" + source_path + fmt_env.VariantDir(fmt_out, source_path, duplicate=True) + sources = env.GlobRecursive("*.cc", [fmt_out], fmt_out + "/fmt.cc") env.lexy_sources = sources - library_name = "libfmt" + env["LIBSUFFIX"] - library = fmt_env.StaticLibrary(target=os.path.join(source_path, library_name), source=sources) + library_name = "libfmt" + env.get("suffix", "") + env["LIBSUFFIX"] + library = fmt_env.StaticLibrary(target=fmt_out + "/" + library_name, source=sources) Default(library) include_dir = fmt_env.Dir(include_path) @@ -139,7 +145,7 @@ def build_fmt(env): else: env.Append(CXXFLAGS=["-isystem", include_dir]) env.Append(CXXFLAGS=[""]) - env.Append(LIBPATH=[fmt_env.Dir(source_path)]) + env.Append(LIBPATH=[fmt_env.Dir(fmt_out)]) env.Prepend(LIBS=[library_name]) env.exposed_includes += env.fmt["INCPATH"] @@ -171,11 +177,15 @@ def build_vmcontainer(env): source_path = "vmcontainer/lib/src" paths = [include_path, source_path] vmcontainer_env.Append(CPPPATH=[[vmcontainer_env.Dir(p) for p in paths]]) - sources = env.GlobRecursive("*.cpp", paths) + # Mirror vmcontainer's source tree into the per-config build dir. + build_root = (env.get("build_dir") or env.Dir("#").abspath).replace("\\", "/") + vmcontainer_out = build_root + "/vmcontainer/" + source_path + vmcontainer_env.VariantDir(vmcontainer_out, source_path, duplicate=True) + sources = env.GlobRecursive("*.cpp", [vmcontainer_out]) env.vmcontainer_sources = sources - library_name = "libvmcontainer" + env["LIBSUFFIX"] - library = vmcontainer_env.StaticLibrary(target=os.path.join(source_path, library_name), source=sources) + library_name = "libvmcontainer" + env.get("suffix", "") + env["LIBSUFFIX"] + library = vmcontainer_env.StaticLibrary(target=vmcontainer_out + "/" + library_name, source=sources) Default(library) include_dir = vmcontainer_env.Dir(include_path) @@ -189,7 +199,7 @@ def build_vmcontainer(env): else: env.Append(CXXFLAGS=["-isystem", include_dir]) env.Append(CXXFLAGS=[""]) - env.Append(LIBPATH=[vmcontainer_env.Dir(source_path)]) + env.Append(LIBPATH=[vmcontainer_env.Dir(vmcontainer_out)]) env.Prepend(LIBS=[library_name]) env.exposed_includes += env.vmcontainer["INCPATH"] diff --git a/scripts b/scripts index 4ddc9a1..d8f908c 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 4ddc9a1b9b14fddab397325a1d016de56d465d75 +Subproject commit d8f908cb2d7b4a4564d12f71cbfe97bed241195f