Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/11-machine-output.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,13 @@ mcpp xpkg parse <file.lua> --format json

`data` is the same document `--json` prints bare.

A descriptor whose `mcpp` field is an inline table yields the full document:
`namespace`, `name`, `versions`, `standard`, `import_std`, `sources`,
`include_dirs`, `generated_files`, `generated_contents`, `targets`,
`unknown_keys`. A descriptor without an inline table reports `"form": "A"`
in place of the build information. Both forms carry `versions` — the per-OS
version keys of the descriptor's `xpm` tables.

### `mcpp.cache` — the global build cache

```
Expand Down
6 changes: 6 additions & 0 deletions docs/zh/11-machine-output.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,12 @@ mcpp xpkg parse <file.lua> --format json

`data` 就是 `--json` 裸打印的那份文档。

`mcpp` 字段为内联表的描述符产出完整文档:`namespace`、`name`、`versions`、
`standard`、`import_std`、`sources`、`include_dirs`、`generated_files`、
`generated_contents`、`targets`、`unknown_keys`。没有内联表的描述符以
`"form": "A"` 代替构建信息。两种形态都携带 `versions` —— 描述符各平台
`xpm` 表的版本键。

### `mcpp.cache` —— 全局构建缓存

```
Expand Down
58 changes: 39 additions & 19 deletions src/cli/cmd_xpkg.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,32 @@ std::string json_array(const std::vector<std::string>& v) {
return out + "]";
}

// The per-OS xpm version lists, in the payload shape both forms share:
// {"linux":[...],"macosx":[...],"windows":[...]}. Form A emits it too —
// the tables are computed before the form branch either way, and dropping
// them there is what made envelope consumers read Form A descriptors as
// version-less (mcpp-vscode#8).
std::string versions_json(
const std::map<std::string, std::vector<std::string>>& versions) {
std::string out = "{";
bool first = true;
for (auto& [plat, v] : versions) {
if (!first) out += ",";
first = false;
out += "\"" + plat + "\":" + json_array(v);
}
return out + "}";
}

std::string joined_versions(const std::vector<std::string>& v) {
std::string joined;
for (std::size_t i = 0; i < v.size(); ++i) {
if (i) joined += ", ";
joined += v[i];
}
return joined;
}

} // namespace

export int cmd_xpkg_parse(const mcpplibs::cmdline::ParsedArgs& parsed) {
Expand Down Expand Up @@ -164,14 +190,22 @@ export int cmd_xpkg_parse(const mcpplibs::cmdline::ParsedArgs& parsed) {

// Form A descriptors carry no `mcpp = {}` table — build info comes
// from the fetched source's own mcpp.toml. Nothing further to parse.
// The xpm version tables are independent of that segment and are the
// resolver's source of truth, so both spellings publish them here.
auto field = mcpp::manifest::extract_mcpp_field(lua);
if (field.kind != mcpp::manifest::McppField::TableBody) {
if (asJson) {
emit_xpkg(std::format(
"{{\"namespace\":\"{}\",\"name\":\"{}\",\"form\":\"A\"}}",
json_escape(id.ns), json_escape(id.name)), enveloped);
"{{\"namespace\":\"{}\",\"name\":\"{}\",\"versions\":{},"
"\"form\":\"A\"}}",
json_escape(id.ns), json_escape(id.name),
versions_json(versions)), enveloped);
} else {
std::println("package {} (namespace '{}')", fqn, id.ns);
for (auto& [plat, v] : versions) {
if (v.empty()) continue;
std::println("versions {:<8} {}", plat, joined_versions(v));
}
std::println("form A — no mcpp segment (build info from the "
"source's mcpp.toml)");
std::println("parse OK");
Expand Down Expand Up @@ -254,16 +288,6 @@ export int cmd_xpkg_parse(const mcpplibs::cmdline::ParsedArgs& parsed) {
targets += "\"" + json_escape(m->targets[i].name) + "\"";
}
targets += "]";
std::string vers = "{";
{
bool f2 = true;
for (auto& [plat, v] : versions) {
if (!f2) vers += ",";
f2 = false;
vers += "\"" + plat + "\":" + json_array(v);
}
}
vers += "}";
std::string genContents = "{";
{
bool f3 = true;
Expand All @@ -281,7 +305,8 @@ export int cmd_xpkg_parse(const mcpplibs::cmdline::ParsedArgs& parsed) {
"\"include_dirs\":{},\"generated_files\":{},"
"\"generated_contents\":{},\"targets\":{},"
"\"unknown_keys\":{}}}",
json_escape(id.ns), json_escape(id.name), vers,
json_escape(id.ns), json_escape(id.name),
versions_json(versions),
json_escape(m->package.standard),
m->language.importStd ? "true" : "false",
json_array(m->modules.sources),
Expand All @@ -299,12 +324,7 @@ export int cmd_xpkg_parse(const mcpplibs::cmdline::ParsedArgs& parsed) {
std::println("package {} (namespace '{}')", fqn, id.ns);
for (auto& [plat, v] : versions) {
if (v.empty()) continue;
std::string joined;
for (std::size_t i = 0; i < v.size(); ++i) {
if (i) joined += ", ";
joined += v[i];
}
std::println("versions {:<8} {}", plat, joined);
std::println("versions {:<8} {}", plat, joined_versions(v));
}
std::println("standard {} import_std={}", m->package.standard,
m->language.importStd);
Expand Down
42 changes: 42 additions & 0 deletions tests/e2e/93_xpkg_parse.sh
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,46 @@ if "$MCPP" xpkg parse broken.lua > /dev/null 2>&1; then
echo "FAIL: broken descriptor should fail"; exit 1
fi

# ── 5. Form A descriptor (no inline `mcpp` segment): versions still ship ──
# Form A = the descriptor carries no `mcpp = {}` table (build info comes from
# the fetched source's own mcpp.toml). The xpm version tables are still the
# resolver's source of truth, so both spellings must publish them — dropping
# already-computed versions here is what made the vscode completion layer
# treat 18/81 index descriptors as version-less (mcpp-vscode#8).
cat > forma.lua <<'EOF'
package = {
spec = "1",
namespace = "demo",
name = "demo.forma",
xpm = {
linux = { ["0.9.0"] = { url = "u", sha256 = "h" } },
macosx = { ["0.9.0"] = { url = "u", sha256 = "h" },
["1.0.0"] = { url = "u2", sha256 = "h2" } },
windows = { ["1.0.0"] = { url = "u", sha256 = "h" } },
},
}
EOF

"$MCPP" xpkg parse forma.lua | tee forma.out
grep -q "form A" forma.out
grep -q "versions linux" forma.out
grep -q "1.0.0" forma.out
grep -q "parse OK" forma.out

"$MCPP" xpkg parse --json forma.lua > forma.json
"$MCPP" xpkg parse --format json forma.lua > forma.env.json
python3 - <<'PY'
import json
j = json.load(open("forma.json"))
assert j["namespace"] == "demo" and j["name"] == "forma", j
assert j["form"] == "A", j
assert sorted(j["versions"]["linux"]) == ["0.9.0"], j
assert sorted(j["versions"]["macosx"]) == ["0.9.0", "1.0.0"], j
assert sorted(j["versions"]["windows"]) == ["1.0.0"], j
e = json.load(open("forma.env.json"))
assert e["kind"] == "mcpp.xpkg", e
assert sorted(e["data"]["versions"]["macosx"]) == ["0.9.0", "1.0.0"], e
print("form a ok")
PY

echo "PASS 93_xpkg_parse"
Loading