Skip to content

Commit 8eef8b6

Browse files
committed
examples: 每个示例只写规则那一条边
规则包在 0.2.4 起自带它驱动的载荷,所以示例里那些 `[xlings.workspace]` 块是 在重复规则已经知道的事 —— 一份会悄悄过期的副本,因为规则会动而这些工程不会。 删掉的行就是使用者不必再写的行:cuda 五条、hip 七条、sycl 五条、cann 一条、 vulkan 一条、multi-backend 六条。 留下的两类,各有理由: - `xim:mesa-lavapipe`(vulkan 与 multi-backend):它是**设备**不是构建工具。 规则声明它会给每一个有 GPU 的消费者强加一个软件渲染器。规则声明它编译时 用的,工程声明它运行时跑在上面的。 - `xim:cuda-nvcc`(仅 multi-backend 一行):整个仓库唯一一处覆盖示范。留着 是为了让例外路径可见——离产物更近的声明赢,只装一个版本,而不满足规则所述 下界的钉会被拒绝并点出两侧。 `compat:*` 运行期适配器全部保留:插件是一条 `[build-dependencies]` 边,它自己的 `[dependencies]` 有意不到达消费者的 target。 需要 mcpp 2026.9.6.6 与 mcpp:plugins 0.2.4。
1 parent 83c9100 commit 8eef8b6

12 files changed

Lines changed: 125 additions & 124 deletions

File tree

.github/tools/build_examples.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ SKIP=(
5151
"examples/09-heterogeneous/hip/app|same, for the HIP payloads"
5252
"examples/09-heterogeneous/sycl/app|needs the dpcpp payload (over a gigabyte) and a device its runtime accepts"
5353
"examples/09-heterogeneous/vulkan/app|built AND RUN by the next step of this job, on the lavapipe payload, which needs no GPU"
54-
"examples/09-heterogeneous/cann/app|its device leg needs the Ascend DRIVER, which a runner does not have: the kernel compiles and the object links, and then `libascend_hal.so` is missing, which is correct on a machine with no NPU. Its CPU leg does build -- and is not built here only because the plugins pin would make this job resolve a fifth rule package for one example. Covered by the measurements in its README"
54+
"examples/09-heterogeneous/cann/app|its device leg needs the Ascend DRIVER, which a runner does not have: the kernel compiles and the object links, and then `libascend_hal.so` is missing -- and only that one, since 2026.9.6.6 models DT_RPATH inheritance. Correct on a machine with no NPU. Its CPU leg does build, and is not built here only because it would make this job resolve a fifth rule package for one example. Covered by the measurements in its README"
5555
)
5656

5757
# Every ROOT manifest in the tree: a directory with an `mcpp.toml` that has no

examples/09-heterogeneous/README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ working build stops working on another machine.
102102
|---|---|---|
103103
| engine | the graph, the identity, the axis | mcpp itself |
104104
| rule package | the spelling of one model | `mcpp.rules.cuda` in `mcpp:plugins` |
105-
| payload | the binaries, versioned by the project | `xim:cuda-nvcc`, `xim:dpcpp`, `xim:glslang` |
105+
| payload | the binaries, versioned by the rule and overridable by the project | `xim:cuda-nvcc`, `xim:dpcpp`, `xim:glslang` |
106106
| adapter | a built artifact's reach to something the host owns | `compat:cuda-driver`, `compat:vulkan-runtime`, `compat:sycl-runtime` |
107107

108108
The adapter layer exists for one reason. An mcpp-built program runs under
@@ -112,9 +112,10 @@ NVIDIA driver, a Vulkan ICD — is reached by an index package that puts a
112112
directory on the artifact's runtime search path. A project declares it as an
113113
ordinary dependency and does not otherwise think about it.
114114

115-
Everything else is a payload, so the version is the project's choice rather
116-
than the machine's, and no example here contains an absolute path or reads the
117-
host's toolkit.
115+
Everything else is a payload. Which package and how old it may be belongs to
116+
the rule; *exactly which version* is the project's to override and nobody's to
117+
discover from the machine. No example here contains an absolute path or reads
118+
the host's toolkit, and only `multi-backend/` names a payload version at all.
118119

