recipes ask which C environment this is, not which operating system - #458
Merged
Merged
Conversation
…t from the platform" This reverts commit 75f5313.
Every member of this suite keeps its sources under `tests/`, and for a target with no runner the harness measured the cell with `mcpp build`, which builds the PACKAGE. For these members that compiles nothing of the member at all: it compiled their dependencies, exited 0, and this file recorded `builds`. Measured on `archive` for `aarch64-macos`, 1990 objects, none of them from `tests/compression.cpp` or `tests/versions.cpp`. The reading was never wrong in CI, because both pinned targets are runnable there — linux natively and windows through Wine, which the workflow installs and checks. It was wrong the moment a third target was considered, which is what this change was written for. `mcpp test --no-run` compiles and links the member's own tests for the target and does not execute them. The command choice moves into `command_for` so it has a criterion that needs no toolchain, network or member: three selftest legs, one per branch, and the one that matters names `--no-run` rather than `build`. Also drops a tracked `__pycache__/*.pyc` — a compiled artefact of one interpreter version, which produced a diff for anyone running the harness on 3.13 and an untracked second copy for anyone on 3.12 — and widens .gitignore so the next one is not committed either.
c-ares cannot run its own configure here, so this package ships gRPC 1.83.0's
three frozen `ares_config.h` snapshots and selected between them by the
operating system. That was the same question as "which C environment is this"
until a C library began presenting POSIX on Windows. Over openkal the target
is still PE and still Win64, and `config_windows` then describes nothing that
is there: of the sixteen headers it declares absent, fifteen are present in
that graph.
The diagnostics arrive one macro at a time, which is what made this look like
a list of small fixes rather than one wrong answer:
ares_setup.h:81 fatal error: 'windows.h' file not found
ares_timeval.c:70 call to undeclared function 'gettimeofday'
ares_parse_ns_reply.c:71 invalid application of 'sizeof' to an
incomplete type 'struct hostent'
with thirteen more behind them. Measured directly: give that target
`config_linux` and the member goes from `fails` to `runs`.
`_WIN32` and `__APPLE__` are the engine's answer to exactly this question —
mcpp defines `_WIN32` for a Win32 environment and suppresses it for a
POSIX-presenting one — so the three snapshots move into one top-level
`generated_files` entry with three arms and the preprocessor selects. This
needs no new key in the descriptor and no new axis in the engine.
Each arm is its snapshot verbatim, checked byte for byte against the file it
came from; none is edited to suit another. Linux and macOS select what they
selected before, so neither reading moves. `cflags`/`ldflags` stay per-OS: a
real Windows target still needs ws2_32, and a POSIX-presenting one asks for
nothing in it.
Measured after the change: `runs` on x86_64-windows-gnu and on
x86_64-linux-gnu.
`compat.openssl` runs OpenSSL's own Configure and make from `install()`,
outside mcpp's compile rules. `cc_override()` hands that build
`gcc --sysroot=<xim:glibc payload>` on linux and `/usr/bin/cc` on macOS, so
the archive is a glibc or libSystem artefact — and the hook cannot be
otherwise, because it runs once at install time and knows nothing about the
graph a later consumer will resolve.
Linked into a graph whose C library is musl, it fails at symbols that library
does not have. Measured on x86_64-linux-gnu over openkal:
ld.lld: error: undefined symbol: setcontext
>>> referenced by async.c
>>> libcrypto-lib-async.o in archive .../libcrypto.a
musl omits the `*context` family deliberately, and `__isoc23_strtol` is
glibc's header redirection for `strtol`. Neither names the cause, and both
arrive after a successful configure, compile and archive step.
`provides = ["platform-sdk"]` is the engine's existing word for "this package
brings the platform's own side into the graph" (docs/06). It does not make the
link succeed and is not meant to. Measured, it puts the fact where a reader
and a machine can both see it:
Target platform-deps openssl@3.5.1
and with `[build] platform-dependencies = "refuse"`:
error: [build] platform-dependencies = "refuse", and the dependency graph
brings a platform dependency: openssl@3.5.1.
Building OpenSSL inside the graph is what would make the link succeed. That is
a different change: its source list comes from Configure, so it needs the
snapshot treatment compat.c-ares and compat.ffmpeg have.
Both targets fail because this graph supplies no TLS provider, and the member's own test refuses a curl without one — transfer.cpp:45 fails when CURL_VERSION_SSL is clear, on the stated reasoning that "a curl built with no SSL still links and still runs, it just silently cannot do https". Configuring the member without TLS would turn both cells green and measure a different library. On linux the provider is compat.openssl, which is built by an install() hook through OpenSSL's own Configure and is therefore a glibc artefact; it now declares `provides = ["platform-sdk"]` and fails this link at `setcontext` and `__isoc23_strtol`. On windows the provider is Schannel, selected together with the checked-in lib/config-win32.h by `_WIN32` — the macro a C library presenting POSIX on Windows does not define. With neither HAVE_CONFIG_H nor _WIN32 curl reads no configuration at all and stops at `curl_setup.h:591: "too small curl_off_t"`, which names a consequence 591 lines away from its cause. compat.openssl has no Windows build, so there is no second provider to fall back to. Declared rather than left unexplained, and the declaration is falsifiable: `compat.py check` fails if a declared cell builds, so an in-graph OpenSSL would force these lines to be removed rather than quietly keeping a stale excuse.
…rive
mimalloc's prim-tls.h:176 enables `__builtin_thread_pointer()` for x86_64 with
clang >= 14 unless `__APPLE__`, `__CYGWIN__` or `MI_LIBC_MUSL` says otherwise.
Two of those three describe this target and neither reaches it: mcpp suppresses
`__CYGWIN__` deliberately, because source must not read the realisation triple
as a statement about Cygwin, and `MI_LIBC_MUSL` is a build option upstream
expects the packager to set ("Enable this when linking with musl libc",
CMakeLists.txt:39) rather than something it detects.
So the guard passes and LLVM has no lowering for the builtin on this OS:
fatal error: error in backend: Target OS doesn't support
__builtin_thread_pointer() yet.
which arrives as a backend crash with no source location, and reaches the
compatibility results as the bare line "error: build failed".
prim-tls.h:176 opens with `#if !defined(MI_USE_BUILTIN_THREAD_POINTER) /* allow
user override */`, so the fix is to give it one. It goes in the Windows branch,
where a Win32 target never reads it — `_WIN32` selects `NtCurrentTeb()` three
arms earlier — so it changes only the target that has no `_WIN32`.
Measured: `runs` on x86_64-windows-gnu and unchanged `runs` on
x86_64-linux-gnu.
expat's member asserts that the code which parsed is not the ecosystem payload's libexpat, and asks `dladdr` to attribute it. musl's `dladdr` is `weak_alias(stub_dladdr, dladdr)` returning 0 (src/ldso/dladdr.c); the dynamic loader overrides it, and a static link has no loader to do so. An openkal program is statically linked, so the call returns 0 for every address and the member failed one check on both targets. The risk the check exists for cannot arise there either: nothing was loaded at runtime, so the only expat in the process is the one that was linked in. Skipping it would make "no loader" and "the loader answered wrongly" the same reading, so the fallback asserts what distinguishes them. `main` is in this executable by construction; if `dladdr` cannot locate that either, there is no dynamic symbol information at all. If it locates `main` and not `XML_ParserCreate`, the check fails as it did before. Measured both ways: `runs` on both openkal targets, and on a dynamic host build the positive branch still runs and still prints what it resolved.
macOS is measurable now that a target with no runner compiles the member's own tests, so it was measured: 20 of 30 members build for aarch64-macos over openkal and 10 do not. Nine of the ten are reached under `#ifdef __APPLE__` — TargetConditionals.h five times, then sys/cdefs.h, sys/event.h, xlocale.h and pthread_threadid_np — and on this target `__APPLE__` is correct. It is an Apple platform. What it does not say is which C library is underneath, and upstream uses it to mean both because on a real macOS the two coincide. This is the Windows problem again with one difference: there the engine has a lever, because presenting POSIX is realised as a cygwin triple and `_WIN32` goes away. On macOS the realisation adds `-D__unix__` and leaves `__APPLE__` standing, because it is true. The whole identity a source file sees there is `__APPLE__ __MACH__ __MCPP_TARGET_MACOS__ __OPENKAL__ __unix__`, measured, and none of it answers the question. So the target is not pinned: it would add ten red cells whose repair is one design question. The number is recorded so the question is asked with one.
`mcpp test --no-run` is what measures a target with no runner, and 2026.9.21.3 is the first release to carry it. `pins.toml` and the workflow's `MCPP_VERSION` move together because the workflow checks that they agree. `runtime` does not move with it. 0.15.0 is the graph the engine change was measured against, and moving both at once would make a red cell ambiguous between the two. aarch64-macos is still not pinned, and that is a measurement rather than an omission: 20 of 30 members build there and 10 do not, nine of the ten reaching for the Apple C environment under `#ifdef __APPLE__` on a target where `__APPLE__` is true and libSystem is not present. The number is in docs/openkal-compat.md so the design question behind it is asked with one attached.
CI measured mimalloc's Windows cell as `builds`: the backend crash is gone and the member compiles and links, and its `alloc` test then exits 1 under the Wine that CI installs while passing under the one on this machine. The recipe comment claimed `runs` on the strength of the local run. The build repair stands — the cell was `fails: error: build failed` — but a repaired build is not a passing test, and both the recipe and docs now say which one this is. The sixty-cell table is CI's, not mine: 56 runs, 1 builds, 3 fails, all three declared.
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.
A C library that presents POSIX on Windows is a target these recipes did not
have when they were written, and each of them answers "which C environment is
this" by asking a different question about the operating system. This branch
replaces those questions one at a time, and finishes with the measurement that
reads them.
The measurement
Thirty members, two targets, sixty cells, against mcpp 2026.9.21.2 and
openkal-llvm-runtime 0.15.0:
runsfailsbuilds(built, tests failed)compat.py checkagainst the published baseline: no published labelregressed, and no declaration was contradicted.
What each commit measured
A configure snapshot describes a C environment, not an operating system
(
compat.c-ares). The three frozenares_config.hfiles from gRPC wereselected by OS. Over openkal the target is still PE and still Win64, and
config_windowsthen describes nothing that is there: of the sixteen headersit declares absent, fifteen are present in that graph. The diagnostics arrive
one macro at a time —
— with thirteen more behind them. Giving that target
config_linuxmakes themember run, so the three snapshots move into one top-level
generated_filesentry with three arms and
_WIN32/__APPLE__select between them. Each armis its snapshot verbatim, checked byte for byte; linux and macOS select what
they selected before.
A library built outside the graph says so (
compat.openssl). Itsinstall()hook runs OpenSSL's own Configure withgcc --sysroot=<glibc payload>, so the archive is a glibc artefact and the hook cannot be otherwise— it runs once at install time and knows nothing about the graph a consumer
will resolve.
provides = ["platform-sdk"]puts that where a reader and amachine can both see it, instead of
undefined symbol: setcontextthreelayers down.
curl's two cells have one cause, and it is not curl. No TLS provider
exists in this graph, and the member's own test refuses a curl without one
(
transfer.cpp:45). Declared per target with the evidence, and thedeclaration is falsifiable:
compat.py checkfails if a declared cell builds.Two of the three guards that would have excluded this target never arrive
(
compat.mimalloc).prim-tls.h:176enables__builtin_thread_pointer()unless
__APPLE__,__CYGWIN__orMI_LIBC_MUSLsays otherwise; mcppsuppresses
__CYGWIN__deliberately andMI_LIBC_MUSLis a build optionupstream expects the packager to set. The guard passes and LLVM has no
lowering, which reaches the results as the bare line
error: build failed.The header documents a user override; this uses it, in the Windows branch,
where a Win32 target never reads it.
A static image cannot answer dladdr, and that is not a failure (expat's
member). musl's
dladdris a weak stub returning 0 and a static link has noloader to override it. Skipping would make "no loader" and "the loader
answered wrongly" the same reading, so the fallback asserts what distinguishes
them.
buildswas a statement about the dependencies (the harness). Everymember keeps its sources under
tests/, and a target with no runner wasmeasured with
mcpp build, which builds the package — for these members,nothing. Measured on
archivefor aarch64-macos: 1990 objects, none from themember. The reading was never wrong in CI, because both pinned targets are
runnable there; it was wrong the moment a third target was considered.
mcpp test --no-runcompiles and links the member's own tests instead, andthe command choice moves into
command_forwith three selftest legs.Dependency
The harness change needs
mcpp test --no-run, which ismcpp-community/mcpp#682.
pins.tomland the workflow'sMCPP_VERSIONmovetogether to the release that carries it; until then both pinned targets are
runnable and the new path is not reached.
Process
24f77d9is a reapply: the original was pushed directly tomain, which thisrepository does not allow for anything but documentation. It was reverted in
75f5313and resubmitted here.