ci: add fresh-install workflow for first-time user experience#63
Open
Sunrisepeak wants to merge 10 commits into
Open
ci: add fresh-install workflow for first-time user experience#63Sunrisepeak wants to merge 10 commits into
Sunrisepeak wants to merge 10 commits into
Conversation
Sunrisepeak
added a commit
that referenced
this pull request
May 21, 2026
Linux CI now tests all 3 toolchains with freshly-built mcpp: - GCC 16.1.0: mcpp new → build → run - musl-gcc 15.1.0: mcpp new → build → run - LLVM 20.1.7: install + mcpp new → build → run Remove continue-on-error "Fresh user experience" tests from all 3 CI workflows — moved to separate ci-fresh-install.yml (PR #63). Those tests validate the xlings-distributed mcpp binary, not the PR's code, so they don't belong in the main CI gate.
Sunrisepeak
added a commit
that referenced
this pull request
May 21, 2026
…ysroot (#62) * fix: remove subos sysroot override, use payload paths for toolchain sysroot (#62) Remove M5.5 logic that forced tc->sysroot to mcpp's xlings subos (~/.mcpp/registry/subos/default). The subos created by `xlings self init` lacks linux kernel headers (linux/limits.h, asm/, asm-generic/), causing std module precompilation to fail on user machines. Root cause: commit 063fb6f changed MCPP_HOME to ~/.mcpp/, where the subos exists but is incomplete. M5.5 only checked exists(usr/include) and overwrote the toolchain's correct sysroot. CI never caught this because its subos is fully populated by `xlings self install`. Design principle: mcpp uses xlings only as a package index + download tool. Sysroot comes from the toolchain payload itself, not from subos. Changes: - cli.cppm: delete M5.5 subos sysroot override entirely - probe.cppm: parse clang++.cfg --sysroot= when -print-sysroot fails (Clang doesn't support -print-sysroot), so tc->sysroot reflects the payload's actual configuration - stdmod.cppm: generalize --no-default-config from macOS-only to all Clang toolchains with a cfg file (cfg paths become stale after mcpp copies the payload to its sandbox) - flags.cppm: sync the same --no-default-config logic for regular compilation flags * fix: GCC sysroot fallback — remap stale build-time path to registry GCC bakes the build-time sysroot into the binary via --with-sysroot. For xlings-built GCC this is a path like <buildhost>/.xlings/subos/default that doesn't exist on the user's machine. When -print-sysroot returns such a non-existent path ending in subos/default, remap it to the equivalent sysroot relative to the compiler's own xpkgs directory. This is payload-derived (from the compiler binary's location in the registry), not a config-level dependency on subos. * fix: add target-specific libc++ include path for --no-default-config When bypassing clang++.cfg with --no-default-config, we must provide both libc++ include paths that the cfg originally supplied: -isystem <llvmRoot>/include/c++/v1 -isystem <llvmRoot>/include/<triple>/c++/v1 The target-specific path contains __config_site which is required by __config. Without it, std module precompilation fails with '__config_site' file not found. * fix: restrict --no-default-config to macOS only On Linux, clang++.cfg contains essential linker flags (-fuse-ld=lld, --rtlib=compiler-rt, --unwindlib=libunwind). Using --no-default-config strips these, causing "cannot find crtbeginS.o" link failures because clang falls back to system GNU ld looking for GCC runtime objects. On Linux, let the cfg apply normally. The cfg's --sysroot points to the xlings subos which is valid and complete. Pass --sysroot explicitly only when needed (to override a stale cfg value), leveraging the fact that command-line --sysroot takes precedence over the cfg's value. Keep --no-default-config for macOS only, where the cfg-baked paths genuinely become stale (pointing to CommandLineTools SDK when Xcode SDK is active). * feat: payload-first sysroot — assemble compile env from xpkgs payloads Replace --sysroot dependency on xlings subos with fine-grained -isystem paths derived from sibling xpkgs payloads (glibc, linux-headers). Phase 2: PayloadPaths model - Add PayloadPaths struct to Toolchain model (glibcInclude, glibcLib, linuxInclude) - probe_payload_paths() finds sibling glibc and linux-headers xpkgs via find_sibling_package() which searches across all index prefixes - Falls back to host /usr/include for linux kernel headers if no xpkg found Phase 3: Payload-first flags - flags.cppm: use -isystem for glibc + linux-headers instead of --sysroot; Clang with cfg uses --no-default-config + explicit flags including -fuse-ld=lld, --rtlib=compiler-rt, --unwindlib=libunwind - stdmod.cppm: unified Clang cfg bypass on all platforms for std module precompile (no linker needed, so --no-default-config is safe) Phase 4: Clang cfg fixup - fixup_clang_cfg() rewrites clang++.cfg paths after payload copy, similar to fixup_gcc_specs() for GCC - Called during `mcpp toolchain install llvm` Phase 5: Sysroot dependency auto-install - Toolchain install ensures glibc and linux-headers xpkgs are installed before the main toolchain package * fix: GCC needs --sysroot for include-fixed, supplement with -isystem GCC's include-fixed directory contains stdlib.h wrappers that use #include_next to find the sysroot's stdlib.h. This mechanism only works with --sysroot, not standalone -isystem paths. Fix: for GCC, keep --sysroot from probe_sysroot() and supplement with -isystem for linux kernel headers from payload when the probed sysroot is missing them. For Clang, continue using --no-default-config + explicit -isystem (Clang doesn't have include-fixed). * fix: add -nostdinc++ -stdlib=libc++ for Clang --no-default-config When bypassing clang++.cfg, the cfg's -nostdinc++ and -stdlib=libc++ flags are also stripped. Without -nostdinc++, Clang may find host libstdc++ headers before the payload's libc++ headers. Without -stdlib=libc++, Clang defaults to libstdc++ runtime. Also remove the /usr/include fallback for linux-headers — mixing host headers with xpkg glibc causes bits/wordsize.h conflicts. * feat: ensure sysroot complete by symlinking from payload xpkgs When GCC's probed sysroot (subos/default) is missing linux kernel headers or glibc headers, symlink them from the payload xpkgs: - linux/, asm/, asm-generic/ ← scode-x-linux-headers xpkg - features.h, bits/, etc. ← xim-x-glibc xpkg This makes mcpp self-sufficient: it uses subos/default as a sysroot directory for GCC's include-fixed mechanism, but actively populates it from payload rather than depending on xlings init completeness. Principle: subos is just a directory layout that mcpp manages. Content comes from xpkgs payloads. Clang doesn't use subos at all (--no-default-config + explicit -isystem from payload). * fix: include MSVC effective triple in fingerprint (Windows) Clang on Windows auto-detects the MSVC version at compile time and embeds it in module AST files (e.g. x86_64-pc-windows-msvc19.44.35227). But -dumpmachine returns just x86_64-pc-windows-msvc (no version). When MSVC updates a patch version (35226 → 35227), the fingerprint didn't change, so mcpp reused cached std.pcm compiled for the old version → "AST file was compiled for different target" error. Fix: probe clang's -print-effective-triple which includes the MSVC version, and append to driverIdent for fingerprint computation. Also: ensure sysroot complete by symlinking linux kernel headers from payload xpkgs into the GCC sysroot directory. * feat: add is_msvc_target() predicate + ci-fresh-install workflow Refactor: - Extract is_msvc_target() to model.cppm alongside is_musl_target() - Replace 3 scattered tc.targetTriple.find("msvc") checks in clang.cppm, detect.cppm, provider.cppm CI: - Add ci-fresh-install.yml: validates first-time user install flow on all platforms (Linux, macOS, Windows) with zero cache. - Tests: xlings install mcpp → self-host build → mcpp new → mcpp run - Tests: import std with both GCC and LLVM (Linux), LLVM (macOS), LLVM+MSVC STL (Windows) - Catches issues that cached CI misses: incomplete sysroot, stale cfg paths, missing xpkg dependencies * fix: ci-fresh-install — correct asset names + MCPP_VENDORED_XLINGS - macOS tarball: macos-aarch64 → macosx-arm64 (matches release assets) - Windows: use explicit extract dir name - All steps: export MCPP_VENDORED_XLINGS so freshly-built mcpp uses the installed xlings binary for package operations - Use MCPP_BOOTSTRAP for bootstrap mcpp, MCPP for freshly-built - Set MCPP_HOME explicitly for consistent sandbox location * fix: ci-fresh-install — simulate real user, no extra config Strip all env overrides, self-host builds, and MCPP_VENDORED_XLINGS. Simulate exactly what a real user does: 1. Install xlings 2. xlings install mcpp -y 3. mcpp new hello && cd hello && mcpp run * fix: ci-fresh-install Windows — use pwsh for native path handling Git Bash on Windows mangles GITHUB_PATH entries. Switch to pwsh which handles Windows paths natively. * fix: ci-fresh-install — workflow_dispatch only This CI tests xlings-distributed mcpp (not the PR's code), so it will fail until fixes are released to the xlings mcpp package. Change to manual trigger only — run after a release to verify the real end-to-end user experience. * fix: ci-fresh-install — build PR's mcpp then test fresh user flow Restore PR trigger. Flow: 1. Bootstrap xlings + old mcpp via xlings install 2. Build THIS PR's mcpp from source (self-host) 3. Use freshly-built mcpp to simulate fresh user: new → run 4. Linux: test both GCC (default) and LLVM toolchains 5. macOS: test LLVM (default) 6. Windows: test LLVM + MSVC STL * ci: retrigger after clearing Windows cache * fix: ci-fresh-install — simplify, use GITHUB_PATH only - Remove all extra env vars (MCPP_VENDORED_XLINGS, XLINGS_BIN) - Just add xlings bin to GITHUB_PATH, everything else works - Windows: set PATH inline in bootstrap step so xlings install mcpp can find xlings immediately * fix: ci-fresh-install — no $MCPP var, just PATH; add mirror for LLVM - Remove $MCPP variable, put freshly-built mcpp dir on PATH instead - All steps just use `mcpp` command directly - Linux LLVM step: add `mcpp self config --mirror GLOBAL` before toolchain install (CI runners are outside CN) - Windows: use pwsh throughout for native path handling * fix: ci-fresh-install — test released mcpp, not freshly-built Use xlings-installed mcpp directly to test: 1. mcpp build (self-host compile) 2. mcpp new hello → mcpp run (default toolchain) 3. Linux: install LLVM → mcpp new → mcpp run (continue-on-error since released mcpp may not have latest fixes yet) No extra env vars, no $MCPP variable — just mcpp on PATH via xlings. * chore: move ci-fresh-install to separate PR The fresh-install CI workflow tests the released mcpp binary via xlings, not this PR's code. Move it to its own branch/PR to keep this PR focused on the sysroot fix. * ci: multi-toolchain coverage on Linux, remove continue-on-error tests Linux CI now tests all 3 toolchains with freshly-built mcpp: - GCC 16.1.0: mcpp new → build → run - musl-gcc 15.1.0: mcpp new → build → run - LLVM 20.1.7: install + mcpp new → build → run Remove continue-on-error "Fresh user experience" tests from all 3 CI workflows — moved to separate ci-fresh-install.yml (PR #63). Those tests validate the xlings-distributed mcpp binary, not the PR's code, so they don't belong in the main CI gate. * fix: actually update ci.yml (previous edit didn't take effect)
Validates the released mcpp binary via xlings on all platforms:
- Linux: xlings install mcpp → mcpp build (self) → mcpp new → mcpp run
+ install LLVM → mcpp new → mcpp run (continue-on-error)
- macOS: xlings install mcpp → mcpp build → mcpp new → mcpp run
- Windows: xlings install mcpp → mcpp build → mcpp new → mcpp run
No caches — simulates a clean machine. Catches issues like incomplete
sysroot, stale cfg paths, missing xpkg dependencies.
Main CI changes: - ci-windows.yml: add explicit "Toolchain: LLVM — mcpp new → build → run" smoke test (previously only soft fallback via || true) - All three main CIs (ci.yml, ci-macos.yml, ci-windows.yml) no longer have continue-on-error fresh user tests (removed in PR #62) Toolchain coverage after this change: Linux: gcc@16.1.0 ✓ musl-gcc@15.1.0 ✓ llvm@20.1.7 ✓ macOS: llvm@20.1.7 ✓ (comprehensive module tests) Windows: llvm@20.1.7 ✓ (explicit smoke test) New workflow: - ci-fresh-install.yml: tests released mcpp via xlings on clean machines (no caches). Validates real first-time user experience separately from PR code testing.
e1ba0cf to
e36c924
Compare
The musl-gcc test step assumed the toolchain was already cached. After cache clears, `toolchain default gcc@15.1.0-musl` fails with "not installed". Add explicit install before setting default.
Each toolchain now builds mcpp from source (self-host) instead of
a trivial hello-world project. This validates that the toolchain
can compile a real C++23 modules codebase.
Coverage:
Linux: gcc@16.1.0 builds mcpp ✓
musl-gcc@15.1.0 builds mcpp ✓
llvm@20.1.7 builds mcpp ✓
macOS: llvm@20.1.7 builds mcpp ✓ (default, in self-host smoke)
Windows: llvm@20.1.7 builds mcpp ✓ (new step)
Merge redundant self-host smoke steps into unified toolchain steps. Each supported toolchain per platform does: clean → build mcpp → verify. Linux: gcc@16.1.0 (+ test), musl-gcc@15.1.0, llvm@20.1.7 macOS: llvm@20.1.7 Windows: llvm@20.1.7
Two-tier CI design:
Tier 1 — PR/dev CI (ci.yml, ci-macos.yml, ci-windows.yml):
Runs on every PR. Builds mcpp from source, runs tests + e2e,
then verifies each platform's supported toolchains can build mcpp:
Linux: gcc@16.1.0 (+test), musl-gcc@15.1.0, llvm@20.1.7
macOS: llvm@20.1.7
Windows: llvm@20.1.7
Tier 2 — Release validation CI (ci-fresh-install.yml):
Manual trigger + daily schedule. Tests released mcpp via xlings
on clean machines (no caches). For each platform, every supported
toolchain: new → run (basic project) + build mcpp (self-host).
Linux: gcc, musl-gcc, llvm — new+run + build mcpp
macOS: llvm — new+run + build mcpp
Windows: llvm — new+run + build mcpp
ci-fresh-install no longer runs on PRs (it tests released mcpp,
not PR code). Moved to workflow_dispatch + daily cron.
- GCC: don't set toolchain default (GCC is already default after xlings install mcpp), fixes "gcc@ is not installed" error - Remove pull_request trigger: ci-fresh-install tests released mcpp (not PR code), so it should not block PRs. Runs on push to main, workflow_dispatch, and daily schedule only.
mcpp clean deletes target/ which contains the freshly-built binary. Copy it to /tmp before running clean so it survives. Also: clear Windows BMI cache (--bmi-cache) to avoid MSVC version mismatch errors from stale std.pcm.
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
ci-fresh-install.ymlworkflow that validates the released mcpp binary on clean machinesxlings install mcpp → mcpp build → mcpp new hello → mcpp runRelated
Split from #62 to keep that PR focused on the sysroot fix.