119120
## Running them
120121

examples/09-heterogeneous/cann/app/mcpp.toml

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,22 @@ import_std = true
1414
# imports it as `mcpp.rules.ascendc`. `[build-dependencies]`, because a rule
1515
# package's library must never reach the target while its rule is wanted.
1616
[build-dependencies.mcpp]
17-
plugins = { version = "0.2.3", features = ["rules-ascendc"], host-module = true }
17+
plugins = { version = "0.2.4", features = ["rules-ascendc"], host-module = true }
1818

19-
# The toolkit carries BOTH halves this lane needs, so it is one payload rather
20-
# than two:
19+
# NO [xlings.workspace]. `mcpp.rules.ascendc` declares `xim:cann-toolkit`
20+
# itself, gated on this accelerator and on the feature that selects the rule --
21+
# so a build that names no accelerator installs none of its 2.9 GB, and a
22+
# project that wants the lane writes the edge above and nothing else.
23+
#
24+
# The toolkit carries BOTH halves the rule needs, which is why it is one payload
25+
# rather than two:
2126
#
2227
# <toolkit>/cann/<arch>-linux/ccec_compiler/bin/bisheng the device compiler
2328
# <toolkit>/cann/<arch>-linux/simulator/<SoC>/lib 38 SoCs, no hardware
2429
#
25-
# Gated on the accelerator: it is 2.9 GB installed, and `mcpp build` with no
26-
# accelerator has no use for it. That gating needs mcpp 2026.9.6.5; before it
27-
# the only spellings were "unconditionally" and "not at all".
28-
[target.'cfg(accelerator = "ascend")'.xlings.workspace]
29-
"xim:cann-toolkit" = "8.5.0"
30+
# The rule states `>=8.5.0` rather than an exact version: nothing here is
31+
# coupled to a driver, and that is the release the mixed-mode object and the
32+
# simulators arrive in. Needs mcpp 2026.9.6.6 and mcpp:plugins 0.2.4.
3033

3134
[build]
3235
# `dav-2201` is the device architecture, the role `sm_89` plays for CUDA. The

examples/09-heterogeneous/cuda/README.md

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,17 +70,26 @@ is compiled, with `accel-mismatch` on the machine-readable channel.
7070

7171
## Where the toolkit comes from
7272

73-
The project names it:
73+
The rule names it. This project writes one edge and no payload list at all:
7474

7575
```toml
76-
[xlings.workspace]
77-
"xim:cuda-nvcc" = "12.9.86"
78-
"xim:cuda-cudart" = "12.9.79"
76+
[build-dependencies.mcpp]
77+
plugins = { version = "0.2.4", features = ["rules-cuda"], host-module = true }
7978
```
8079

81-
These are payloads, so the version is the project's choice and not the
82-
machine's. The rule package resolves them with `mcpp::xpkg_dir` and builds the
83-
whole invocation from what it finds — the compiler, the include directories
80+
`mcpp.rules.cuda` declares nvcc, cudart, cuRAND's headers, CCCL and the driver
81+
sentinel for itself, under `cfg(accelerator = "cuda")` and the feature that
82+
selects it — so a build that names no accelerator installs none of them. To use
83+
a different CUDA line, write the entry in this project and it wins:
84+
85+
```toml
86+
[target.'cfg(accelerator = "cuda")'.xlings.workspace]
87+
"xim:cuda-nvcc" = "13.3.33"
88+
```
89+
90+
One version is installed either way, and mcpp says which. The rule resolves
91+
what it declared with `mcpp::xpkg_dir` and builds the whole invocation from what
92+
it finds — the compiler, the include directories
8493
and the library search paths. No path in this example is absolute, and a build
8594
here touches nothing of the host's CUDA:
8695

