The C library layer declares the C environment it presents, the engine realises and checks it - #668
Open
Sunrisepeak wants to merge 7 commits into
Open
Sunrisepeak wants to merge 7 commits into
Sunrisepeak wants to merge 7 commits into
Conversation
…e realises and checks it
Until now the compiler payload's target triple implied the environment a C
program compiles against. The moment a package supplies the C library
instead (mcpp:c-abi=<impl>), that stops being true: openkal-musl on
x86_64-windows-gnu generates PE/Win64 code while presenting a POSIX
environment, and every #ifdef _WIN32 above it is asking the wrong layer.
- [c-abi] manifest block: presents (posix/windows/none), data-model
(arch-default/lp64/llp64/ilp32), wchar (16/32), builtins (iso/platform).
Only a package that also provides mcpp:c-abi=<impl> may declare it.
presents/data-model/wchar carry no default; an unknown key or value is
always a parse error naming the key. Absent block changes nothing.
(modules/manifest/src/{targetside_model,toml,types}.cppm)
- Realisation (src/toolchain/cenv.cppm): a pure, table-driven mapping from
request to compiler tokens with no package names. The flagship case --
presents=posix on Windows/x86_64 -- realises as a Cygwin-flavoured
compile-only identity switch (--target=x86_64-pc-cygwin,
-U__CYGWIN__ -U__CYGWIN32__), leaving the link line on the graph's
resolved triple, because the two triples measure identical machine code.
Reaches C, C++, the dependency scan and the std module precompile.
A request this engine cannot realise is refused naming the target,
request and what is missing. [package] c-environment = "platform" opts a
package out of the realisation entirely.
- Verification, not trust (src/toolchain/cenv_probe.cppm): one -E -dM
predefined-macro dump, no codegen and no execution, checked against the
declaration and cached per configuration. Caught a real mapping bug
during development (data-model = "llp64" alongside presents = "posix" on
Windows silently read as already-satisfied); the mapping now refuses that
combination directly instead of relying on the probe to catch it late.
- __openkal__ defined for every target-side unit when the resolved
kernel-abi layer's interface is openkal, read from the layer's value.
- Closure visibility: provides = ["platform-sdk"] is a package's own
statement; the Target report gains a platform-deps line, and
[build] platform-dependencies = "refuse" fails the build when one is
present. (src/build/prepare.cppm)
- The realised environment and __openkal__ fold into the build fingerprint,
so LP64 and LLP64 builds of one source/manifest never share a directory.
The equivalent store-key gap for install-hook artifacts is designed but
not built here -- documented in docs/22 as a known gap.
Two pre-existing defects found while verifying this are filed separately,
not fixed here: #666 (a Clang-built mcpp binary SIGSEGVs
in its own ELF runtime inspector; GCC-built does not) and #667 (a GCC
self-host ICE importing mcpp.targetside from a new consumer under a
parallel build; -j1 avoids it).
Docs: docs/22 ([c-abi], verification, fingerprint), docs/21 (a declared
environment moves the compiled triple, not the linked one), docs/24 (the
three macro families, the __openkal__ rule, platform units), docs/06
(platform-sdk), and their zh mirrors. Tests: test_manifest.cpp,
test_cenv.cpp (14 cases covering the whole mapping table), e2e 741.
Design: mcpplibs/openkal .agents/docs/2026-09-18-openkal-c-environment-and-personalities-design.md
- docs/zh/21-the-target-triple.md was missing the zh mirror of the new "a declared environment can move the compiled triple" note; heading count parity with the English document now holds (25/25). - docs/zh/24-openkal-cross.md's macro-family table header "由谁定义" reads as a question (contains 谁); renamed to the noun phrase "定义者". - Three cross-file anchors in the zh docs pointed at the ENGLISH heading slug of their target section instead of the zh one (#adaptation-to-the-resolved-target-side, #closure-visibility) — fixed to the zh anchors check_docs_structure.sh actually validates against. Caught by CI on #668 ("build + unit tests" job's doc-lint step); verified locally afterwards with check_docs_style.sh, check_docs_structure.sh, check_version_pins.sh, check_target_tiers.py and check_modules_wiring.sh.
Design revision from the openkal-musl spike. The Cygwin-flavoured Windows realisation no longer undefines __CYGWIN__/__CYGWIN32__ -- the -U tokens are dropped, everything else (the --target= substitution, LP64, no _WIN32, -fno-short-wchar for wchar=32) is unchanged. The reason, stated once and correctly (an earlier private draft of this change mis-attributed the cause to libunwind's own branch selection; that was wrong on inspection of the vendored source and is not repeated here): third-party portable code that needs to know the OBJECT FORMAT -- distinct from the C environment and from the platform API -- has no other name for "PE format with a POSIX-presenting C environment" than __CYGWIN__, and such code cannot be patched the way this ecosystem's own packages can. This is a trade-off for the 30-member measurement to settle: a library reaching for __CYGWIN__ may also reach for a real Cygwin interface that does not exist here, and if defining it produces more new failures than it fixes, the answer flips. Updated: the realisation table and its verification expectations (src/toolchain/cenv.cppm), the unit test and e2e assertions (now checking __CYGWIN__ stays DEFINED, the opposite of the first version), and the English + zh docs/CHANGELOG prose.
A .S unit's command line is assembled independently of a .c/.cpp unit's
(mcpp.build.flags::CompileFlags::as, not ::cc/::cxx), and only inherits the
-D/-U/-I subset of a package's C flags on purpose -- a -std= or -O token
meant for the C compiler is meaningless to GAS. The realised environment
broadcast landed only in privateBuild.cflags/cxxflags, so it never reached
that narrower channel: within one package a .c unit saw _WIN32 undefined
while a .S unit still saw it defined, because --target=/-fno-short-wchar
never reached the assembler's command line at all. Found by the openkal-musl
spike (okm_setjmp.S, and upstream libunwind's assembly.h, both select
register-save sets on that macro) -- a jmp_buf written by one save set and
sized by the other header is a silent mismatch, the exact failure class this
feature exists to remove.
Adds UsageRequirements::asmflags, a broadcast-only channel parallel to
cflags/cxxflags (no [build] asmflags manifest key backs it -- per-glob
`flags = [{ asmflags }]` remains the author-facing one), and routes the
realised environment tokens into it for GAS units only: NASM has no
--target= concept and accepts none of these flags, so it must never receive
them, and a unit test pins that split directly. Every token was checked
against clang's assembler-with-cpp front end first; none is rejected, so
nothing is filtered a second time.
e2e 741 gains a .S unit asserting it carries the same tokens main.cpp does.
key now covers the realised environment A package providing mcpp:kernel-abi=<impl> is by definition the boundary where the platform's own interfaces are called, so it can never want the graph's presented [c-abi] environment instead of the triple's own. Rather than require every kernel-abi implementation to write c-environment = "platform" (and every already-released one to bump a version to add it), that value is now inferred from provides alone, in both manifest parsers (mcpp.toml and the xpkg index descriptor). The explicit key still wins when present. Measured without it: openkal-windows, compiled under the POSIX substitution like everything else in its graph, got a 32-bit wchar_t from -fno-short-wchar while the Win32 calls it makes hand back genuine 16-bit UTF-16, misreading its own results. Separately: the global build cache's key (~/.mcpp/build-cache/v1, mcpp.build.cache_key) read only a package's OWN declared cflags/cxxflags, never the engine's broadcast channel the realised environment (and __openkal__, and targetSideUsage) actually travels through. Two builds realising different environments for the identical package produced the identical key. Measured: upgrading mcpp in place, with the cache directory left alone, served objects compiled under the old realised environment into an image built under the new one. fill_package_config now folds in privateBuild.cflags/cxxflags/asmflags (the post-broadcast values) alongside the declared ones, exactly as it already did for include directories.
from a trustworthy one The previous commit made fill_package_config read the realised environment, but a corrected derivation does not make an entry written under the old, wrong one safe to keep. An entry is poisoned exactly when its recorded key and its actual compiled inputs already disagreed at write time, and the package most likely to still produce an unchanged key after the fix is the one this same PR newly exempts from the realisation: a kernel-abi provider inferred into c-environment = "platform" now has an empty privateBuild.cflags, so its new key is computed from nothing -- matching its old key, which was also computed from nothing, while the object on disk was compiled WITH the substitution. No cheaper mechanism distinguishes a pre-fix entry from a post-fix one, so kCacheEpoch moves (2 -> 3): every existing ~/.mcpp/build-cache/v1 entry is orphaned unconditionally, one cold rebuild for everyone, rather than trust a key equality that is wrong for exactly the entries that matter most. Also adds CacheKey.EveryPrivateBuildBroadcastFieldReachesTheKey, a durable guard for the class of defect rather than only this one instance of it: PackageRoot::privateBuild (UsageRequirements) is the engine's own broadcast channel, and every one of its members -- today includeDirs, includeDirsAfter, cflags, cxxflags, asmflags, ldflags, modules -- has to move the cache key, because the key's only job is to describe what reaches the compiler. This predates the c-abi wave (targetSideUsage's own broadcast, and __openkal__, had the identical exposure before [c-abi] existed); the two fields nothing broadcasts into yet (ldflags, modules) are asserted uncovered on purpose, with an explicit instruction to flip that assertion in the same change that adds their broadcast.
mode written out plainly The declaration probe (mcpp.toolchain.cenv_probe, design 2026-09-18 §3.2) landed in an earlier commit and is wired into prepare.cppm, but had zero direct test coverage: e2e 741 only exercises its success path (the build would not otherwise complete) and its one refusal leg comes from cenv::realise refusing an unrealisable request before the probe is ever reached. Adds tests/unit/test_cenv_probe.cpp, calling cenv_probe::verify directly against a real host compiler with deliberately wrong expectations -- a declared long/wchar_t width the compiler disagrees with, a macro declared defined that isn't and vice versa -- confirming each produces the declared-vs-measured Mismatch entry prepare.cppm renders into its refusal text, plus the cache's hit/miss behavior. Each test gets its own temp cache directory: the probe's cache is keyed on (compiler, argv) alone, not on what expectations are being checked, so two tests sharing a default cache root and identical argv would have the second read the first's cached dump rather than genuinely probe. Separately, traced the install-hook store's code path (prepare.cppm, install_hook_env) to answer directly: yes, an install hook can and does compile target-side code into the shared store, and no, there is no way today for it to know the realised [c-abi] environment, because the hook runs before the toolchain resolves at all -- prepare.cppm resolves tc only after the dependency graph installs, since resolving the target side can itself depend on which package the graph supplies a layer from. This is not a gap this PR could close with a local fix; docs/22 (+ zh mirror) now states the failure mode plainly (objects sized for one wchar_t linked against headers sized for another, with nothing checking it) rather than as a line in a gap table, names what closing it for real would take, and says explicitly that this is the same shape as the already-shipped c++-abi store-key gap, not a new one this PR introduces.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the mcpp side of "C environment declared by the C library layer"
(design 2026-09-18): a
mcpp:c-abi=<impl>provider can declare, in a new[c-abi]block, the C environment it actually presents to source — whichenvironment-identity macros are defined, the data model,
wchar_twidth,and whether the compiler may assume platform-C-library extensions. mcpp
turns that declaration into compiler configuration for every target-side
unit, verifies the result against the real compiler rather than trusting
the declaration, and folds it into the build fingerprint.
Design:
.agents/docs/2026-09-18-openkal-c-environment-and-personalities-design.mdin
mcpplibs/openkal(§2, §3.2, §3.2.1, §3.3, §3.4, §5.4, §6, §8, §11, §12).What's implemented
[c-abi]manifest block —presents(posix/windows/none),data-model(arch-default/lp64/llp64/ilp32),wchar(16/32),builtins(iso/platform, defaultplatform). Only a package thatalso lists
mcpp:c-abi=<impl>inprovidesmay declare it — anythingelse is a parse error.
presents/data-model/wcharcarry no default;an unknown key or value is always a parse error naming the key. Absent
block = today's behaviour, byte for byte.
src/toolchain/cenv.cppm) — a pure, table-drivenmapping from
(request, target)to compiler tokens, generic knowledgewith no package names. The flagship case:
presents = "posix"onWindows/x86_64 realises as a Cygwin-flavoured compile-only identity
switch (
--target=x86_64-pc-cygwin), leaving the link line on thegraph's own resolved triple, because the two triples produce identical
machine code (measured, design §1.4).
__CYGWIN__/__CYGWIN32__areleft DEFINED under this switch — a design revision from the openkal-musl
spike, not the original design text; see the dedicated note below.
Reaches C, C++, assembly (
.S, via its own broadcast channel — see thededicated note below) and the dependency scan via each package's own
build inputs, and the
stdmodule precompile viaToolchain. A requestthis engine cannot realise is refused, naming the target/request/what's
missing.
[package] c-environment = "platform"opts a package out ofthe realisation entirely — and is now INFERRED (not merely permitted) for
any package that provides
mcpp:kernel-abi=<impl>; see the dedicatednote below.
src/toolchain/cenv_probe.cppm) — one-E -dMpredefined-macro dump (no codegen, no execution — the realisedenvironment is routinely a cross target) compiled with the exact
identity-affecting tokens, checked against the declaration
(
__SIZEOF_LONG__,__SIZEOF_WCHAR_T__, environment-identity macros).A mismatch fails the build printing both declared and measured. Cached
per configuration.
__openkal__— defined for every target-side unit whenever theresolved
kernel-abilayer's interface name isopenkal(read from thelayer, never a package name), independent of
c-environment.with the ordinary capability
provides = ["platform-sdk"]; theTargetreport gains a
platform-depsline (empty when none), and[build] platform-dependencies = "refuse"fails the build outright whenone is present.
__openkal__fold intothe build fingerprint, so an LP64 and an LLP64 build of the same
source/manifest never share an output directory.
What's designed but not implemented (documented as a known gap)
left as a line in a gap table. An install hook CAN and does compile
target-side code into the shared store (confirmed from
install_hook_env/prepare.cppm), and the store is keyed by package +version only. This is NOT fixable the way the build-cache key above was:
a hook runs BEFORE the toolchain resolves —
prepare.cppmresolvestconly after the dependency graph installs, because resolving the target
side can itself depend on which package the graph supplies a layer from
— so at the moment a hook runs, no realised environment exists yet for it
to consult. The failure mode, plainly: a hook compiling
environment-sensitive C code has no way to ask what this build realised,
so an inconsistent graph gets objects sized for one
wchar_tlinkedagainst headers sized for another, with nothing checking it — the store
records no environment for what it holds. This is the SAME shape as the
already-shipped
requires = ["mcpp:c++-abi=<impl>"]store-key gap, not anew one this PR introduces. Closing it for real needs either a two-phase
install (defer target-side compilation in a hook until after target-side
resolution) or extending the
c++-abirequires-check pattern toc-abi/c-environment; both are out of this PR's scope. Full write-up:docs/22, "Store key — not yet closed".
modules/manifest/src/xpkg.cppm) doesnot yet parse
[c-abi]/c-environment— onlymcpp.tomldoes. A packageconsumed as a local/git/path dependency is fully covered; one resolved
through the package index is not yet. Flagged for follow-up.
Five design revisions from the openkal-musl spike, mid-PR
__CYGWIN__/__CYGWIN32__stay defined, not undefined as originallydesigned. Portable third-party code that needs to know the object
format — not the C environment, not the platform API — has no name for
"PE format with a POSIX-presenting C environment" other than
__CYGWIN__,and such code cannot be patched the way this ecosystem's own packages can.
This is a trade-off for the 30-member measurement to settle, not a
settled fact: a library reaching for
__CYGWIN__may also reach for areal Cygwin interface (
sys/cygwin.h,cygwin_conv_path) that does notexist here, and the answer flips if that costs more than it fixes.
The realisation now reaches assembly (
.S) units, not only C/C++. A.Sunit's command line is assembled independently(
mcpp.build.flags::CompileFlags::as), and only inherits the-D/-U/-Iwords of a package's C flags on purpose — a-std=/-Otoken meant for the C compiler is meaningless to GAS — so the broadcast
into
privateBuild.cflags/cxxflagsnever reached it. Real code(openkal-musl's
okm_setjmp.S, upstream libunwind'sassembly.h) selectsregister-save sets on these macros, so a
.cunit and a.Sunit in thesame package disagreeing about
_WIN32is exactly the silent-mismatchclass this feature exists to remove. Fixed with a new,
broadcast-only
UsageRequirements::asmflagschannel, applied to GAS unitsonly — NASM has no
--target=concept and accepts none of these flags, soit must never receive them (
Scanner.ResolvedCEnvAsmflagsReachGasUnitsOnlyNotNasmpins the split).
c-environment = "platform"is INFERRED for anymcpp:kernel-abi=<impl>provider, not merely permitted. Such a package is BY DEFINITION the
platform boundary — its whole job is to speak the platform's ABI, so it
can never be the package that wants the graph's presented environment
instead. Measured without this: openkal-windows, compiled under the POSIX
substitution like everything else in its graph, got a 32-bit
wchar_tfrom
-fno-short-wcharwhile the Win32 calls it makes hand back genuine16-bit UTF-16 — a
wchar_t*loop then read two UTF-16 units as one codepoint. Inferred rather than required so every already-released
implementation (openkal-windows 0.8.0, -macos 0.10.0, -linux 0.13.0, and
whatever comes next) gets the boundary right with no new release and no
coordinated version bump. Implemented in BOTH manifest parsers
(
modules/manifest/src/toml.cppmandxpkg.cppm— the latter mattersbecause that is how a released implementation is actually consumed). An
explicit
c-environmentin the package's own manifest still wins;precedence is written down in docs/22.
The global build cache's key (
~/.mcpp/build-cache/v1,mcpp.build.cache_key) did not cover the realised environment at all —a release blocker, not a footnote. It read only a package's OWN
declared
cflags/cxxflags; the realised environment reaches apackage's actual command line entirely through an engine broadcast
(
privateBuild.cflags/cxxflags, the same channeltargetSideUsageand-D__openkal__use). Two builds of the identical package realising twodifferent environments therefore produced the identical key. Measured:
upgrading
mcppin place, with the cache directory left alone, servedobjects compiled under the OLD realised environment into an image built
under the new one — the exact invariant this whole design exists to
protect, with no diagnosis.
fill_package_confignow folds inprivateBuild.cflags/cxxflags/a newasmflagsaxis (post-broadcast)alongside the declared ones, exactly as it already did for include
directories.
The cache-key fix above orphans the whole build cache:
kCacheEpochmoves 2 → 3, so the first build after upgrading to this release is
cold. A corrected key does not make an entry written under the old,
wrong derivation safe to keep — an entry is poisoned exactly when its
recorded key and its actual compiled inputs already disagreed at write
time. The package most likely to still show an UNCHANGED key after the
fix is precisely the one this same PR newly exempts from the realisation
(a
kernel-abiprovider inferred intoc-environment = "platform"): itsprivateBuild.cflagsis now empty, so the new key is computed fromnothing — matching the old key, which was also computed from nothing,
while the object on disk was compiled WITH the substitution. No cheaper
mechanism tells a pre-fix entry from a post-fix one. Also adds
CacheKey.EveryPrivateBuildBroadcastFieldReachesTheKey, a durable guardfor the class rather than only this instance: every member of
PackageRoot::privateBuildhas to move the cache key, and the twomembers nothing broadcasts into yet (
ldflags,modules) are asserteduncovered on purpose, with an instruction to flip that assertion in the
same change that adds their broadcast — this predates the c-abi wave
(
targetSideUsage's own broadcast and-D__openkal__had the identicalexposure before
[c-abi]existed).Two pre-existing defects found while verifying this, filed separately
Neither is caused by or fixed in this PR — both reproduce on unmodified
main.mcppbinary SIGSEGVs inside its own ELF runtimeinspector (
mcpp::platform::elf::inspect_elf_runtime) when validating abuild artifact; the same source built with GCC does not crash. CI never
exercises this combination (the LLVM self-host leg checks only that the
build resolved LLVM, never runs the resulting binary against anything).
mcpp.targetsidefrom the newsrc/toolchain/cenv.cppmunder a parallelself-host build;
-j1avoids it. Not bisected further.The release binaries below are GCC-built to sidestep #666.
Two macOS CI jobs on THIS PR fail with a malformed-TAPI-file SDK error
("e2e suite (macOS ARM64, self-host, xcode-27)" and "macOS ARM64 — xlings
LLVM end-to-end (xcode-27)") — both out of scope, both pre-existing. Run
35213851816 on
main, from before this PR existed, fails the same job("macOS ARM64 — xlings LLVM end-to-end (xcode-27)") with the identical
signature (
ld64.lld: error: could not load TAPI file at .../MacOSX.sdk/usr/lib/libc++.tbd: malformed file). It's the runner-imageSDK problem already recorded in
.agents/docs/2026-09-17-openkal-0.13-ecosystem-record.md(open-limitstable); #665 already moved the raw compiles off that SDK, and the "xlings
LLVM end-to-end" job is the one still reading it directly.
Testing
tests/unit/test_manifest.cpp—[c-abi]/c-environmentparsing,validation, closed value sets, the provides-gate refusal, and that
omitting
[c-abi]leaves every read[package]key warning-free.tests/unit/test_cenv.cpp— the full realisation mapping table (14cases): the three realisable rows, the
builtins = "iso"survey result,and every refusal this table names, including the LP64/LLP64
identity-switch contradiction the verification probe caught during
development (see commit history) and which the mapping now refuses
directly.
tests/e2e/741_c_abi_environment_declared_by_the_c_library.sh— a fakec-abiprovider onx86_64-windows-gnudeclaringpresents=posix, wchar=32, builtins=iso; asserts viamcpp emit build-databasethat an ordinary consumer AND the provider'sown units carry the realised tokens, that a
c-environment = "platform"package does not, that a real GAS (
.S) unit in the ordinary consumercarries the same tokens a
.c/.cppunit in the same package does, andthat a
presents=posix, data-model=llp64declaration (unrealisable oncethe identity switch is in play) is refused naming the target and the
request.
tests/unit/test_modgraph.cpp—Scanner.ResolvedCEnvAsmflagsReachGasUnitsOnlyNotNasm: a resolvedpackage's
privateBuild.asmflagsreaches a GAS unit'spackageAsmflagsand does NOT reach a NASM unit's, in the same package, in the same scan.
tests/unit/test_manifest.cpp—Manifest.CEnvironmentIsInferredForAKernelAbiProviderandSynthesizeFromXpkgLua.CEnvironmentIsInferredForAKernelAbiProvider: amcpp:kernel-abi=<impl>provider infersc-environment = "platform"with no key of its own, in both manifest parsers; an ordinary package and
a
mcpp:c-abi=<impl>provider do not.tests/unit/test_cache_key.cpp—CacheKey.TwoDifferentRealisedCEnvironmentsDoNotShareASlot: the samepackage and version, realising two different environments, produce two
different cache keys (this failed before the cache-key fix, confirming it
reproduces the defect the coordinator's experiment found).
CacheKey.EveryPrivateBuildBroadcastFieldReachesTheKey: every currentmember of
PackageRoot::privateBuild(includeDirs,includeDirsAfter,cflags,cxxflags,asmflags) individually moves the key, and the twothat nothing broadcasts into yet (
ldflags,modules) are asserted NOTto, on purpose — a documented, testable contract for the next broadcast
channel someone adds.
tests/unit/test_cenv_probe.cpp(NEW —cenv_probe::verifyhad zerodirect test coverage before this) — calls the verification probe directly
against a real host compiler with deliberately wrong expectations: a
declared
sizeof(long)/wchar_twidth the compiler disagrees with, amacro declared defined that isn't and vice versa. Each confirms the exact
Mismatch{fact, declared, measured}prepare.cppmrenders into itsrefusal text, plus the probe's cache hit/miss behavior across two
different argv strings.
tests/unit/test_targetside.cpp— unaffected, 45/45 still pass (confirmsmoving
CAbiDecl/the new enums ahead ofLayer/TargetSideintargetside_model.cppm, required for declaration order, changed nothingobservable).
mcpp test(test binaries compiled with--toolchain llvm@22.1.8; the driver binary itself is GCC-built, sidestepping Clang-built mcpp SIGSEGVs in ELF runtime inspection (validate_changed_artifacts); GCC-built does not #666):121/122 unit tests pass. The one failure,
unit/test_elf_runtime(
std::bad_alloc/ SIGSEGV), is Clang-built mcpp SIGSEGVs in ELF runtime inspection (validate_changed_artifacts); GCC-built does not #666, reproduced on unmodifiedmain,not a regression from this PR — I did not touch, weaken or adjust that
test. CI's own
build-testjob compiles its test binaries with theproject's default (GCC) toolchain, so it is not expected to hit Clang-built mcpp SIGSEGVs in ELF runtime inspection (validate_changed_artifacts); GCC-built does not #666 at
all; if it does, that failure is Clang-built mcpp SIGSEGVs in ELF runtime inspection (validate_changed_artifacts); GCC-built does not #666, not this PR.
Spike support
Per the coordinator's request, a release-mode binary was built and reported
as soon as items 1/2/3/5 worked end to end, ahead of the rest of this list;
see the conversation record. The binary path in the final report below is
the last one built from the tip of this branch.