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 stay under the component BUILDDIR;RENAMEstill runs there).
NOINSTALL=and truthyNOINSTALL=ON|TRUE|1|YESenable with WARNING (write NOINSTALL, not NOINSTALL=…).
Falsy values (OFF|FALSE|0|NO) are FATAL (omit the key to install).
BUILDONLYis removed and FATAL (use NOINSTALL).
MetaNOINSTALLis prevalent: finalize stamps every member.
A unique backend is still used when detected; dual markers stay FATAL.
buildmaster_meta(id title "REPACK")plusbuildmaster_meta_addmerges every produced static archive of the member leaves into one prefix archive named after the meta id.
Wait edge is_installfor every static member, includingNOINSTALL.
That target does not callcmake --install/meson installwhenNOINSTALL; it only runs oficios (rename, outputs, strip) on the BUILDDIR so REPACK sees the produced stem, notfoo-static.lib.
Shared members that install stay INTERFACE (WARNING: they are not folded into the pack).
NOINSTALL+ shared as aREPACKmember is FATAL (the.so/.dllis not in the prefix and the builddir is not public).
REPACKonbuildmaster_componentis valid for static only: first-level depend/link dests that are NOINSTALL static are the members; the publisher archive in the prefix is rewritten (POST_BUILDon<id>_install).
REPACK+NOINSTALLon the same id is FATAL.
REPACK+ headers or executable on the same id is FATAL.
Shared +REPACKon a component is WARNING + skip at finalize.
Zero static members is FATAL.
Anexecutabledest of a REPACK publisher is skipped (INFO), not a member.
Anexecutableleaf of aREPACKmeta is the same INFO skip and is not a merge INPUT.
buildmaster_linkto aNOINSTALLdest is FATAL (order-only:buildmaster_depend, or publish via aREPACKmeta / component).- Helper
.pc(PC={…}).
After install, write${BUILDMASTER_INSTALL_LIBDIR}/pkgconfig/<Name>.pcfor this prefix.
FATAL if the path already exists.
Not a portable package.
NOINSTALL+ enabledPC=is FATAL (no shared prefix).
executable+ enabledPC=is FATAL. - Post-install oficios.
Sealed GLOBALBUILDMASTER_COMPONENT_<id>_INSTALL_OFICIOSis the ordered list (rename_library|rename_executable,outputs,strip_res,pc).
_bm_install_rules_writeemits only those files underscripts/install_rules/and a rules aggregator with bareinclude().
Templates have noif(@_BM_*_ENABLED@)feature flags.
CMake and Meson share the same oficios.
The stage wrapper isinstall_library.cmake.in→<id>_install_library.cmake(install_execis gone).
_BM_BUILDONLYremains the NOINSTALL token in wrappers and in theoutputsoficio. - Meta
TOOLCHAINinheritance.
buildmaster_meta(… "TOOLCHAIN=<profile>")copies the profile onto members and onto empty dests.
An explicit childTOOLCHAINwins. - Hooks.
buildmaster_hook_component(id fn alias [CAPTURE …])/buildmaster_hook_graph(fn alias [CAPTURE …]).
fnmust exist at registration.
Alias is the only order key (ASCII ascending).
CAPTUREsnapshots by copy.
A hook is not an edge and does not flip the component to deferred. - Unified logging.
_bm_log_message(<module> <level> "<text>" [<indent>])is internal.
Publicbuildmaster_message(<level> "<text>" [<indent>])always uses moduleUSER.
WARNINGandFATALare never filtered.
WhenBUILDMASTER_LOG_NOCOLORisOFF, lines are painted: STATUS stays the default CMake color; WARNING yellow (message(NOTICE)); INFO green; DEBUG cyan; LOWLEVEL dim; FATAL red.
ONleaves the text unpainted. BUILDMASTER_LOG_NOCOLOR.
Truthy env or-D(1/ON/TRUE/YES) storesONand turns ANSI off; anything else isOFF(color on).
Same pattern asBUILDMASTER_VERBOSE.
DefaultOFF.
Written intopropagate_varsand the toolchain dump so nested-Pscripts see the same switch.- Silent env runner
Replays nested[BuildMaster/…]lines live; the full child log is dumped on failure. - Prefix search.
Nested CMake getsBUILDMASTER_INSTALL_DIRprepended onCMAKE_PREFIX_PATH(runners export it; empty install dir is FATAL).
Nested pkg-config still getsPKG_CONFIG_PATHfirst.
find_package/*Config.cmakeunder the shared prefix resolve without per-leaf-DFOO_ROOT=. - Toolchain translator.
One pass per profile: drop foreign dialect and IPO tokens (-flto*,-ffat-lto-objects,-fno-fat-lto-objects,/GL,/LTCG,-fuse-ld=), rewrite-I/-L↔/I//LIBPATH:, then inject this profile’s linker flavor and IPO without writing-flto/-ffat-lto-objectsback onto gcc/clangCMAKE_C{XX}_FLAGS.
Those compile tokens live inBM_TC_IPO_COMPILE_OPTIONSso the leafCMAKE_INTERPROCEDURAL_OPTIMIZATIONmodule is the single dialect (-DCMAKE_<LANG>_COMPILE_OPTIONS_IPO=).
LD still gets-fltoor/LTCG.
MSVC/clang-cl still write/GLor-fltoon C/CXX.
-fuse-ld=as before (see Fixed).
Called from CMake/Meson stages before the env runner refresh.
-fPICstays on gcc/clang; it is dropped on msvc. - On-demand tools.
Bootstrap always initializesninja.
The archiver is not a tool: it is a field of theTOOLCHAIN=profile (CMAKE_AR/ Meson[binaries] arandld).
cmake,meson,git, andfileinitialize on first use: backend wrappers (cmake/meson),GIT={…}/FILES={…}on the optstr, or a pendingFILES SOURCEafter the tree is unpacked.
Extra tools live undertools/extra/<id>/(pkgconfigis the only extra in this release) and start only viaREQUIRE_TOOL=<id>/REQUIRE_TOOL={id;id2}onbuildmaster_componentorbuildmaster_meta.
EmptyREQUIRE_TOOL/REQUIRE_TOOL=/REQUIRE_TOOL={}is WARNING and ignored.
An id that is not inBUILDMASTER_TOOLS_EXTRA_KNOWN(or whose directory is missing) is FATAL — BM never silently falls back to a same-named system binary.
A second request is a no-op.
PC={…}only writes a helper.pc; it does not demandpkgconfig.
pkgconfigstill prefers a working system pkg-config/pkgconfand builds the bundled tree only when that probe fails.
BUILDMASTER_INITIALIZE_EXTRA_TOOLSis gone.
Configure printsSetting up tools: <name>only when that tool actually starts.
Compiler profiles follow the same rule: configure loads the parent profile inferred fromCMAKE_C_COMPILER/CMAKE_CXX_COMPILERand writes onlynative_default.iniplus that profile’s Meson file.
Any other name (gcc,clang,clang-cl,msvc) is demanded when a component or meta setsTOOLCHAIN=to it.
Missing cc/cxx/ar/ld is FATAL on that id and names the parent profile plusCMAKE_C_COMPILER.
A gcc job does not need clang, lld or llvm-ar on PATH.
There is noREQUIRE_TOOL=gcc. - Harness + consumer tests
Recursive cmake/meson, helper.pc, meta-toolchain, LINKFLAGS (OPTIONS fold + no INTERFACE leak; meta ignore), hooks, late link, rawLINK=, duplicate edges,GIT={RESET;PATCH=…}, reset-then-patch, PC clobber (install FATAL),REPACKmeta and component (including NOINSTALL members that emit*-staticuntil_installrename), private-headers-I,FILES=unpack / SOURCE, FILES-on-meta FATAL, outline groups (CMake + Meson leaves),NOINSTALL(build without prefix publish),REQUIRE_TOOL=pkgconfigbundled path,BUILDONLYremoved /NOINSTALL=OFFFATAL, modeexecutable(plain CMake/Meson, link a BM static, rename stem, NOINSTALL, REPACK publisher that ignores an executable dest, WHOLE meta + SHARED host that must not see the exe,exe-rescanstatic provider-before-consumer underIPO=fat), negativesexe-pc/exe-repack/exe-empty-produced, toolchain profilear/ldtext check (only the parent profile plus anyTOOLCHAIN=actually used), translator dialect +/linkorder. - Harness entry
run_buildmaster_main.
One target: smoke (install stages + artifacts) already depends onrun_buildmaster_negativeandrun_buildmaster_consumer(consumer_nested); then a wiped siblingconsumer_cibindir.
Local one-liner:rm -rf build/harness && cmake -S .github/tests/harness -B build/harness -G Ninja && cmake --build build/harness --target run_buildmaster_main. - Configure report (
BUILDMASTER_VERBOSE).
After graph hooks, the primary bootstrap printsBuildMaster <version> Configuration:(moduleReport): parent toolchain paths/flags, then an alphabetical component table (ID/TYPE/LINK) with one-levelNEEDED BYand explicit overrides only (CFLAGS,CXXFLAGS,FILES,LINKFLAGS,NOINSTALL).
Groups are omitted.
Row order is readability, not the graph walk.
Nested bootstraps stay silent. IPO=on component and meta.
Per-id LTO, independent of parentCMAKE_INTERPROCEDURAL_*.
Omitted → inherit the parent (and leftover-flto//GLtokens).
IPO/IPO=/IPO=on→ thin LTO (gcc/clang:CMAKE_INTERPROCEDURAL_OPTIMIZATION+ compile-options-flto; clang-cl-fltoon C/CXX; msvc/GL+/LTCG).
IPO=offstrips every IPO token even if the parent has IPO on.
IPO=fatis thin plus-ffat-lto-objectson gcc/clang compile options only (BM_TC_IPO_COMPILE_OPTIONS/ Mesonc_args), never a second copy onCMAKE_C_FLAGS(that mixed-flto -ffat-lto-objectswith CMake’s-flto=auto -fno-fat-lto-objectsand the last token won).
MSVC and clang-cl treat fat as on; no fat objects on LD.
Darwin: AppleClang rejects-ffat-lto-objects, sofatison(-fltoonly) and one STATUS notice is emitted per configure.
Invalid values are FATAL.
A meta withIPO=stamps members that omit the key (same destinations asTOOLCHAIN=); an explicit childIPO=wins and a second meta does not FATAL.
CMake/Meson stages call_bm_tc_translate_component; Meson-Db_lto=follows the same mode.
Harness: translatoron/off/fatvs parent cache andBM_TC_IPO_COMPILE_OPTIONS;exe-rescan(provider before consumer +IPO=fat); negativeipo-bad(IPO=nope).
Changed
- Breaking — public API is
buildmaster_*only.
Ten commands (see public_functions.md):buildmaster_component,buildmaster_depend,buildmaster_link,buildmaster_meta,buildmaster_meta_add,buildmaster_group,buildmaster_group_add,buildmaster_hook_component,buildmaster_hook_graph,buildmaster_message.
Internals are_bm_<craft>_*and are not a supported API.
In particular:create_cmake_component/create_meson_component/create_*_headers_component/create_componentare gone. Usebuildmaster_component.component_link→buildmaster_link.component_dependency→buildmaster_depend.create_meta_component→buildmaster_meta.meta_component_add→buildmaster_meta_add.create_git_*/buildmaster_git_*→GIT={…}on the component.file_download/file_download_cached/file_decompress→FILES={…}on the component. There is no publicbuildmaster_download*/buildmaster_decompress/buildmaster_prerequisite.component_repack/buildmaster_repackare gone. Usebuildmaster_meta(… "REPACK")+buildmaster_meta_add, orREPACKon a staticbuildmaster_component.buildmaster_on_component_materialize→buildmaster_hook_component.buildmaster_on_graph_finalized→buildmaster_hook_graph.buildmaster_messagepublic arity is<level> "<text>" [<indent>]. Module is alwaysUSER.ensure_build_dir,sanitize_for_filename, import hints, toolchain profile/validate, archiver lookup, checksum and git marker are internal.
- Breaking —
INDENT=is gone as a layout knob.
The optstr key still parses so old trees configure, but it is WARNING + ignored (Use buildmaster_group / buildmaster_group_add for configure outline).
Visual indent comes only from the group walk at configure time.
Compile and install comments stay at indent 0. - Breaking — no public build directory.
buildmaster_component/_bm_backend_*_createdo not take a builddir.
The 2.0/2.1 arity heuristic is gone.
Extra arguments are FATAL.
_bm_path_builddiris gone.
Callers that still pass a path get the arity FATAL (or CMake unknown-command if they called the deleted helper). - Breaking — no
buildmaster_repack.
There is no public merge command, noOUTPUT=/INPUTS=parse arguments, no path tokens.
Members of aREPACKmeta are the inputs.
The published stem is the meta id.
Callers that still invokebuildmaster_repackget CMake’s unknown-command FATAL. - Breaking — no generated fragment to
include().
No out-variable. - Breaking — dependant factories are gone.
Usebuildmaster_component+buildmaster_depend(orbuildmaster_link, which waits). - Breaking —
LINK_EXTRAis gone.
Graph nodes:buildmaster_link.
Raw system libs:LINK=. - Breaking —
LINKFLAGSis not INTERFACE.
There is notarget_link_optionson<id>.
Flags fold into that id’s nested OPTIONS only.
A parent that links the component does not inherit-Wl,-Bsymbolic//FORCE:MULTIPLE.
Put flags on the id that actually builds, or on the final executable yourself.
MetaLINKFLAGSis WARNING + ignored. - Breaking —
BUILDONLYis gone.
WriteNOINSTALL.
The old key is FATAL.
There is noNOINSTALL=OFF. - Breaking —
BUILDMASTER_DEBUGis gone. - Breaking — options string.
Flag keys may omit=.
Known flags:RENAME,NOINSTALL,WHOLE,STRIPRES,PC,GIT,REPACK,FILES,REQUIRE_TOOL.
BUILDONLYis accepted by the splitter only so the parser can FATAL.
BACKEND=andSOURCE=requireKEY=value.
INDENT=is accepted only to warn.
;inside{…}is not a pair break.
Unknown keys warn; extra positionals are fatal. - Breaking — install wrapper.
install_exec.cmake.in/<id>_install_exec.cmakeare gone.
Wrapper isinstall_library.cmake.in.
Generated rules live underscripts/install_rules/.
Workernormalize_install_outputs.cmakeisnormalize_install_libraries.cmake. - Archiver is toolchain, not a tool.
CMAKE_ARand Meson[binaries] ar/ldcome from the componentTOOLCHAIN=profile:msvc→lib.exe+link.exe;clang-cl→llvm-lib+lld-link; Linuxclang→llvm-ar+ld.lld; Darwinclang→ cctoolsar+ld; Linuxgcc→ binutilsar+-fuse-ld=bfd; Darwingcc→ Homebrewgcc-N+ cctoolsar/ld(noCMAKE_LINKER_TYPE=BFD).
A clang-cl parent does not forcellvm-libonto anmsvcleaf or onto REPACK merge.
Missing AR on a profile is FATAL (incomplete toolchain), not an optional tool miss. - Stage COMMENT:
configure stays CMake/Meson; compile and install use[BuildMaster/Ninja]. - Root
CMakeLists.txtincludes helpers beforeinit_vars, soadd_subdirectory(buildmaster)alone bootstraps a consumer. - Root configure includes
GNUInstallDirs. - Post-install git reset + clean runs only after a PATCH, and only in the component work tree (
GIT ROOT=), never in the host repo. - Tools bootstrap is only
ninja.
The rest start on demand (REQUIRE_TOOLfor extras).
There is noBUILDMASTER_INITIALIZE_EXTRA_TOOLS.
Fixed
- Per-component
TOOLCHAINdump.
Nested configures load a component toolchain file built from the parent registry:BUILDMASTER_INSTALL_*, template dirs (BUILDMASTER_TOOLS_CMAKE_SRCDIR, …) andENV_*stay unified with the parent; only compilers/binutils are overridden.
A toolchain override no longer bootstraps a second install tree under the component build dir.
Incomplete snapshots (missing tool*_SRCDIR/ENV_*) no longer die withFile /templates/configure.cmake.in does not existwhen a nested project creates further components.
Harness fixturetc-prefixlocks the dump. - 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.
Nested BM keepsBUILDMASTER_ROOT,BUILDMASTER_INSTALL_DIRandBUILDMASTER_CONFIGURED. - Meson nested setups with system
ldno longer pass-fuse-ld=/usr/bin/ld(or other absolute linker paths) intoc_link_args/cpp_link_args. clang-cl+ inherited MSVC LTCG flags.
When the parent job usesclang-cl(or a stage selectsTOOLCHAIN=clang-cl), stages strip/GLand/LTCG*from C/CXX and linker flags instead of forwarding them.
The translator is the single place that puts profile IPO back (-fltoor/GL+/LTCG).- Meson on Windows (MSVC-like toolchains) does not inject
/std:c11into nested Mesonc_args. - Meson stages:
SCCACHE_DIRpath normalization no longer writes intoCCACHE_DIR. - Dependant configure targets under Ninja.
Long configures no longer look hung; each dependant configure setsUSES_TERMINALand a clearCOMMENT.
On Windows + Ninja the redundantcmake -E echothat concatenatedConfiguring xtwice is gone. - Git operations never run in the host repository.
A work tree equal toCMAKE_SOURCE_DIR, or aROOT=/SOURCE=path that escapes the componentsrcdir, is FATAL before the tree is probed. - Fragment GLOBAL
BUILDMASTER_COMPONENT_<id>_NAMES/_FILES.
Sealed afterwrite_fragmentso REPACK / meta merge see the produced archives. - REPACK +
NOINSTALLmembers wait<id>_install(oficios on the BUILDDIR), not<id>_build, so variant stems (*-static) are renamed before merge.
MergeCMAKE_ARis the publisherTOOLCHAIN=archiver, not the parentCMAKE_AR. STRIPRESuses the component archiver.
Amsvcleaf built withlib.exeno longer lists/removes members with the parent’sllvm-lib(or the reverse) and warnsno such file or directoryfor a name/LISThad just printed.- Toolchain triples are mandatory.
Linuxgccis binutilsar+-fuse-ld=bfd.
Darwingccis Homebrewgcc-N+ cctoolsar/ld(noCMAKE_LINKER_TYPE=BFD, no-fuse-ld=bfd; Apple CMake rejectsLINKER_TYPE 'BFD').
Linuxclangisllvm-ar+-fuse-ld=lld.
Darwinclangis cctoolsar+ld.
clang-clisllvm-lib+lld-link+-fuse-ld=lldon C/CXX and LD (Meson sanity ignores the flag after/link).
msvcislib.exe+link.exe.
A missing tool of that triple is FATAL.
Linuxgcc/clangget-fuse-ld=on LD only (cc -c -Werror=unused-command-line-argument).
Darwin gcc/clang get none (ld64). -fuse-ld=placement.
clang-cl: on C/CXX and LD (Meson sanity isclang-cl <c_args> /link <c_link_args>; the driver ignores the flag after/link).
Linuxgcc/clang: LD only (cc -c -Werror=unused-command-line-argument+ fuse-ld on C made dav1d report “Atomics not supported”).
Darwingcc/clang: no-fuse-ld(ld64).
Harness checks all four.- Darwin
IPO=fat.
-ffat-lto-objectsis not passed on Apple (AppleClang rejects it; nested try_compile died).
Fat becomeson.
One STATUS per configure.