diff --git a/src/toolchain/registry.cppm b/src/toolchain/registry.cppm index 0dd21fb9..eae2c842 100644 --- a/src/toolchain/registry.cppm +++ b/src/toolchain/registry.cppm @@ -229,6 +229,35 @@ parse_toolchain_spec(std::string compilerArg, "supported alias like mingw / musl-gcc)", compilerArg)); } + // `@system` means "whatever this machine has", and it is deliberately + // available for MSVC ONLY. + // + // xlings depends on the host as little as it can: a toolchain comes from + // a payload, which is what makes "the manifest says 14.44.35207" true on + // every machine instead of on the one that happened to have it. Offering + // `gcc@system` would invite the uncertainty back in, and the alternative + // is one command away. + // + // MSVC is the exception because Windows is: Visual Studio is frequently + // already installed and cannot always be redistributed, so refusing to + // use it would mean refusing to build. That is a platform fact, not a + // general capability, so it is not generalised. + // + // Rejected here rather than left unimplemented: an unimplemented spelling + // fails somewhere further in with a message about something else. + if (norm->version == "system" && norm->family != "msvc") { + return std::unexpected(std::format( + "'{}@system' is not a thing — mcpp does not build with the " + "machine's own {}.\n" + " A toolchain comes from a payload, so a manifest means the same " + "thing on every machine.\n" + " Name a version instead: `{}@` " + "(`mcpp toolchain list` shows what is available).\n" + " Only `msvc@system` exists, because Visual Studio cannot always " + "be redistributed.", + norm->family, norm->family, norm->family)); + } + ToolchainSpec spec; if (norm->family == "llvm") spec.family = Family::Llvm; else if (norm->family == "msvc") spec.family = Family::Msvc; diff --git a/tests/unit/test_toolchain_msvc.cpp b/tests/unit/test_toolchain_msvc.cpp index 034a9c0e..4bd1b48c 100644 --- a/tests/unit/test_toolchain_msvc.cpp +++ b/tests/unit/test_toolchain_msvc.cpp @@ -84,6 +84,30 @@ TEST(MsvcSpec, SystemOriginIsTheUnversionedSpec) { EXPECT_FALSE(is_system_toolchain(*gcc)); } +TEST(MsvcSpec, OnlyMsvcHasASystemOrigin) { + // xlings depends on the host as little as it can: a toolchain comes from + // a payload, which is what makes "the manifest says 14.44.35207" true on + // every machine rather than on the one that happened to have it. + // `gcc@system` would invite that uncertainty back. + // + // MSVC is the exception because Windows is — Visual Studio is often + // already installed and cannot always be redistributed. A platform fact, + // not a general capability, so it is not generalised. + // + // Rejected rather than merely unimplemented: an unimplemented spelling + // fails later, somewhere else, with a message about something else. + for (auto s : {"gcc@system", "llvm@system", "clang@system"}) { + auto spec = parse_toolchain_spec(s); + ASSERT_FALSE(spec.has_value()) << s << " was accepted"; + // The message has to say what to do instead, or it is just a refusal. + EXPECT_NE(spec.error().find("@"), std::string::npos) + << s << ": " << spec.error(); + EXPECT_NE(spec.error().find("msvc@system"), std::string::npos) + << s << ": " << spec.error(); + } + EXPECT_TRUE(parse_toolchain_spec("msvc@system").has_value()); +} + TEST(MsvcSpec, ToolsetVersionIsAManagedPayloadNotASystemSpec) { // The defect this closes: EVERY msvc spec used to be a system spec, so a // manifest could name a toolset and silently get whatever the machine