@@ -35,20 +35,35 @@ work="$(mktemp -d)"
3535trap ' rm -rf "$work"' EXIT
3636cd " $work "
3737
38- mkdir -p src abi/src abi/abi-include dep/src dep/dep-include
38+ mkdir -p src abi/src abi/abi-include abi/abi-internal dep/src dep/dep-include
3939
40- # The provider: supplies a target-side layer and publishes one directory.
40+ # The provider: supplies a target-side layer, publishes one directory, and
41+ # keeps one to itself.
42+ #
43+ # ⭐⭐ THE TWO KEYS OF THIS RELEASE MEET IN THIS ONE PACKAGE, AND THAT IS THE
44+ # REAL-WORLD SHAPE. `openkal-musl` declares `provides = ["mcpp:c-abi=musl"]`
45+ # AND carries an internal header overlay. A layer's directories reach EVERY
46+ # unit, so publishing the overlay by mistake would put musl's `hidden`/`weak`
47+ # macros not merely on its consumers but on every translation unit of the
48+ # build — a strictly wider blast radius than the leak `private_include_dirs`
49+ # was written for.
50+ #
51+ # The two compose correctly by construction (the target side is assembled from
52+ # the provider's `publicUsage`, which is the filtered set), and "by
53+ # construction" is what an assertion is for.
4154cat > abi/mcpp.toml << 'EOF '
4255[package]
4356name = "abiprov"
4457version = "0.1.0"
4558provides = ["mcpp:c++-abi=libc++"]
4659
4760[build]
48- include_dirs = ["abi-include"]
61+ include_dirs = ["abi-internal", "abi-include"]
62+ private_include_dirs = ["abi-internal"]
4963EOF
5064printf ' export module abiprov;\nexport int abiprov_v() { return 1; }\n' > abi/src/abiprov.cppm
5165printf ' #pragma once\n' > abi/abi-include/marker.h
66+ printf ' #pragma once\n' > abi/abi-internal/private.h
5267
5368# The sibling: a dependency package that does NOT depend on the provider.
5469cat > dep/mcpp.toml << 'EOF '
@@ -125,4 +140,36 @@ if [ -n "$leaked" ]; then
125140 exit 1
126141fi
127142
128- echo " OK: the graph-supplied target side reaches every unit ($siblings sibling units), and nothing else does"
143+ # ⭐⭐ AND THE LAYER'S *PRIVATE* DIRECTORY REACHES NOBODY BUT ITSELF.
144+ #
145+ # A layer reaches every unit, so publishing an internal overlay by mistake is
146+ # wider here than anywhere else: it would land on every translation unit of the
147+ # build rather than on the provider's consumers.
148+ private_leak=" $( jq -r '
149+ [ .[]
150+ | select((.file | test("/abi/src/")) | not)
151+ | { f: .file, has: ((.arguments // (.command | split(" "))) | map(test("abi-internal")) | any) }
152+ | select(.has)
153+ | .f
154+ ] | .[]' compile_commands.json) "
155+
156+ if [ -n " $private_leak " ]; then
157+ echo " FAIL: the layer's PRIVATE directory reached units outside the package:"
158+ printf ' %s\n' " $private_leak " | sed ' s/^/ /'
159+ exit 1
160+ fi
161+
162+ # The control: private is not the same as dropped. The provider's own units
163+ # must still be built with it, or the assertion above passes for a build that
164+ # simply lost the directory.
165+ own_private=" $( jq -r '
166+ [ .[]
167+ | select(.file | test("/abi/src/"))
168+ | select((.arguments // (.command | split(" "))) | map(test("abi-internal")) | any)
169+ ] | length' compile_commands.json) "
170+ if [ " $own_private " -lt 1 ]; then
171+ echo " FAIL: the provider lost its own private directory — the check above proves nothing"
172+ exit 1
173+ fi
174+
175+ echo " OK: the graph-supplied target side reaches every unit ($siblings sibling units), its private directory reaches only its own $own_private unit(s), and nothing else spreads"
0 commit comments