-
Notifications
You must be signed in to change notification settings - Fork 452
Description
In a nutshell: in some cases, dune will restore some artifacts in a way that causes subsequent runs to digest them differently.
Reproduction case
This can be reproduced in the following cram test:
$ export DUNE_CACHE_ROOT=$PWD/.cache
$ export DUNE_CACHE=enabled
$ cat > dune-project <<EOF
> (lang dune 3.0)
> (using directory-targets 0.1)
> EOF
$ cat > dune <<EOF
> (rule
> (targets (dir d))
> (action
> (progn
> (bash "echo building d")
> (run mkdir d)
> (run chmod 755 d)
> (run touch d/x))))
>
> (rule
> (deps d)
> (action
> (progn
> (echo building other)
> (write-file other data))))
> EOF
$ dune build other
building d
building other
$ dune_cmd stat permissions _build/default/d
755
$ dune clean
$ dune build other
building other
$ dune_cmd stat permissions _build/default/d
775
$ dune clean
$ dune build other
When the action is executed, the directory is created with mode 755 (in particular, g-w), but when it it restored, it gets mode 775 (in particular g+w).
This causes a cache miss because the stored result for the rule that produces other has a different digest for its dependency d.
The third run can restore cleanly because it has cached the 775 version.
(note that this behavior does not happen if chmod 775 is in the dune file; and that the issue also happens when a subdirectory of the directory target has such a permission)
Expected behavior
The expected behavior would be that the second run can build cleanly (with only cache hits).
Context
This issue manifests itself in the package management rules. In particular, ocamlfind.1.9.6+dune will create ocamlfind/target/bin/ with mode 755, so cleaning and rebuilding will restore a different version of ocamlfind/target/bin/ (with mode 775) and trigger rebuilds of all the packages that depend on ocamlfind. And if such dependent packages have similar directory permissions, they will be rebuilt on subsequent builds.
Suggested solution
Storing just an executable bit instead of the full permissions seems like a good tradeoff - this is what git does for example.
To fix this issue, digesting could be changed to only take into account what is going to be restored. For example, by clearing or normalizing the group bits in Digest.Stats_for_digest.of_unix_stats, or maybe by replacing that part by just an executable boolean.
FTR, git determines that a file is executable by checking if any of the +x bits are set.
@mefyl also mentioned that something similar might be happening with the +w bits - when hardlinking we reset these bits so they might alter the digest.