Releases: StormBytePP/StormByte-BuildMaster
Release list
Version 2.0.1
[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:
- How to write a component, every optstr key, and the contract: README.md
- The ten names, and nothing else: public_functions.md
- Porting an older caller: MIGRATE.md
Fixed
- Nested
project()no longer stacksbm/<id>under the child's-B. Component build dirs stay${BUILDMASTER_BINDIR}/bm/<id>of the outermost consumer, so WindowsCMAKE_OBJECT_PATH_MAX(250) is not blown by Suite BUNDLED graphs (Crypto → Buffer → Logger → String → Base). - Windows env runner
Add-TypedumpedSOURCE_CODE_ERRORunder MSVCLIB.Enable-BmAnsiConsolecompiled the VT P/Invoke while the process still had the parent jobLIB/INCLUDE/LIBPATH(invalid SDK fragments such as10.0.26100.0//x64). PowerShell 5.1 treats those as compiler errors. The variables are cleared only aroundAdd-Typeand restored after, in bothrunner_windows.ps1.inandrunner_windows_silent.ps1.in. NestedLIBprepend for the real compile is unchanged. - Nested
-G Ninjaused a ghostCMAKE_MAKE_PROGRAM. Theninjatool already exportedNINJA_EXECUTABLEon the component toolchain. It did not exportCMAKE_MAKE_PROGRAM, so a child CMake (including aclang-clleaf under anmsvcparent) ranC:/ProgramData/chocolatey/bin/ninja.exe --versionand died when that path was missing. When theninjatool is loaded,update_toolchain.cmakenow also exportsCMAKE_MAKE_PROGRAMto 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). Nosha256sumand nocat. The other half of the key is options, toolchain,CMAKE_BUILD_TYPE, IPO,mode,producedand host OS/arch, stored inbm-stamp.extra.bm-stamp.keyis 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 latermakemay re-apply and re-hash. The hit still does not parse upstreamCMakeLists.txt. Fixturestamp-patch: upstreamcmake_minimum_required(VERSION 99.0), the patch lowers it to 3.20, the secondstamp-leaf_installis a stamp hit and the worktree is back on 99.0. buildmaster_group_addrequired the group to already exist. Membership is stored even whenbuildmaster_grouphas not run yet. Finalize (_bm_group_plan) is the only existence check: an id named bybuildmaster_group_addthat 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. Fixturegroups-late:buildmaster_group_addnameslate-outerandlate-innerbefore eitherbuildmaster_group. The outline is stilllate-outer→late-inner→late-leaf(indent 2). Negativegroup-undefineddies at configure withgroup was never created.- Shared-dep skip dropped the consumer's need. First-wins still skips configure and build when
links/<id>.cmakealready exists (already built by). That skip is not "this consumer has no dependency". The nested process appends the skipped id tobm-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>_buildwait on<id>_installwhen 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. Fixturelinks-race: RaceA and RaceB both declare RaceMid, RaceMid declares RaceBase, no parentbuildmaster_dependand no hoist.RaceB_buildwaits onRaceA_install. Both shared libs call a Base-only symbol and link only RaceMid. - Transitive
buildmaster_linkstopped at one recorded hop.buildmaster_link(A B)still names only B._bm_links_write_onenow unions this process's link edges withLINKS_ATTACHEDand the dests already stored inlinks/<id>.cmake, then walks those ids to a fixpoint (seen-set, cycle cut). A parent rewrite no longer replaceslinks/StormByte-String.cmakewith the edges it knows and drops String → Base. A later configure that skips the winner still flattens-l/Stem.libfor the whole chain (--no-allow-shlib-undefined, Apple-undefined,error; Windows already fails unresolved). The caller does not name grandparents. Fixturelinks-transitive: shared TransBase ← TransMid ← TransUpper, and deferred TransLeaf links only TransUpper after the skip, while calling a Base-only symbol. WHOLEwrap was empty on ELF._bm_opt_whole_itemsbuilt-Wl,--whole-archive+ produced.a+-Wl,--no-whole-archive, thenfragment.cmakeflattened the CMake list to spaces andtarget_link_libraries(<id> INTERFACE …)let CMake classify the-Wltokens as flags and the archives as libraries. The DSO line becamelibavutil.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 registersCMAKE_{,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.WHOLEstill means one region around all produced archives of the id, not one wrap per file.
Version 2.0.0
[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:
- How to write a component, every optstr key, and the contract: README.md
- The ten names, and nothing else: public_functions.md
- Porting an older caller: MIGRATE.md
2.x versus 1.0.x is a different product: declarative graph, no generated fragment to include(), no public dependant factories, no public create_cmake_component / create_meson_component.
A 1.x CMakeLists.txt will not configure.
That is the point.
What 1.0.1 already did internally (headers mode, per-component toolchains, nested binutils, env runners) is still there; the caller and the declaration shape changed.
Added
- Declarative component graph.
buildmaster_depend(source dest)is order-only (id, stage name, or existing CMake target).
buildmaster_link(source dest)waits and records the same depend edge, sobuildmaster_link(A B)beforebuildmaster_component(B)still defers A.
destmay be a component (all produced libs), a library spec (name/subdir/name), a target, or an archive path.
Spec dests are listed on the source installOUTPUTso Ninja has a production rule.
A spec or on-disk archive stays link-only.
Duplicate explicit edges are WARNING + no-op; internal auto-deps do not warn.
Unresolvable dest at finalize is FATAL. - Deferred materialization + eager INTERFACE stub.
Registration only stores metadata.
Fragments and stage targets exist at the end of parent configure (cmake_language(DEFER)onCMAKE_SOURCE_DIR).
Declaration order does not matter.
Components without edges still configure during parent configure; components with edges configure at build time under<id>_configure.
add_library(<id> INTERFACE)at registration so a siblingALIAS/target_link_librariesbefore DEFER does not see a missing target. buildmaster_component.
Backend is inferred fromsrcdir(CMakeLists.txtvsmeson.build; neither +headers→none).
Dual markers are FATAL unlessBACKEND=cmakeorBACKEND=meson(allowed set:BUILDMASTER_FACTORY_BACKENDS).
BACKEND=empty or an unknown name is FATAL.
SOURCE=<rel>is applied before that detect: the value is always under the positionalsrcdir(a leading/is still a child ofsrcdir, not an absolute path).
Escape above the componentsrcdir(or above the hostCMAKE_SOURCE_DIR) is FATAL before any existence probe.
After the boundary check, a missing directory is FATAL.
The resolved tree must contain a.gitonly whenGIT={…}is also set;SOURCE=itself is not a git root.
Arity isid title srcdir options mode produced [optstr].
Mode isstatic,shared,headers, orexecutable.
optionsis a backend-agnostic CMake list ofKEY=value(a single string is one pair).
IdiomsCFLAGS,CXXFLAGS,CPPFLAGS,LDFLAGS,INCLUDES,DEFINITIONSare rewritten for the nested compile and append to the parent job / toolchain.
Every other key is forwarded as-DKEY=valueto the nested CMake configure or Meson setup (Meson also uses-D).
A leading-D/-d//Don the key is stripped.
Private to that nested step, not INTERFACE.
noneignores the list.
noneoutside headers mode is FATAL unlessNOINSTALLis set; a unique backend is still used when present.- Mode
executable.
Produced specs are binary stems underBUILDMASTER_INSTALL_BINDIR(<stem>on Unix,<stem>${CMAKE_EXECUTABLE_SUFFIX}on Windows; never.exe.exe).
The parent stub is stilladd_library(<id> INTERFACE)— it is not anIMPORTEDexecutable.
Nested CMake/Meson build the binary;LINK/LINKFLAGSapply to that nested link.
Linux gcc/clang: nested and parentCMAKE_<LANG>_LINK_EXECUTABLEwrap<LINK_LIBRARIES>with-Wl,--start-group/--end-groupso a static exe that lists provider before consumer (ld.bfd single pass) still resolves, including under LTO.
Darwin ld64 and MSVC/clang-cl do not get those flags.
SHARED/MODULE recipes are unchanged.
WHOLEon the executable itself is INFO and ignored.
A leaf of aWHOLEmeta is the same INFO skip: the meta must not emit-WHOLEARCHIVE:<bindir>/<stem>.exe/--whole-archiveof the binary.
STRIPRESdoes not run.
PC={…}ENABLED is FATAL.
REPACKon the executable itself is FATAL.
A first-leveldepend/linkdest that isexecutableis not a REPACK member (INFO skip; not FATAL as a “publishing member”).
A leaf of aREPACKmeta that isexecutableis the same INFO skip and is not an INPUT of the merge.
Extrabuildmaster_linkdests that are raw library specs are not folded as IMPORTED archives on an executable.
Produced exe stems are neverBM_LINKS_LIBNAMESand never land on a consumer or meta link line (gzip.exeis not a library).
Membership in a meta is order-only (*_install).
Oficios:rename_executable(whenRENAME) thenoutputs. - Assigned build directory.
There is no public builddir argument.
The graph uses${CMAKE_CURRENT_BINARY_DIR}/bm/<id>andfile(MAKE_DIRECTORY).
The path is an internal property, not part of the DSL. - Outline groups.
buildmaster_group(id [title])andbuildmaster_group_add(group member…).
A group is not a component, a meta, or a link: no targets, no edges, no install.
After the graph is complete it only walks members in addition order and prints configure banners with indent.
Nested groups are allowed; a member must not contain a group.
Cycles and id clashes with a component/meta/group are FATAL (caller file:line).
Configure-time messages inherit the walk indent; compile/install stay flat.
Eager vs deferred is unchanged — the outline is cosmetic order, not a wait edge. - Headers island (mode
headers, with or without a backend).
No backend, orNOINSTALLheaders: private.
Direct consumers get a quoted-Ion that id’s nested configure only (CMakeCMAKE_{C,CXX}_FLAGSand Mesonc_args/cpp_args).
It does not recurse through further BM components, runners, or INTERFACE.
Publishing headers (backend + notNOINSTALL) install into the shared prefix; the prefix-Ialready covers them.
Several private islands on one consumer accumulate several-I. FILES={URL=…;NAME=…;UNPACK;SOURCE[=rel];FORCE;MD5=|SHA256=|EXPECTED_HASH=…}.
Declarative download onbuildmaster_component(meta + any FILES group is FATAL).
Always cached underBUILDMASTER_DOWNLOADSDIR(FORCErefetches).
Unpack is${BUILDMASTER_BINDIR}/files/<NAME>/, before nested configure (eager components included).
InnerSOURCE(at most one group, requiresUNPACK) is the srcdir after unpack — the positional path is ignored by design (WARNING).
That key is not the component optstrSOURCE=.
Other unpacked groups inject a private-Ion that id only (same rule as the headers island).
GIT={…}+ FILESSOURCEis FATAL.
Replaces the 1.xfile_download*+ dependant-component pattern.LINK=/LINK={…}.
Raw system linker names on the component or meta INTERFACE.
They propagate to whoever links that id.
Not BM nodes.
Revives 1.xLINK_EXTRAunder a shorter name.LINKFLAGS=/LINKFLAGS={…}.
Raw linker flags (/FORCE:MULTIPLE,-Wl,-Bsymbolic) for the nested cmake/meson link only.
Groups:WINDOWS,LINUX,MAC,UNIX(UNIX= Linux + macOS).
A group that does not apply is skipped at INFO.
Unknown platform key is FATAL.
Folded into that id’s OPTIONS at finalize (CMAKE_EXE/SHARED/MODULE_LINKER_FLAGSor Mesonc_link_args/cpp_link_args).
Nottarget_link_optionson the INTERFACE — a consumer of this id does not inherit the flags.
Meta: WARNING + ignore (no nested link).
Headers: WARNING + ignore.GIT={FETCH;SWITCH=<branch>;RESET;PATCH=<file>;ROOT=<rel>;TITLE=…}.
Srcdir git work onbuildmaster_component.
ROOT=is always under the componentsrcdir(same isolation as optstrSOURCE=): escape FATAL before existence, missing tree FATAL, work tree that is the hostCMAKE_SOURCE_DIRFATAL.
Flush order is fixed: FETCH → SWITCH → RESET → PATCH (PATCH order is declaration order).
RelativePATCH=is fromCMAKE_CURRENT_SOURCE_DIR.
EmptyGIT/GIT={}is WARNING.
Meta + any git op is FATAL.
FETCH/RESETare inner flags.
Post-install reset runs only when a PATCH was queued, and only inside that work tree (ROOT=).RENAME(flag, default ON).
Post-install normalize of variant basenames.
Libraries: oficiorename_library, workercomponent/rename/normalize_install_libraries.cmake.
Executables: oficiorename_executable, workernormalize_install_executables.cmake.
Headers mode never registers a rename oficio (stamps are not archives).WHOLE.
Whole-archive link of produced static archives.
Ignored (INFO) on headers and executable, including when that executable is a leaf of aWHOLEmeta.STRIPRES(flag, default ON).
AfterRENAME, strip*.resfrom static MSVC / clang-cl archives.
Shared / headers / executable never strip.NOINSTALL+REPACK.
BareNOINSTALLbuilds without publishing to the shared prefix (artifacts sta...
Version 1.0.1
Added
Header-only components
- New library mode
headersincreate_component/create_cmake_stages/create_meson_stages - Templates
component_headers.cmake.inandcomponent_headers_dependant.cmake.inINTERFACElibrary only (noIMPORTEDstatic/shared archives)target_include_directories(... SYSTEM INTERFACE "${BUILDMASTER_INSTALL_INCLUDEDIR}")- Depends on
<component>_installlike library modes
- Install
OUTPUTis a stamp file${builddir}/.buildmaster_headers_installed(avoids emptyOUTPUT/ CMP0175 with header-only trees) install_exec(CMake and Meson) creates missing stamp paths after a successful install- Simple API:
create_cmake_headers_componentcreate_cmake_headers_dependant_componentcreate_meson_headers_componentcreate_meson_headers_dependant_component
- Build stage is kept for a uniform graph (header-only projects are typically no-ops)
Nested linker and binutils propagation
- Nested CMake configures forward the parent toolchain so third-party builds match the top level:
- Linker:
CMAKE_LINKER_TYPE,CMAKE_LINKER,CMAKE_C_COMPILER_LINKER,CMAKE_CXX_COMPILER_LINKER,CMAKE_MT - Archiver / nm:
CMAKE_AR,CMAKE_C_COMPILER_AR,CMAKE_CXX_COMPILER_AR,CMAKE_RANLIB,CMAKE_C_COMPILER_RANLIB,CMAKE_CXX_COMPILER_RANLIB,CMAKE_NM CMAKE_MODULE_LINKER_FLAGS(in addition to existing EXE/SHARED linker flags)
- Linker:
tools/cmake/update_toolchain.cmakeregisters non-empty linker and archiver cache entries for the BuildMaster toolchain dump (paths normalized to forward slashes)- Nested Meson setups:
- Build
_MESON_LINK_ARGSfromCMAKE_EXE_LINKER_FLAGS - Linker selection via
buildmaster_fuse_ld_flag()(driver-safe-fuse-ld=flavors only):CMAKE_LINKER_TYPE=LLD/ forced LLD →-fuse-ld=lld-link(Windows) or-fuse-ld=lld(elsewhere)CMAKE_LINKER_TYPE=MSVC→-fuse-ld=link- Else map
CMAKE_LINKER/BM_TC_LINKERbasename (lld,ld.lld,gold,mold,bfd, …); systemldand absolute paths such as/usr/bin/ldemit no-fuse-ld(GCC rejects path-form-fuse-ld=/usr/bin/ld)
- Pass
AR/RANLIBinto Meson setup viacmake -E env(fromCMAKE_AR/CMAKE_RANLIBor the process environment)
- Build
- Env runners (
runner_linux.sh.in/runner_windows.bat.in) exportAR,RANLIB, andNMso any command launched throughENV_RUNNERinherits the same binutils as nested CMake/Meson env/init_vars.cmakeandupdate_env_runner()resolveAR/RANLIB/NMfromCMAKE_AR/CMAKE_RANLIB/CMAKE_NMorENV{…}before regenerating the runner scripts
Per-component toolchains
- Optional trailing
TOOLCHAINargument on the simple component API (create_cmake_*/create_meson_*, including headers and dependant variants) and on the atomic stage helpers (create_cmake_stages/create_meson_stages); the same profile applies to that component’s configure, build and install whether stages are generated via the factory or wired explicitly - Named profiles under
toolchain/profiles/:gcc,clang,clang-cl,msvcclang: LLD required on Linux; LLD not forced on macOSclang-cl: LLD (lld-link) +llvm-lib(Windows only)msvc:cl+link.exe+lib.exe(Windows only)gcc: system linker/archiver (LLD not forced)
- New module
toolchain/(init, helpers, profiles): validation, platform guards, profile load - Toolchain file registry (single source of truth for parent and component dumps):
buildmaster_toolchain_reset/export/export_raw/write- Modules register state in
*/update_toolchain.cmake; the parenttoolchain.cmakeis written once at the end of the BuildMaster rootCMakeLists.txt buildmaster_toolchain_write_component: parent registry snapshot + profile compiler/binutilsCACHE FORCEoverlay (no hand-maintained variable list in stage generators)
buildmaster_clean_ldflags()/buildmaster_clean_cflags()strip known-incoherent tokens by profile:msvc: remove LLD / Clang-LTO switches; other flags preservedclang-cl: remove MSVC LTCG tokens (/GL,/LTCGand variants) that clang-cl ignores or mishandles
- Component-local env runners (normal + silent) when
TOOLCHAINis set; parent global runners are not rewritten - When
TOOLCHAINis set, configure and build status lines (and dependant configureCOMMENT) include(with toolchain <name>); omitted when inheriting the parent job - IPO/LTO is never enabled by a profile; if the parent already had IPO on, nested stages keep a coherent setting (
CMAKE_INTERPROCEDURAL_OPTIMIZATION_*/ Mesonb_lto) without re-injecting MSVC/GL - Fully backward compatible: omitting
TOOLCHAINkeeps previous behaviour
Fixed
- Per-component
TOOLCHAINand nested BuildMaster installs: components with a toolchain override no longer bootstrap a second install tree under the component build dir (e.g.…/buffer/build/thirdparty/buildmaster/install). Nested configures load a component toolchain file built from the parent registry soBUILDMASTER_INSTALL_*, template dirs (BUILDMASTER_TOOLS_CMAKE_SRCDIR, …) andENV_*stay unified with the parent; only compilers/binutils are overridden - Incomplete component toolchain snapshots (missing tool
*_SRCDIR/ENV_*) that led to failures such asFile /configure.cmake.in does not existwhen nested projects created further components - Nested
add_subdirectory(buildmaster)no longer corrupts the sharedtoolchain.cmake: whenBUILDMASTER_CONFIGUREDis already TRUE (host loaded the parent dump asCMAKE_TOOLCHAIN_FILE), the rootCMakeLists.txtloads helpers, propagates vars, and returns—withouttoolchain_reset/ module re-export /toolchain_write. Previously a nested bootstrap cleared the registry, re-wrote only root-level keys, and overwrote the parent dump, which then broke deeper components with empty template roots (/configure.cmake.in,/component_shared.cmake.in) - Meson nested setups with system
ld: no longer pass-fuse-ld=/usr/bin/ld(or other absolute linker paths) intoc_link_args/cpp_link_args. GCC rejects path-form-fuse-ld=;buildmaster_fuse_ld_flag()only emits driver flavor names, so PostgreSQL and other Meson components configure correctly under a default Linux linker - clang-cl + inherited MSVC LTCG flags: when the parent job uses clang-cl (or a stage selects
TOOLCHAIN clang-cl),create_cmake_stages/create_meson_stagesstrip/GLand/LTCG*from C/CXX and linker flags instead of forwarding them. clang-cl was warningunknown argument ignoredfor/GL; IPO remains driven byCMAKE_INTERPROCEDURAL_OPTIMIZATION_*and Mesonb_lto, not by those MSVC-only switches - Meson on Windows (MSVC-like toolchains): do not inject
/std:c11into nested Mesonc_args. That flag did not fix clang-cl PostgreSQL C99 probes (UCRTcomplex/tgmathvs Clang_Complex) and could break realclbuilds (e.g. PostgresVA_ARGS_NARGS_/ non-constant initializers). PreferTOOLCHAIN msvcfor PostgreSQL on Windows. Upstream projects set their own C standard; only/Z7is still appended for CodeView on MSVC-like drivers - Meson stages:
SCCACHE_DIRpath normalization wrote intoCCACHE_DIRinstead ofSCCACHE_DIR, so sccache cache directories could be lost or overwrite the ccache path during nested Meson setup - Dependant configure targets (
component_*_dependant.cmake.in): under the Ninja generator, long configures (e.g. FFmpegmeson setup) looked hung — the silent env runner swallowedmessage(STATUS)from the configure-Pscript. Makefiles still printed progress. Now each dependant configure target setsUSES_TERMINALand a clearCOMMENT "Configuring <component>"so Ninja shows the step as soon as it starts - Dependant configure progress on Windows + Ninja:
cmake -E echo "Configuring …"plus the sameCOMMENTconcatenated on one line (Configuring x265Configuring x265). Dropped the redundantecho; a singleCOMMENTis enough - Dependant components:
indent_levelis forced to0increate_componentwhen a dependency is set. Hierarchical tabs are only meaningful in the parent configure log (message_indented); dependant stages run at build time and must not inherit plugin-level indentation in status lines or nested stage scripts
Version 1.0.0
Initial public release of StormByte-BuildMaster: a CMake DSL to configure, build, install and consume external CMake and Meson projects as first-class parts of a parent tree, with stage-based orchestration, explicit targets, coherent environment propagation, portable static-library bundling, and controlled failure propagation across the dependency graph.
Added
Core orchestration
- Parent-configure generation of configure / build / install stage scripts for external projects
- Explicit stage targets:
<component>_configure,<component>_build,<component>_install <component>_builddepends on<component>_configure- Shared install prefix (
BUILDMASTER_INSTALL_DIR) and generated script tree across the whole dependency graph - Safe recursive nesting via
BUILDMASTER_CONFIGURED(single initialization, no prefix fights) - IMPORTED targets (static and shared, including MSVC import libraries and DLLs) wired to install stages
- INTERFACE libraries also depend on
<component>_installso parenttarget_link_librarieswaits for a successful install - Simple API:
create_cmake_component,create_meson_component - Dependant variants:
create_cmake_dependant_component,create_meson_dependant_component - Advanced/explicit API:
create_cmake_stages,create_meson_stages - Project version exposed as
BUILDMASTER_VERSIONand shown in the bootstrap status line
Fail-fast and failure propagation
- Optional
BUILDMASTER_FAIL_FAST(env or-D; truthy:1/ON/TRUE/YES; default OFF) - On stage failure with fail-fast ON: write
markers/buildmaster.failedandmarkers/<component_id>.failed - Later stages print
Skipped <component title>and exit non-zero when the global marker exists - Env runners refuse further work if the global fail marker is present (
Skipped due to previous errors) - Unique
buildmaster_build_inittarget resets the markers directory at the start of every parent build (Ninja, Make,cmake --build) - Markers directory under
${BUILDMASTER_BINDIR}/markers/(no persistent success stamps) - Fail-fast OFF writes no markers so independent components can keep building (cache warming with ccache/sccache)
- Stage exec scripts (
configure_exec/build_exec/install_execfor CMake;setup_exec/compile_exec/install_execfor Meson) centralize exit-code handling and marker writes
Environment and toolchain
- Platform env runners (Linux/macOS shell, Windows batch) with silent variants
- Propagation of compilers, flags,
PATH,PKG_CONFIG_PATH,LIB,INCLUDE - Compiler-cache support (
CMAKE_*_COMPILER_LAUNCHER,CCACHE_DIR,SCCACHE_DIR) into child CMake and Meson builds - Optional full live output (
BUILDMASTER_DEBUG) and verbose compile-only output (BUILDMASTER_VERBOSE) - Failure diagnostics: silent runners dump captured logs on non-zero exit
CMake and Meson backends
- Nested CMake configures with Ninja, toolchain file, PIC, LTO and launcher injection
- Nested Meson setup/compile/install with matching environment and library type control
- Parallel builds via
NPROC/CMAKE_BUILD_PARALLEL_LEVEL - Stage targets depend on
buildmaster_build_initwhen available
File helpers
- Cache-aware downloads (
file_download_cached) with hash verification and retries - Force downloads (
file_download) with progressive backoff - Flexible
EXPECTED_HASH(ALGORITHM=digest, including forms such asSHA3_256=…; bare digest defaults to SHA256) - Portable archive extraction (
file_decompress) viafile(ARCHIVE_EXTRACT) - Strict path-traversal protection and consistent status messages
Git helpers
- Generated fragments for fetch, reset/clean, patch apply and branch switch
- API binds each operation to a component id (same id as
create_*_component):create_git_reset_file(out, component_id, title, repo)create_git_patch_file(out, component_id, title, repo, patches)create_git_fetch(out, component_id, title, repo)create_git_switch_branch(out, component_id, title, repo, branch)
- Registered git scripts run at the start of
<component>_configure(before nested CMake/Meson setup), in registration order - Call
create_git_*beforecreate_*_component/create_*_stagesfor that component - Optional aggregate target
buildmaster_clean(BUILDMASTER_CLEAN_RESET_REPOS, default ON)- Only components that used
create_git_*are affected - Per component:
git reset --hard+git clean -fdfrom the git toplevel (rev-parse --show-toplevel) - Invalidates that component’s configure (removes Meson
build.ninja/meson-private, or CMakeCMakeCache.txt/build.ninjaunder the component build dir) - Next
cmake --build/ninja/makere-enters<component>_configure→ re-applies git ops → nested setup → build - Controlled exclusively via environment variable (falsy:
0/OFF/FALSE/NO) - Propagated to nested BuildMaster instances through the toolchain file
- Not wired to the generator’s native
cleantarget (unreliable with Ninja); use:
cmake --build <builddir> --target buildmaster_clean
- Only components that used
- Automatic per-component post-install git reset
- After a successful
*_install, runsreset --hard+clean -fdonly for that component’s repo - Does not invalidate configure (avoids full reconfigure after every install)
- Removes the need for manual
POST_BUILDreset hooks in consumer projects (e.g. VPX, VMAF)
- After a successful
Static library support
- Portable static archive merging (
create_bundle_static_libraries):- Linux: GNU
ar -M(MRI) - macOS:
libtool -static - Windows:
lib /OUT:
- Linux: GNU
- Optional post-install rename of static libraries to canonical names
Platform support
- Linux, Windows (MSVC) and macOS (x86_64 and Apple Silicon)
- Extra-tool registration (e.g. bundled
pkgconf)
Notes
- Requires CMake ≥ 3.20; Meson and Ninja when using the corresponding backends.
- Stage scripts are generated at parent configure time — change
BUILDMASTER_DEBUG/BUILDMASTER_VERBOSE/BUILDMASTER_FAIL_FAST/BUILDMASTER_CLEAN_RESET_REPOSand re-run CMake to regenerate them. - Designed as a building block for multi-dependency projects (e.g. FFmpeg plugin graphs, multi-bitdepth codecs, database client bundles).