Skip to content

Version 2.0.1

Choose a tag to compare

@StormBytePP StormBytePP released this 27 Sep 00:00
· 2 commits to master since this release

[Summary]

BuildMaster is a small CMake DSL for a graph of other people’s builds.
You declare each dependency once — how it is produced, what it waits on — and the parent gets one shared prefix instead of a pile of ExternalProject / FetchContent glue.
CMake and Meson trees are first-class.

Typical use: add the submodule, add_subdirectory, declare components, then depend / link.
Declaration order does not matter.
The public surface is ten commands on purpose; everything else is internal.

If you landed here from a release link and have not read the tree:

Fixed

  • Nested project() no longer stacks bm/<id> under the child's -B. Component build dirs stay ${BUILDMASTER_BINDIR}/bm/<id> of the outermost consumer, so Windows CMAKE_OBJECT_PATH_MAX (250) is not blown by Suite BUNDLED graphs (Crypto → Buffer → Logger → String → Base).
  • Windows env runner Add-Type dumped SOURCE_CODE_ERROR under MSVC LIB. Enable-BmAnsiConsole compiled the VT P/Invoke while the process still had the parent job LIB/INCLUDE/LIBPATH (invalid SDK fragments such as 10.0.26100.0//x64). PowerShell 5.1 treats those as compiler errors. The variables are cleared only around Add-Type and restored after, in both runner_windows.ps1.in and runner_windows_silent.ps1.in. Nested LIB prepend for the real compile is unchanged.
  • Nested -G Ninja used a ghost CMAKE_MAKE_PROGRAM. The ninja tool already exported NINJA_EXECUTABLE on the component toolchain. It did not export CMAKE_MAKE_PROGRAM, so a child CMake (including a clang-cl leaf under an msvc parent) ran C:/ProgramData/chocolatey/bin/ninja.exe --version and died when that path was missing. When the ninja tool is loaded, update_toolchain.cmake now also exports CMAKE_MAKE_PROGRAM to the same binary. If the tool was not requested, nothing is written.
  • A later build re-entered the leaf on the unpatched tree. Configure, build and install now hash the component srcdir with CMake only (file(SHA256) per file, string(SHA256) of the sorted paths). No sha256sum and no cat. The other half of the key is options, toolchain, CMAKE_BUILD_TYPE, IPO, mode, produced and host OS/arch, stored in bm-stamp.extra. bm-stamp.key is created when install finishes and refreshed when a later build actually recompiles, and is the file a later install cache will read. A match skips nested cmake/meson and does not touch the prefix. A miss runs that stage. GIT={PATCH} is applied before the hash and the git root is reset when the last live holder finishes, including on a hit, so a later make may re-apply and re-hash. The hit still does not parse upstream CMakeLists.txt. Fixture stamp-patch: upstream cmake_minimum_required(VERSION 99.0), the patch lowers it to 3.20, the second stamp-leaf_install is a stamp hit and the worktree is back on 99.0.
  • buildmaster_group_add required the group to already exist. Membership is stored even when buildmaster_group has not run yet. Finalize (_bm_group_plan) is the only existence check: an id named by buildmaster_group_add that was never created is FATAL, including when that was the only declaration. Cycles and id clashes stay FATAL where they already were. A group is still not a component, a meta, or a link edge. Fixture groups-late: buildmaster_group_add names late-outer and late-inner before either buildmaster_group. The outline is still late-outer → late-inner → late-leaf (indent 2). Negative group-undefined dies at configure with group was never created.
  • Shared-dep skip dropped the consumer's need. First-wins still skips configure and build when links/<id>.cmake already exists (already built by). That skip is not "this consumer has no dependency". The nested process appends the skipped id to bm-reuse-needs.txt. After that configure returns, the parent walks the id and the dests already stored in its links file and hangs the closure on the consumer (LINKS_ATTACHED, so a later rewrite keeps it). <consumer>_configure / <consumer>_build wait on <id>_install when that stage is in this process, otherwise on the parent component that created the links file (<owner>_install). A skipped configure is still not a second build. Fixture links-race: RaceA and RaceB both declare RaceMid, RaceMid declares RaceBase, no parent buildmaster_depend and no hoist. RaceB_build waits on RaceA_install. Both shared libs call a Base-only symbol and link only RaceMid.
  • Transitive buildmaster_link stopped at one recorded hop. buildmaster_link(A B) still names only B. _bm_links_write_one now unions this process's link edges with LINKS_ATTACHED and the dests already stored in links/<id>.cmake, then walks those ids to a fixpoint (seen-set, cycle cut). A parent rewrite no longer replaces links/StormByte-String.cmake with the edges it knows and drops String → Base. A later configure that skips the winner still flattens -l / Stem.lib for the whole chain (--no-allow-shlib-undefined, Apple -undefined,error; Windows already fails unresolved). The caller does not name grandparents. Fixture links-transitive: shared TransBase ← TransMid ← TransUpper, and deferred TransLeaf links only TransUpper after the skip, while calling a Base-only symbol.
  • WHOLE wrap was empty on ELF. _bm_opt_whole_items built -Wl,--whole-archive + produced .a + -Wl,--no-whole-archive, then fragment.cmake flattened the CMake list to spaces and target_link_libraries(<id> INTERFACE …) let CMake classify the -Wl tokens as flags and the archives as libraries. The DSO line became libavutil.a … libavfilter.a -Wl,--whole-archive -Wl,--no-whole-archive. GNU ld.bfd (single pass) then dropped unreferenced avutil objects (av_md5_sum, AES/HMAC, …) while lld still linked. ELF now emits one $<LINK_GROUP:BM_WHOLE,…> and registers CMAKE_{,C_,CXX_}LINK_GROUP_USING_BM_WHOLE (prefix / suffix --whole-archive / --no-whole-archive) so every produced static of that id stays inside the wrap. Apple (-force_load) and MSVC (-WHOLEARCHIVE:) are unchanged. The fragment no longer replaces ; with spaces. WHOLE still means one region around all produced archives of the id, not one wrap per file.