Skip to content

Commit d92a1b9

Browse files
committed
platform: one macos/macosx spelling rule, and the manifest asks it
Two names for one platform: mcpp's triple vocabulary says macos, an xpkg descriptor and xlings' project file say macosx. The manifest parser had a second copy of that fold and a second copy of the host key, both hand-rolled beside a rule modules/platform already had. mcpp.platform.axis gains xpkg_platform_key_for, which is now the only place that knows, and TargetPlatform::for_os is written in terms of it rather than repeating the table. The manifest reads it for both directions — canonicalising a written key and folding a requested platform — and takes the host from xpkg_platform instead of its own #if. Both spellings are therefore accepted wherever a platform is named, and a new unit test asserts that the two write the same declaration on every host by resolving explicitly rather than against the one it runs on.
1 parent 84423bd commit d92a1b9

5 files changed

Lines changed: 88 additions & 26 deletions

File tree

docs/05-mcpp-toml.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1909,9 +1909,11 @@ The namespace may be written on either half. Writing it on the key requires
19091909
halves with different values is an error, and so is naming one package twice
19101910
under two spellings.
19111911

1912-
Platform keys are xlings' own — `linux`, `macosx`, `windows` — plus `default`;
1913-
`macos` is accepted as an alias. A table with no key for this host and no
1914-
`default` declares nothing here.
1912+
Platform keys are xlings' own — `linux`, `macosx`, `windows` — plus `default`.
1913+
`macos` and `macosx` are the same platform written in two vocabularies (mcpp's
1914+
triples say one, descriptors and xlings' project file say the other) and both
1915+
are accepted wherever a platform is named. A table with no key for this host
1916+
and no `default` declares nothing here.
19151917

19161918
#### Which version a tool the project did not name resolves to
19171919

docs/zh/05-mcpp-toml.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1629,8 +1629,10 @@ mcpp 既供给它——机器上没有就装,有就映射——也把它物化
16291629
命名空间写在哪一半都可以。写在键上**必须带引号**,因为 TOML 的裸键不能含冒号。
16301630
两半都写且不一致是错误;同一个包用两种拼法出现两次也是错误。
16311631

1632-
平台键是 xlings 自己的 —— `linux``macosx``windows`,外加 `default`;`macos`
1633-
作为别名接受。表里既没有本机这一项也没有 `default`,就表示在这里什么都不声明。
1632+
平台键是 xlings 自己的 —— `linux``macosx``windows`,外加 `default``macos`
1633+
`macosx` 是同一个平台的两套词汇(mcpp 的三元组说前者,描述符与 xlings 的项目
1634+
文件说后者),**凡是点名平台的地方两者都接受**。表里既没有本机这一项也没有
1635+
`default`,就表示在这里什么都不声明。
16341636

16351637
#### 工程没点名的工具,其版本的来源
16361638

modules/manifest/src/toml.cppm

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import mcpp.version_req;
1212
import mcpp.pm.dependency_selector;
1313
import mcpp.pm.index_spec;
1414
import mcpp.platform;
15+
import mcpp.platform.axis; // the one macos/macosx spelling rule
1516

1617
// ⚠️ ANONYMOUS NAMESPACE, AND THIS COST TWO WINDOWS JOBS TO LEARN.
1718
//
@@ -119,15 +120,17 @@ struct LoadContext {
119120
// list. The unresolved declaration is kept beside it in
120121
// `XlingsConfig::workspaceByPlatform`, because the descriptor emitter needs
121122
// every platform at once and cannot re-derive what was already collapsed.
123+
//
124+
// ⚠️ `macos` AND `macosx` ARE ONE PLATFORM, AND THE RULE IS NOT WRITTEN HERE.
125+
// mcpp's triple vocabulary says `macos`; a descriptor and xlings' project file
126+
// say `macosx`. `mcpp::platform::xpkg_platform_key_for` is the one place that
127+
// knows, and `xpkg_platform` is the host in the same vocabulary — an earlier
128+
// draft of this section hand-rolled both, which is the second copy of a rule
129+
// this file exists to avoid.
122130
inline std::string_view host_platform_key() {
123-
if constexpr (mcpp::platform::is_windows) return "windows";
124-
else if constexpr (mcpp::platform::is_macos) return "macosx";
125-
else return "linux";
131+
return mcpp::platform::xpkg_platform;
126132
}
127133

128-
// The three platforms a descriptor has a block for, in xlings' spelling.
129-
inline constexpr std::string_view kXlingsPlatforms[] = {"linux", "macosx", "windows"};
130-
131134
// Split `<scope>:<rest>` on the FIRST colon. xlings writes a namespace this
132135
// way on a version (`"mcpp": "xim:2026.8.30.2"` in a real subos file) and mcpp
133136
// additionally accepts it on the key, so one splitter serves both halves.
@@ -148,15 +151,16 @@ platform_values(const mcpp::libs::toml::Value& v) {
148151
return std::unexpected(std::string(
149152
"expected a string or a { <platform> = \"...\" } table"));
150153
for (auto& [k, val] : v.as_table()) {
151-
std::string canon = (k == "macos") ? "macosx" : k;
152-
const bool known = canon == "default"
153-
|| std::ranges::find(kXlingsPlatforms, canon) != std::ranges::end(kXlingsPlatforms);
154-
if (!known)
154+
auto canon = k == "default"
155+
? std::optional<std::string_view>("default")
156+
: mcpp::platform::xpkg_platform_key_for(k);
157+
if (!canon)
155158
return std::unexpected(std::format(
156-
"unknown platform key '{}'; expected one of linux, macosx, windows, default", k));
159+
"unknown platform key '{}'; expected one of linux, macosx "
160+
"(or macos), windows, default", k));
157161
if (!val.is_string())
158162
return std::unexpected(std::format("platform key '{}' must be a string", k));
159-
out.emplace_back(std::move(canon), val.as_string());
163+
out.emplace_back(std::string(*canon), val.as_string());
160164
}
161165
return out;
162166
}
@@ -166,9 +170,10 @@ platform_values(const mcpp::libs::toml::Value& v) {
166170
inline std::optional<std::string>
167171
value_for_platform(const std::vector<std::pair<std::string, std::string>>& vals,
168172
std::string_view platform) {
169-
// The alias is folded on BOTH sides: a caller may name the host `macos`
170-
// (mcpp's spelling elsewhere) while the stored key is canonical.
171-
const std::string_view want = (platform == "macos") ? "macosx" : platform;
173+
// Folded on BOTH sides: a caller may name the host `macos` (mcpp's triple
174+
// spelling) while the stored key is already canonical.
175+
const std::string_view want =
176+
mcpp::platform::xpkg_platform_key_for(platform).value_or(platform);
172177
auto pick = [&](std::string_view k) -> std::optional<std::string> {
173178
for (auto const& [key, v] : vals) if (key == k) return v;
174179
return std::nullopt;
@@ -1505,7 +1510,7 @@ std::expected<Manifest, ManifestError> parse_string(std::string_view content,
15051510
// Every platform, for the descriptor emitter. Resolved per
15061511
// platform rather than stored raw: the emitter wants the answer,
15071512
// and `default` is part of producing it.
1508-
for (auto plat : kXlingsPlatforms) {
1513+
for (auto plat : mcpp::platform::xpkg_platforms) {
15091514
auto v = value_for_platform(*vals, plat);
15101515
if (!v) continue;
15111516
auto e = make_xlings_entry(k, *v);

modules/platform/src/axis.cppm

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,25 @@ import mcpp.platform;
3232

3333
export namespace mcpp::platform {
3434

35+
// THE spelling rule, and the only one.
36+
//
37+
// One platform has two names: the triple vocabulary says `macos`, an xpkg
38+
// descriptor and xlings' own project file say `macosx`. Every place that has
39+
// to accept both — a target's `os` token, a `[xlings.workspace]` platform
40+
// table, a descriptor's per-OS section — asks here, so the alias cannot be
41+
// half-known. `nullopt` means the token names no platform at all, which the
42+
// caller decides how to treat: `for_os` falls back to the host, a manifest
43+
// parser reports it.
44+
inline std::optional<std::string_view> xpkg_platform_key_for(std::string_view os) {
45+
if (os == "macos" || os == "macosx") return "macosx";
46+
if (os == "windows") return "windows";
47+
if (os == "linux") return "linux";
48+
return std::nullopt;
49+
}
50+
51+
// The three keys a descriptor has a block for, in that vocabulary.
52+
inline constexpr std::string_view xpkg_platforms[] = {"linux", "macosx", "windows"};
53+
3554
// A resolved xpkg platform key. Only obtainable through one of the axis
3655
// types below, so possession of one implies somebody decided which axis it
3756
// came from.
@@ -66,12 +85,11 @@ public:
6685
// "macos" where xpkg descriptors say "macosx"; that translation lives
6786
// here so no caller has to remember it.
6887
static TargetPlatform for_os(std::string_view tripleOs) {
69-
if (tripleOs == "macos" || tripleOs == "macosx") return TargetPlatform("macosx");
70-
if (tripleOs == "windows") return TargetPlatform("windows");
71-
if (tripleOs == "linux") return TargetPlatform("linux");
7288
// Unknown/absent os token: fall back to the host, which is what the
7389
// whole code base did unconditionally before #254.
74-
return TargetPlatform(std::string(mcpp::platform::xpkg_platform));
90+
return TargetPlatform(std::string(
91+
xpkg_platform_key_for(tripleOs)
92+
.value_or(mcpp::platform::xpkg_platform)));
7593
}
7694

7795
// For tooling that walks platforms it is not running on and is not

tests/unit/test_manifest.cpp

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4520,7 +4520,8 @@ deps = [{ linxu = "qemu-user-aarch64" }]
45204520
)");
45214521
ASSERT_FALSE(m.has_value());
45224522
EXPECT_NE(m.error().message.find("linxu"), std::string::npos) << m.error().message;
4523-
EXPECT_NE(m.error().message.find("linux, macosx, windows, default"), std::string::npos)
4523+
EXPECT_NE(m.error().message.find("linux, macosx (or macos), windows, default"),
4524+
std::string::npos)
45244525
<< m.error().message;
45254526
EXPECT_NE(m.error().message.find("deps[0]"), std::string::npos) << m.error().message;
45264527
}
@@ -4706,3 +4707,37 @@ OPENBLAS_NUM_THREADS = "1"
47064707
EXPECT_NE(m.error().message.find("[xlings.envs]"), std::string::npos)
47074708
<< m.error().message;
47084709
}
4710+
4711+
// `macos` and `macosx` are one platform under two vocabularies — mcpp's triple
4712+
// says the first, a descriptor and xlings' project file say the second. The
4713+
// rule lives in mcpp.platform.axis; this asserts the manifest reaches it,
4714+
// on every host, by resolving explicitly rather than against this one.
4715+
TEST(Manifest, XlingsWorkspaceAcceptsBothMacosSpellings) {
4716+
auto with_macos = mcpp::manifest::parse_string(R"(
4717+
[package]
4718+
name = "app"
4719+
version = "0.1.0"
4720+
[xlings.workspace]
4721+
llvm = { macos = "20", default = "22" }
4722+
)");
4723+
ASSERT_TRUE(with_macos.has_value()) << with_macos.error().format();
4724+
auto with_macosx = mcpp::manifest::parse_string(R"(
4725+
[package]
4726+
name = "app"
4727+
version = "0.1.0"
4728+
[xlings.workspace]
4729+
llvm = { macosx = "20", default = "22" }
4730+
)");
4731+
ASSERT_TRUE(with_macosx.has_value()) << with_macosx.error().format();
4732+
// Same platform, so the same per-platform declaration and the same
4733+
// host-resolved answer, whichever way it was written.
4734+
EXPECT_EQ(with_macos->xlings.workspaceByPlatform,
4735+
with_macosx->xlings.workspaceByPlatform);
4736+
EXPECT_EQ(with_macos->xlings.workspace, with_macosx->xlings.workspace);
4737+
EXPECT_NE(std::ranges::find(with_macos->xlings.workspaceByPlatform.at("macosx"),
4738+
"llvm@20"),
4739+
with_macos->xlings.workspaceByPlatform.at("macosx").end());
4740+
EXPECT_NE(std::ranges::find(with_macos->xlings.workspaceByPlatform.at("linux"),
4741+
"llvm@22"),
4742+
with_macos->xlings.workspaceByPlatform.at("linux").end());
4743+
}

0 commit comments

Comments
 (0)