GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — 0.4.0: every payload a compile reads is in the build graph (#12) · mcpp-community/mcpp-plugins@9249553

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
  • .github/workflows
    • ci.yml
  • README.md
  • mcpp.toml
  • rules
    • ascendc.cppm
    • cuda.cppm
    • hip.cppm
    • slang.cppm
    • spirv.cppm
    • sycl.cppm
  • src
    • declare.cppm
    • plugins.cppm
  • tests/spirv-object-storage
    • mcpp.toml
  • tools
    • embed.cppm
    • embed_main.cpp
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎.github/workflows/ci.yml‎

Copy file name to clipboardExpand all lines: .github/workflows/ci.yml
+188-5Lines changed: 188 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ on:
99
env:
1010
# The mcpp release the consumers build with. Raising it is what admits a
1111
# member that relies on a newer engine; the README states each member's floor.
12-
MCPP_VERSION: 2026.9.7.1
12+
MCPP_VERSION: 2026.9.8.1
1313
# PINNED, AND WITHOUT IT THE CACHE BELOW CACHED NOTHING.
1414
#
1515
# A released mcpp is self-contained: with no `MCPP_HOME`, `mcpp self env`
@@ -472,6 +472,157 @@ jobs:
472472
|| { echo "FAIL: it failed for some other reason"; cat /tmp/away.log; exit 1; }
473473
echo "ok: found from the package root, reported missing from elsewhere"
474474
475+
# AN EDITED SHADER REACHES THE ARTIFACT, UNDER EVERY STORAGE.
476+
#
477+
# The three storage steps above assert STRUCTURE: the `.S` names the
478+
# payload, the section is aligned, no C initialiser was written. All three
479+
# passed against 0.3.0, in which object storage served a STALE artifact --
480+
# the shader recompiled, the object did not, and the program reported the
481+
# previous payload's byte count with a green build. Structure cannot see
482+
# that. Only editing a shader and reading what comes out can.
483+
#
484+
# THE DENOMINATOR IS THE POINT. Header storage was already correct, so a
485+
# run where only `object` fails names the mechanism, and a run where all
486+
# three fail names the harness -- a distinction one fixture cannot make.
487+
#
488+
# The edit adds a statement guarded by a value the host never passes, so
489+
# the compiled module must grow while the program's output stays
490+
# comparable.
491+
- name: an edited shader reaches the artifact under every storage
492+
run: |
493+
set -u
494+
fail=0
495+
for fx in spirv-module-consumer:header spirv-object-storage:object spirv-sidecar:sidecar; do
496+
dir=${fx%%:*}; store=${fx##*:}
497+
cd "$GITHUB_WORKSPACE/tests/$dir"
498+
shader=$(find shaders -name '*.comp' | sort | head -1)
499+
cp "$shader" /tmp/shader.bak
500+
# `|| { ...; continue; }` and not a bare command: a `run:` block
501+
# runs under `bash -e`, so a bare failing build would end the STEP
502+
# here and the other two storages would never run -- losing the
503+
# denominator this check is built around.
504+
"$MCPP" build > /dev/null \
505+
|| { echo "FAIL[$store]: build"; fail=1; cp /tmp/shader.bak "$shader"; continue; }
506+
before=$("$MCPP" run 2>&1 | sed -n 's/.*bytes=\([0-9]*\).*/\1/p' | awk '{s+=$1} END {print s+0}')
507+
sed -i 's|void main() {|void main() {\n if (push.n == 0xdeadu) { v[0] = 1.0; v[1] = 2.0; v[2] = 3.0; }|' "$shader"
508+
"$MCPP" build > /dev/null \
509+
|| { echo "FAIL[$store]: rebuild"; fail=1; cp /tmp/shader.bak "$shader"; continue; }
510+
after=$("$MCPP" run 2>&1 | sed -n 's/.*bytes=\([0-9]*\).*/\1/p' | awk '{s+=$1} END {print s+0}')
511+
cp /tmp/shader.bak "$shader"
512+
if [ -z "$before" ] || [ -z "$after" ]; then
513+
echo "FAIL[$store]: no byte count printed (before='$before' after='$after')"
514+
fail=1
515+
elif [ "$before" = "$after" ]; then
516+
echo "FAIL[$store]: the shader changed and the artifact did not (bytes=$before both times)"
517+
fail=1
518+
else
519+
echo "ok[$store]: $before -> $after"
520+
fi
521+
done
522+
[ "$fail" -eq 0 ] || exit 1
523+
524+
# AN EDITED `#include` REACHES THE ARTIFACT.
525+
#
526+
# The step above edits the shader itself, which every rule declares as an
527+
# action input. This edits a file the shader INCLUDES, which no rule
528+
# declares: `a.input()` is fixed when build.mcpp runs, before the compiler
529+
# has read a line, so the only channel is the depfile the compiler writes
530+
# afterwards. `grep depfile rules/` returned nothing across all six rules
531+
# until 0.3.1, and editing an included `.glsl` rebuilt nothing.
532+
#
533+
# ON THE HEADER-STORAGE FIXTURE, AND THAT IS NOT ARBITRARY. Run against
534+
# `spirv-object-storage` this step tests two mechanisms at once: measured,
535+
# it fails when EITHER the depfile or the `.incbin` declaration is absent,
536+
# so a red run would not say which. Header storage compiles the payload in
537+
# as generated source, where the compile edge is dirty for the ordinary
538+
# reason, and what remains under test is the depfile alone.
539+
#
540+
# Both shader compilers were measured writing one that names the include:
541+
# glslangValidator --depfile -> out.spv: scale.comp ./common.glsl
542+
# glslc -MD -MF -> out2.spv: scale.comp common.glsl
543+
- name: an edited include reaches the artifact
544+
working-directory: tests/spirv-module-consumer
545+
run: |
546+
set -u
547+
shader=shaders/default/scale.comp
548+
inc=shaders/default/common.glsl
549+
cp "$shader" /tmp/scale.bak
550+
printf 'const float kBias = 0.0;\n' > "$inc"
551+
sed -i 's|#version 450|#version 450\n#extension GL_GOOGLE_include_directive : require\n#include "common.glsl"|' "$shader"
552+
sed -i 's|+ v\[push.n + i\];|+ v[push.n + i] + kBias;|' "$shader"
553+
"$MCPP" build > /dev/null
554+
before=$("$MCPP" run 2>&1 | sed -n 's/.*bytes=\([0-9]*\).*/\1/p' | awk '{s+=$1} END {print s+0}')
555+
# ONLY THE INCLUDED FILE CHANGES NOW. Nothing any rule declared as an
556+
# input is touched, so a rebuild happens only if the depfile said so.
557+
printf 'const float kBias = 1.0;\nconst float kPad0 = 2.0;\nconst float kPad1 = 3.0;\n' > "$inc"
558+
"$MCPP" build > /dev/null
559+
after=$("$MCPP" run 2>&1 | sed -n 's/.*bytes=\([0-9]*\).*/\1/p' | awk '{s+=$1} END {print s+0}')
560+
cp /tmp/scale.bak "$shader"
561+
rm -f "$inc"
562+
if [ -z "$before" ] || [ -z "$after" ]; then
563+
echo "FAIL: no byte count printed (before='$before' after='$after')"
564+
exit 1
565+
fi
566+
if [ "$before" = "$after" ]; then
567+
echo "FAIL: the included file changed and the artifact did not (bytes=$before both times)"
568+
exit 1
569+
fi
570+
echo "ok: an included file reaches the artifact ($before -> $after)"
571+
572+
# OBJECT STORAGE WITHOUT THE TOOL IS REFUSED, AND THE REFUSAL SAYS WHAT
573+
# TO ADD.
574+
#
575+
# `tools = [...]` is default-off: nothing is built unless a consumer asks,
576+
# which is what keeps the DEFAULT storage free of a tool build. The cost
577+
# is that a project can ask for object storage and not for the tool, and
578+
# what it gets then is a message -- so the message is a contract. Asserted
579+
# on the three strings a reader needs: that the tool is what is missing,
580+
# the key that supplies it, and the storage that needs none.
581+
- name: object storage without the tool is refused, naming the fix
582+
run: |
583+
set -u
584+
W="$GITHUB_WORKSPACE/tests/spirv-object-storage"
585+
cp "$W/mcpp.toml" /tmp/objstore.toml
586+
# Remove only the tools request, leaving everything else in place.
587+
sed -i 's/, tools = \["mcpp-embed"\]//' "$W/mcpp.toml"
588+
cd "$W"
589+
rm -rf target
590+
if "$MCPP" build > refusal.log 2>&1; then
591+
cp /tmp/objstore.toml mcpp.toml
592+
echo "FAIL: object storage built with no way to generate its assembly"
593+
exit 1
594+
fi
595+
cp /tmp/objstore.toml mcpp.toml
596+
fail=0
597+
grep -q 'mcpp-embed' refusal.log || { echo "FAIL: the refusal does not name the tool"; fail=1; }
598+
grep -q 'tools = ' refusal.log || { echo "FAIL: the refusal does not name the key that supplies it"; fail=1; }
599+
grep -q 'storage::header' refusal.log || { echo "FAIL: the refusal does not name the way out"; fail=1; }
600+
if [ "$fail" -ne 0 ]; then cat refusal.log; exit 1; fi
601+
rm -f refusal.log
602+
echo "ok: refused, naming the tool, the key and the alternative"
603+
604+
# EVERY RULE PASSES A DEPFILE, AND THE DENOMINATOR IS THE RULE COUNT.
605+
#
606+
# The step above proves the channel works for the one lane CI can run.
607+
# The other four compile for accelerators no runner has, so what can be
608+
# asserted about them is that each one declares the field -- read from the
609+
# code rather than from a list written here, so a seventh rule is counted
610+
# the day it is added and not the day someone remembers this step.
611+
- name: every rule declares a depfile
612+
run: |
613+
set -u
614+
cd "$GITHUB_WORKSPACE"
615+
rules=$(ls rules/*.cppm | wc -l)
616+
withdep=$(grep -l 'depfile' rules/*.cppm | wc -l)
617+
echo "rules with a depfile: $withdep of $rules"
618+
if [ "$withdep" -ne "$rules" ]; then
619+
echo "FAIL: these rules pass no depfile:"
620+
for f in rules/*.cppm; do
621+
grep -q 'depfile' "$f" || echo " $f"
622+
done
623+
exit 1
624+
fi
625+
475626
# SLANG: A DIFFERENT LANGUAGE, THE SAME SURFACE.
476627
#
477628
# The point of this step is not that Slang compiles -- it is that a
@@ -601,8 +752,25 @@ jobs:
601752
# `<name> = { ... }`. Reading keys out of the table BODY matched
602753
# `sources` once per feature and reported the fixture as
603754
# incomplete while the extractor was what broke.
604-
feats=$(grep -oE '^\[features\.[a-z0-9-]+\]' mcpp.toml \
605-
| sed 's/^\[features\.//; s/\]$//' | sort)
755+
# MEMBERS, NOT EVERY FEATURE. A feature that another feature
756+
# IMPLIES is internal -- `surface` carries the build-program half of
757+
# the generated surface and every member implies it, and a consumer
758+
# never writes it. Subtracting the implied ones keeps this check
759+
# meaning "the fixture names every member a consumer can activate";
760+
# padding the fixture instead would make it mean less.
761+
#
762+
# The rule is read out of the manifest, not listed here, so a seventh
763+
# MEMBER is still caught: nothing implies it.
764+
# A TEMP FILE, NOT `<(...)`. This step also runs on windows-2022
765+
# through Git Bash, where process substitution is emulated and not
766+
# dependable -- and a check that behaves differently on one of the
767+
# three hosts is the exact class of difference this job exists to
768+
# catch, so it must not introduce one.
769+
grep -oE '^implies[[:space:]]*=.*' mcpp.toml \
770+
| grep -oE '"[a-z0-9-]+"' | tr -d '"' | sort -u > /tmp/implied.txt
771+
grep -oE '^\[features\.[a-z0-9-]+\]' mcpp.toml \
772+
| sed 's/^\[features\.//; s/\]$//' | sort > /tmp/allfeats.txt
773+
feats=$(comm -23 /tmp/allfeats.txt /tmp/implied.txt)
606774
used=$(sed -n '/features = \[/,/\], host-module/p' tests/all-rules-compile/mcpp.toml \
607775
| grep -oE '"[a-z-]+"' | tr -d '"' | sort)
608776
[ -n "$feats" ] || {
@@ -898,8 +1066,23 @@ jobs:
8981066
# `<name> = { ... }`. Reading keys out of the table BODY matched
8991067
# `sources` once per feature and reported the fixture as
9001068
# incomplete while the extractor was what broke.
901-
feats=$(grep -oE '^\[features\.[a-z0-9-]+\]' mcpp.toml \
902-
| sed 's/^\[features\.//; s/\]$//' | sort)
1069+
# MEMBERS, NOT EVERY FEATURE. A feature that another feature
1070+
# IMPLIES is internal -- `surface` carries the build-program half of
1071+
# the generated surface and every member implies it, and a consumer
1072+
# never writes it. Subtracting the implied ones keeps this check
1073+
# meaning "the fixture names every member a consumer can activate";
1074+
# padding the fixture instead would make it mean less. The rule is
1075+
# read out of the manifest, so a seventh MEMBER is still caught.
1076+
#
1077+
# A temp file, not `<(...)`: this step also runs on windows-2022
1078+
# through Git Bash, where process substitution is emulated and not
1079+
# dependable -- and a check that behaves differently on one of the
1080+
# three hosts is the class of difference this job exists to catch.
1081+
grep -oE '^implies[[:space:]]*=.*' mcpp.toml \
1082+
| grep -oE '"[a-z0-9-]+"' | tr -d '"' | sort -u > /tmp/implied.txt
1083+
grep -oE '^\[features\.[a-z0-9-]+\]' mcpp.toml \
1084+
| sed 's/^\[features\.//; s/\]$//' | sort > /tmp/allfeats.txt
1085+
feats=$(comm -23 /tmp/allfeats.txt /tmp/implied.txt)
9031086
used=$(sed -n '/features = \[/,/\], host-module/p' tests/all-rules-compile/mcpp.toml \
9041087
| grep -oE '"[a-z-]+"' | tr -d '"' | sort)
9051088
[ -n "$feats" ] || {
Collapse file

‎README.md‎

Copy file name to clipboardExpand all lines: README.md
+56-5Lines changed: 56 additions & 5 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,28 @@ device source that reached no action, naming the file. That is the engine's
127127
half of this rule and it needs 2026.9.6.5.
128128

129129
The floor is the mcpp release whose engine carries what the member relies on.
130-
From 0.3.0 every rule shares one: **2026.9.7.1**, the release that reads
130+
From 0.4.0 every rule shares one: **2026.9.8.1**, the release in which a
131+
package's host modules are ordered by their IMPORT GRAPH rather than by their
132+
paths. This package needs that: `src/declare.cppm` is imported by every member,
133+
and `rules/` sorts before `src/`, so before that release the members were
134+
compiled first and failed with "failed to read compiled module".
135+
136+
**The floor could have been avoided, and was not.** `src/declare.cppm` sorts
137+
after `rules/`, which is exactly why it needs the ordering fix -- and naming it
138+
`aa_declare.cppm` at the package root would make the old PATH order happen to be
139+
correct, so 0.4.0 would run on 2026.9.7.1 with no floor move at all. That is
140+
declined on purpose: it encodes a load-bearing constraint in a filename with
141+
nothing enforcing it, which is the fragility the engine fix removes. A file
142+
renamed for a reason nobody can see is a defect waiting for the rename that
143+
looks harmless.
144+
145+
The previous shared floor was 2026.9.7.1, the release that reads
131146
`device_extensions` and `rule_module`, reports `[language] modules` and the
132-
package's own name to a build program, and writes the build program a declared
133-
rule set describes. A client below it does not get a degraded surface; it gets a
134-
build in which the rules never route -- the file falls through to the ordinary
135-
source scan and mcpp says it has no role for the extension.
147+
package's own name to a build program, writes the build program a declared rule
148+
set describes, and gives `mcpp::action` its `depfile` field. A client below it
149+
does not get a degraded surface; it gets a build in which the rules never route
150+
-- the file falls through to the ordinary source scan and mcpp says it has no
151+
role for the extension.
136152

137153
The previous shared floor was 2026.9.6.6, the release in which a payload a
138154
DEPENDENCY declared is both installed and answerable. Before it a rule could
@@ -224,6 +240,41 @@ identical under all three, so a project changes this and no consumer changes.
224240
| `object` | a section, through `.incbin` in a generated `.S` | total payload is large |
225241
| `sidecar` | a file beside the artifact, read at run time | hot reload, or a payload too large to link |
226242
243+
**Every payload a compile reads is in the build graph.** Two mechanisms carry
244+
that, and which one applies is decided by WHEN the thing is known.
245+
246+
A shader's `#include` is discovered by the compiler while it runs, so it arrives
247+
afterwards, in a depfile. `mcpp::action::depfile` carries it and all six rules
248+
pass one -- each spelling measured against the tool rather than read from its
249+
help text.
250+
251+
An `.incbin` is discovered by nobody. The assembler opens the file at assembly
252+
time; the generated `.S`'s own text does not change when the payload does; the
253+
object is assembled once. Measured on 0.3.0, in a sandbox against the published
254+
packages: editing a shader left the program printing the previous payload's byte
255+
count, with a green build.
256+
257+
Asking the assembler does not fix it, and that was measured rather than assumed.
258+
The compiler driver's `-MD` is a preprocessor channel that never sees `.incbin`;
259+
GNU as names it in its own `--MD`; clang's integrated assembler has no
260+
dependency output of any kind. Tracking it that way would work under GCC and
261+
fail silently under Clang -- worse than failing under both.
262+
263+
**So under `object` storage the generation is an ACTION and the payloads are its
264+
declared inputs.** That is the one graph primitive the engine has, used for what
265+
it is: a payload changes, the action reruns, its outputs count as new, and the
266+
edge that assembles them reruns. The command is `mcpp-embed`, built from this
267+
package through `tools = ["mcpp-embed"]` -- not published separately, because
268+
docs/05 section 2.14 states what that costs: "the tool's version IS the
269+
dependency's version, so a `protoc` that does not match its runtime is not
270+
expressible."
271+
272+
`header` and `sidecar` need none of it, and that was checked rather than
273+
assumed. Under `header` the bytes reach the artifact through generated data
274+
headers the payload's own compiler already writes as action outputs; under
275+
`sidecar` they are never compiled at all. So the default path builds no tool,
276+
and a consumer that never opts into object storage writes nothing extra.
277+
227278
**Which one is a measurement, not a preference.** With GCC 16.1 on 100 payloads
228279
of 16 KB each -- the size of an ordinary compute shader:
229280

0 commit comments

Comments
0 (0)
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.