Skip to content

fix: make the build.mcpp host helper self-contained on musl and MinGW - #298

Merged
Sunrisepeak merged 3 commits into
mcpp-community:mainfrom
wellwei:fix/musl-build-mcpp-helper
Jul 28, 2026
Merged

fix: make the build.mcpp host helper self-contained on musl and MinGW#298
Sunrisepeak merged 3 commits into
mcpp-community:mainfrom
wellwei:fix/musl-build-mcpp-helper

Conversation

@wellwei

@wellwei wellwei commented Jul 27, 2026

Copy link
Copy Markdown
Member

Summary

The compiled build.mcpp helper is exec'd by the host OS, outside anything mcpp controls — no wrapper, no injected LD_LIBRARY_PATH, no PATH guarantee. Whether it is actually runnable is a per-platform mechanism, and only two of the four were closed:

host mechanism status
Linux + glibc payload absolute payload paths baked into -Wl,-rpath ok
Linux + musl rpath cannot reach PT_INTERP (/lib/ld-musl-<arch>.so.1) broken — #295
Windows PE PE has no rpath; libstdc++-6 / libgcc_s / libwinpthread resolve via process PATH broken — #299
macOS system libc++ always present ok

Both gaps are the same defect — build_program.cppm re-derives the helper link policy instead of reusing the main build's (cf. prepare.cppm's musl → linkage = "static" default) — so they get one decision point rather than two parallel predicates.

  • single staticHostHelper in run_build_program, with the chosen policy folded into the build-program cache identity so pre-fix helpers are rebuilt, not reused
  • -static goes on the final link argv only, never into base (which also feeds the bundled module's compile/precompile commands)
  • host_base_flags stops emitting -Wl,-rpath on PE, where it is inert
  • posix_spawnp failures keep their error code and message in the captured output instead of collapsing to an unexplained exit 127

Test plan

  • Negative control: reverting the -static push reproduces fix: statically link build.mcpp host helpers with musl toolchains #295 exactly — posix_spawnp(...) failed (error 2): No such file or directory, helper carries PT_INTERP=/lib/ld-musl-x86_64.so.1
  • Non-regression: a plain glibc project's helper stays dynamic with an absolute payload interpreter + RUNPATH — the rpath mechanism is untouched
  • Unit: mcpp test (37 binaries, incl. the new CaptureExec.MissingProgramReportsSpawnFailure)
  • tests/e2e/89_build_mcpp.sh, tests/e2e/92_build_mcpp_import.sh
  • tests/e2e/168_build_mcpp_musl_host_static.sh — discovers the installed musl-gcc payload rather than hardcoding a version, so it runs on the aarch64 host where the regression lives (renamed from 167, which main now uses for 167_build_defines_module_scan)
  • tests/e2e/97_mingw_toolchain.sh extended to hold the helper to the same standalone bar as the produced exe (import table + run with the toolchain bin off PATH) — runs under real MinGW GCC 16.1.0 on Windows CI, which is fix: build.mcpp binary fails at runtime with missing DLL on Windows #299's configuration
  • ci-aarch64-fresh-install builds the PR source and runs e2e 168 against it; the published-asset validation steps keep using the installed mcpp. Dropped the duplicated inline assertions, including ! readelf … | grep -q …, which bash exempts from errexit and which therefore could never fail.

Closes #295
Closes #299
Supersedes #300 (same -static idea, narrowed to the two platforms that need it and keeping the Linux rpath path intact) — thanks @ZheFeng7110, credited via Co-authored-by.

@Sunrisepeak
Sunrisepeak force-pushed the fix/musl-build-mcpp-helper branch from 876792d to 6225db0 Compare July 28, 2026 12:49
@Sunrisepeak Sunrisepeak changed the title fix: statically link musl build.mcpp helpers fix: make the build.mcpp host helper self-contained on musl and MinGW Jul 28, 2026
… (2026.7.28.2)

The compiled build.mcpp helper is exec'd by the host OS, outside anything
mcpp controls — no wrapper, no injected LD_LIBRARY_PATH, no PATH guarantee.
Whether it is actually runnable is a per-platform mechanism, and only two of
the four were closed:

  Linux + glibc payload  ok  — host_base_flags bakes absolute payload paths
                               into -Wl,-rpath
  Linux + musl (mcpp-community#295)    bad — rpath cannot reach PT_INTERP, an absolute
                               /lib/ld-musl-<arch>.so.1 no payload installs
                               and no glibc distro ships -> execve ENOENT,
                               surfacing as a bare exit 127
  Windows PE (mcpp-community#299)      bad — PE has no rpath at all, so the helper resolves
                               libstdc++-6 / libgcc_s / libwinpthread via the
                               process PATH -> STATUS_DLL_NOT_FOUND
  macOS                  ok  — the system libc++ is always present

Both gaps are the same defect (build_program.cppm re-derives the helper link
policy instead of reusing the main build's, cf. prepare.cppm's musl ->
linkage="static" default), so they get ONE decision point rather than two
parallel predicates: a single staticHostHelper in run_build_program, with the
chosen policy folded into the build-program cache identity so pre-fix helpers
are rebuilt instead of reused. `-static` is appended to the final link argv
only — never to `base`, which also feeds the bundled module's compile and
precompile commands.

Also:
- process.cppm: keep the posix_spawnp error code and message in the captured
  output instead of collapsing to an unexplained exit 127 (both capture_exec
  and capture_exec_deadline). All three consumers of RunResult::output gate on
  exit_code first, so nothing downstream changes.
- host_base_flags: stop emitting -Wl,-rpath on PE, where it is inert.
- tests/e2e/168 (was 167 — taken on main by 167_build_defines_module_scan):
  discovers the installed musl-gcc payload instead of hardcoding 15.1.0, so it
  runs on the aarch64 host where the regression actually lives; asserts static
  + no PT_INTERP. Verified by negative control: reverting the -static push
  reproduces the exact mcpp-community#295 failure.
- tests/e2e/97: hold the build.mcpp helper to the same standalone bar as the
  produced exe (import table + run with the toolchain bin off PATH). This runs
  under the real MinGW GCC 16.1.0 on Windows CI, which is mcpp-community#299's configuration.
- ci-aarch64-fresh-install: append a PR regression gate that builds this source
  and runs e2e 168 against it. The checkout must come AFTER the fresh-install
  steps: .xlings.json declares an `mcpp` workspace pin, so a checkout present
  in $GITHUB_WORKSPACE makes `xlings install mcpp` workspace-scoped — which
  both voids the fresh-install charter (validating the pinned version, not the
  published one) and leaves bare `mcpp` unresolvable outside the workspace.
  Dropped the duplicated inline assertions, including
  `! readelf … | grep -q …`, which bash exempts from errexit and which could
  therefore never fail.

Closes mcpp-community#295
Closes mcpp-community#299
Supersedes mcpp-community#300

Co-authored-by: Zhe Feng <zhefeng7110@outlook.com>
@Sunrisepeak
Sunrisepeak force-pushed the fix/musl-build-mcpp-helper branch from 6225db0 to 44f012a Compare July 28, 2026 12:56
@Sunrisepeak
Sunrisepeak merged commit 0caca69 into mcpp-community:main Jul 28, 2026
16 checks passed
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.

fix: build.mcpp binary fails at runtime with missing DLL on Windows fix: statically link build.mcpp host helpers with musl toolchains

2 participants