examples/09-heterogeneous/cuda/app/mcpp.toml

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ default = "llvm@22.1.8"
2020
# The rule that compiles the island lives in the official plugin collection,
2121
# selected by its feature; `build.mcpp` imports it as `mcpp.rules.cuda`.
2222
[build-dependencies.mcpp]
23-
plugins = { version = "0.2.1", features = ["rules-cuda"], host-module = true }
23+
plugins = { version = "0.2.4", features = ["rules-cuda"], host-module = true }
2424

2525
# The driver's userspace library, reached through an index package that owns
2626
# the one hop mcpp needs: a directory on the artifact's runtime search path.
@@ -31,23 +31,17 @@ plugins = { version = "0.2.1", features = ["rules-cuda"], host-module = true }
3131
[dependencies.compat]
3232
cuda-driver = "2026.09.05"
3333

34-
# The toolkit this project builds with, named rather than discovered. These
35-
# are PAYLOADS, so the version is the project's choice and not the machine's.
36-
# The 12.9 line is named on purpose: a runtime must not be newer than the
37-
# driver it will meet, and 12.x reaches every driver from r525 onward. The
38-
# rule package states the driver relation and mcpp compares it before the
39-
# first compile.
40-
[xlings.workspace]
41-
"xim:cuda-nvcc" = "12.9.86"
42-
"xim:cuda-cudart" = "12.9.79"
43-
# cuRAND's headers and CCCL. clang's CUDA wrapper includes
44-
# curand_mtgp32_kernel.h for every device unit, and that header includes
45-
# <nv/target> from CCCL, so the clang route needs both even though this kernel
46-
# calls neither; on a developer machine the host's /usr/include used to supply
47-
# them silently. 10.3.x and 12.9.27 are the 12.9 line.
48-
"xim:libcurand" = "10.3.10.19"
49-
"xim:cuda-cccl" = "12.9.27"
50-
"xim:libcuda-host-link" = { linux = "0.0.1" }
34+
# NO [xlings.workspace]. The toolkit is declared by the rule itself, under
35+
# `cfg(accelerator = ...)` and the feature that selects it, so this project
36+
# names the rule and nothing else. To pin a different version, write the same
37+
# entry here and it wins -- see `One package, one version` in
38+
# docs/05-mcpp-toml.md. Needs mcpp 2026.9.6.6 and mcpp:plugins 0.2.4.
39+
#
40+
# What the rule declares for this lane: nvcc, cudart, cuRAND's headers, CCCL and
41+
# the driver sentinel. The 12.9 line is an EXACT default rather than a floor,
42+
# because a runtime must not be newer than the driver it will meet -- 12.x
43+
# reaches every driver from r525 onward and 13.x raises that to r580, which is a
44+
# property of the machines the artifact will run on.
5145

5246
[build]
5347
# What this build compiles device code FOR. Written once, here: the rule

examples/09-heterogeneous/hip/app/mcpp.toml

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import_std = true
2020
default = "llvm@22.1.8"
2121

2222
[build-dependencies.mcpp]
23-
plugins = { version = "0.2.1", features = ["rules-hip"], host-module = true }
23+
plugins = { version = "0.2.4", features = ["rules-hip"], host-module = true }
2424

2525
# The driver's userspace library. HIP reaches the device through the CUDA
2626
# runtime here, so this is the same one hop the CUDA consumer needs: mcpp's
@@ -30,24 +30,16 @@ plugins = { version = "0.2.1", features = ["rules-hip"], host-module = true }
3030
[dependencies.compat]
3131
cuda-driver = "2026.09.05"
3232

