@@ -251,7 +251,7 @@ jobs:
251251 TOML
252252 sed -i 's/^ //' "$d/mcpp.toml"
253253 printf '#include <unistd.h>\n#include <stdio.h>\nint main(void){ printf("%%d\\n", isatty(1)); return 0; }\n' > "$d/src/main.c"
254- ( cd "$d" && "$MCPP" build --toolchain '${{ matrix.toolchain }}' )
254+ ( cd "$d" && mcpp build --toolchain '${{ matrix.toolchain }}' )
255255 bin="$(find "$d/target" -name isattyprobe -type f | head -1)"
256256 test -n "$bin" || { echo "::error::the probe did not build"; exit 1; }
257257
@@ -291,26 +291,47 @@ jobs:
291291 run : |
292292 extra=''
293293 [ -n '${{ matrix.target }}' ] && extra='--target ${{ matrix.target }}'
294- "$MCPP" build --toolchain '${{ matrix.toolchain }}' $extra
294+ mcpp build --toolchain '${{ matrix.toolchain }}' $extra
295295 test -s compile_commands.json \
296296 || { echo "::error::no compile_commands.json — nothing to check"; exit 1; }
297297
298- # ⚠️ A DENOMINATOR. With no consumer row the greps below are
299- # vacuously true, which is the false green this check must not have.
300- rows="$(grep -c '"file"' compile_commands.json || true)"
301- [ "${rows:-0}" -ge 1 ] \
302- || { echo "::error::compile_commands.json has no rows"; exit 1; }
298+ # ⚠️⚠️ PER ROW, NOT OVER THE FILE. This example depends on the
299+ # package by path, so THE PROVIDER'S OWN ROWS ARE IN THIS SAME FILE
300+ # and they carry the overlay legitimately — that is what "private,
301+ # not unused" means. A `grep` over the whole file cannot tell the two
302+ # apart and would call the package's own build a leak.
303+ #
304+ # ⚠️ Separators are normalised because the Windows runner writes
305+ # `…\musl\src\include`, and a check that silently stops matching on
306+ # one platform is a check that platform does not have.
307+ norm='(.arguments // (.command | split(" "))) | join(" ") | gsub("\\\\"; "/")'
308+ jq -r ".[] | select((.file | gsub(\"\\\\\\\\\"; \"/\")) | test(\"examples/cross-hello\")) | $norm" \
309+ compile_commands.json > consumer.txt
310+ jq -r ".[] | select((.file | gsub(\"\\\\\\\\\"; \"/\")) | test(\"examples/cross-hello\") | not) | $norm" \
311+ compile_commands.json > provider.txt
312+
313+ # ⚠️ DENOMINATORS ON BOTH SIDES. With no consumer row every absence
314+ # below is vacuously true; with no provider row the control is.
315+ cons="$(wc -l < consumer.txt)"; prov="$(wc -l < provider.txt)"
316+ echo " rows: consumer=$cons provider=$prov"
317+ [ "$cons" -ge 1 ] && [ "$prov" -ge 1 ] \
318+ || { echo "::error::consumer=$cons provider=$prov — nothing was checked"; exit 1; }
303319
304320 bad=0
305321 for d in musl/src/include musl/src/internal musl-generated/internal; do
306- if grep -q -- "$d" compile_commands.json ; then
322+ if grep -q -- "$d" consumer.txt ; then
307323 echo "::error::the internal overlay '$d' reached a consumer"
308324 bad=1
309325 fi
326+ # The other half of the same key: private is not the same as
327+ # dropped. musl's own sources must still reach their declarations.
328+ grep -q -- "$d" provider.txt \
329+ || { echo "::error::'$d' is on nobody's command line — private_include_dirs withheld it from this package too"
330+ bad=1; }
310331 done
311- # The control: a PUBLIC directory must still be there , or this check
332+ # And something PUBLIC must still cross the boundary , or this check
312333 # would pass for a build that published nothing at all.
313- grep -q -- 'port/include' compile_commands.json \
334+ grep -q -- 'port/include' consumer.txt \
314335 || { echo "::error::no public include directory reached the consumer — the check above proves nothing"
315336 exit 1; }
316337 [ "$bad" = 0 ] || exit 1
0 commit comments