|
| 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.0", ... }`, 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 `v0.5.0`, GitHub release from the tag, `gtc` upload of the same bytes to |
| 202 | +GitCode, then T6. |
| 203 | +
|
| 204 | +## T11 -- ecosystem verification |
| 205 | +
|
| 206 | +In an xlings sandbox, with the CN mirror configured for both mcpp and xlings: |
| 207 | +resolve `mcpp:plugins@0.5.0` from the index, build and run the `boundary` |
| 208 | +example against the published package rather than a path override, and confirm |
| 209 | +the qualified names in the generated module. |
| 210 | +
|
| 211 | +*Criterion:* the sandbox is the only thing that verifies the published artifact; |
| 212 | +a path override in a working tree verifies the working tree. |
0 commit comments