Skip to content

Commit 39264fc

Browse files
docs, examples: an island's entry points arrive in the module's own namespace (#591)
* docs, examples: an island's entry points arrive in the module's own namespace `docs/42` states one rule for both lanes -- the module name and the namespace are one identifier path -- and the shader lane followed it while `mcpp.tools.island` did not: every entry point was emitted at global scope, so `import app.kernels` bought a file name and nothing else. `mcpp:plugins` 0.5.0 makes the island lane follow it, and this updates the three examples that use the generator and the chapters that describe it. The rule is now stated once, for both lanes, with the leaf identifier as the only difference: the data lane derives it from a file name because a payload has no name of its own, and the island lane takes the entry point's name because the author wrote one. That is also why a file name reaches nothing on the island lane, and why moving a function between two files in one directory renames nothing a consumer wrote. The generator's section moves from 31 to 42. 31's reader is a rule author, and no rule package calls the generator -- `tools/island.cppm` says a project does, from its own build program. What stays in 31 is the one field a rule owes it, `options::flags`, and a link. `docs/README.md`'s concept row moves with it, and `MCPP_EXPORT_C` is now explained in the chapter that row points at rather than named in a table cell. `examples/09-heterogeneous/boundary` gains a second island one directory deeper, because the namespace mirroring the tree is the thing the example exists to show, and it takes `strip_prefix` -- it is the one example whose consumer writes a boundary name directly. `cuda` and `sycl` name two roots and say which supplies the shape; their seams call `kernels::saxpy_device` from inside `namespace app`. Every example pins 0.5.0, so one version of the package is resolved across the tree. Verified against the released mcpp 2026.9.8.1 before the pin was restored: `boundary` prints `6 12 18 24` and `nm` shows one `boundary_saxpy` and no `saxpy`; the CPU legs of `cuda` and `sycl` build and run with the namespaced boundary. `check_docs_structure.sh`, `check_docs_style.sh`, `check_version_pins.sh` and e2e 616 pass. * examples: pin mcpp:plugins 0.5.1, and record what shipped 0.5.1 adds the refusal of overlapping roots that the design record states and 0.5.0 shipped without. Every example resolves one version of the package. * examples: pin mcpp:plugins 0.5.2 0.5.2 fixes a defect the cross-platform run of the generator's fixture found: `namespace_of` trimmed a base directory off a file's directory as a string, and on Windows a root stated with forward slashes against a directory iterator appending with the preferred separator left `\image` rather than `image` -- so the root directory became a namespace segment and an entry point landed in `app::kernels::_::image`. The shader lane shares that function and had the defect latent: its Windows fixture keeps every payload in one directory, so a segment was never derived there. `examples/09-heterogeneous/vulkan` and `examples/10-graphics/offscreen` therefore take this pin for a reason of their own. Verified against the published package: `boundary` resolves `mcpp-x-plugins/0.5.2` from the index and prints `6 12 18 24`. * docs(plan): the plan records three releases, not one 0.5.0 carried the design, 0.5.1 the refusal of overlapping roots the design states and 0.5.0 omitted, and 0.5.2 the separator defect the first cross-platform run of the generator's fixture found. --------- Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com>
1 parent 9a71c9a commit 39264fc

30 files changed

Lines changed: 1090 additions & 148 deletions
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
---
2+
subject: heterogeneous
3+
status: active
4+
---
5+
6+
# Implementation plan: the island boundary's names
7+
8+
Executes [2026-09-08-island-boundary-names.md](2026-09-08-island-boundary-names.md)
9+
across three repositories. One pull request per repository, ordered by what each
10+
one's CI needs to be green.
11+
12+
## Global constraints
13+
14+
* `mcpp:plugins` 0.5.0. The floor stays **mcpp 2026.9.8.1** -- nothing here uses
15+
an engine API newer than 0.4.0 already required, so `MCPP_VERSION` in the
16+
plugins CI does not move and neither does any index floor.
17+
* The generated header stays flat, `extern "C"`, free of namespaces, and is what
18+
a device compiler reads. Only the `.cppm` gains namespaces.
19+
* No compatibility flag. The four call sites are updated in the same batch.
20+
* Prose is declarative and free of decoration; no emoji in documentation, in
21+
code comments, or in commit messages.
22+
* Every generated name passes through one sanitiser, shared by both lanes.
23+
24+
## The order, and why it is not free
25+
26+
```
27+
T1..T5 mcpp-plugins PR ──> merge ──> tag v0.5.0 ──> GitHub release
28+
29+
├─> gtc: CN asset
30+
31+
T6 mcpp-index PR (0.5.0 + latest)
32+
33+
T7..T10 mcpp PR (docs + examples)
34+
35+
T11 sandbox verification
36+
```
37+
38+
`examples/09-heterogeneous/boundary` is built by mcpp's own CI
39+
(`.github/tools/build_examples.sh`) and resolves `mcpp:plugins` through the
40+
index, so the mcpp pull request cannot be green until 0.5.0 is published and
41+
indexed. The plugins fixtures use `path = "../.."` and are therefore independent.
42+
43+
---
44+
45+
## T1 -- one sanitiser, shared by both lanes
46+
47+
**Files:** `src/plugins.cppm` (add `mcpp::plugins::names`),
48+
`rules/spirv.cppm` (call it instead of defining it).
49+
50+
`identifier`, `split_module_name`, `common_base_dir` and `namespace_of` become
51+
`mcpp::plugins::names::*`. `surface` and the island generator both call them, so
52+
a directory named `2d` or `default` gets one answer rather than two that agree
53+
today by inspection.
54+
55+
Exported inline functions in this package must not range-for over a
56+
`std::string`: GCC 16 then instantiates `std::string::iterator` in the BMI and
57+
every consumer's build program fails to compile in `<bits/stl_iterator.h>`.
58+
Index instead. `tools/island.cppm` records the measurement.
59+
60+
*Criterion:* `rules/spirv.cppm` contains no definition of `common_base_dir` or
61+
`namespace_of`, and `tests/spirv-module-consumer` still emits
62+
`namespace default_ {`.
63+
64+
## T2 -- roots, layout root, and namespaced emission
65+
66+
**Files:** `tools/island.cppm`.
67+
68+
```cpp
69+
struct options {
70+
std::string module_name; // the module root
71+
std::string out_dir;
72+
std::string produced_by;
73+
bool emit_module = true;
74+
std::string marker = "MCPP_EXPORT_C";
75+
std::vector<std::string> roots; // directories, or single files
76+
std::string layout_root; // default: roots.front()
77+
std::vector<std::string> extensions; // default: device table + C/C++
78+
std::string strip_prefix; // empty: no short name
79+
};
80+
81+
struct entry {
82+
std::string decl; // verbatim, as scanned
83+
std::string name; // the identifier before `(`
84+
std::vector<std::string> name_space; // segments below the layout root
85+
std::string origin; // the file it was first seen in
86+
};
87+
88+
std::optional<std::vector<entry>> scan(const options&);
89+
std::optional<emitted> emit(std::span<const entry>, const options&);
90+
entry declared(std::string decl, std::vector<std::string> ns = {}); // rung L2
91+
```
92+
93+
`scan` walks each root in declaration order, sorted by path, reading files whose
94+
extension is in the set. For each marked declaration it records the entry, its
95+
name and the namespace segments of its directory relative to its own root. An
96+
entry's namespace is the one it has in the layout root; an entry the layout root
97+
does not declare keeps the namespace of the first root that does.
98+
99+
`emit` writes one flat header and one module. The module groups entries by
100+
namespace path and opens each block once.
101+
102+
*Criteria:* the four fixtures below.
103+
104+
## T3 -- the two checks
105+
106+
**Files:** `tools/island.cppm`.
107+
108+
* A name declared twice within one root is refused, naming both files and saying
109+
that two implementations of one entry point belong in two roots.
110+
* A name declared in several roots whose declarations differ is refused, naming
111+
both files and both declarations. This is the check 0.4.0 performs; what
112+
changes is that it no longer doubles as the collision check.
113+
* `scan` that finds no marked entry point in any root is an error naming the
114+
roots. An empty module is a misspelled path or a marker that never arrived.
115+
116+
## T4 -- the short name
117+
118+
**Files:** `tools/island.cppm`.
119+
120+
`strip_prefix` non-empty emits `inline constexpr auto <short> = <name>;` beside
121+
`using ::<name>;`. The short name passes through `names::identifier`. Two entries
122+
stripping to one short name are refused naming both. An entry not carrying the
123+
prefix gets no short name.
124+
125+
Measured 2026-09-08: `app::kernels::image::blur == &app::kernels::image::opkit_blur`
126+
under clang++ (DPC++ 7.1.0) `-std=c++23`, and the same declarations compile under
127+
GCC 13.
128+
129+
## T5 -- fixtures and CI
130+
131+
**Files:** `tests/island-interface/**`, `.github/workflows/ci.yml`,
132+
`README.md`, `mcpp.toml` (0.5.0), `src/plugins.cppm` (the version constant).
133+
134+
The fixture gains a subdirectory in the layout root and a flat fallback file, so
135+
the namespace, the flat-fallback allowance and the layout root's authority are
136+
all exercised by the shape of the tree rather than by an assertion about it:
137+
138+
```
139+
src/kernels/saxpy.c island_saxpy_device -> ::kernels
140+
src/kernels/image/scale.c island_scale_device -> ::kernels::image
141+
src/cpu/ops.c both, flat
142+
```
143+
144+
CI steps, each with a denominator:
145+
146+
1. the module carries `export namespace island_interface::kernels {` and
147+
`export namespace island_interface::kernels::image {`;
148+
2. the header carries neither `namespace` nor any of the short names;
149+
3. the flat fallback is accepted and `image` survives -- moving `src/cpu/ops.c`
150+
to `src/cpu/deep/ops.c` leaves the generated `.cppm` byte-identical;
151+
4. two files in ONE root declaring one name are refused naming both;
152+
5. two entries stripping to one short name are refused naming both;
153+
6. roots that hold no marker are refused naming the roots;
154+
7. the short name and the long name are one entity, and the artifact holds one
155+
symbol for the pair;
156+
8. the 0.4.0 disagreement check still fires, unchanged;
157+
9. the CPU leg (`--no-accel`) reaches the same qualified names.
158+
159+
## T6 -- mcpp-index
160+
161+
**Files:** `pkgs/m/mcpp.plugins.lua`.
162+
163+
A `0.5.0` entry in each of the three platform tables with the release tarball's
164+
sha256, and `["latest"] = { ref = "0.5.0" }` in each. The header comment gains
165+
the paragraph describing what 0.5.0 changes. Existing consumers pin exact
166+
versions, so moving `latest` breaks none of them.
167+
168+
*Criterion:* `mcpp add mcpp:plugins` in a sandbox resolves 0.5.0, and the
169+
descriptor holds exactly one `["latest"]` per platform table.
170+
171+
## T7 -- examples
172+
173+
**Files:** `examples/09-heterogeneous/boundary/**`,
174+
`examples/09-heterogeneous/cuda/app/**`, `examples/09-heterogeneous/sycl/app/**`.
175+
176+
Each pins `plugins = { version = "0.5.2", ... }`, passes `roots` and a
177+
`layout_root`, and reaches the boundary through the qualified name. `boundary`
178+
additionally shows `strip_prefix`, because it is the example whose whole subject
179+
is the generated interface.
180+
181+
## T8 -- documentation
182+
183+
**Files:** `docs/42-heterogeneous-builds.md`, `docs/zh/42-...`,
184+
`docs/31-authoring-a-rule-package.md`, `docs/zh/31-...`, `docs/README.md`,
185+
`docs/zh/README.md`, `examples/09-heterogeneous/boundary/README.md`,
186+
`examples/09-heterogeneous/README.md`.
187+
188+
The island generator's section moves from 31 to 42, because 31's reader is a
189+
rule author and no shipped rule calls the generator. 31 keeps one sentence and a
190+
link. The name table in 42 gains the island rows. Both languages, with the
191+
section numbering and the cross-references checked.
192+
193+
## T9 -- the check that keeps the two languages equal
194+
195+
`.github/tools/` already holds the documentation parity check that CI runs. The
196+
new sections are added to both languages in the same commit, and the check is
197+
run locally before the pull request.
198+
199+
## T10 -- release
200+
201+
Tag the release, publish it from the tag, `gtc` upload of the same bytes to
202+
GitCode, then T6. This ran three times: 0.5.0 for the design, 0.5.1 for the
203+
refusal of overlapping roots that the design states and 0.5.0 omitted, and
204+
0.5.2 for the separator defect the first cross-platform run of the fixture
205+
found.
206+
207+
## T11 -- ecosystem verification
208+
209+
In an xlings sandbox, with the CN mirror configured for both mcpp and xlings:
210+
resolve `mcpp:plugins@0.5.0` from the index, build and run the `boundary`
211+
example against the published package rather than a path override, and confirm
212+
the qualified names in the generated module.
213+
214+
*Criterion:* the sandbox is the only thing that verifies the published artifact;
215+
a path override in a working tree verifies the working tree.

0 commit comments

Comments
 (0)