diff --git a/.agents/skills/add-mcpp-index-package/SKILL.md b/.agents/skills/add-mcpp-index-package/SKILL.md index 5bfbc924..7037206f 100644 --- a/.agents/skills/add-mcpp-index-package/SKILL.md +++ b/.agents/skills/add-mcpp-index-package/SKILL.md @@ -139,5 +139,6 @@ rm -rf "tests/examples//target" "tests/examples//.mcpp" # 冷 | 声称实现了 feature 却未做负向验证 | 验证默认构建确实不含该组件 | | 未对齐 CI 的 mcpp 版本,本地通过而 CI 失败 | 读取 `MCPP_VERSION` 并使用同一版本 | | 直接推送 `main` | 切出分支并提交 PR | +| 构建插件把模块命名为 `mcpp.rules.*`、`mcpp.plugins.*` 等保留名,或把命名空间取作 `rules`、`tools` 等 | 模块命名为 `mcpp.<自身命名空间>.*`;保留第二段属于 mcpp 项目(docs/zh/package-types.md §I),成员构建出现引擎的保留名警告即失败 | 完成前应遵循 `verification-before-completion`:在声明“通过/完成”之前,须给出真实命令输出作为证据。 diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index eca1b9f2..0d3ae0ce 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -371,7 +371,13 @@ env: # that never match one target (linux, macos), so no descriptor's flags move. # # `index.toml` min_mcpp does not move: no descriptor grammar changed. - MCPP_VERSION: "2026.9.28.2" + # + # 2026.9.28.3 (mcpp-community/mcpp#734): build programs speak protocol 14, + # so every member's build program re-runs once; mcpp.plugins 0.17.0 states it + # as its floor, and tests/examples/mcpp-plugins builds against it. Engine + # warnings the members may newly print: W1 to W3 of SPEC-008 (the library + # interface), warnings only. + MCPP_VERSION: "2026.9.28.3" jobs: lint: @@ -422,6 +428,14 @@ jobs: if ! lua5.4 tests/check_package_name.lua "$f"; then fail=1 fi + # 5b. The build-plugin naming rule (mcpp#734): a namespace may not be + # one of the reserved second segments of `mcpp.` (core, plugins, + # deps, rules, dist, tools), which name the mcpp project's own + # modules. The module names themselves are checked where members + # build (tests/run_members.sh), by the engine's own warning. + if ! lua5.4 tests/check_reserved_namespace.lua "$f"; then + fail=1 + fi # 6. c++fly admission policy (mcpp design 2026-07-14 §11-Q2, v1): # c++fly means "toolchain's latest level + every experimental # gate" — deliberately toolchain-dependent, so a published diff --git a/docs/package-types.md b/docs/package-types.md index d6077676..9a514792 100644 --- a/docs/package-types.md +++ b/docs/package-types.md @@ -294,6 +294,38 @@ Four things to get right: The matching member is `tests/examples/protobuf-protoc`, and it is the complement of `tests/examples/protobuf`: that one deliberately uses no generated code, this one is generated code end to end. +## I. Build-plugin package (`mcpp.plugins`) + +A build plugin is a package whose modules a consumer's `build.mcpp` imports: its +units import `mcpp.core` (also spelled `mcpp`) and reach a consumer through a +`[build-dependencies]` edge with `host-module = true`. Three rules admit one to +this index (mcpp#734): + +- **Module names.** A plugin names its build-program modules + `mcpp..*` after its own namespace (`mcpp.acme.protobuf`). The second + segments `core`, `plugins`, `deps`, `rules`, `dist` and `tools` belong to the + mcpp project. The engine warns when another package uses them (mcpp SPEC-007 + §9); a member whose build prints that warning fails `tests/run_members.sh`, + and the lint refuses a namespace spelled like one of them + (`tests/check_reserved_namespace.lua`). Modules the package also offers to + programs follow the library rule, `..*` (mcpp SPEC-008 I3). +- **The manifest.** The official general library is reached through the feature + `plugins-core`. A plugin built on it depends on `mcpp.plugins` with + `host-module = true` and `features = ["plugins-core"]`, and adds + `reexport = true` when its consumers' build programs import the library + themselves: + + ```toml + [build-dependencies.mcpp] + plugins = { version = "0.17.0", features = ["plugins-core"], host-module = true, reexport = true } + ``` +- **The engine floor.** The package states the first mcpp release it needs, + `[package] mcpp = ">="`; an older engine stops and names the upgrade. + A plugin that reads the build information (the resolved tools, `mcpp::report`) + needs 2026.9.28.3. + +The matching member is `tests/examples/mcpp-plugins`. + --- ## The minimal project (`tests/examples//`) diff --git a/docs/repository-and-schema.md b/docs/repository-and-schema.md index 10ec679c..aabef946 100644 --- a/docs/repository-and-schema.md +++ b/docs/repository-and-schema.md @@ -18,6 +18,7 @@ tests/examples// one test project per library (a workspace member; < tests/*.cpp behavioral assertions (standalone main; a non-zero exit code is a failure) tests/check_mirror_urls.lua lint: GLOBAL+CN table completeness, and that CN points at mcpp-res tests/check_package_name.lua lint: identity shape (name is a single atomic segment, hierarchy belongs to namespace) +tests/check_reserved_namespace.lua lint: a namespace is not a reserved second segment of `mcpp.` (core, plugins, deps, rules, dist, tools) tests/list_cn_urls.lua extracts the CN urls for mirror-cn-reachable tests/run_members.sh runs workspace members one at a time and times each. The entry point used both by CI and locally; see "Running workspace members locally" below @@ -164,7 +165,7 @@ locally with `mcpp xpkg parse pkgs//.lua`. - `env.MCPP_VERSION` is the mcpp version every job uses; local verification should match it. - `lint` (always runs): lua syntax via `loadfile(f,'t')`; `spec=`/`name=`/`xpm=` must be present; leading-v versions are rejected; runs `check_mirror_urls.lua`; runs `check_package_name.lua` (identity shape, see "Package identity" - above); then runs `mcpp xpkg parse` over every descriptor with the mcpp version CI pins (strict — an unknown key + above); runs `check_reserved_namespace.lua` (the build-plugin naming rule, docs/package-types.md §I); then runs `mcpp xpkg parse` over every descriptor with the mcpp version CI pins (strict — an unknown key fails). `xpkg parse` in mcpp >= 0.0.106 enforces the identity shape itself, which makes the lua lint an earlier and cheaper redundant gate. - `mirror-cn-reachable` (always runs): `curl`s each CN url; all must return 200. @@ -227,6 +228,7 @@ for f in pkgs/*/*.lua; do grep -nqE '\["v[0-9]+|\["[^"]+"\][[:space:]]*=[[:space:]]*"v[0-9]+' "$f" && { echo "LEADING-V $f"; fail=1; } lua5.4 tests/check_mirror_urls.lua "$f" >/dev/null 2>&1 || { echo "MIRROR $f"; fail=1; } lua5.4 tests/check_package_name.lua "$f" || fail=1 + lua5.4 tests/check_reserved_namespace.lua "$f" || fail=1 done [ $fail -eq 0 ] && echo "ALL LINT PASS" ``` diff --git a/docs/zh/package-types.md b/docs/zh/package-types.md index 8352e3dc..f2106b83 100644 --- a/docs/zh/package-types.md +++ b/docs/zh/package-types.md @@ -272,6 +272,30 @@ protobuf 最经典的坑——在这里**语法上无法表达**。`mcpp build - 对应的成员是 `tests/examples/protobuf-protoc`,它与 `tests/examples/protobuf` 互为补集:那个刻意**不用** 任何生成代码,这个从头到尾都是生成代码。 +## I. 构建插件包(`mcpp.plugins`) + +构建插件是其模块由消费方 `build.mcpp` 导入的包:它的单元导入 `mcpp.core`(也写作 `mcpp`), +经带 `host-module = true` 的 `[build-dependencies]` 边到达消费方。进入本索引需满足三条规则 +(mcpp#734): + +- **模块名。** 插件以自身命名空间为其构建程序模块命名:`mcpp..*`(例如 + `mcpp.acme.protobuf`)。第二段 `core`、`plugins`、`deps`、`rules`、`dist`、`tools` 属于 mcpp + 项目。其他包使用它们时引擎给出警告(mcpp SPEC-007 §9);构建输出含该警告的成员在 + `tests/run_members.sh` 中失败,lint 拒绝与之同名的命名空间(`tests/check_reserved_namespace.lua`)。 + 包同时提供给程序的模块遵循库的规则 `..*`(mcpp SPEC-008 I3)。 +- **清单。** 官方通用库经 feature `plugins-core` 提供。基于它的插件以 `host-module = true` 与 + `features = ["plugins-core"]` 依赖 `mcpp.plugins`;其消费方的构建程序也直接导入该库时,再加 + `reexport = true`: + + ```toml + [build-dependencies.mcpp] + plugins = { version = "0.17.0", features = ["plugins-core"], host-module = true, reexport = true } + ``` +- **引擎下限。** 包以 `[package] mcpp = ">="` 写明所需的最早 mcpp 版本;更旧的引擎停止并 + 写出升级方式。读取构建信息(解析出的工具、`mcpp::report`)的插件需要 2026.9.28.3。 + +对应的成员是 `tests/examples/mcpp-plugins`。 + --- ## 最小工程(`tests/examples//`) diff --git a/docs/zh/repository-and-schema.md b/docs/zh/repository-and-schema.md index 426bbd41..86d52fa4 100644 --- a/docs/zh/repository-and-schema.md +++ b/docs/zh/repository-and-schema.md @@ -17,6 +17,7 @@ tests/examples// 每库测试工程(workspace 成员; 为包 tests/*.cpp 行为断言(独立 main,退出码非 0 即失败) tests/check_mirror_urls.lua lint:GLOBAL+CN 表完整性,以及 CN 指向 mcpp-res tests/check_package_name.lua lint:身份形态(name 为单一原子段,层级归 namespace) +tests/check_reserved_namespace.lua lint:命名空间不得是 `mcpp.` 的保留第二段(core、plugins、deps、rules、dist、tools) tests/list_cn_urls.lua 抽取 CN url,供 mirror-cn-reachable 使用 tests/run_members.sh 逐个运行 workspace 成员并计时。CI 与本地共用的入口,见下文「本地运行 workspace 成员」 tests/plan_shards.lua 依实测耗时将成员分配到各分片。由 `select` job 调用一次,`run_members.sh --shard` @@ -125,7 +126,7 @@ mcpp 跑 `xpkg parse`(strict:未知键即失败),所以需要更新文法/键的 条件下比较,代价是显著更慢。 - `env.MCPP_VERSION` 为全部 job 使用的 mcpp 版本,本地验证应与之对齐。 - `lint`(始终运行):lua 语法 `loadfile(f,'t')`;须含 `spec=`/`name=`/`xpm=`;禁止前导 v 版本;执行 - `check_mirror_urls.lua`;执行 `check_package_name.lua`(身份形态,见上文「包身份」);再用 CI pin 的 + `check_mirror_urls.lua`;执行 `check_package_name.lua`(身份形态,见上文「包身份」);执行 `check_reserved_namespace.lua`(构建插件命名规则,见 package-types §I);再用 CI pin 的 mcpp 对每个描述符跑 `mcpp xpkg parse`(strict,未知键即失败)。mcpp ≥ 0.0.106 的 `xpkg parse` 自身 也强制身份形态,lua lint 因此是更早、更便宜的冗余闸门。 - `mirror-cn-reachable`(始终运行):逐个 `curl` CN url,均须返回 200。 @@ -179,6 +180,7 @@ for f in pkgs/*/*.lua; do grep -nqE '\["v[0-9]+|\["[^"]+"\][[:space:]]*=[[:space:]]*"v[0-9]+' "$f" && { echo "LEADING-V $f"; fail=1; } lua5.4 tests/check_mirror_urls.lua "$f" >/dev/null 2>&1 || { echo "MIRROR $f"; fail=1; } lua5.4 tests/check_package_name.lua "$f" || fail=1 + lua5.4 tests/check_reserved_namespace.lua "$f" || fail=1 done [ $fail -eq 0 ] && echo "ALL LINT PASS" ``` diff --git a/index.toml b/index.toml index c04d0082..3feb98ba 100644 --- a/index.toml +++ b/index.toml @@ -120,7 +120,13 @@ # Moved in the pull request that moves validate.yml's pin to 2026.9.28.2, # which runs every member on every platform with it. The release changes no # descriptor grammar. +# ── 2026-09-28: latest_mcpp -> 2026.9.28.3, min_mcpp unchanged ───────────── +# +# Moved in the pull request that moves validate.yml's pin to 2026.9.28.3 and +# registers mcpp.plugins 0.17.0, whose package floor it is. The release changes +# no descriptor grammar; its `[package] mcpp` key is manifest vocabulary, which +# an older engine reports as unsupported and ignores. [index] spec = "1" min_mcpp = "2026.9.18.3" -latest_mcpp = "2026.9.28.2" +latest_mcpp = "2026.9.28.3" diff --git a/mcpp.toml b/mcpp.toml index cb71190f..5f87eae6 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -71,6 +71,7 @@ members = [ "tests/examples/imgui-window", "tests/examples/jwt-cpp", "tests/examples/marzer.tomlplusplus", + "tests/examples/mcpp-plugins", "tests/examples/mio", "tests/examples/libmysqlclient", "tests/examples/mysql-connector-cpp", diff --git a/pkgs/m/mcpp.plugins.lua b/pkgs/m/mcpp.plugins.lua index a633572b..1d39bcdd 100644 --- a/pkgs/m/mcpp.plugins.lua +++ b/pkgs/m/mcpp.plugins.lua @@ -2,12 +2,23 @@ -- each member selected by a feature. -- -- [dependencies.mcpp] --- plugins = { version = "0.16.0", features = ["rules-spirv"], host-module = true } +-- plugins = { version = "0.17.0", features = ["rules-spirv"], host-module = true } -- -- // build.mcpp -- import mcpp; -- import mcpp.rules.spirv; -- +-- 0.17.0 (package floor 2026.9.28.3, `[package] mcpp`): the general library +-- for build programs behind `plugins-core` (renamed from `surface`, kept as an +-- alias until 2027-03-28) -- `mcpp.plugins.toolset`, `mcpp.plugins.fs` -- and +-- the test kit `mcpp.plugins.testing` behind `plugins-testing`. deps-vcpkg and +-- deps-cmake build with the toolset mcpp resolved: a Visual Studio instance is +-- selected with VCPKG_VISUAL_STUDIO_PATH (the ABI hash is unchanged), a managed +-- MSVC toolset and the clang rows are named in a derived triplet +-- `-mcpp-`, and the Linux GCC row keeps vcpkg's detection; the C +-- runtime follows the program's contract. mcpp-community/mcpp#734, +-- mcpp-community/mcpp-plugins#37. +-- -- 0.16.0 (mcpp floor 2026.9.27.1 for `rules-qt`): the Linux C++ runtime a -- Qt program needs is stated in the host rows `[target.x86_64-linux-gnu]` and -- `[target.aarch64-linux-gnu]`, which a plain `mcpp build` now applies @@ -608,6 +619,13 @@ package = { xpm = { linux = { + ["0.17.0"] = { + url = { + GLOBAL = "https://github.com/mcpp-community/mcpp-plugins/archive/refs/tags/v0.17.0.tar.gz", + CN = "https://gitcode.com/mcpp-res/mcpp-plugins/releases/download/0.17.0/mcpp-plugins-0.17.0.tar.gz", + }, + sha256 = "20e9fc467c83dce33fbb706f5373eeb14eb0880c1e08e9c2c30594147875c57e", + }, ["0.16.0"] = { url = { GLOBAL = "https://github.com/mcpp-community/mcpp-plugins/archive/refs/tags/v0.16.0.tar.gz", @@ -846,9 +864,16 @@ package = { }, sha256 = "adf1f9d6691a5d05a8a4a94e83c733ea39caee1510ce2c9af4cb23bebabea9f5", }, - ["latest"] = { ref = "0.16.0" }, + ["latest"] = { ref = "0.17.0" }, }, macosx = { + ["0.17.0"] = { + url = { + GLOBAL = "https://github.com/mcpp-community/mcpp-plugins/archive/refs/tags/v0.17.0.tar.gz", + CN = "https://gitcode.com/mcpp-res/mcpp-plugins/releases/download/0.17.0/mcpp-plugins-0.17.0.tar.gz", + }, + sha256 = "20e9fc467c83dce33fbb706f5373eeb14eb0880c1e08e9c2c30594147875c57e", + }, ["0.16.0"] = { url = { GLOBAL = "https://github.com/mcpp-community/mcpp-plugins/archive/refs/tags/v0.16.0.tar.gz", @@ -1087,9 +1112,16 @@ package = { }, sha256 = "adf1f9d6691a5d05a8a4a94e83c733ea39caee1510ce2c9af4cb23bebabea9f5", }, - ["latest"] = { ref = "0.16.0" }, + ["latest"] = { ref = "0.17.0" }, }, windows = { + ["0.17.0"] = { + url = { + GLOBAL = "https://github.com/mcpp-community/mcpp-plugins/archive/refs/tags/v0.17.0.tar.gz", + CN = "https://gitcode.com/mcpp-res/mcpp-plugins/releases/download/0.17.0/mcpp-plugins-0.17.0.tar.gz", + }, + sha256 = "20e9fc467c83dce33fbb706f5373eeb14eb0880c1e08e9c2c30594147875c57e", + }, ["0.16.0"] = { url = { GLOBAL = "https://github.com/mcpp-community/mcpp-plugins/archive/refs/tags/v0.16.0.tar.gz", @@ -1328,7 +1360,7 @@ package = { }, sha256 = "adf1f9d6691a5d05a8a4a94e83c733ea39caee1510ce2c9af4cb23bebabea9f5", }, - ["latest"] = { ref = "0.16.0" }, + ["latest"] = { ref = "0.17.0" }, }, }, diff --git a/tests/check_reserved_namespace.lua b/tests/check_reserved_namespace.lua new file mode 100644 index 00000000..ce19663b --- /dev/null +++ b/tests/check_reserved_namespace.lua @@ -0,0 +1,38 @@ +-- Lint `package.namespace` against the build-plugin naming rule (mcpp#734; +-- mcpp SPEC-007 §9, "plugin names"). +-- +-- A build plugin names its modules `mcpp..*` after its own +-- package's namespace, and the second segments `core`, `plugins`, `deps`, +-- `rules`, `dist` and `tools` of `mcpp.` belong to the mcpp project: `mcpp.core` +-- is the engine's interface, the others are the families of mcpp-plugins. A +-- namespace spelled like one of them would make a third party's +-- `mcpp..*` indistinguishable from an official family, so the index +-- refuses such a namespace. The engine warns on the module names themselves; +-- the index can refuse, because it knows which namespaces are registered. +-- +-- Usage: lua5.4 tests/check_reserved_namespace.lua + +function import(...) + return setmetatable({}, {__index = function() return function() end end}) +end + +local path = assert(arg[1], "usage: check_reserved_namespace.lua ") +package = nil +local chunk = assert(loadfile(path, "t")) +chunk() + +local p = package +if type(p) ~= "table" then os.exit(0) end +local ns = p.namespace +if type(ns) ~= "string" then os.exit(0) end + +local reserved = { core = true, plugins = true, deps = true, rules = true, dist = true, tools = true } +local first = ns:match("^([^.]+)") +if first and reserved[first] then + io.stderr:write(string.format( + "::error file=%s::namespace '%s' is reserved: `mcpp.%s.*` names modules of the mcpp project " .. + "(mcpp SPEC-007 §9), so a plugin of this namespace could not be told from an official one\n", + path, ns, first)) + os.exit(1) +end +os.exit(0) diff --git a/tests/examples/mcpp-plugins/build.mcpp b/tests/examples/mcpp-plugins/build.mcpp new file mode 100644 index 00000000..a645bbf7 --- /dev/null +++ b/tests/examples/mcpp-plugins/build.mcpp @@ -0,0 +1,14 @@ +import std; +import mcpp; +import mcpp.tools.embed; +import mcpp.plugins.fs; + +int main() { + mcpp::tools::embed::options opt; + opt.name_space = "fixture"; + opt.null_terminate = true; + // L2: a generated file keeps its time stamp while its content is unchanged. + const std::filesystem::path stamp = std::filesystem::path(mcpp::out_dir()) / "l2-probe.txt"; + mcpp::plugins::fs::write_if_changed(stamp, "mcpp.plugins.fs\n"); + return mcpp::tools::embed::file("data/message.txt", opt) ? 0 : 1; +} diff --git a/tests/examples/mcpp-plugins/data/message.txt b/tests/examples/mcpp-plugins/data/message.txt new file mode 100644 index 00000000..2a9bad68 --- /dev/null +++ b/tests/examples/mcpp-plugins/data/message.txt @@ -0,0 +1 @@ +hello from mcpp.plugins \ No newline at end of file diff --git a/tests/examples/mcpp-plugins/mcpp.toml b/tests/examples/mcpp-plugins/mcpp.toml new file mode 100644 index 00000000..cce5ea78 --- /dev/null +++ b/tests/examples/mcpp-plugins/mcpp.toml @@ -0,0 +1,16 @@ +# mcpp.plugins consumer: the official plugin package, resolved from this +# checkout, reached from a build program. `tools-embed` needs no payload, so +# the member runs on every platform; its build program imports L3 +# (`mcpp.tools.embed`) and L2 (`mcpp.plugins.fs`, through `plugins-core`, which +# every member implies). The member is also the positive leg of the naming rule +# tests/run_members.sh enforces: namespace `mcpp` provides `mcpp.tools.*` and +# `mcpp.plugins.*` without the engine's reserved-name warning. +[package] +name = "mcpp-plugins-tests" +version = "0.1.0" + +[indices] +mcpp = { path = "../../.." } + +[build-dependencies.mcpp] +plugins = { version = "0.17.0", features = ["tools-embed"], host-module = true } diff --git a/tests/examples/mcpp-plugins/tests/embed.cpp b/tests/examples/mcpp-plugins/tests/embed.cpp new file mode 100644 index 00000000..99464c49 --- /dev/null +++ b/tests/examples/mcpp-plugins/tests/embed.cpp @@ -0,0 +1,12 @@ +#include +#include +#include +#include "message_txt.h" + +int main() { + const char* text = reinterpret_cast(fixture::message_txt); + if (fixture::message_txt_size != std::strlen("hello from mcpp.plugins")) return 1; + if (std::strcmp(text, "hello from mcpp.plugins") != 0) return 1; + std::puts("mcpp-plugins-tests: the embedded file reached the program"); + return 0; +} diff --git a/tests/run_members.sh b/tests/run_members.sh index d6d3a669..a6c36de3 100755 --- a/tests/run_members.sh +++ b/tests/run_members.sh @@ -118,10 +118,23 @@ rows=$(mktemp) trap 'rm -f "$rows"' EXIT rc=0 +# THE BUILD-PLUGIN NAMING RULE (mcpp#734). The engine warns when a package +# provides a build-program module under a reserved second segment of `mcpp.` +# (`mcpp.rules.*`, `mcpp.plugins.*`, ...) without being in namespace `mcpp` +# (mcpp SPEC-007 §9). The index admits no such package: a member whose build +# prints that warning fails. One rule, stated once, in the engine. +naming_rule='which belongs to the modules maintained by the mcpp project' +log=$(mktemp) +trap 'rm -f "$rows" "$log"' EXIT + for m in "${members[@]}"; do echo "::group::mcpp test -p $m" t0=$(date +%s) - if "$MCPP" test -p "$m"; then status=ok; else status=FAIL; rc=1; fi + if "$MCPP" test -p "$m" 2>&1 | tee "$log"; [ "${PIPESTATUS[0]}" -eq 0 ]; then status=ok; else status=FAIL; rc=1; fi + if grep -q "$naming_rule" "$log"; then + echo "::error::member $m builds a plugin whose module names a reserved segment of mcpp. (mcpp SPEC-007 §9)" + status=FAIL; rc=1 + fi t1=$(date +%s) echo "::endgroup::" printf '%s\t%s\t%s\n' "$((t1 - t0))" "$m" "$status" >> "$rows"