Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 43 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,23 @@ env:
# it is the release that honours the `provision = "on-request"` entries this
# collection now declares, so a consumer that names its own tool downloads
# nothing. An older engine refuses the manifest at the package floor.
MCPP_VERSION: 2026.10.1.3
#
# 2026.10.26.1 IS WHAT 0.20.0'S SYCL LANE NEEDS (mcpp#841): a graph-supplied
# libstdc++ under clang (`gcc.libstdcxx`) and `MCPP_CXX_ABI_ROOT`. The package
# floor stays 2026.10.1.3: the rule reads the variable from the environment,
# so a gcc SYCL build still works on the older engine.
#
# 2026.10.26.2 writes a WebAssembly link in place again: 2026.10.26.1 linked
# every POSIX output through `$out.tmp`, Emscripten named its `.wasm` after
# that, and `dist-web` found no stem family (mcpp#844, found by this CI).
#
# 2026.10.27.1 IS WHAT 0.20.0's `plugins-platform` NEEDS, and the package
# floor states it (mcpp#847, SPEC-013, SPEC-014): protocol 17 -- the
# platform's directives `mcpp.plugins.platform` states, and the `platform`
# phase -- and `mcpp-package.json` beside every staged tree, which
# `mcpp::plugins::package` reads. Until that release exists the jobs build it
# from `MCPP_SOURCE_REF` (the repository variable names the branch).
MCPP_VERSION: 2026.10.27.1
# THE RELEASE EVERY JOB FETCHES, WHICH IS NOT ALWAYS THE ONE UNDER TEST.
#
# `MCPP_VERSION` states the engine this collection needs -- the package floor
Expand All @@ -78,26 +94,30 @@ env:
#
# It moves to `MCPP_VERSION` once that release exists, so the ordinary path
# runs the engine this collection states.
MCPP_BOOTSTRAP: 2026.10.1.3
MCPP_BOOTSTRAP: 2026.10.26.2
# AN ENGINE BUILT FROM SOURCE, WHEN A DISPATCH NAMES ONE.
#
# Empty on every push and pull request, so the steps run the release above.
# A dispatch that names a branch or tag of mcpp-community/mcpp through the
# `mcpp_source_ref` input has `.github/scripts/mcpp-under-review.sh` build
# Empty on every push and pull request, so the steps run the release above,
# unless the repository variable `MCPP_SOURCE_REF` names the engine a pending
# release of this collection is written against (it is removed once that
# engine is released). A dispatch that names a branch or tag of
# mcpp-community/mcpp through the `mcpp_source_ref` input, or the variable,
# has `.github/scripts/mcpp-under-review.sh` build
# that reference with the release above and point `$MCPP` at the result, in
# each job that fetches mcpp, and the step after it checks that the steps run
# what it built. This is how a change to the engine is measured against this
# collection before either is released.
MCPP_SOURCE_REF: ${{ inputs.mcpp_source_ref || '' }}
MCPP_SOURCE_REF: ${{ inputs.mcpp_source_ref || vars.MCPP_SOURCE_REF || '' }}
# THE EDITOR LANE'S TWO PROGRAMS (0.19.1), PINNED. mcppls reads a fixture's
# `build.mcpp` as an editor does, with its own clangd over the GCC
# toolchain's libstdc++ -- the one combination no build compiles. The mcpp it
# runs is the first release that describes build programs in the build
# database (2026.10.11.1); under an older one mcppls reads no build program
# and says so, which the lane counts as a failure. Raising either is a
# one-line change here.
# one-line change here. The mcpp is the bootstrap release, which also builds
# the engine under review when `MCPP_SOURCE_REF` names one.
MCPPLS_VERSION: 0.0.15
MCPP_EDITOR_VERSION: 2026.10.11.1
MCPP_EDITOR_VERSION: 2026.10.26.2
# PINNED, AND WITHOUT IT THE CACHE BELOW CACHED NOTHING.
#
# A released mcpp is self-contained: with no `MCPP_HOME`, `mcpp self env`
Expand Down Expand Up @@ -1828,8 +1848,23 @@ jobs:
"$MCPP_EDITOR" --version
"$MCPP_EDITOR" self config --mirror GLOBAL
echo "MCPP_EDITOR=$MCPP_EDITOR" >> "$GITHUB_ENV"
echo "MCPP=$MCPP_EDITOR" >> "$GITHUB_ENV"
echo "MCPP_VENDORED_XLINGS=$MCPP_VENDORED_XLINGS" >> "$GITHUB_ENV"

