Skip to content

0.2.2: a rule takes the extensions it claims, not every device source - #6

Merged
Sunrisepeak merged 1 commit into
mainfrom
fix/rules-claim-their-own-extensions
Sep 6, 2026
Merged

0.2.2: a rule takes the extensions it claims, not every device source#6
Sunrisepeak merged 1 commit into
mainfrom
fix/rules-claim-their-own-extensions

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

What this fixes

mcpp::device_sources() is the package's whole device set, and every rule
in a build program reads the same variable. All four rules took all of it,
which is right for exactly as long as a build has one rule in it.

A project with two backends puts a .cu and a .comp in that one list.
Measured on the unfixed rules with both imported:

cuda:scale  clang -x cuda shaders/scale.comp -o scale.cu.o
mcpp.rules.spirv: src/kernels/saxpy.cu has no shader stage.

Two failures, and the quieter one is worse: the CUDA rule did not refuse the
shader, it compiled it and produced an object. The shader rule then failed
on the .cu with a message about stages, pointing at a file it was never meant
to see. Neither names the actual condition.

The change

Each rule selects the extensions it claims and leaves the rest:

feature claims
rules-cuda .cu
rules-hip .hip
rules-sycl .sycl
rules-spirv the shader stages, plus .glsl / .hlsl so a stage-less name is refused by name

A file no rule claims is not silently dropped either — mcpp 2026.9.6.5
refuses a device source that reached no action, which is the one place that can
see every rule's share at once.

Second half: a rule whose backend this build does not name now returns
immediately instead of warning. A build program with several rules calls them
all, and the ones whose backend is absent have nothing to do. That is what
makes --no-accel work with two rules present.

Verification

tests/multi-rule-consumer is the fixture the change exists for: one package
with a .cu and a .comp, both rules in one build program, both products
asserted, and test ! -f scale.cu.o for the shader that must not have been
compiled as CUDA. Run on a real device as well — one binary prints the SPIR-V
magic number and the kernel's result:

magic=07230203 words=370
12 24 36 48

All five existing fixtures re-run green, on the CI-pinned 2026.9.6.1 as well as
on the development engine.

Also

Every fixture moves its rule dependency from [dependencies] to
[build-dependencies]: a rule package's library must never reach the target
while its rule is still wanted. The old spelling works, which is why the
distinction has to be stated rather than left to a failure to teach.

`mcpp::device_sources()` is the package's WHOLE device set, and every rule in a
build program reads the same variable. All four rules took all of it, which is
right for exactly as long as a build has one rule in it.

A project with two backends puts a `.cu` and a `.comp` in that one list.
Measured on the unfixed rules, with both imported:

    cuda:scale  clang -x cuda shaders/scale.comp -o scale.cu.o
    mcpp.rules.spirv: src/kernels/saxpy.cu has no shader stage.

Two failures, and the quieter one is the worse. The CUDA rule did not refuse
the shader -- it compiled it, and produced an object. The shader rule then
failed on the `.cu`, with a message about stages that points at a file it was
never meant to see. Neither names the actual condition, which is that two rules
were handed each other's inputs.

So each rule now selects the extensions it claims -- `.cu`, `.hip`, `.sycl`,
and the shader stages -- and leaves the rest to whoever claims those. A file no
rule claims is not silently dropped either: mcpp 2026.9.6.5 refuses a device
source that reached no action, which is the one place that can see every rule's
share at once. `rules-spirv` deliberately claims `.glsl` and `.hlsl` too,
though it refuses them, because "rename it to one of these stages" is a better
message than "nothing compiled this".

Second half: a rule whose backend this build does not name now returns
immediately instead of warning. A build program with several rules calls them
all, and the ones whose backend is absent have nothing to do -- which is not a
mistake and must not be reported as one. That is what makes `--no-accel` work
with two rules present.

tests/multi-rule-consumer is the fixture the change exists for: one package
with a `.cu` and a `.comp`, both rules in one build program, both products
asserted, and `test ! -f scale.cu.o` for the shader that must not have been
compiled as CUDA. Verified on a real device as well -- the artifact prints the
SPIR-V magic number and the kernel's result from one binary.

Every fixture also moves its rule dependency from `[dependencies]` to
`[build-dependencies]`. A rule package's library must never reach the target
while its rule is still wanted, which is what that section is for; the old
spelling works, which is why the distinction has to be stated rather than left
to a failure to teach.
@Sunrisepeak
Sunrisepeak merged commit d60b271 into main Sep 6, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant