Skip to content

Windows: installing a payload during mcpp build leaves the registry's shim directory at the front of PATH for every action, so llvm's cl/link/lib shims shadow MSVC #726

Description

@speak-agent

Observation

GalTranslPP on windows-2025 (Sunrisepeak/GalTranslPP#3), mcpp 2026.9.27.1, llvm@22.1.8 with msvc@system, mcpp:plugins 0.16.0 deps-vcpkg. On a build that installs the toolchain or a payload itself, the vcpkg install action fails:

Detecting compiler hash for triplet x64-windows...
error: while detecting compiler information:
The log file content at "...\bt\detect_compiler\stdout-x64-windows.log" is:
'C:\Program' is not recognized as an internal or external command,
operable program or batch file.

The same vcpkg install command run outside mcpp succeeds. A second mcpp build, or a build after mcpp emit build-database installed the payloads in another process, succeeds.

Cause

On Windows, build_command_prefix (src/xlings/xlings.cppm) sets XLINGS_HOME and prepends the registry's shim directory to the process PATH:

mcpp::platform::env::set("XLINGS_HOME", env.home.string());
mcpp::platform::windows::prepend_path(xvmBin);

ScopedInvocationEnv restores only the scope variables. As its comment states, "XLINGS_HOME and the PATH prefix keep their process-wide lifetime". Each xlings invocation during provisioning therefore prepends <mcpp home>/registry/subos/default/bin once more; three copies were observed. ninja and every action started afterwards inherit this PATH.

That directory holds the shims xim:llvm registers on Windows:

  • cl for clang-cl;
  • link for lld-link;
  • lib for llvm-lib;
  • rc for llvm-rc.

vcpkg runs vcvarsall to detect MSVC for the host triplet, and that detection then meets these shims instead of MSVC's tools.

On POSIX the same values are part of the xlings command prefix and never reach the build.

Evidence

  • Environment of a build action. It was compared with the job's environment by an action running cmd /c set (run 36321504631). The action's PATH gains <mcpp home>\registry\data\xpkgs\xim-x-llvm\22.1.8\lib;<mcpp home>\registry\subos\default\bin (the second directory three times). The action also gets XLINGS_HOME=<mcpp home>\registry.

  • Isolation. A direct vcpkg install was run under each environment, 60 s each (run 36322488531):

    Environment Result
    The job's environment Passes compiler detection
    The registry's shim directory in front of PATH Fails with the message above
    The XLINGS_* variables alone Passes
    The project's own SubOS bin in front of PATH Passes
  • mcpp started directly. When provisioning is not part of the same process, compiler detection passes and the installation proceeds (run 36321827736).

Expected

The registry's shim directory and XLINGS_HOME reach the xlings child process only, as on POSIX. They should not reach the build.

Suggested direction

  • Have ScopedInvocationEnv save and restore PATH and XLINGS_HOME on Windows as well, so that build_command_prefix no longer changes the process.
  • Alternatively, give the xlings child its environment directly.

Activity

  1. speak-agent commented on Sep 27, 2026

    @speak-agent
    MemberAuthor

    A second difference from the POSIX prefix surfaced in the next run (Sunrisepeak/GalTranslPP run 36323236500). In that run the payloads were installed by a separate mcpp emit build-database, so the build process itself never called xlings:

    Detecting compiler hash for triplet x64-windows...
    The log file content at "...\detect_compiler\stdout-x64-windows.log" is:
    [error] cmake is not installed in this subos (default)
              declared by project   D:\a\GalTranslPP\GalTranslPP
    

    The POSIX prefix runs xlings from its home; the Windows command runs it from mcpp's working directory. The two lines are:

    • POSIX: cd <home> && env ... xlings
    • Windows: the binary alone, with no change of directory.

    xlings chooses project mode from that directory. It walks up from its working directory to a .xlings.json (src/core/config.cpp) and exports XLINGS_PROJECT_DIR.

    GalTranslPP pins its mcpp in such a file at the root. So the registry's xlings adopted the project and wrote the shims of mcpp's toolchain and payloads into the project's SubOS, .xlings\subos\_\bin:

    • cl, link, lib;
    • clang;
    • cmake;
    • 7z;
    • the llvm-* tools.

    That directory is on PATH in any shell inside the project, and the shims there resolve against the user's home. In that home the payloads are not installed, which produces the error above.

    The fix in #727 covers both differences:

    • ScopedInvocationEnv applies and restores PATH and XLINGS_HOME, not only the scope variables;
    • the Windows command starts with cd /d "<home>" &&, as the POSIX one starts with cd.
  2. added a commit that references this issue on Sep 27, 2026
  3. speak-agent commented on Sep 27, 2026

    @speak-agent
    MemberAuthor

    Implemented in 2026.9.28.1 (#727). On Windows an xlings invocation made by mcpp now affects only the xlings child process, as on POSIX.

    • The environment is restored. ScopedInvocationEnv applies XLINGS_HOME, the scope variables and the PATH prefix of the registry's subos/default/bin for the duration of one invocation, and restores them afterwards, newest first.
    • xlings starts in the registry's home. Each command begins with cd /d "<home>" &&, so xlings no longer walks up from the project to a .xlings.json and adopts the project.

    Real-world reading. GalTranslPP (Sunrisepeak/GalTranslPP#3) builds a workspace with vcpkg and Qt, and has a .xlings.json at its root.

    mcpp Windows CI run Reading
    2026.9.27.1 36329637073 fails: vcpkg was unable to detect the active compiler's information
    2026.9.28.1 36346122142 passes: mcpp build --workspace, mcpp run -p GPPCLI, mcpp pack --format release, the CLI starting from the release layout, emit build-database

    The only change between the two runs is the mcpp pin in .xlings.json (commit bf8ffc4f).

    In the suite, tests/unit/test_xlings.cpp asserts that neither PATH nor XLINGS_HOME outlives an invocation, and asserts the exact Windows prefix for a home whose path contains a space. PR #727's Windows CI rows pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions