From 1ccb591959be945409c6497074a1a5e2e5d099e7 Mon Sep 17 00:00:00 2001 From: Caleb Burke Date: Fri, 25 Sep 2026 19:58:11 -0700 Subject: [PATCH] build system update --- .vscode/settings.json | 4 +-- CMakeLists.txt | 4 +-- CMakePresets.json | 66 ++++++++++++++++++++++++++++++++++++++++ README.md | 12 +++++--- bin/dev | 71 +++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 146 insertions(+), 11 deletions(-) create mode 100644 CMakePresets.json diff --git a/.vscode/settings.json b/.vscode/settings.json index 9fc89bf..43db259 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -41,8 +41,8 @@ "LD_LIBRARY_PATH": "/home/caleb/.local/share/vulkan-sdk/1.4.357.1/x86_64/lib/VulkanLoader/lib:/home/caleb/.local/share/vulkan-sdk/1.4.357.1/x86_64/lib", "VK_ADD_LAYER_PATH": "/home/caleb/.local/share/vulkan-sdk/1.4.357.1/x86_64/share/vulkan/explicit_layer.d" }, - "cmake.buildDirectory": "${workspaceFolder}/build", - "cmake.generator": "Ninja", + "cmake.useCMakePresets": "always", + "cmake.copyCompileCommands": "${workspaceFolder}/build/compile_commands.json", "cmake.configureOnOpen": true, "files.associations": { ".clang-format": "yaml", diff --git a/CMakeLists.txt b/CMakeLists.txt index 56431db..89400a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22.2) +cmake_minimum_required(VERSION 3.25.2) set(NAME sophia) @@ -15,8 +15,6 @@ endif() if(CMAKE_BUILD_TYPE STREQUAL "Debug") message(STATUS "Debug build: enabling debug logging") -else() - add_compile_definitions(NDEBUG) endif() set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..c5e528c --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,66 @@ +{ + "version": 6, + "configurePresets": [ + { + "name": "base", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/${presetName}", + "cacheVariables": { + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "debug", + "displayName": "Debug", + "inherits": "base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "release", + "displayName": "Release (with debug info)", + "inherits": "base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo" + } + }, + { + "name": "asan", + "displayName": "Debug + AddressSanitizer/UBSan", + "inherits": "debug", + "cacheVariables": { + "CMAKE_CXX_FLAGS": "-fsanitize=address,undefined -fno-omit-frame-pointer", + "CMAKE_C_FLAGS": "-fsanitize=address,undefined -fno-omit-frame-pointer" + } + }, + { + "name": "tsan", + "displayName": "Debug + ThreadSanitizer", + "inherits": "debug", + "cacheVariables": { + "CMAKE_CXX_FLAGS": "-fsanitize=thread", + "CMAKE_C_FLAGS": "-fsanitize=thread" + } + } + ], + "buildPresets": [ + { + "name": "debug", + "configurePreset": "debug" + }, + { + "name": "release", + "configurePreset": "release" + }, + { + "name": "asan", + "configurePreset": "asan" + }, + { + "name": "tsan", + "configurePreset": "tsan" + } + ] +} diff --git a/README.md b/README.md index 2637f3c..6e268ef 100644 --- a/README.md +++ b/README.md @@ -8,17 +8,21 @@ COMING SOON ### Dependencies -- [cmake](https://cmake.org/) +- [cmake](https://cmake.org/) 3.25+ +- [Ninja](https://ninja-build.org/) - [Vulkan SDK](https://vulkan.lunarg.com/) - With validation layers ### Build instructions +Builds use the presets in `CMakePresets.json` (`debug`, `release`), each in its own `build/` directory: + ```sh -cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -cd build -make -j +cmake --preset debug +cmake --build --preset debug ``` +Or with the dev helper: `dev build [preset] [--fresh]`. + ## Third Party libraries used - [GLFW](https://github.com/glfw/glfw) - Window and input handling diff --git a/bin/dev b/bin/dev index 3519cf2..f9425eb 100755 --- a/bin/dev +++ b/bin/dev @@ -20,6 +20,7 @@ WAIT_FOR_PROCESS = "wait_for_process" class DevHelper: SHADER_EXTENSIONS = (".vert", ".frag", ".comp", ".geom", ".tesc", ".tese") PROJECT_NAME = "sophia" + DEFAULT_PRESET = "debug" def __init__(self): self.last_exit_status = 0 @@ -39,9 +40,29 @@ class DevHelper: return method(*rest, **kwargs) # Primary command wrappers + + ## + # Configures and builds a CMake preset (see CMakePresets.json). + # dev build [preset] [--fresh] + # The preset defaults to debug and builds into build/. --fresh + # discards the preset's CMake cache before configuring. def build(self, *args, **kwargs): - command = f"rm -rf build && cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug" - self._exec_in_root(command, replace=True) + preset, fresh = self._parse_preset_args(args, "build") + self._build_preset(preset, fresh) + + ## + # Builds the debug preset (incrementally), then runs the testbed from + # build/debug. Any arguments are passed through to the testbed. + # dev testbed [args...] + def testbed(self, *args, **kwargs): + preset = self.DEFAULT_PRESET + self._build_preset(preset, fresh=False) + + preset_dir = os.path.join(self._project_root(), "build", preset) + binary = os.path.join(preset_dir, "bin", "testbed") + print(f"Running: {shlex.join([binary, *args])}") + os.chdir(preset_dir) + os.execv(binary, [binary, *args]) ## # Checks out a branch named after a Gitea issue URL. @@ -149,6 +170,52 @@ class DevHelper: if not name.startswith("_") and name != "call" and callable(getattr(DevHelper, name)) ) + def _parse_preset_args(self, args, command): + preset = None + fresh = False + for arg in args: + if arg == "--fresh": + fresh = True + elif arg.startswith("-"): + raise ValueError(f"Unknown option: {arg}") + elif preset is None: + preset = arg + else: + raise ValueError(f"Usage: dev {command} [preset] [--fresh]") + + preset = preset or self.DEFAULT_PRESET + presets = self._configure_presets() + if preset not in presets: + raise ValueError(f"Unknown preset: {preset} (available: {', '.join(presets)})") + return preset, fresh + + def _configure_presets(self): + with open(os.path.join(self._project_root(), "CMakePresets.json")) as f: + presets = json.load(f)["configurePresets"] + return [preset["name"] for preset in presets if not preset.get("hidden")] + + def _build_preset(self, preset, fresh): + configure = ["cmake", "--preset", preset] + if fresh: + configure.append("--fresh") + self._run_in_root(configure) + self._copy_compile_commands(preset) + self._run_in_root(["cmake", "--build", "--preset", preset]) + + # Editor tooling reads build/compile_commands.json, so point it at the + # most recently configured preset. + def _copy_compile_commands(self, preset): + build_dir = os.path.join(self._project_root(), "build") + source = os.path.join(build_dir, preset, "compile_commands.json") + if os.path.isfile(source): + shutil.copyfile(source, os.path.join(build_dir, "compile_commands.json")) + + def _run_in_root(self, command): + print(f"Running: {shlex.join(command)}") + result = subprocess.run(command, cwd=self._project_root()) + if result.returncode != 0: + sys.exit(result.returncode) + def _parse_issue_url(self, url): match = re.match(r"^(https?://[^/]+)/([^/]+)/([^/]+)/issues/(\d+)", url) if match is None: