Skip to content

feat(doctor): detect missing toolchain runtime deps / dangling subos symlinks#141

Merged
Sunrisepeak merged 1 commit into
mainfrom
fix/doctor-runtime-dep-check
Jun 21, 2026
Merged

feat(doctor): detect missing toolchain runtime deps / dangling subos symlinks#141
Sunrisepeak merged 1 commit into
mainfrom
fix/doctor-runtime-dep-check

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

Problem

When a provider xim package is removed, an already-installed toolchain is left in a silently-broken state: its compiler binary keeps an absolute RUNPATH baked in (and subos/default/lib holds symlinks) that now point at files which no longer exist.

The concrete case that motivated this: deleting xim-x-zlib left clang++'s RUNPATH pointing at the now-gone xim-x-zlib/<v>/lib. The package still compiled fine, but the produced binary died at runtime with:

libz.so.1: cannot open shared object file: No such file or directory   (exit 127)

Nothing in mcpp self doctor surfaced this — the breakage only showed up later as a mysterious build/link failure.

Fix

A new Checking toolchain runtime deps section in doctor_report(), Linux/ELF only (guarded by #if !defined(__APPLE__) && !defined(_WIN32); macOS/Windows skip it):

  1. Enumerate installed toolchains the same way mcpp toolchain list does — iterate <xlingsHome>/data/xpkgs/xim-x-<compiler>/<version>/bin and resolve the frontend via toolchain::toolchain_frontend().
  2. Read each compiler's RUNPATH/RPATH with readelf -d, run through the existing mcpp::platform::process::capture helper (no new process code, no new deps). Parse the Library runpath: [a:b:c] line, split on :. For each absolute entry whose directory is missing, warn(...) naming the toolchain + missing path, hinting the providing xim package may have been removed (reinstall the toolchain to repair).
  3. Scan <xlingsHome>/subos/default/lib for dangling symlinks (std::filesystem::exists follows links → false when the target is gone) and warn(...) with the symlink + its broken target.

Exit-code semantics are unchanged — warnings only, no new errors.

The RUNPATH parsing is factored into an exported, process-free helper parse_readelf_runpath() so it can be unit-tested without spawning anything.

Testing

  • mcpp build — passes (only the pre-existing imgui warnings).
  • mcpp test — all 23 unit test binaries pass, including the new test_doctor_runpath (covers the zlib-removal RUNPATH shape, legacy DT_RPATH, no-runpath, and empty-token cases).
  • mcpp self doctor (freshly built binary) — runs without crashing and prints the new section:
        Checking toolchain runtime deps
              ok all installed toolchain RUNPATH dirs present
              ok subos lib symlinks all resolve (/home/.../registry/subos/default/lib)
    
  • Verified the dangling-symlink semantics with a fixture: a symlink to a removed xim-x-zlib/.../libz.so.1 reports as non-existent (follows the link), matching the detection branch.

Files changed

  • src/doctor.cppm — new section + exported parse_readelf_runpath() helper + imports (mcpp.platform.process, mcpp.toolchain.registry).
  • tests/unit/test_doctor_runpath.cpp — self-contained gtest coverage for the parser.

…symlinks

Add a new "toolchain runtime deps" section to `mcpp self doctor` that
catches the failure class where a provider xim package gets removed,
leaving an installed toolchain's RUNPATH (and subos/default/lib symlinks)
pointing at files that no longer exist.

Concretely: deleting `xim-x-zlib` left clang++'s baked RUNPATH pointing
at the gone `xim-x-zlib/<v>/lib` dir, so the package compiled fine but
the produced binary died at runtime with
"libz.so.1: cannot open shared object" (exit 127). Nothing surfaced the
broken state until a build mysteriously failed.

The new section (Linux/ELF only, guarded by
`#if !defined(__APPLE__) && !defined(_WIN32)`):
  1. Enumerates installed xim toolchains the same way `mcpp toolchain
     list` does (iterate <xlingsHome>/data/xpkgs/xim-x-<compiler>/<ver>,
     resolve the frontend via toolchain_frontend()).
  2. Reads each compiler's RUNPATH/RPATH via `readelf -d` (reusing
     mcpp::platform::process::capture — no new process code, no new
     deps) and warns for every absolute RUNPATH dir that is now missing,
     naming the toolchain and hinting the providing xim package may have
     been removed.
  3. Scans <xlingsHome>/subos/default/lib for dangling symlinks
     (std::filesystem::exists follows links -> false when the target is
     gone) and warns with the symlink + its broken target.

Exit-code semantics are unchanged (warnings only). RUNPATH parsing is
factored into an exported, process-free `parse_readelf_runpath()` helper
with self-contained gtest coverage (tests/unit/test_doctor_runpath.cpp),
including the zlib-removal RUNPATH shape, legacy DT_RPATH, no-runpath,
and empty-token cases.
@Sunrisepeak Sunrisepeak merged commit cced0fd into main Jun 21, 2026
3 checks passed
Sunrisepeak added a commit that referenced this pull request Jun 21, 2026
* fix(build): self-heal stale build.ninja on 'missing and no known rule'

When a dependency package under the registry is reinstalled/moved but keeps
the same version string, the build fingerprint (which does not yet cover
registry dep state) is unchanged, so the cached build.ninja is reused.
Ninja then aborts with 'missing and no known rule to make it' and the build
hard-fails, forcing the user to run `mcpp clean` by hand.

Add that signature to is_stale_ninja_failure so try_fast_build drops to a
full graph regeneration (same invocation) instead of failing — the stale
graph is rewritten against the current dep state and the build proceeds.

(Folding registry dep state into the fingerprint to avoid the regen entirely
is a larger follow-up; see .agents/docs/2026-06-22 §T-j.)

* chore: bump version 0.0.57 -> 0.0.58

Release carrying the follow-up batch: scanner raw-string fix (#138),
first-run progress (#139), toolchain effective-resolution (#140),
doctor runtime-dep check (#141), and the build.ninja self-heal above.
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.

1 participant