This commit is contained in:
swrup 2025-11-11 02:07:51 +01:00
parent aa2ff7b2f0
commit 2f3113f55d
11742 changed files with 1223940 additions and 0 deletions

View file

@ -0,0 +1,11 @@
(cram
(applies_to sandboxing)
(deps %{bin:file}))
(cram
(applies_to sandboxing-stale-directory-target)
(deps %{bin:bash}))
(cram
(applies_to interfere-sandbox-deletion)
(deps %{bin:bash}))

View file

@ -0,0 +1,39 @@
Create a bad rule that doesn't allow the sandbox to be deleted.
Note that this rule also fails. We make sure that we see both the error from
the rule and the sandbox cleanup.
$ cat >dune <<EOF
> (rule
> (target foo)
> (action (bash "\| touch foo && mkdir bar && touch bar/x && chmod -w bar &&
> "\| echo failed action && exit 1
> )))
> EOF
$ cat >dune-project <<EOF
> (lang dune 3.11)
> EOF
$ dune build ./foo --sandbox=copy 2>&1 | sed -E 's#/.*.sandbox/[^/]+#/.sandbox/$SANDBOX#g'
File "dune", lines 1-5, characters 0-157:
1 | (rule
2 | (target foo)
3 | (action (bash "\| touch foo && mkdir bar && touch bar/x && chmod -w bar &&
4 | "\| echo failed action && exit 1
5 | )))
Error: failed to delete sandbox in
_build/.sandbox/$SANDBOX
Reason:
rmdir(_build/.sandbox/$SANDBOX/default/bar): Directory not empty
File "dune", lines 1-5, characters 0-157:
1 | (rule
2 | (target foo)
3 | (action (bash "\| touch foo && mkdir bar && touch bar/x && chmod -w bar &&
4 | "\| echo failed action && exit 1
5 | )))
failed action
Manual cleaning step so the dune executing the test suite doesn't croak trying
to delete the readonly dir
$ chmod -R +w _build && rm -rf _build

View file

@ -0,0 +1,29 @@
Remove write permissions from a sandbox directory and observe the error we get
$ cat >dune-project <<EOF
> (lang dune 3.11)
> (using directory-targets 0.1)
> EOF
$ cat >dune <<EOF
> (rule
> (target (dir foo))
> (action (system "mkdir foo && touch foo/bar && chmod -w foo")))
> EOF
$ dune build ./foo --sandbox=copy 2>&1 | sed -E 's#/.*.sandbox/[^/]+#/.sandbox/$SANDBOX#g'
File "dune", lines 1-3, characters 0-90:
1 | (rule
2 | (target (dir foo))
3 | (action (system "mkdir foo && touch foo/bar && chmod -w foo")))
Error: failed to delete sandbox in
_build/.sandbox/$SANDBOX
Reason:
rmdir(_build/.sandbox/$SANDBOX/default/foo): Directory not empty
Error:
rename(_build/.sandbox/$SANDBOX/default/foo): Permission denied
-> required by _build/default/foo
Manual cleaning step so the dune executing the test suite doesn't croak trying
to delete the readonly dir
$ chmod -R +w _build && rm -rf _build

View file

@ -0,0 +1,30 @@
Test sandboxing when depending on things from the install context using
(package ..).
$ cat >dune-project <<EOF
> (lang dune 3.11)
> (package (name foo))
> EOF
$ mkdir foo/
$ cat >foo/dune <<EOF
> (executable
> (package foo)
> (public_name mybin))
> EOF
$ cat >foo/mybin.ml <<EOF
> print_endline "hello from package foo";;
> EOF
$ cat >dune <<EOF
> (rule
> (alias foo)
> (deps (sandbox always) (package foo))
> (action (system "pwd; command -v mybin; echo %{bin:mybin}; mybin")))
> EOF
$ dune build --sandbox symlink @foo 2>&1 | sed -E 's#.*.sandbox/[^/]+/#.sandbox/$SANDBOX/#g'
.sandbox/$SANDBOX/default
.sandbox/$SANDBOX/install/default/bin/mybin
../install/default/bin/mybin
hello from package foo