# THE EDITOR READS WITH THE ENGINE UNDER REVIEW WHEN ONE IS NAMED: a
# fixture whose package floor is that engine is refused by the release.
- name: the mcpp under review, built from MCPP_SOURCE_REF when it is set
timeout-minutes: 45
run: bash .github/scripts/mcpp-under-review.sh

- name: the editor runs the mcpp under review
if: env.MCPP_SOURCE_REF != ''
timeout-minutes: 5
run: |
set -eu
echo "MCPP_EDITOR=$MCPP_UNDER_REVIEW" >> "$GITHUB_ENV"
echo "ok: the editor runs $MCPP_UNDER_REVIEW_VERSION, built from $MCPP_SOURCE_REF"

- name: Fetch the mcppls release payload, checked against its SHA256SUMS
run: |
base="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/Sunrisepeak/mcpp-language-server/releases/download/v${MCPPLS_VERSION}"
Expand Down
21 changes: 15 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module name the member declares, and configures it there.

```toml
[build-dependencies.mcpp]
plugins = { version = "0.19.1", features = ["rules-spirv"], host-module = true }
plugins = { version = "0.20.0", features = ["rules-spirv"], host-module = true }
```

```cpp
Expand Down Expand Up @@ -51,9 +51,11 @@ naming it to avoid a download, and the scenarios on both sides -- is in

## Members

From 0.19.0 the package as a whole needs mcpp 2026.10.1.3 (`[package] mcpp`),
whose protocol 15 states where each tool and payload comes from; the column
states the release each member's behaviour first needed.
From 0.20.0 the package as a whole needs mcpp 2026.10.27.1 (`[package] mcpp`),
whose protocol 17 states a platform's fields and whose `mcpp pack` writes
`mcpp-package.json` beside every staged tree (0.19.0 needed 2026.10.1.3, whose
protocol 15 states where each tool and payload comes from); the column states
the release each member's behaviour first needed.

| feature | module | mcpp floor | what it does | doc |
|---|---|---|---|---|
Expand Down Expand Up @@ -91,7 +93,12 @@ dependency edge. `plugins-testing` adds `mcpp.plugins.testing`, which runs a
plugin function against a stated build context and compares what it declared.
`plugins-toolchain` adds `mcpp.plugins.toolchain`, the builders a root build
program states the build toolchain with (0.19.0).
All three need mcpp 2026.10.1.3. `surface`, the name of `plugins-core` before
`plugins-platform` adds `mcpp.plugins.platform`, what a platform's plugin and a
package format's provider state -- the link, the artifacts' names, the seal,
where the runner runs, and `provide_format` -- and `mcpp.plugins` reads a staged
package's `mcpp-package.json` (`mcpp::plugins::package`), 0.20.0, mcpp
2026.10.27.1 ([plugin-development](docs/plugin-development.md#3-layers-names-and-the-engine-floor)).
The first three need mcpp 2026.10.1.3. `surface`, the name of `plugins-core` before
0.17.0, is kept until 2027-03-28. A feature states a mechanism and the tools that
mechanism runs; the libraries and SDKs a program links are the project's
declaration (0.15.0 removed `rules-qt-xim*`; see [rules-qt](docs/rules-qt.md)).
Expand All @@ -101,11 +108,13 @@ declaration (0.15.0 removed `rules-qt-xim*`; see [rules-qt](docs/rules-qt.md)).
```
mcpp.toml the package: one feature per member
src/plugins.cppm export module mcpp.plugins; the version, mcpp::plugins::surface
(the declarations a consumer names) and mcpp::plugins::xml
(the declarations a consumer names), mcpp::plugins::xml and
mcpp::plugins::package (a staged package's metadata)
src/declare.cppm, the general library for build programs (`plugins-core`):
src/toolset.cppm, mcpp.plugins.declare, mcpp.plugins.toolset, mcpp.plugins.fs
src/fs.cppm
src/testing.cppm mcpp.plugins.testing (`plugins-testing`)
src/platform.cppm mcpp.plugins.platform (`plugins-platform`)
src/compat/, deps/compat/
compatibility units, each retired on the date its header states
rules/<x>.cppm export module mcpp.rules.<x>;
Expand Down
73 changes: 67 additions & 6 deletions docs/plugin-development.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ mcpp is a general build engine with a framework for build plugins. Plugins come
| layer | modules | provided by |
|---|---|---|
| L1 `mcpp.core` | `mcpp.core`, spelled `mcpp` as well (the two are permanently equivalent) | the engine |
| L2 general library | `mcpp.plugins.declare`, `mcpp.plugins.toolset`, `mcpp.plugins.fs`, `mcpp.plugins.tool`; `mcpp.plugins.testing`, `mcpp.plugins.toolchain` | this package, feature `plugins-core` (`plugins-testing` for the kit, `plugins-toolchain` for the toolchain builders) |
| L2 general library | `mcpp.plugins.declare`, `mcpp.plugins.toolset`, `mcpp.plugins.fs`, `mcpp.plugins.tool`; `mcpp.plugins.testing`, `mcpp.plugins.toolchain`, `mcpp.plugins.platform` | this package, feature `plugins-core` (`plugins-testing` for the kit, `plugins-toolchain` for the toolchain builders, `plugins-platform` for a platform's plugin and a package format's provider) |
| L3 plugins | `mcpp.deps.*`, `mcpp.rules.*`, `mcpp.dist.*`, `mcpp.tools.*` here; `mcpp.<namespace>.*` elsewhere | this package, by feature; any package |

A layer depends only on the layers below it. L2 knows no foreign tool: it turns
Expand All @@ -52,16 +52,18 @@ mcpp-index refuses such a package.

```toml
[build-dependencies.mcpp]
plugins = { version = "0.19.1", features = ["plugins-core"], host-module = true, reexport = true }
plugins = { version = "0.20.0", features = ["plugins-core"], host-module = true, reexport = true }
```

`reexport = true` is needed when the plugin's consumers import L2 modules in
their own build programs.

**The engine floor.** A plugin states the first mcpp release it needs in
`[package] mcpp = ">=<release>"`; an older engine stops before any other work
and names the upgrade. This package's floor is 2026.10.1.3, the release that
states where each tool and payload comes from (protocol 15).
and names the upgrade. This package's floor is 2026.10.27.1, the release that
speaks protocol 17 (the platform's directives and the `platform` phase, mcpp
SPEC-013) and writes `mcpp-package.json` beside every staged tree (SPEC-014);
it carries protocol 15, which states where each tool and payload comes from.

**Where a member's tool comes from.** A member that runs a program resolves it
through `mcpp.plugins.tool`, which answers in one order for every member:
Expand Down Expand Up @@ -110,11 +112,70 @@ toolchain phase, for a project whose `[toolchain]` says
`newest_under`, `env`, the `with_launcher` / `with_sysroot` / `with_family` /
`with_tool` refinements, and `configure(fn)` / `use(d)`.

**A platform's plugin and a package format's provider.** A platform package
(mcpp SPEC-013) states its targets' configuration in `[platform]` and the part
it computes in a plugin (`[platform] plugin = "build.mcpp"`), which runs in the
`platform` phase. `mcpp.plugins.platform` (feature `plugins-platform`) states
each field of that phase as one checked call:

| call | directive | accepts |
|---|---|---|
| `link::driver(d)` | `link-driver` | `"lld"` (ld.lld itself links) or `"compiler"` |
| `link::start(symbol)` | `link-start` | a symbol |
| `link::input_file(path)` | `link-input=file:` | a file; a relative path is the platform package's own |
| `link::input_from(as, argv)` | `link-input=from:` | a program's standard output saved as `as` (no `:`), `argv[0]` a tool's name |
| `artifact_names(exe, shared)` | `artifact-name` | `{name}` once, an executable's at its start; an empty pattern leaves the kind unstated |
| `seal(steps)` | `seal` | at least one step, each a non-empty argv; `{}` the artifact, `{needed}` the shared artifacts it needs |
| `run_host(where)` | `run-host` | `"same-arch"` or `"any"` |

A malformed value is refused at the call: nothing is stated, the call returns a
`statement` whose `reason` names the value and what the call accepts, and the
reason is written to standard error and stated as a warning. Every token is one
line, since a directive is.

`provide_format(name, make)` provides a package format (SPEC-014 §6) in both of
its halves. The name is declared on every pass (`mcpp::provides_pack_format`), so
that `mcpp pack --pkg-format <unknown>` names it among the formats the graph
provides; only in the pass that asks for it is the staged package read and
`make(const package::info&, mcpp::action&)` called, and the action -- role
`artifact`, id and description the format's name unless `make` sets them -- is
submitted when `make` returns true. The engine's own formats (`tar`, `dir`,
`zip`, `single-file`) are refused as names.

