diff --git a/.agents/docs/2026-09-05-heterogeneous-build-ecosystem-design-v2.md b/.agents/docs/2026-09-05-heterogeneous-build-ecosystem-design-v2.md index db6a699f..834dd998 100644 --- a/.agents/docs/2026-09-05-heterogeneous-build-ecosystem-design-v2.md +++ b/.agents/docs/2026-09-05-heterogeneous-build-ecosystem-design-v2.md @@ -333,7 +333,7 @@ Status is one of `done`, `open`, `deferred (reason)`. | I4 | CI green; merge; index artifact published | `Publish Index Artifact` green on the merge commit | I1–I3 | done: PR #349 green (10 pass; the `full sweep` job skips by design), merged as 754d775, `Publish Index Artifact` green on it (run 33968256961) | | I5 | `compat.vulkan-runtime` 2026.09.06: the payload set is declared as `xpm.linux.deps.runtime` rather than discovered in the store; `PAYLOAD_PACKAGES` maps each soname to the package declared for it and reports a declaration that did not take effect; both adapters refuse a payload built for another machine (ELF `e_machine`); the Vulkan adapter gains the aarch64 multiarch directories | a fresh environment substitutes the same set a developer machine does; the report contains no line saying a declaration did not take effect | I4, X5 | done: merged as 0bbf3ad, index artifact published; measured in a fresh subos, 24 payload substitutions against 1, and four host sonames without a payload against thirty | -| I6 | Both adapters 2026.09.07: a soname carried by more than one installed payload is decided by symbol coverage rather than by which store path sorts last; `compat.vulkan` and `compat.opencl` move their pins | in a fresh subos, `libX11.so.6` comes from `xim:libX11` rather than staying on the host with `xim:mesa-lavapipe`'s bundled copy named as the reason | I5 | PR #351 | +| I6 | Both adapters 2026.09.07: a soname carried by more than one installed payload is decided by symbol coverage rather than by which store path sorts last; `compat.vulkan` and `compat.opencl` move their pins | in a fresh subos, `libX11.so.6` comes from `xim:libX11` rather than staying on the host with `xim:mesa-lavapipe`'s bundled copy named as the reason | I5 | done: merged as f15d4fe and published; 25 substitutions against 24, and `libdrm_amdgpu` moved to a payload copy that covers the host's where the dedicated package's did not | #### xim-pkgindex (PR #762) @@ -353,21 +353,31 @@ Status is one of `done`, `open`, `deferred (reason)`. | M7 | The fast path compares every declared build-program input, not only glob path sets | e2e 612: editing a file declared with `rerun_if_changed` changes what the binary prints, and an unchanged input still takes the fast path | — | done: verified failing on 2026.9.5.3 and passing on this branch | | M8 | A version conflict on a package with no named C++ module names both versions and both requesters | the message example 10 produced now says which two versions are in the graph | — | done | | M9 | The markers removed from `bench/`, `tools/`, `scripts/`, `mcpp.toml` and `README.zh-CN.md`; the Chinese target table gains the row and the words its English counterpart has | a sweep over the emoji ranges returns nothing outside program output | — | done | -| M10 | Release 2026.9.5.4, mirror, index bump, bootstrap pin | both mirrors return 200 and identical bytes; xim-pkgindex names it as `latest` | M7-M9 | open | +| M10 | Release 2026.9.5.4, mirror, index bump, bootstrap pin | both mirrors return 200 and identical bytes; xim-pkgindex names it as `latest` | M7-M9 | done: release run 33979414813 green on all four platform jobs; the four archives compare byte for byte between GitHub and GitCode; openxlings/xim-pkgindex#764 merged (7fd026e) with `Publish Index Artifact` green; bootstrap pin bumped on main | #### mcpp-plugins, second round (PR #2, version 0.1.1) | # | task | criterion | depends on | status | |---|---|---|---|---| | P4 | `mcpp.tools.embed`, the first member of the tools half, selected by `tools-embed` | the fixture activating only that feature embeds a data file, and its second run proves an edit reaches the binary | M7 | done, CI pending on the 2026.9.5.4 release | -| P5 | The markers removed from the rule sources; version 0.1.1; release and mirror; index descriptor; the two examples move their pin | the same two URLs return 200 with one sha256 | P4, M10 | open | +| P5 | The markers removed from the rule sources; version 0.1.1; release and mirror; index descriptor; the two examples move their pin | the same two URLs return 200 with one sha256 | P4, M10 | done: PR #2 merged (f1bec00), tag v0.1.1, GitHub release, GitCode mirror byte-identical at 30,644 bytes; mcpp-index#352 merged (d4b36d7) and published; the examples and both manifest chapters pin 0.1.1 in mcpp#571 | #### Verification | # | task | criterion | depends on | status | |---|---|---|---|---| -| V1 | Fresh sandbox (`xlings subos --sandbox --cmd`), CN mirror configured for both mcpp and xlings: install mcpp 2026.9.5.3, build examples 09 and 10 against `mcpp:plugins`, run 10 on the lavapipe payload, run the OpenCL probe on pocl | `12 24 36 48` from example 10 with no GPU; the pocl platform enumerated; every declared payload substitution took effect (see 6.4 for why an empty host surface is not the criterion) | M6, P3, I4, X5, I5 | open | -| V2 | The same on this host with the GPU: example 10 on the host ICD, example 09 on both routes | `12 24 36 48` in every case; the host entries of `HOST-SURFACE.txt` are proprietary userspace, host Mesa drivers, and the recorded symbol-set and soname gaps only | V1 | open | +| V1 | Fresh subos created for this run, CN mirror configured for both tools: install mcpp 2026.9.5.4 from the index, build examples 09 and 10 against `mcpp:plugins` 0.1.1, run 10 on the lavapipe payload, run the OpenCL probe on pocl | `12 24 36 48` from example 10 with no GPU; the pocl platform enumerated; every declared payload substitution took effect (see 6.4 for why an empty host surface is not the criterion) | M10, P5, I6, X5 | **done, green.** A machine that had never seen this ecosystem: `mcpp 2026.9.5.4` from the store path, both mirrors CN, index snapshot `d4b36d7`, `mcpp.rules.spirv` from the collection produced a SPIR-V header, example 10 answered `12 24 36 48` on the lavapipe payload and again with `--no-accel`, **25 payload substitutions against 6 host entries with every declared payload in effect**, example 09 answered on the CPU variant and compiled its device variant, and the Khronos loader listed `Portable Computing Language` through `OCL_ICD_FILENAMES` | +| V2 | The same on this host with the GPU: example 10 on the host ICD, example 09 on both routes | `12 24 36 48` in every case; the host entries of `HOST-SURFACE.txt` are proprietary userspace, host Mesa drivers, and the recorded symbol-set and soname gaps only | V1 | **done, green (0 assertions failed).** Same five identity readings, example 10 on the payload driver and with `--no-accel`, the farm written by 2026.09.07 with 25 substitutions and 6 host entries, example 09 `12 24 36 48` **on the RTX 4080**, and the OpenCL loader listing `NVIDIA CUDA` and then pocl beside it | + +### 6.5 What the two runs corrected in the harness itself + +Three assertions failed on something other than the ecosystem, and each is the same shape: a criterion pointed at the wrong object. + +**The mirror was read from the tool's stdout.** `xlings config` renders its banner through a ui layer that prints nothing when stdout is a pipe, so a command substitution read an empty string and reported a mirror that was in fact CN. The check now reads the file the tool writes. + +**The report was chosen by `ls | head -1`.** A store that has seen three adapter versions holds three reports, and the first one alphabetically is the oldest: the host run reported 20 substitutions and 11 host entries from the 2026.09.05 farm while the 2026.09.07 farm beside it had 25 and 6. The pinned version is now asked for by name, with the newest as the fallback for a store that holds one. + +**pocl's presence was read from an exit code.** `xlings install` returns non-zero for a package that is already installed, so a payload that had installed correctly a minute earlier was reported as `not installable here`. The criterion is now the library the loader needs. ### 6.4 What the first verification run measured, and the two criteria it retired diff --git a/.agents/docs/2026-09-05-heterogeneous-verify.sh b/.agents/docs/2026-09-05-heterogeneous-verify.sh index 0f7b835e..5b05aa6b 100644 --- a/.agents/docs/2026-09-05-heterogeneous-verify.sh +++ b/.agents/docs/2026-09-05-heterogeneous-verify.sh @@ -142,15 +142,24 @@ if [ -n "$SRC" ] && [ -d "$SRC/examples/10-vulkan-compute/app" ]; then out=$(cd "$ex10" && "$STORE" build --no-accel 2>&1 && "$STORE" run --no-accel 2>&1) printf '%s\n' "$out" | grep -q '12 24 36 48' && ok "example 10 --no-accel answered 12 24 36 48" \ || fail "example 10 --no-accel: $(printf '%s' "$out" | tail -3 | tr '\n' ' ')" - hs=$(ls "$HOME"/.mcpp/registry/data/xpkgs/compat-x-vulkan-runtime/*/mcpp_generated/vulkan_runtime/HOST-SURFACE.txt \ - "$ex10"/.mcpp/.xlings/data/xpkgs/compat-x-vulkan-runtime/*/mcpp_generated/vulkan_runtime/HOST-SURFACE.txt 2>/dev/null | head -1) + # THE VERSION THIS EXAMPLE PINS, NOT WHICHEVER SORTS FIRST. A store that has + # seen three adapter versions holds three reports, and `ls | head -1` reads + # the oldest: the run of 2026-09-06 reported 20 substitutions and 11 host + # entries from the 2026.09.05 farm while the 2026.09.07 one beside it had + # 25 and 6. The pinned version is asked for by name, and the newest is the + # fallback for a store that has only one. + want="${MCPP_VERIFY_ADAPTER:-2026.09.07}" + hs=$(ls "$HOME"/.mcpp/registry/data/xpkgs/compat-x-vulkan-runtime/"$want"/mcpp_generated/vulkan_runtime/HOST-SURFACE.txt \ + "$ex10"/.mcpp/.xlings/data/xpkgs/compat-x-vulkan-runtime/"$want"/mcpp_generated/vulkan_runtime/HOST-SURFACE.txt 2>/dev/null | head -1) + [ -n "$hs" ] || hs=$(ls "$HOME"/.mcpp/registry/data/xpkgs/compat-x-vulkan-runtime/*/mcpp_generated/vulkan_runtime/HOST-SURFACE.txt \ + "$ex10"/.mcpp/.xlings/data/xpkgs/compat-x-vulkan-runtime/*/mcpp_generated/vulkan_runtime/HOST-SURFACE.txt 2>/dev/null | sort -V | tail -1) # WHICH ADAPTER WROTE IT. The substitution criteria below are properties of # 2026.09.06, the version that declares its payload set; an older report # carries neither the declarations nor the class they produce, so both # would pass on an empty finding. The version is a path segment. adapter=$(printf '%s' "$hs" | sed -n 's#.*/compat-x-vulkan-runtime/\([^/]*\)/.*#\1#p') - [ "$adapter" = "${MCPP_VERIFY_ADAPTER:-2026.09.06}" ] && ok "the farm was written by compat.vulkan-runtime $adapter" \ - || fail "the farm was written by compat.vulkan-runtime '$adapter', not ${MCPP_VERIFY_ADAPTER:-2026.09.06}" + [ "$adapter" = "$want" ] && ok "the farm was written by compat.vulkan-runtime $adapter" \ + || fail "the farm was written by compat.vulkan-runtime '$adapter', not $want" if [ -n "$hs" ]; then printf -- '--- %s\n' "$hs"; sed -n '/^## farmed/,$p' "$hs" | head -60 # WHAT A SANDBOX CAN AND CANNOT ASSERT. A subos shares the host's @@ -219,14 +228,20 @@ EOT out=$(cd "$work/ocl" && "$STORE" test 2>&1) printf '%s\n' "$out" | grep -q '^platforms=' && ok "the loader linked and enumerated: $(printf '%s' "$out" | grep '^platform' | tr '\n' ' ')" \ || fail "opencl probe: $(printf '%s' "$out" | tail -4 | tr '\n' ' ')" -if "$XL" install pocl -y >/dev/null 2>&1; then - ok "xim:pocl installed" - lib=$(ls "$HOME"/.xlings/data/xpkgs/xim-x-pocl/*/lib/libpocl.so "$HOME"/.mcpp/registry/data/xpkgs/xim-x-pocl/*/lib/libpocl.so 2>/dev/null | head -1) +# THE INSTALLED FILE, NOT THE EXIT CODE. `xlings install` returns non-zero on a +# package that is already present and on other conditions that leave the payload +# in place; the run of 2026-09-05 reported "not installable here" for a pocl +# that had installed correctly a minute earlier. The library the loader needs is +# the thing being asked about, so it is what the criterion reads. +"$XL" install pocl -y >/dev/null 2>&1 || true +lib=$(ls "$HOME"/.xlings/data/xpkgs/xim-x-pocl/*/lib/libpocl.so "$HOME"/.mcpp/registry/data/xpkgs/xim-x-pocl/*/lib/libpocl.so 2>/dev/null | head -1) +if [ -n "$lib" ]; then + ok "xim:pocl installed at $lib" out2=$(cd "$work/ocl" && OCL_ICD_FILENAMES="$lib" "$STORE" test 2>&1) printf '%s\n' "$out2" | grep -q 'Portable Computing Language' && ok "the pocl platform is enumerated through OCL_ICD_FILENAMES" \ || fail "pocl not enumerated: $(printf '%s' "$out2" | grep '^platform' | tr '\n' ' ')" else - printf 'note: xim:pocl not installable here (not published yet, or this arch)\n' + fail "xim:pocl left no libpocl.so in either store" fi printf '\n== result: %d assertion(s) failed ==\n' "$fails" diff --git a/docs/05-mcpp-toml.md b/docs/05-mcpp-toml.md index 01ac9f3b..cfb1dd55 100644 --- a/docs/05-mcpp-toml.md +++ b/docs/05-mcpp-toml.md @@ -2268,7 +2268,7 @@ rules-spirv = { sources = ["rules/spirv.cppm"] } # export module mcpp.rules.spir ```toml # a consumer [dependencies.mcpp] -plugins = { version = "0.1.0", features = ["rules-spirv"], host-module = true } +plugins = { version = "0.1.1", features = ["rules-spirv"], host-module = true } ``` The module set is the feature set: a unit whose feature is not active is not diff --git a/docs/07-build-mcpp.md b/docs/07-build-mcpp.md index 9cdf7392..4abba938 100644 --- a/docs/07-build-mcpp.md +++ b/docs/07-build-mcpp.md @@ -649,7 +649,17 @@ and warns when the two disagree — the mcpp project. Nothing breaks; the name claims an origin the package does not have. A rule -outside the project picks its own prefix. `examples/09-cuda-kernel` and +outside the project picks its own prefix. + +**A tool is not a rule.** A rule states how a translation unit is compiled by a +compiler mcpp does not drive: it submits an action and the engine schedules it. +A tool states something the build program needs that no compiler performs, and +does it while the program runs. `mcpp.tools.embed` (feature `tools-embed`, +mcpp 2026.9.5.4+) is the first: it writes a data file into a header the program +compiles in, as a byte array or a 32-bit word array, and rewrites nothing when +the content is unchanged, so calling it unconditionally costs no rebuild. + +`examples/09-cuda-kernel` and `examples/10-vulkan-compute` consume `mcpp.rules.cuda` and `mcpp.rules.spirv` from `mcpp:plugins`, the way any project does. diff --git a/docs/zh/05-mcpp-toml.md b/docs/zh/05-mcpp-toml.md index 6fa57dd6..f04acec6 100644 --- a/docs/zh/05-mcpp-toml.md +++ b/docs/zh/05-mcpp-toml.md @@ -1932,7 +1932,7 @@ rules-spirv = { sources = ["rules/spirv.cppm"] } # export module mcpp.rules.spir ```toml # 消费者 [dependencies.mcpp] -plugins = { version = "0.1.0", features = ["rules-spirv"], host-module = true } +plugins = { version = "0.1.1", features = ["rules-spirv"], host-module = true } ``` 模块集合就是 feature 集合:feature 未激活的单元不编译,import 它会以未知模块失败。 diff --git a/docs/zh/07-build-mcpp.md b/docs/zh/07-build-mcpp.md index f2ffb988..032c2dab 100644 --- a/docs/zh/07-build-mcpp.md +++ b/docs/zh/07-build-mcpp.md @@ -558,6 +558,13 @@ shim,而可用的那份就在项目自己的环境里,根本不在 `PATH` 上。 the mcpp project. 什么都不会坏;只是这个名字声称了一个该包并不具有的来源。项目之外的规则自选前缀。 + +**工具不是规则。** 规则说明一个编译单元如何被 mcpp 并不驱动的编译器编译:它提交一条 +action,由引擎调度。工具说明的是构建程序需要、而没有任何编译器执行的事,并在构建程序 +运行时当场做掉。`mcpp.tools.embed`(feature `tools-embed`,mcpp 2026.9.5.4+)是第一个: +它把数据文件写成程序编译进去的头文件(字节数组或 32 位字数组),内容未变时不重写文件, +因此无条件调用它不会带来任何重编。 + `examples/09-cuda-kernel` 与 `examples/10-vulkan-compute` 像任何工程一样从 `mcpp:plugins` 消费 `mcpp.rules.cuda` 与 `mcpp.rules.spirv`。 diff --git a/examples/09-cuda-kernel/app/mcpp.toml b/examples/09-cuda-kernel/app/mcpp.toml index ae582a3c..131275cd 100644 --- a/examples/09-cuda-kernel/app/mcpp.toml +++ b/examples/09-cuda-kernel/app/mcpp.toml @@ -20,7 +20,7 @@ default = "llvm@22.1.8" # The rule that compiles the island lives in the official plugin collection, # selected by its feature; `build.mcpp` imports it as `mcpp.rules.cuda`. [dependencies.mcpp] -plugins = { version = "0.1.0", features = ["rules-cuda"], host-module = true } +plugins = { version = "0.1.1", features = ["rules-cuda"], host-module = true } # The driver's userspace library, reached through an index package that owns # the one hop mcpp needs: a directory on the artifact's runtime search path. diff --git a/examples/10-vulkan-compute/app/mcpp.toml b/examples/10-vulkan-compute/app/mcpp.toml index 9049704a..bff5926d 100644 --- a/examples/10-vulkan-compute/app/mcpp.toml +++ b/examples/10-vulkan-compute/app/mcpp.toml @@ -13,7 +13,7 @@ import_std = true # The rule that compiles the shaders lives in the official plugin collection, # selected by its feature; `build.mcpp` imports it as `mcpp.rules.spirv`. [dependencies.mcpp] -plugins = { version = "0.1.0", features = ["rules-spirv"], host-module = true } +plugins = { version = "0.1.1", features = ["rules-spirv"], host-module = true } # The Khronos loader, built by the index rather than taken from the host, and # the adapter that makes the host's own ICDs reachable from a binary running