33-
# The payloads. `hip-nvidia` is headers only -- on this platform that is all
34-
# HIP is -- and the four CUDA entries are the back end it compiles through.
35-
# cuRAND and CCCL are on the list for the reason the CUDA consumer records:
36-
# clang's CUDA wrapper includes curand_mtgp32_kernel.h for every device unit
37-
# and that header includes <nv/target>, so a unit that calls neither still
38-
# needs both.
39-
[xlings.workspace]
40-
"xim:hip-nvidia" = "7.2.4"
41-
"xim:cuda-nvcc" = "12.9.86"
42-
"xim:cuda-cudart" = "12.9.79"
43-
"xim:libcurand" = "10.3.10.19"
44-
"xim:cuda-cccl" = "12.9.27"
45-
# `nvidia_hip_runtime_api.h` includes <cuda_profiler_api.h> at its second line,
46-
# and CUDA ships that header in its own component. A machine with a host CUDA
47-
# installation finds it in /usr/include without saying so, which is how this
48-
# entry came to be missing from a build that worked.
49-
"xim:cuda-profiler-api" = "12.9.79"
50-
"xim:libcuda-host-link" = { linux = "0.0.1" }
33+
# NO [xlings.workspace]. `mcpp.rules.hip` declares the payloads it drives:
34+
# `hip-nvidia` (headers only -- on this platform that is all HIP is) plus the
35+
# CUDA back end it compiles through, including `cuda-profiler-api`, which
36+
# `nvidia_hip_runtime_api.h` includes at its second line. A machine with a host
37+
# CUDA installation finds that header in /usr/include without saying so, which
38+
# is how the entry came to be missing from a build that worked -- and it is now
39+
# the rule's business rather than every consumer's.
40+
#
41+
# To pin a different version, write the same entry here and it wins. Needs mcpp
42+
# 2026.9.6.6 and mcpp:plugins 0.2.4.
5143

5244
[build]
5345
# Two chunks: the programming model, and the device. A device is spelled once

