diff --git a/binDir b/binDir new file mode 100644 index 00000000..e69de29b diff --git a/src/toolchain/lifecycle.cppm b/src/toolchain/lifecycle.cppm index 96877f72..b3814add 100644 --- a/src/toolchain/lifecycle.cppm +++ b/src/toolchain/lifecycle.cppm @@ -285,7 +285,11 @@ export int toolchain_list(const mcpp::config::GlobalConfig& cfg) { s.version = vEntry.path().filename().string(); s.target = id->target; auto pkg = mcpp::toolchain::to_xim_package(s); - auto bin = mcpp::toolchain::toolchain_frontend(vEntry.path() / "bin", pkg); + // From the payload ROOT, not `root/bin`: msvc keeps cl.exe + // four levels deeper, and asking for `root/bin` skipped every + // installed toolset silently. + auto bin = mcpp::toolchain::payload_frontend(vEntry.path(), pkg, + id->family); if (bin.empty()) continue; payloads.push_back({ *id, s.version, bin }); } diff --git a/src/toolchain/registry.cppm b/src/toolchain/registry.cppm index d21e0c88..0dd21fb9 100644 --- a/src/toolchain/registry.cppm +++ b/src/toolchain/registry.cppm @@ -106,6 +106,23 @@ ToolchainSpec with_resolved_xim_version(const ToolchainSpec& spec, std::filesystem::path toolchain_frontend(const std::filesystem::path& binDir, const XimToolchainPackage& pkg); +// The frontend inside an installed payload ROOT — for callers that have the +// root rather than a bin directory. +// +// Most families keep it in `bin/`, and for them this is `toolchain_frontend` +// on `root/bin`. MSVC does not: cl.exe sits four levels deeper, under +// `VC/Tools/MSVC//bin/Host//`. +// +// It exists because that difference had to be known in three places and was +// only handled in two. The third — `toolchain list`'s enumeration — asked +// `root/bin`, got nothing, and `continue`d, so an msvc toolset installed +// perfectly well and then did not appear in the list. Empty = no frontend +// here, which is the caller's cue to skip; a wrong LAYOUT and a missing +// PAYLOAD had been reporting the same way. +std::filesystem::path payload_frontend(const std::filesystem::path& payloadRoot, + const XimToolchainPackage& pkg, + Family family); + // Reverse mapping: an installed `xim-x-` payload directory back to its // (family, target) identity. nullopt for non-toolchain xpkgs (ninja, glibc, // python, …) — list/doctor use this to filter what they enumerate. @@ -357,6 +374,20 @@ std::filesystem::path toolchain_frontend(const std::filesystem::path& binDir, return {}; } +std::filesystem::path payload_frontend(const std::filesystem::path& payloadRoot, + const XimToolchainPackage& pkg, + Family family) { + if (family == Family::Msvc) { + // Same resolution the install and build paths use, so the three + // cannot disagree about where an msvc payload keeps its compiler. + if (auto inst = mcpp::toolchain::msvc::installation_at(payloadRoot, + pkg.ximVersion)) + return inst->clPath; + return {}; + } + return toolchain_frontend(payloadRoot / "bin", pkg); +} + std::optional identify_xim_payload(std::string_view ximDirName) { if (ximDirName == "gcc") return PayloadIdentity{ Family::Gcc, {} }; diff --git a/tests/unit/test_toolchain_msvc.cpp b/tests/unit/test_toolchain_msvc.cpp index d5fc30a8..4a69dd0c 100644 --- a/tests/unit/test_toolchain_msvc.cpp +++ b/tests/unit/test_toolchain_msvc.cpp @@ -255,6 +255,34 @@ TEST(MsvcManaged, VersionFallsBackToTheDeclaredOneWhenTheBannerCannotBeRead) { EXPECT_EQ(inst->display_version(), "14.52.36629"); } +TEST(MsvcManaged, PayloadFrontendFindsClWhereMsvcActuallyKeepsIt) { + // The defect this pins: `toolchain list` asked `toolchain_frontend(root / + // "bin", …)`, got nothing, and skipped the row -- so an msvc toolset + // installed correctly and then did not appear anywhere. Three places need + // to know that cl.exe is four levels deeper than `bin/`; two knew. + FakeToolset t{"frontend"}; + t.add_toolset("14.44.35207"); + + auto spec = parse_toolchain_spec("msvc@14.44.35207"); + ASSERT_TRUE(spec.has_value()); + auto pkg = to_xim_package(*spec); + + auto found = payload_frontend(t.root, pkg, Family::Msvc); + ASSERT_FALSE(found.empty()) << "payload_frontend found no cl.exe under " << t.root; + EXPECT_EQ(found.filename(), "cl.exe"); + + // The `bin/`-shaped question is the one that used to be asked, and it + // still answers nothing here — which is exactly why it was the wrong + // question rather than a broken implementation. + EXPECT_TRUE(toolchain_frontend(t.root / "bin", pkg).empty()); + + // A root with no toolset at that version stays empty rather than + // returning a path that does not exist. + EXPECT_TRUE(payload_frontend(t.root, + to_xim_package(*parse_toolchain_spec("msvc@14.52.36629")), + Family::Msvc).empty()); +} + // ─── Windows SDK discovery ─────────────────────────────────────────────── TEST(MsvcSdk, WindowsSdkDirBeatsTheHardcodedPaths) {