```cpp
import mcpp;
import mcpp.plugins.platform;
namespace pf = mcpp::plugins::platform;

int main() {
if (!pf::link::driver("lld") || !pf::artifact_names("{name}.img", "lib{name}.so")) return 1;
pf::provide_format("img", [](const mcpp::plugins::package::info& p, mcpp::action& a) {
static std::string out;
out = p.name + "-" + p.version + ".img";
a.arg("bundle").arg(out.c_str()).arg("${mcpp.stage_dir}").output(out.c_str());
return true;
});
return 0;
}
```

**The staged package.** `mcpp::plugins::package` (in `mcpp.plugins`, std-only,
so an ordinary program reads it too) reads the `mcpp-package.json` `mcpp pack`
writes at the root of a staged tree (SPEC-014 §4.5): `read(stage_dir)` returns
the package -- `name`, `version`, `platform`, `linkage`, `mode`, `legs`,
`systems`, `runner`, `entry` (each architecture's entry, in the document's
order), `program`, and `files`, each with its `path`, `role`, `leg` and `sha256`
-- or the reason it cannot, a document of another schema than 1 among them.
`files_with_role(p, role)` and `entry_of(p, arch)` select from it. A provider
reads which file is the program, an image or a resource here, rather than
deciding again from the tree.

**Testing a plugin.** `mcpp.plugins.testing` runs a plugin function in a child
process against a stated build context (`row::windows_visual_studio()`,
`row::windows_managed()`, `row::linux_gcc()`, `row::linux_libcxx()`, or a
context built with `set`, `file`, `xpkg`, `xpkg_source`, `xpkg_program` and
`phase`) and hands the lines it emitted and the
context built with `set`, `file`, `xpkg`, `xpkg_source`, `xpkg_program`,
`phase` and `pack`, the last a packaging pass's format and staged tree) and
hands the lines it emitted and the
files it wrote to a check. The test is a build program; a failed case fails the
build and prints the report. `tests/plugin-logic` is this package's own use.