examples/09-heterogeneous/multi-backend/README.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,18 @@ glob out the way `--no-accel` leaves both out, and the `cfg(accelerator =
4747
the two halves stay together. Only an accelerator this build *does* name whose
4848
architecture it does not cover is refused (mcpp 2026.9.6.5).
4949

50-
**Nothing is installed for a device this build did not name.** The payloads sit
51-
under `[target.'cfg(accelerator = ...)'.xlings.workspace]`, so `mcpp run`
52-
fetches neither the CUDA toolkit nor the shader compiler. That gating needs
53-
mcpp 2026.9.6.5; before it, the only spellings available were "unconditionally"
54-
and "not at all", and the cheapest build paid for the most expensive one.
50+
**Nothing is installed for a device this build did not name.** The payloads are
51+
declared by the two rules, under `cfg(accelerator = ...)` and the feature that
52+
selects each — so `mcpp run` fetches neither the CUDA toolkit nor the shader
53+
compiler. That gating needs mcpp 2026.9.6.5; before it, the only spellings
54+
available were "unconditionally" and "not at all", and the cheapest build paid
55+
for the most expensive one.
56+
57+
**This is also the one example that overrides a rule's default.** One line names
58+
`xim:cuda-nvcc`, and it is kept to show the escape hatch working: the
59+
declaration nearer the artifact wins, one version is installed, and a pin that
60+
failed a floor the rule stated would be refused naming both sides. Every other
61+
example writes the rule edge and nothing else.
5562

5663
## The CUDA leg takes the clang route
5764

examples/09-heterogeneous/multi-backend/mcpp.toml

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ default = "llvm@22.1.8"
4040
# The rules are declared unconditionally because `build.mcpp` imports them
4141
# unconditionally -- each returns immediately when its own backend is absent.
4242
[build-dependencies.mcpp]
43-
plugins = { version = "0.2.2", features = ["rules-cuda", "rules-spirv"], host-module = true }
43+
plugins = { version = "0.2.4", features = ["rules-cuda", "rules-spirv"], host-module = true }
4444

4545
# ── the payloads, gated on the device they are for ──────────────────────────
4646
#
@@ -65,20 +65,21 @@ plugins = { version = "0.2.2", features = ["rules-cuda", "rules-spirv"], host-mo
6565
# to 13.x fixes that and raises the driver floor to r580, which is a machine
6666
# requirement rather than a project decision. The clang route never includes
6767
# that header and imposes no such floor.
68+
# ONE ENTRY, AND IT IS THE ONLY OVERRIDE IN THIS REPOSITORY'S EXAMPLES.
69+
#
70+
# `mcpp.rules.cuda` declares the whole toolkit -- nvcc, cudart, cuRAND, CCCL and
71+
# the driver sentinel -- so the four beside this one are gone and this project
72+
# would build with no `[xlings.workspace]` at all. The line is kept to show the
73+
# escape hatch working: the declaration nearer the artifact wins, one version is
74+
# installed either way, and a pin that failed a floor the rule stated would be
75+
# refused naming both sides.
76+
#
77+
# Which CUDA line matters here is what makes it a plausible override rather than
78+
# a contrived one: a runtime must not be newer than the driver it will meet, so
79+
# a project whose machines are older or newer than the rule's default is exactly
80+
# the project that should say so.
6881
[target.'cfg(accelerator = "cuda")'.xlings.workspace]
69-
"xim:cuda-nvcc" = "12.9.86"
70-
"xim:cuda-cudart" = "12.9.79"
71-
# clang's CUDA wrapper includes a cuRAND header for every device unit, and that
72-
# header includes <nv/target> from CCCL. Neither is called by this kernel; on a
73-
# developer machine the host's /usr/include used to supply them silently.
74-
"xim:libcurand" = "10.3.10.19"
75-
"xim:cuda-cccl" = "12.9.27"
76-
77-
# The host-link stub is Linux-only, and the OS goes in the PREDICATE rather
78-
# than in the value: a value carrying platform keys under an already-predicated
79-
# table would state the condition twice and let the two disagree.
80-
[target.'cfg(all(accelerator = "cuda", linux))'.xlings.workspace]
81-
"xim:libcuda-host-link" = "0.0.1"
82+
"xim:cuda-nvcc" = "12.9.86"
8283

8384
# The driver's userspace library, reached through an index package rather than
8485
# the host: mcpp's private loader does not consult /usr/lib, so a statically
@@ -88,11 +89,12 @@ plugins = { version = "0.2.2", features = ["rules-cuda", "rules-spirv"], host-mo
8889
[target.'cfg(accelerator = "cuda")'.dependencies.compat]
8990
cuda-driver = "2026.09.05"
9091

92+
# The shader compiler is NOT here: `mcpp.rules.spirv` declares `xim:glslang` for
93+
# itself. What stays is a DEVICE -- a Vulkan driver that is always present
94+
# because it is the CPU, which is what makes the Vulkan leg runnable on a
95+
# machine with no GPU, and what every CI runner in this ecosystem is. A rule
96+
# declares what it needs to COMPILE; a project declares what it needs to RUN.
9197
[target.'cfg(accelerator = "vulkan")'.xlings.workspace]
92-
# The shader compiler, and a Vulkan driver that is always present because it is
93-
# the CPU. The second is what makes the Vulkan leg runnable on a machine with
94-
# no GPU, which is what every CI runner in this ecosystem is.
95-
"xim:glslang" = "15.1.0"
9698
"xim:mesa-lavapipe" = "26.2.1"
9799

98100
# The Khronos loader, built by the index rather than taken from the host, and

examples/09-heterogeneous/sycl/README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,17 @@ engine orders them by the graph rather than by declaration order.
4747

4848
Without the second, this program links, starts, and finds no kernel.
4949

50-
## Three payloads, and what each one closes
50+
## Five payloads, and what each one closes
51+
52+
This project declares none of them. `mcpp.rules.sycl` does, under the feature
53+
that selects it and the accelerator it serves:
5154

5255
```toml
53-
[xlings.workspace]
54-
"xim:dpcpp" = "7.1.0" # the compiler: its clang has the SYCL front end
55-
"xim:gcc" = "15.1.0" # the C++ standard library the unit compiles against
56-
"xim:cuda-nvcc" = "12.9.86" # the NVIDIA back end's libdevice
56+
"xim:dpcpp" = ">=7.1.0" # the compiler: its clang has the SYCL front end
57+
"xim:gcc" = "15.1.0" # the C++ standard library the unit compiles against
58+
"xim:glibc" = "" # …and the C library underneath it, unpinned
59+
"xim:linux-headers" = ""
60+
"xim:cuda-nvcc" = "12.9.86" # the NVIDIA back end's libdevice, only when the accel names cuda
5761
```
5862

5963
`xim:gcc` is not a second toolchain. Left alone, the SYCL compiler takes its

examples/09-heterogeneous/sycl/app/mcpp.toml

Lines changed: 16 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import_std = true
2020
default = "llvm@22.1.8"
2121

2222
[build-dependencies.mcpp]
23-
plugins = { version = "0.2.1", features = ["rules-sycl"], host-module = true }
23+
plugins = { version = "0.2.4", features = ["rules-sycl"], host-module = true }
2424

2525
# The SYCL runtime, on the artifact's runtime search path. mcpp's private
2626
# loader does not consult /usr/lib, so `libsycl.so.9` -- which the rule
@@ -33,37 +33,22 @@ plugins = { version = "0.2.1", features = ["rules-sycl"], host-module = true }
3333
[dependencies.compat]
3434
sycl-runtime = "2026.09.07"
3535

36-
# Five payloads, each closing one hole the host would otherwise fill, written
37-
# on the TWO TOOL AXES rather than one (docs/05 section 2.13, SPEC-004 section 4).
36+
# NO [xlings.workspace]. `mcpp.rules.sycl` declares all five payloads this lane
37+
# needs, each closing one hole the host would otherwise fill: `dpcpp` (the
38+
# compiler, a host-axis entry -- it runs here whatever it is asked to emit);
39+
# `gcc`, `glibc` and `linux-headers` (the C++ and C libraries the SYCL unit is
40+
# compiled against -- dpcpp's clang is configured with neither, so left alone
41+
# its include search list reaches /usr/include with no ecosystem library on it,
42+
# which is measurable in `clang -v` and invisible on the command line); and
43+
# `cuda-nvcc` for the NVIDIA back end's libdevice, declared only when the accel
44+
# actually names cuda.
3845
#
39-
# The compiler runs on THIS machine whatever it is asked to emit, so it is a
40-
# host-axis entry. The other four are what the produced code is compiled
41-
# against, so they belong under a target selector; on a native build the two
42-
# axes name one platform, which is why writing them all in one table was right
43-
# by accident and stops being right the first time this is cross-compiled.
44-
[xlings.workspace]
45-
"xim:dpcpp" = "7.1.0" # the compiler, and it runs here
46-
47-
# gcc NOT a second toolchain: the C++ standard library the SYCL unit
48-
# compiles against. Left alone, dpcpp's clang reads the HOST's
49-
# /usr/include/c++ -- measured, and invisible until a machine
50-
# without it refuses the build.
51-
# cuda-nvcc the NVIDIA back end's libdevice. Also measured: without
52-
# `--cuda-path` clang finds the host's CUDA installation and says
53-
# nothing about it.
54-
# glibc and linux-headers: the C library. `xim:gcc` supplies the C++
55-
# standard library; the C library underneath it is a separate
56-
# declaration, and without it a `.sycl` unit that includes
57-
# `<cstdio>` reaches the host's /usr/include. Both are UNPINNED:
58-
# the C library version is the runtime binding's choice, not the
59-
# project's, and a pinned entry resolves for exactly that version
60-
# or for nothing. rules-sycl 0.2.1 refuses the build and names
61-
# these two lines when they are absent.
62-
[target.'cfg(os = "linux")'.xlings.workspace]
63-
"xim:gcc" = "15.1.0"
64-
"xim:cuda-nvcc" = "12.9.86"
65-
"xim:glibc" = ""
66-
"xim:linux-headers" = ""
46+
# The C library entries are UNPINNED there for a reason worth keeping: its
47+
# version is the runtime binding's choice, not the project's, and a pinned entry
48+
# resolves for exactly that version or for nothing.
49+
#
50+
# To pin a different version, write the same entry here and it wins. Needs mcpp
51+
# 2026.9.6.6 and mcpp:plugins 0.2.4.
6752

6853
[build]
6954
# Two chunks: the programming model, and the device. Written `sycl` alone, the

0 commit comments

Comments
 (0)