Skip to content

Runtime closure: bake dep runtime_dirs into RUNPATH; default Linux toolchain to glibc#109

Open
Sunrisepeak wants to merge 18 commits into
mainfrom
fix/runtime-rpath-and-glibc-default
Open

Runtime closure: bake dep runtime_dirs into RUNPATH; default Linux toolchain to glibc#109
Sunrisepeak wants to merge 18 commits into
mainfrom
fix/runtime-rpath-and-glibc-default

Conversation

@Sunrisepeak
Copy link
Copy Markdown
Member

Two general fixes that let native + GUI packages work out of the box

R2 — dependency [runtime] library_dirs were dropped from binary RUNPATH

src/build/plan.cppm already collects every dependency package's runtime.library_dirs into plan.runtimeLibraryDirs (absolute). But src/build/flags.cppm built the produced binary's RUNPATH from plan.toolchain.linkRuntimeDirs only, so dependency runtime dirs were never emitted as -Wl,-rpath. Result: host-GL passthrough dirs (e.g. compat.glx-runtime's GLVND symlinks) weren't on the binary RUNPATH, so dlopen'd libGL.so.1/libGLX.so.0 failed → GLX: Failed to load GLX. Fix: iterate plan.runtimeLibraryDirs (a superset that includes toolchain dirs).

R1 — fresh-machine Linux default was musl-static

src/cli.cppm first-run default on Linux was gcc@15.1.0-musl (static), which cannot link the glibc world (X11/GL/system libs) — e.g. libXdmcp arc4random_buf. Default to platform-native glibc gcc; musl-static stays opt-in via mcpp build --target x86_64-linux-musl (Cargo-style: default -gnu, -musl explicit).

Verified end-to-end (no special-casing)

Built mcpp with these changes; a fresh consumer mcpp new app && mcpp add imgui && mcpp run:

  • RUNPATH now contains the compat.glx-runtime dir (readelf -d).
  • Window opens and renders (prints consumer demo ok after 120 frames) — previously GLX: Failed to load GLX.
  • Headless mcpp test unaffected.

Design notes: .agents/docs/2026-06-03-runtime-closure-and-toolchain-defaults.md. Part of the mcpp ecosystem打通 plan.

…ux toolchain to glibc

R2: flags.cppm emitted -Wl,-rpath only from toolchain.linkRuntimeDirs, dropping
dependency packages' [runtime] library_dirs (already collected into
plan.runtimeLibraryDirs by plan.cppm). So host-GL passthrough dirs (e.g.
compat.glx-runtime) never reached the binary RUNPATH and dlopen()'d libGL/libGLX
failed at run time (GLX: Failed to load GLX). Iterate plan.runtimeLibraryDirs
(superset) instead.

R1: cli.cppm first-run default on Linux was gcc-musl-static, which cannot link
the glibc world (X11/GL/system libs). Default to platform-native glibc gcc;
musl-static stays opt-in via --target x86_64-linux-musl (Cargo-style).
…design

Adds a builtin gui scaffold to `mcpp new` (Tier-0 imgui.app window starter,
deps imgui 0.0.2, no toolchain pin). Verified: new --template gui -> build ->
window renders. Also documents the long-term package-based template model
(mcpp new name --template pkg@ver:tmpl, libraries ship templates/) as TODO.
mcpp why [toolchain|runtime|deps] (alias: resolve --explain) explains the
resolved toolchain (incl. abi), the runtime library dirs baked into RUNPATH
(surfacing the compat.glx-runtime host-GL closure), and locked deps — so
defaults are inspectable, not magic (I4).

self doctor: add a runtime-capabilities section probing x11/wayland display
and the host GLVND opengl.glx.driver (libGLX + vendor), reporting the
provider — capability-level, not just env health.
Aggregate dependency providers' [runtime] dlopen_libs/capabilities into the
BuildPlan (with capability->provider mapping). doctor now reports each required
host capability + its provider and verifies each provider-declared dlopen
soname against the resolved runtime library_dirs — fully data-driven. Removes
the previous #ifdef __APPLE__/_WIN32 + hardcoded /usr/lib paths: platform
knowledge belongs in provider packages, not in mcpp core. why also surfaces
capability->provider.
A dependency may declare an abi:<x> capability (e.g. compat.glfw -> abi:glibc).
prepare_build now verifies the resolved toolchain's ABI satisfies every such
requirement and fails fast with an actionable message on mismatch — turning a
cryptic deep musl build error (libXdmcp arc4random_buf) into an upfront
capability error. Enforces/diagnoses; abi-driven reselection (toolchain is
resolved before the dep graph) is a resolution-ordering follow-up.
Writes a machine-readable resolution manifest (toolchain/abi, runtime closure
library_dirs, dlopen_libs, capability->provider) next to build outputs — the
serialized capability->plan, same data as `mcpp why`, usable by CI/tooling.
…oncat