View file

@ -0,0 +1,22 @@
A faulty test escapes the sandbox by creating its target outside the sandbox
$ cat >dune-project <<EOF
> (lang dune 3.11)
> (using directory-targets 0.1)
> EOF
$ cat >dune <<EOF
> (rule
> (target (dir foo))
> (action (bash "mkdir $PWD/_build/default/foo && mkdir foo")))
> EOF
$ dune build foo/ --sandbox=copy 2>&1 | sed -E 's/characters [0-9]+-[0-9]+/characters <REDACTED>/'
File "dune", lines 1-3, characters <REDACTED>:
1 | (rule
2 | (target (dir foo))
3 | (action (bash "mkdir $TESTCASE_ROOT/_build/default/foo && mkdir foo")))
Error: Target _build/default/foo of kind "directory" already exists in the
build directory
Hint: delete this file manually or check the permissions of the parent
directory of this file

View file

@ -0,0 +1,384 @@
If an action does not respect the dependency specification, it results in a broken
build. Dune fails to detect that:
$ echo '(lang dune 1.12)' > dune-project
$ true > dune
$ echo '(rule (target a) (deps) (action (bash "echo a | tee a > b")))' >> dune
$ echo '(rule (target b) (deps) (action (bash "echo b | tee a > b")))' >> dune
$ echo '(rule (target c) (deps a b) (action (bash "cat a b > c")))' >> dune
$ dune build c
$ cat _build/default/c
b
b
$ true > dune
$ echo '(rule (target a) (deps) (action (bash "echo a > a")))' >> dune
$ echo '(rule (target b) (deps) (action (bash "echo b > b")))' >> dune
$ echo '(rule (target c) (deps a b) (action (bash "cat a b > c")))' >> dune
$ dune build c
$ cat _build/default/c
b
b
(it's not obvious what the correct result is on the first invocation, but the second
invocation is clearly broken (it uses a wrongly cached result))
These rules clearly depend on sandboxing. Specifying that makes the build
well-behaved:
$ rm -rf _build
$ true > dune
$ echo '(rule (target a) (deps (sandbox always)) (action (bash "echo a | tee a > b")))' >> dune
$ echo '(rule (target b) (deps (sandbox always)) (action (bash "echo b | tee a > b")))' >> dune
$ echo '(rule (target c) (deps a b) (action (bash "cat a b > c")))' >> dune
$ dune build c
$ cat _build/default/c
a
b
Some errors:
$ rm -rf _build
$ true > dune
$ echo '(rule (target a) (deps (sandbox always none)) (action (bash "echo a > a")))' >> dune
$ dune build a
File "dune", line 1, characters 32-43:
1 | (rule (target a) (deps (sandbox always none)) (action (bash "echo a > a")))
^^^^^^^^^^^
Error: Inconsistent sandboxing configuration. Sandboxing mode none is both
allowed and disallowed
[1]
When we don't pass [preserve_file_kind], the rules can observe the file kind
changing based on sandbox mode chosen:
$ rm -rf _build
$ echo text-file > text-file
$ true > dune
$ echo '(rule (deps text-file) (target t) (action (with-stdout-to %{target} (run file -h text-file))))' >> dune
$ dune build t --sandbox symlink
$ cat _build/default/t | grep -Eo 'link|ASCII'
link
$ dune build t --sandbox none
$ cat _build/default/t | grep -Eo 'link|ASCII'
ASCII
$ dune build t --sandbox copy
$ cat _build/default/t | grep -Eo 'link|ASCII'
ASCII
$ dune build t --sandbox hardlink
$ cat _build/default/t | grep -Eo 'link|ASCII'
ASCII
When we pass [preserve_file_kind], the file type seen by the rule is preserved:
$ true > dune
$ echo '(rule (target t) (deps text-file (sandbox preserve_file_kind)) (action (with-stdout-to %{target} (run file -h text-file))))' >> dune
$ dune build t --sandbox symlink
$ cat _build/default/t | grep -Eo 'link|ASCII'
ASCII
$ dune build t --sandbox none
$ cat _build/default/t | grep -Eo 'link|ASCII'
ASCII
$ dune build t --sandbox copy
$ cat _build/default/t | grep -Eo 'link|ASCII'
ASCII
$ dune build t --sandbox hardlink
$ cat _build/default/t | grep -Eo 'link|ASCII'
ASCII
If rule fails to generate targets, we give a good error message, even with sandboxing:
$ true > dune
$ echo '(rule (target t) (deps (sandbox always)) (action (run true)))' >> dune
$ dune build t
File "dune", line 1, characters 0-61:
1 | (rule (target t) (deps (sandbox always)) (action (run true)))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: Rule failed to generate the following targets:
- t
[1]
If rule is configured to require sandboxing, but clearly needs none, we sandbox
anyway.
$ true > dune
$ cat >dune <<EOF
> (rule
> (target t)
> (deps (sandbox always))
> (action (write-file t "")))
> EOF
$ dune build t
If an action [chdir]s to a non-existing directory, it is created.
$ echo '(lang dune 2.6)' > dune-project
$ cat > dune <<EOF
> (rule
> (targets t)
> (deps (sandbox none))
> (action
> (chdir dir
> (progn
> (no-infer (write-file file hi))
> (write-file ../t hi)))))
> EOF
$ dune build t
$ cat _build/default/dir/file
hi
$ cat _build/default/t
hi
Now the same but with sandboxing. The action succeeds but the directory is not
re-created in the build directory.
$ cat > dune <<EOF
> (rule
> (targets t)
> (deps (sandbox always))
> (action
> (chdir dir
> (progn
> (run true)
> (no-infer (write-file file hi))
> (write-file ../t hi)))))
> EOF
$ dune build t
$ cat _build/default/dir/file
cat: _build/default/dir/file: No such file or directory
[1]
$ cat _build/default/t
hi
Show errors when [chdir]ing outside of the build directory.
$ cat > dune <<EOF
> (rule
> (targets t)
> (deps (sandbox none))
> (action
> (chdir /dir
> (progn
> (no-infer (write-file file hi))
> (write-file ../t hi)))))
> EOF
$ dune build t
File "dune", line 5, characters 9-13:
5 | (chdir /dir
^^^^
Error: Directory /dir is outside the build directory. This is not allowed.
[1]
$ cat > dune <<EOF
> (rule
> (targets t)
> (deps (sandbox none))
> (action (chdir ../../dir (progn (no-infer (write-file file hi)) (write-file ../t hi)))))
> EOF
$ dune build t
File "dune", line 4, characters 16-25:
4 | (action (chdir ../../dir (progn (no-infer (write-file file hi)) (write-file ../t hi)))))
^^^^^^^^^
Error: Directory
$TESTCASE_ROOT/../../dir
is outside the build directory. This is not allowed.
[1]
$ cat > dune <<EOF
> (rule
> (targets t)
> (deps (sandbox none))
> (action
> (chdir ../dir
> (progn
> (no-infer (write-file file hi))
> (write-file ../t hi)))))
> EOF
$ dune build t
File "dune", line 5, characters 9-15:
5 | (chdir ../dir
^^^^^^
Error: Directory
$TESTCASE_ROOT/../dir
is outside the build directory. This is not allowed.
[1]
Sandboxing with hard links correctly resolves (follows) symbolic links.
$ echo hi > file
$ ln -s file link
$ cat > dune <<EOF
> (rule
> (targets t)
> (deps link)
> (action (bash "cp %{deps} t; dune_cmd stat kind %{deps}")))
> EOF
The action observed [link] as a regular file with the right contents.
$ dune build t --sandbox hardlink
regular file
$ cat _build/default/t
hi
# CR-someday amokhov: Apparently, Dune turns [link] into a regular file when
# copying it into the build directory. This seems wrong. Fix this or document
# why this is the right thing to do.
$ dune_cmd stat kind _build/default/link
regular file
Now let's test what happens with external files.
$ mkdir test; cd test
$ echo '(lang dune 2.9)' > dune-project
$ echo '(lang dune 2.9)' > dune-workspace
Now [../link] is an external file from the point of view of Dune, so it will not
be copied to the build directory.
$ cat > dune <<EOF
> (rule
> (targets t)
> (deps ../link)
> (action (bash "cp %{deps} t; dune_cmd stat kind %{deps}")))
> EOF
# CR-someday amokhov: The action observe a symlink. It should not be the case
# since the symlink will surely not point to the right place.
$ cat ../link
hi
$ dune build t --sandbox hardlink
symbolic link
Using the absolute path [$PWD/../link].
$ cat > dune <<EOF
> (rule
> (targets t)
> (deps $PWD/../link)
> (action (bash "cp %{deps} t; dune_cmd stat kind %{deps}")))
> EOF
Sandboxed actions can observe external dependencies as symbolic links.
$ dune build t --sandbox hardlink
$ cat _build/default/t
hi
Now let's see what happens if the the symbolic link is produced by a rule.
$ cat > dune <<EOF
> (rule
> (deps file)
> (targets link)
> (action (run ln -s file link)))
> (rule
> (targets t)
> (deps link)
> (action (system "cp %{deps} t; dune_cmd stat kind %{deps}")))
> EOF
$ echo hi again > file
The action observed [link] as a regular file with the right contents.
$ dune build t --sandbox hardlink
regular file
$ cat _build/default/t
hi again
Test that [link] is in fact a symbolic link.
$ dune_cmd stat kind _build/default/link
symbolic link
Test how Dune handles a loop with symbolic links.
$ cat > dune <<EOF
> (rule
> (targets link)
> (action (bash "ln -s link link")))
> (rule
> (targets t)
> (deps link)
> (action (bash "cp %{deps} t; dune_cmd stat kind %{deps}")))
> EOF
This loop is caught immediately after running the rule that creates it.
$ dune build t --sandbox hardlink
File "dune", lines 1-3, characters 0-57:
1 | (rule
2 | (targets link)
3 | (action (bash "ln -s link link")))
Error: Error trying to read targets after a rule was run:
- link: Cyclic symbolic link
[1]
Let's try to create it in another way. We'll link to the external [../link],
which is circular.
$ cat > dune <<EOF
> (rule
> (deps $PWD/../link)
> (targets link)
> (action (run ln -s $PWD/../link link)))
> (rule
> (targets t)
> (deps link)
> (action (system "cp %{deps} t; dune_cmd stat kind %{deps}")))
> EOF
$ (cd ..; rm link; ln -s link link)
Still doesn't work: now the loop is detected when running [stat] on the
dependency.
$ dune build t --sandbox hardlink 2>&1 | grep -v "lines 1"
1 | (rule
2 | (deps $TESTCASE_ROOT/test/../link)
3 | (targets link)
4 | (action (run ln -s $TESTCASE_ROOT/test/../link link)))
Error: File unavailable:
$TESTCASE_ROOT/test/../link
Cyclic symbolic link
So, it seems like we must play dirty to create a symbolic link loop.
$ cat > dune <<EOF
> (rule
> (targets link)
> (action (run ln -s $PWD/../link link)))
> (rule
> (deps link (sandbox none))
> (targets dirty-rule)
> (action (system "(cd $PWD/..; rm link; ln -s link link); touch dirty-rule")))
> (rule
> (targets t)
> (deps link dirty-rule)
> (action (system "cp %{deps} t; dune_cmd stat kind %{deps}")))
> EOF
$ (cd ..; rm link; ln -s file link)
Finally, we get to see the error message printed out at sandbox creation.
$ dune build t --sandbox hardlink
File "dune", lines 8-11, characters 0-105:
8 | (rule
9 | (targets t)
10 | (deps link dirty-rule)
11 | (action (system "cp %{deps} t; dune_cmd stat kind %{deps}")))
Error: Sandbox creation error: cannot resolve symbolic link
"_build/default/link".
Reason: Too many indirections; is this a cyclic symbolic link?
[1]