Expand Down
4 changes: 3 additions & 1 deletion docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ Module `mcpp.rules.swift`; engine floor: 2026.9.8.1.

## `rules-sycl`

Module `mcpp.rules.sycl`; engine floor: 2026.10.1.3 (0.19.0, mcpp#755; 2026.9.6.6 before).
Module `mcpp.rules.sycl`; engine floor: 2026.10.1.3 (0.19.0, mcpp#755; 2026.9.6.6 before); an llvm build needs 2026.10.26.1 (0.20.0).

**One C++ runtime, libstdc++ (0.20.0).** `libsycl.so` is compiled against libstdc++ and throws `sycl::exception` on it, and a typed `catch` does not match across two C++ runtimes in one process (mcpp#838). The program's own C++ library is therefore libstdc++: a gcc toolchain, or llvm with `gcc.libstdcxx` in the graph (`[target.'cfg(accelerator = "sycl")'.dependencies] gcc.libstdcxx = "16.1.0.1"`). On Linux the rule refuses any other C++ library before a unit is compiled, naming both remedies, and `compat:sycl-runtime` states the same requirement. The rule names no C++ library on the link line (0.19 added `-l:libstdc++.so.6` beside an llvm build's libc++); `--gcc-install-dir` follows the build's GCC: the graph's supplier (`MCPP_CXX_ABI_ROOT`), else the gcc toolchain's payload, else the `xim:gcc` the rule declares.

**Needs and behaviour.** `[build] accel = "sycl"` or `"sycl, cuda12.9+{sm_89}"`, a constrained glob for `*.sycl`, and `compat:sycl-runtime` so the artifact can reach `libsycl.so.9` at run time. Its own engine need is `.sycl` in the device-source table, 2026.9.6.1
23 changes: 18 additions & 5 deletions mcpp.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
[package]
name = "plugins"
namespace = "mcpp"
version = "0.19.1"
# The first mcpp release that states where each tool and payload comes from
# (protocol 15, mcpp#755): `xpkg_source`, `xpkg_program`, `xpkg_request`,
# `mcpp::decision`, `mcpp::toolchain`, `mcpp::phase`, and the
version = "0.20.0"
# The first mcpp release that speaks protocol 17 (SPEC-013, mcpp#847): the
# platform's directives `mcpp.plugins.platform` states (`link_driver`,
# `link_input`, `artifact_name`, `seal`, `run_host`), the `platform` phase, and
# `mcpp-package.json` beside every staged tree, which `mcpp::plugins::package`
# reads. It carries protocol 15 (mcpp#755): `xpkg_source`, `xpkg_program`,
# `xpkg_request`, `mcpp::decision`, `mcpp::toolchain`, `mcpp::phase`, and the
# `provision = "on-request"` entries below.
mcpp = ">=2026.10.1.3"
mcpp = ">=2026.10.27.1"
description = "Official mcpp build plugins: rule packages under mcpp.rules.*, build-time utilities under mcpp.tools.*, each member selected by a feature"
license = "Apache-2.0"
authors = ["mcpp-community"]
Expand Down Expand Up @@ -100,6 +103,16 @@ sources = ["src/declare.cppm", "src/toolset.cppm", "src/fs.cppm", "src/tool.cppm
sources = ["src/toolchain.cppm"]
implies = ["plugins-core"]

# `mcpp.plugins.platform` -- what a platform's plugin (mcpp SPEC-013 §12) and a
# package format's provider (SPEC-014 §6) state: the link's driver and inputs,
# the artifacts' names, the seal, where the runner runs, and `provide_format`,
# which declares a format on every pass and makes its action from the staged
# package's `mcpp-package.json` in the pass that asks for it. Its own feature:
# a project that provides no platform and no format compiles nothing of it.
[features.plugins-platform]
sources = ["src/platform.cppm"]
implies = ["plugins-core"]

# The test kit: a plugin function runs against a stated build context, and its
# directives are compared. Separate from `plugins-core`, so that no build
# program compiles it unless it asks.
Expand Down
Loading
Loading