Use the existing json module for safe serialization/escaping instead of
hand-built JSON.
Adds bundled build settings: built-in release (-O2) / dev (-O0 -g) / dist
(-O3 -flto -s), overridable/extendable via [profile.<name>] (opt/debug/lto/
strip) in mcpp.toml. --profile selects; flags.cppm applies opt/debug/lto to
compile and lto/strip to link. Verified: release/dev/dist emit distinct flags.
R2 precision: baking ALL of plan.runtimeLibraryDirs into -L/-rpath pulled the
glibc payload dir into musl/static links (undefined _DYNAMIC; broke e2e 28/30).
Split out plan.depRuntimeLibraryDirs (dependency [runtime] library_dirs only,
e.g. compat.glx-runtime) and emit toolchain.linkRuntimeDirs + dep dirs, as
before plus the dep closure. e2e 29 updated for the intentional glibc
first-run default (musl-static is opt-in via --target).
…..])

[features] declares feature -> implied-features (with a 'default' set);
long-form dep specs' features=[...] is now stored (was accepted-but-ignored)
and requests features of that dependency. Activation = default ∪ requested,
expanded transitively; each active feature becomes -DMCPP_FEATURE_<NAME> on
that package's compile flags. Root activation via --features a,b.
Verified: default set, --features, and implication closure all observable
via #ifdef. Transitive dep->dep feature propagation is a follow-up.
…] platforms

- dep spec backend = "<impl>" — sugar for requesting the dependency's
  backend-<impl> feature (library backend selection knob; verified the dep
  compiles with -DMCPP_FEATURE_BACKEND_<IMPL>).
- [runtime.<capability>] provider = "<pkg>" — explicit provider selection:
  prefers the named provider for matching capabilities (surfaced by why/
  doctor/resolution.json), warns when the provider isn't in the graph.
- [package] platforms = [...] — declared platform support, surfaced by
  mcpp why as the CI matrix hint.
…headers)

The glibc first-run default (R1) needs the sysroot payloads exactly like
`mcpp toolchain install` provides; the old musl-static default was
self-contained, which masked this. Fixes fresh-home e2e 29/31 in CI
(std module precompile: stdlib.h not found).
Extract gcc_post_install_fixup (patchelf PT_INTERP/RUNPATH for gcc/binutils +
linker-specs wiring against sandbox glibc) out of `toolchain install` and run
it from the first-run auto-install too. A fresh-sandbox glibc default
previously skipped the fixup and could not find the C library (stdlib.h: No
such file or directory — e2e 29/31 on CI). One shared pipeline, no duplicate.
gcc_post_install_fixup now skips payloads whose canonical path resolves
outside this MCPP_HOME's registry: inherited (symlinked) payloads belong to
their owner home, whose fixup is already valid; patching through the symlink
rewrote the canonical binaries against ephemeral paths and bricked the
owner's toolchain. Verified: fresh-home e2e (26/28/29/31) pass and the
owner toolchain stays intact.
probe_payload_paths found glibc/linux-headers only as compiler SIBLINGS.
With an inherited (symlinked) compiler the binary resolves into its owner
home, so sysroot payloads freshly installed into the ACTIVE home (first-run
default path) were invisible -> std module precompile failed with
stdlib.h not found (CI fresh-home e2e 29/31). Add
paths::active_home_xpkgs()/find_home_tool() and consult them after sibling
search: discovery = compiler siblings ∪ active home registry.
…pile error

A probed/remapped sysroot that exists but lacks stdlib.h (e.g. a
partially-bootstrapped sandbox subos in a fresh MCPP_HOME) silently shadowed
the payload -isystem fallback in both stdmod and flags, failing deep in the
std module build. probe_sysroot now only returns a sysroot that actually
contains the C headers (glibc usr/include or musl include layout) so all
consumers uniformly fall through to payload paths. std-precompile failures
now include the full compile command for actionable diagnosis.
…ity)

Ground truth from CI (command now in the error): -isystem'd payload glibc
include was present yet #include_next <stdlib.h> still failed. GCC's
libstdc++ wraps libc headers with #include_next, which only searches
directories AFTER the current header's dir — gcc's built-in dirs are last,
so -isystem (inserted before them) is unreachable. Use -idirafter (appended
to the very end) for GCC payload headers in both the std-module precompile
and per-TU flags; clang keeps -isystem. Verified with a faithful fresh-home
repro (no subos => invalid sysroot): first-run installs glibc default and
builds clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants