diff --git a/test/blackbox-tests/test-cases/pkg/bin-narrowing/ambient-path-fallback.t b/test/blackbox-tests/test-cases/pkg/bin-narrowing/ambient-path-fallback.t new file mode 100644 index 00000000000..af7c582356b --- /dev/null +++ b/test/blackbox-tests/test-cases/pkg/bin-narrowing/ambient-path-fallback.t @@ -0,0 +1,69 @@ +Currently nothing is narrowed, so a lockdir binary shadows an identically-named +binary on the ambient PATH -- [%{bin:X}] and [(system X)] both resolve to the +lockdir copy, not the ambient one. Once narrowing hides the lockdir copy (its +package isn't a declared dep), [Context.which] for a Lock context falls back to +[Which.which ~path:builder.path] (the ambient/system PATH), so the binary would +then resolve from the surrounding environment. This test pins the current +"lockdir shadows ambient" behavior; it flips to the ambient fallback once +narrowing lands. + + $ make_lockdir + +A lockdir package [provider] installs [mybin] ("from lockdir"), which we will +NOT declare as a dependency: + + $ make_lockpkg provider <<'EOF' + > (version 0.0.1) + > (build + > (progn + > (system "echo '#!/bin/sh' > mybin") + > (system "echo 'echo from lockdir' >> mybin") + > (system "chmod +x mybin") + > (system "echo 'bin: [ \"mybin\" ]' > provider.install"))) + > EOF + +A [mybin] on the ambient PATH ("from system"): + + $ mkdir fakebin + $ cat >fakebin/mybin <<'EOF' + > #!/bin/sh + > echo from system + > EOF + $ chmod +x fakebin/mybin + + $ cat >dune <<'EOF' + > (rule + > (with-stdout-to mybin-avail (echo %{bin-available:mybin}))) + > (rule + > (enabled_if %{bin-available:mybin}) + > (action (with-stdout-to mybin-out (run %{bin:mybin})))) + > (rule + > (enabled_if %{bin-available:mybin}) + > (action (with-stdout-to mybin-system (system mybin)))) + > (rule + > (action (with-stdout-to path-output (bash "echo $PATH")))) + > EOF + + $ make_dune_project 3.25 + $ cat >> dune-project << 'EOF' + > (package (name mypkg) (allow_empty) (dir .)) + > EOF + +[mybin] is not a declared dep, but the lockdir copy is still resolved before +the ambient PATH copy: + + $ PATH="$PWD/fakebin:$PATH" dune build @all + $ cat _build/default/mybin-avail + true + $ cat _build/default/mybin-out + from lockdir + +[(system mybin)] resolves via $PATH. The lockdir provider's bin layout is on +$PATH and the shell finds the lockdir binary. + + $ cat _build/default/mybin-system + from lockdir + + $ env_added "$(cat _build/default/path-output)" "$PATH" | censor + $PWD/_build/_private/default/.pkg/provider.0.0.1-$DIGEST/target/bin + $PWD/fakebin diff --git a/test/blackbox-tests/test-cases/pkg/bin-narrowing/duplicate-workspace.t b/test/blackbox-tests/test-cases/pkg/bin-narrowing/duplicate-workspace.t new file mode 100644 index 00000000000..156173a67dd --- /dev/null +++ b/test/blackbox-tests/test-cases/pkg/bin-narrowing/duplicate-workspace.t @@ -0,0 +1,50 @@ +Two workspace packages installing a binary of the SAME name produce a "more +than one definition" error. + + $ make_lockdir + +Two workspace packages, each installing a binary named [dup]: + + $ mkdir -p a b + $ cat >a/dup.sh <<'EOF' + > #!/bin/sh + > echo from a + > EOF + $ chmod +x a/dup.sh + $ cat >a/dune <<'EOF' + > (install (package pkg-a) (section bin) (files (dup.sh as dup))) + > EOF + $ cat >b/dup.sh <<'EOF' + > #!/bin/sh + > echo from b + > EOF + $ chmod +x b/dup.sh + $ cat >b/dune <<'EOF' + > (install (package pkg-b) (section bin) (files (dup.sh as dup))) + > EOF + +A consumer that only depends on [pkg-a]: + + $ mkdir -p c + $ cat >c/dune <<'EOF' + > (rule (with-stdout-to dup-out (run %{bin:dup}))) + > EOF + + $ make_dune_project 3.25 + $ cat >> dune-project << 'EOF' + > (package (name pkg-a) (allow_empty) (dir a)) + > (package (name pkg-b) (allow_empty) (dir b)) + > (package (name pkg-c) (allow_empty) (dir c) (depends pkg-a)) + > EOF + +Today the lookup errors with "more than one definition". Once narrowing lands, +it would instead select [pkg-a]'s [dup], the only declared dep: + + $ dune build c/dup-out 2>&1 + File "b/dune", line 1, characters 47-53: + 1 | (install (package pkg-b) (section bin) (files (dup.sh as dup))) + ^^^^^^ + Error: binary "dup" is available from more than one definition. It is also + available in: + - a/dune:1 + [1] diff --git a/test/blackbox-tests/test-cases/pkg/bin-narrowing/env-binaries.t b/test/blackbox-tests/test-cases/pkg/bin-narrowing/env-binaries.t new file mode 100644 index 00000000000..4b1fb98fdd1 --- /dev/null +++ b/test/blackbox-tests/test-cases/pkg/bin-narrowing/env-binaries.t @@ -0,0 +1,34 @@ +A binary registered via [(env (_ (binaries ...)))] becomes a [Resolved] entry +in [local_bins] (Artifacts.add_binaries) with NO owning package. So, +env-registered binaries are exempt from narrowing entirely. + + $ make_lockdir + +A workspace executable exposed under a different name via [(env (binaries ...))]: + + $ cat >mytool.ml <<'EOF' + > let () = print_endline "from env binary" + > EOF + $ cat >dune <<'EOF' + > (executable (name mytool)) + > (env (_ (binaries (mytool.exe as mybin)))) + > (rule + > (with-stdout-to mybin-avail (echo %{bin-available:mybin}))) + > (rule + > (enabled_if %{bin-available:mybin}) + > (action (with-stdout-to mybin-out (run %{bin:mybin})))) + > EOF + + $ make_dune_project 3.25 + $ cat >> dune-project << 'EOF' + > (package (name mypkg) (allow_empty) (dir .)) + > EOF + +[mybin] resolves despite no declared deps, because env-registered binaries are +not narrowed: + + $ dune build @all + $ cat _build/default/mybin-avail + true + $ cat _build/default/mybin-out + from env binary diff --git a/test/blackbox-tests/test-cases/pkg/bin-pform-from-undeclared-pkg.t b/test/blackbox-tests/test-cases/pkg/bin-narrowing/lockdir-deps.t similarity index 100% rename from test/blackbox-tests/test-cases/pkg/bin-pform-from-undeclared-pkg.t rename to test/blackbox-tests/test-cases/pkg/bin-narrowing/lockdir-deps.t diff --git a/test/blackbox-tests/test-cases/pkg/bin-narrowing/mixed-transitive-deps.t b/test/blackbox-tests/test-cases/pkg/bin-narrowing/mixed-transitive-deps.t new file mode 100644 index 00000000000..d830045936d --- /dev/null +++ b/test/blackbox-tests/test-cases/pkg/bin-narrowing/mixed-transitive-deps.t @@ -0,0 +1,99 @@ +A "mixed" transitive dependency chain [p] -> [q] -> [r] has one edge crossing +the workspace/lockdir boundary. The two directions behave differently today. + +[p] (workspace) -> [q] (workspace) -> [r] (lockdir). The [q] -> [r] edge is +allowed, so this builds and [r-tool] resolves from [p]'s stanza. + + $ make_lockdir + +A lockdir package [r] that installs [r-tool]: + + $ make_lockpkg r <<'EOF' + > (version 0.0.1) + > (build + > (progn + > (system "echo '#!/bin/sh' > r-tool") + > (system "echo 'echo from r' >> r-tool") + > (system "chmod +x r-tool") + > (system "echo 'bin: [ \"r-tool\" ]' > r.install"))) + > EOF + + $ mkdir -p p q + $ cat >p/dune <<'EOF' + > (rule (with-stdout-to r-avail (echo %{bin-available:r-tool}))) + > EOF + + $ make_dune_project 3.25 + $ cat >> dune-project <<'EOF' + > (package (name p) (allow_empty) (dir p) (depends q)) + > (package (name q) (allow_empty) (dir q) (depends r)) + > EOF + + $ dune build p/r-avail +[r-tool] is resolved since all lockdir packages binaries are available: + + $ cat _build/default/p/r-avail + true + +Declaring [r] directly on [p] works too: + + $ make_dune_project 3.25 + $ cat >> dune-project <<'EOF' + > (package (name p) (allow_empty) (dir p) (depends q r)) + > (package (name q) (allow_empty) (dir q) (depends r)) + > EOF + $ dune clean + $ dune build p/r-avail + $ cat _build/default/p/r-avail + true + +Both blocks above print [true] today, so the transitive-only block looks +redundant. It earns its place only once narrowing lands. + +[p] (workspace) -> [q] (lockdir) -> [r] (workspace). Now the [q] -> [r] edge is +a lockdir package depending on a workspace package. This is a lock-VALIDATION +restriction, not a narrowing case: it is rejected before any binary resolution +runs, so narrowing does not change it -- it flips only when the in-out work +lifts the restriction (see [../lockdir-workspace-deps/basic.t] for the bare +rejection), at which point [p] should resolve [r-tool], since [r] is then in +[p]'s transitive closure. + + $ rm -rf p q r dune.lock + $ dune clean + + $ make_lockdir + $ make_lockpkg q <<'EOF' + > (version 0.0.1) + > (depends r) + > (build (system "true")) + > EOF + + $ mkdir -p p r + $ cat >r/r-tool.sh <<'EOF' + > #!/bin/sh + > echo from r + > EOF + $ chmod +x r/r-tool.sh + $ cat >r/dune <<'EOF' + > (install (package r) (section bin) (files (r-tool.sh as r-tool))) + > EOF + $ cat >p/dune <<'EOF' + > (rule (with-stdout-to r-avail (echo %{bin-available:r-tool}))) + > EOF + + $ make_dune_project 3.25 + $ cat >> dune-project <<'EOF' + > (package (name p) (allow_empty) (dir p) (depends q)) + > (package (name r) (allow_empty) (dir r)) + > EOF + + $ dune build p/r-avail 2>&1 + File "_build/_private/default/.lock/dune.lock/q.pkg", line 2, characters + 9-10: + The package "q" depends on the package "r", but "r" does not appear in the + lockdir _build/_private/default/.lock/dune.lock. + Error: At least one package dependency is itself not present as a package in + the lockdir _build/_private/default/.lock/dune.lock. + Hint: This could indicate that the lockdir is corrupted. Delete it and then + regenerate it by running: 'dune pkg lock' + [1] diff --git a/test/blackbox-tests/test-cases/pkg/bin-narrowing/multi-package.t b/test/blackbox-tests/test-cases/pkg/bin-narrowing/multi-package.t new file mode 100644 index 00000000000..009204e12dc --- /dev/null +++ b/test/blackbox-tests/test-cases/pkg/bin-narrowing/multi-package.t @@ -0,0 +1,94 @@ +Per-package narrowing with multiple owning packages: Currently, any workspace +package's stanzas resolve all the lockdir packages' binaries. Every workspace +package can see and resolve all the binaries provided by the lockdir packages. + + $ make_lockdir + +Two independent lockdir packages, each installing a distinct binary: + + $ make_lockpkg tool-a <<'EOF' + > (version 0.0.1) + > (build + > (progn + > (system "echo '#!/bin/sh' > bin-a") + > (system "echo 'echo from-a' >> bin-a") + > (system "chmod +x bin-a") + > (system "echo 'bin: [ \"bin-a\" ]' > tool-a.install"))) + > EOF + + $ make_lockpkg tool-b <<'EOF' + > (version 0.0.1) + > (build + > (progn + > (system "echo '#!/bin/sh' > bin-b") + > (system "echo 'echo from-b' >> bin-b") + > (system "chmod +x bin-b") + > (system "echo 'bin: [ \"bin-b\" ]' > tool-b.install"))) + > EOF + +Two workspace packages, each owning a subdirectory and depending on a +different lockdir tool: + + $ mkdir -p a b + $ cat >a/dune <<'EOF' + > (rule (with-stdout-to a-sees-a (echo %{bin-available:bin-a}))) + > (rule (with-stdout-to a-sees-b (echo %{bin-available:bin-b}))) + > (rule (action (with-stdout-to a-path (bash "echo $PATH")))) + > EOF + $ cat >b/dune <<'EOF' + > (rule (with-stdout-to b-sees-a (echo %{bin-available:bin-a}))) + > (rule (with-stdout-to b-sees-b (echo %{bin-available:bin-b}))) + > (rule (action (with-stdout-to b-path (bash "echo $PATH")))) + > EOF + + $ make_dune_project 3.25 + $ cat >> dune-project << 'EOF' + > (package (name pkg-a) (allow_empty) (dir a) (depends tool-a)) + > (package (name pkg-b) (allow_empty) (dir b) (depends tool-b)) + > EOF + + $ dune build @all +pkg-a (depends tool-a) sees both bin-a and bin-b: + + $ cat _build/default/a/a-sees-a + true + $ cat _build/default/a/a-sees-b + true + +pkg-b (depends tool-b) sees both bin-a and bin-b: + + $ cat _build/default/b/b-sees-b + true + $ cat _build/default/b/b-sees-a + true + +Both packages' env also gets every lockdir package's bin layout on $PATH, +regardless of which tool each declares: + + $ env_added "$(cat _build/default/a/a-path)" "$PATH" | censor + $PWD/_build/_private/default/.pkg/tool-b.0.0.1-$DIGEST1/target/bin + $PWD/_build/_private/default/.pkg/tool-a.0.0.1-$DIGEST2/target/bin + $ env_added "$(cat _build/default/b/b-path)" "$PATH" | censor + $PWD/_build/_private/default/.pkg/tool-b.0.0.1-$DIGEST1/target/bin + $PWD/_build/_private/default/.pkg/tool-a.0.0.1-$DIGEST2/target/bin + +The narrowing is ultimately about the build-dependency set, not just what is +visible: an unnarrowed lookup forces every lockdir package's cookie. From a +clean build, resolving only [pkg-a]'s rule still builds [tool-b] -- its install +cookie is materialised -- even though [pkg-a] does not depend on it: + + $ dune clean + $ dune build a/a-sees-a + $ if test -f "$(get_build_pkg_dir tool-b)/target/cookie" + > then echo "tool-b *was* built"; else echo "tool-b was not built"; fi + tool-b *was* built + +Once narrowing lands, each package sees only its own declared tool: [a-sees-b] +and [b-sees-a] flip to false while [a-sees-a]/[b-sees-b] stay true, and each +[*-path] shrinks to just its own tool. Crucially, the cookie check above flips +-- [tool-b] is no longer built -- proving the force-set itself is narrowed per +owner, not merely visibility and PATH. That is the property that actually +breaks the in-and-out cycle. The load-bearing pair is [a-sees-b] false +alongside [b-sees-b] true -- the same lockdir package [tool-b] is hidden from +[pkg-a] yet visible to [pkg-b], showing the narrowing set is keyed on each +dir's owning package, not a single global set. diff --git a/test/blackbox-tests/test-cases/pkg/resolve-program-from-undeclared-pkg.t b/test/blackbox-tests/test-cases/pkg/bin-narrowing/resolve-program.t similarity index 100% rename from test/blackbox-tests/test-cases/pkg/resolve-program-from-undeclared-pkg.t rename to test/blackbox-tests/test-cases/pkg/bin-narrowing/resolve-program.t diff --git a/test/blackbox-tests/test-cases/pkg/bin-narrowing/transitive-deps.t b/test/blackbox-tests/test-cases/pkg/bin-narrowing/transitive-deps.t new file mode 100644 index 00000000000..3f83d114a7c --- /dev/null +++ b/test/blackbox-tests/test-cases/pkg/bin-narrowing/transitive-deps.t @@ -0,0 +1,123 @@ +Currently, no narrowing of lockdir %{bin:X} and %{bin-available:X} lookups is +present, and all the binaries provided by all of the lockdir packages resolve. + + $ make_lockdir + +A lockdir package [transitive] that installs [transitive-bin]: + + $ make_lockpkg transitive <<'EOF' + > (version 0.0.1) + > (build + > (progn + > (system "\| cat > transitive-bin <<'EOI' + > "\| #!/usr/bin/env bash + > "\| echo from transitive + > "\| EOI + > ) + > (system "chmod +x transitive-bin") + > (system "echo 'bin: [ \"transitive-bin\" ]' > transitive.install") + > )) + > EOF + +A lockdir package [direct] that depends on [transitive] and installs [direct-bin]: + + $ make_lockpkg direct <<'EOF' + > (version 0.0.1) + > (depends transitive) + > (build + > (progn + > (system "\| cat > direct-bin <<'EOI' + > "\| #!/usr/bin/env bash + > "\| echo from direct + > "\| EOI + > ) + > (system "chmod +x direct-bin") + > (system "echo 'bin: [ \"direct-bin\" ]' > direct.install") + > )) + > EOF + +A sibling lockdir package [other] that installs [other-bin], not depended on +by [direct]: + + $ make_lockpkg other <<'EOF' + > (version 0.0.1) + > (build + > (progn + > (system "\| cat > other-bin <<'EOI' + > "\| #!/usr/bin/env bash + > "\| echo from other + > "\| EOI + > ) + > (system "chmod +x other-bin") + > (system "echo 'bin: [ \"other-bin\" ]' > other.install") + > )) + > EOF + +The project's package depends only on [direct] (with a [dir] field so +narrowing kicks in): + + $ make_dune_project 3.25 + $ cat >> dune-project << 'EOF' + > (package + > (allow_empty) + > (name my-pkg) + > (dir .) + > (depends direct)) + > EOF + + $ cat >dune <<'EOF' + > (rule + > (action + > (with-stdout-to path-output (bash "echo $PATH")))) + > (rule + > (with-stdout-to direct-out (echo %{bin-available:direct-bin}))) + > (rule + > (with-stdout-to transitive-out (echo %{bin-available:transitive-bin}))) + > (rule + > (with-stdout-to other-out (echo %{bin-available:other-bin}))) + > EOF + + $ dune build @all + +[direct-bin] (direct dep) is available: + + $ cat _build/default/direct-out + true + +[transitive-bin] is available: + + $ cat _build/default/transitive-out + true + +[other-bin] (package not in the closure) is also available: + + $ cat _build/default/other-out + true + +All the lockdir packages' bin layout is added to $PATH: + + $ env_added "$(cat _build/default/path-output)" "$PATH" | censor + $PWD/_build/_private/default/.pkg/other.0.0.1-$DIGEST1/target/bin + $PWD/_build/_private/default/.pkg/direct.0.0.1-$DIGEST2/target/bin + $PWD/_build/_private/default/.pkg/transitive.0.0.1-$DIGEST3/target/bin + +In the current code, expanding a %{bin:X}/%{bin-available:X} pform forces the +install [cookie] of every lockdir package through [Artifacts_and_deps.of_closure] +(pkg_rules.ml). So even a build of a single pform-expanding target pulls in +[other], a package entirely outside [direct]'s closure: + + $ dune clean + $ dune build direct-out + $ if test -f "$(get_build_pkg_dir other)/target/cookie" + > then echo "other *was* built"; else echo "other was not built"; fi + other *was* built + +[transitive] (in [direct]'s closure) is built too, as it must be: + + $ if test -f "$(get_build_pkg_dir transitive)/target/cookie" + > then echo "transitive *was* built"; else echo "transitive was not built"; fi + transitive *was* built + +The spurious [other] build dependency (not merely the visibility above) is what +causes the in-out cycles that the narrowing removes; once it lands, [other] is +no longer built here, while [direct] and [transitive] still are. diff --git a/test/blackbox-tests/test-cases/pkg/bin-narrowing/workspace-deps.t b/test/blackbox-tests/test-cases/pkg/bin-narrowing/workspace-deps.t new file mode 100644 index 00000000000..a645afaaf6d --- /dev/null +++ b/test/blackbox-tests/test-cases/pkg/bin-narrowing/workspace-deps.t @@ -0,0 +1,51 @@ +Currently there is no narrowing of workspace-installed binaries: a package's +stanzas resolve a sibling package's binary even without declaring a dependency +on it. + +Once narrowing lands, this will be restricted to the owning package's declared +(transitive) dependency closure matching what the package would see built in +isolation (opam-repo-ci), where an undeclared sibling isn't installed. See +[lockdir-deps.t] for the lockdir side. + + $ make_lockdir + +Two workspace packages, each owning a subdirectory. [producer] installs a +binary [producer-bin]; [consumer] uses it WITHOUT declaring a dependency on +[producer]. + + $ mkdir -p producer consumer + $ cat >producer/producer-bin.sh <<'EOF' + > #!/bin/sh + > echo "hello from producer" + > EOF + $ chmod +x producer/producer-bin.sh + $ cat >producer/dune <<'EOF' + > (install + > (package producer) + > (section bin) + > (files (producer-bin.sh as producer-bin))) + > EOF + $ cat >consumer/dune <<'EOF' + > (rule + > (with-stdout-to producer-available (echo %{bin-available:producer-bin}))) + > (rule + > (with-stdout-to producer-run (run %{bin:producer-bin}))) + > EOF + + $ make_dune_project 3.25 + $ cat >> dune-project << 'EOF' + > (package (name producer) (allow_empty) (dir producer)) + > (package (name consumer) (allow_empty) (dir consumer)) + > EOF + + $ dune build @all +Even though [consumer] does not depend on [producer], the workspace binary is +available (workspace binaries are not narrowed): + + $ cat _build/default/consumer/producer-available + true + +And it runs: + + $ cat _build/default/consumer/producer-run + hello from producer diff --git a/test/blackbox-tests/test-cases/pkg/bin-narrowing/workspace-shadows-lockdir.t b/test/blackbox-tests/test-cases/pkg/bin-narrowing/workspace-shadows-lockdir.t new file mode 100644 index 00000000000..4e19b95c2be --- /dev/null +++ b/test/blackbox-tests/test-cases/pkg/bin-narrowing/workspace-shadows-lockdir.t @@ -0,0 +1,127 @@ +When a binary of the same name is provided by both a workspace package and a +lockdir package, resolution of [%{bin:X}] consults [local_bins]. So the +workspace binary shadows the lockdir one. + + $ make_lockdir + +A lockdir package [provider] that installs [mybin] printing "from lockdir": + + $ make_lockpkg provider <<'EOF' + > (version 0.0.1) + > (build + > (progn + > (system "echo '#!/bin/sh' > mybin") + > (system "echo 'echo from lockdir' >> mybin") + > (system "chmod +x mybin") + > (system "echo 'bin: [ \"mybin\" ]' > provider.install"))) + > EOF + +A workspace package [mypkg] that installs its own [mybin] printing "from +workspace": + + $ cat >mybin-ws.sh <<'EOF' + > #!/bin/sh + > echo from workspace + > EOF + $ chmod +x mybin-ws.sh + $ cat >dune <<'EOF' + > (install + > (package mypkg) + > (section bin) + > (files (mybin-ws.sh as mybin))) + > (rule + > (with-stdout-to mybin-out (run %{bin:mybin}))) + > (rule + > (action (with-stdout-to path-output (bash "echo $PATH")))) + > EOF + +With [(depends provider)] the workspace binary still wins over the declared +lockdir binary of the same name: + + $ make_dune_project 3.25 + $ cat >> dune-project << 'EOF' + > (package (name mypkg) (allow_empty) (dir .) (depends provider)) + > EOF + + $ dune build mybin-out + $ cat _build/default/mybin-out + from workspace + +Without depending on [provider] at all, the workspace binary wins just the +same: + + $ make_dune_project 3.25 + $ cat >> dune-project << 'EOF' + > (package (name mypkg) (allow_empty) (dir .)) + > EOF + + $ dune clean + $ dune build mybin-out path-output + $ cat _build/default/mybin-out + from workspace + +The lockdir [provider]'s bin layout is still on $PATH: + + $ env_added "$(cat _build/default/path-output)" "$PATH" | censor + $PWD/_build/_private/default/.pkg/provider.0.0.1-$DIGEST/target/bin + +When the workspace binary is DISABLED via [(enabled_if false)], its +[local_bins] origin is filtered out and resolution falls through to the lockdir +package. Currently that lookup isn't narrowed, so mybin resolves regardless of +(depends). + + $ cat >dune <<'EOF' + > (install + > (package mypkg) + > (section bin) + > (enabled_if false) + > (files (mybin-ws.sh as mybin))) + > (rule + > (with-stdout-to mybin-avail (echo %{bin-available:mybin}))) + > (rule + > (enabled_if %{bin-available:mybin}) + > (action (with-stdout-to mybin-out (run %{bin:mybin})))) + > (rule + > (action (with-stdout-to path-output (bash "echo $PATH")))) + > EOF + +Declaring [provider] makes the (now disabled) workspace binary fall through +to the lockdir binary: + + $ make_dune_project 3.25 + $ cat >> dune-project << 'EOF' + > (package (name mypkg) (allow_empty) (dir .) (depends provider)) + > EOF + + $ dune clean + $ dune build @all + $ cat _build/default/mybin-avail + true + $ cat _build/default/mybin-out + from lockdir + +The lockdir [provider]'s bin layout is on $PATH: + + $ env_added "$(cat _build/default/path-output)" "$PATH" | censor + $PWD/_build/_private/default/.pkg/provider.0.0.1-$DIGEST/target/bin + +Without declaring [provider], the lookup still resolves to the lockdir binary: + + $ make_dune_project 3.25 + $ cat >> dune-project << 'EOF' + > (package (name mypkg) (allow_empty) (dir .)) + > EOF + + $ dune clean + $ dune build @all + $ cat _build/default/mybin-avail + true + +The lockdir [provider]'s bin layout is on $PATH: + + $ env_added "$(cat _build/default/path-output)" "$PATH" | censor + $PWD/_build/_private/default/.pkg/provider.0.0.1-$DIGEST/target/bin + +Both blocks above print [true] with [provider] on $PATH today, so the +without-provider block looks redundant. It earns its place only once narrowing +lands. diff --git a/test/blackbox-tests/test-cases/pkg/bin-narrowing/workspace-transitive-deps.t b/test/blackbox-tests/test-cases/pkg/bin-narrowing/workspace-transitive-deps.t new file mode 100644 index 00000000000..97b9c6f5852 --- /dev/null +++ b/test/blackbox-tests/test-cases/pkg/bin-narrowing/workspace-transitive-deps.t @@ -0,0 +1,69 @@ +Workspace-installed binaries (the local_bins in [Artifacts]) are currently not +narrowed: a package's stanzas resolve any workspace package's binary, including +packages not in its dependency closure. + +Once narrowing lands, this will be restricted to the owning package's TRANSITIVE +workspace dependency closure -- like [transitive-deps.t] for the lockdir side. In +particular a transitively-depended package's binary must still resolve (which is +what distinguishes narrowing to the transitive closure from narrowing to only the +direct dependencies). + + $ make_lockdir + +Three workspace packages forming a chain [p] -> [q] -> [r]. [q] installs +[q-tool] and [r] installs [r-tool]. A sibling [s] installs [s-tool] and is not +in [p]'s dependency closure. + + $ mkdir -p p q r s + $ cat >q/q-tool.sh <<'EOF' + > #!/bin/sh + > echo from q + > EOF + $ cat >r/r-tool.sh <<'EOF' + > #!/bin/sh + > echo from r + > EOF + $ cat >s/s-tool.sh <<'EOF' + > #!/bin/sh + > echo from s + > EOF + $ chmod +x q/q-tool.sh r/r-tool.sh s/s-tool.sh + $ cat >q/dune <<'EOF' + > (install (package q) (section bin) (files (q-tool.sh as q-tool))) + > EOF + $ cat >r/dune <<'EOF' + > (install (package r) (section bin) (files (r-tool.sh as r-tool))) + > EOF + $ cat >s/dune <<'EOF' + > (install (package s) (section bin) (files (s-tool.sh as s-tool))) + > EOF + $ cat >p/dune <<'EOF' + > (rule (with-stdout-to q-avail (echo %{bin-available:q-tool}))) + > (rule (with-stdout-to r-avail (echo %{bin-available:r-tool}))) + > (rule (with-stdout-to s-avail (echo %{bin-available:s-tool}))) + > EOF + + $ make_dune_project 3.25 + $ cat >> dune-project <<'EOF' + > (package (name p) (allow_empty) (dir p) (depends q)) + > (package (name q) (allow_empty) (dir q) (depends r)) + > (package (name r) (allow_empty) (dir r)) + > (package (name s) (allow_empty) (dir s)) + > EOF + + $ dune build @all +[q-tool] (direct dep p -> q) is available: + + $ cat _build/default/p/q-avail + true + +[r-tool] (transitive dep p -> q -> r) is available: + + $ cat _build/default/p/r-avail + true + +[s-tool] (sibling not in p's closure) is available too, since workspace binaries +are not narrowed yet: + + $ cat _build/default/p/s-avail + true