Skip to content

Commit

Permalink
rust: Replace keep_fingerprints logic with unconditional cargo clean
Browse files Browse the repository at this point in the history
Due to Earthly's layer cache code added with `COPY` (even with `--keep-ts`) can
end up with timestamps (`mtime`) corresponding to the point of creation of the
cache entry, not the current time.

However on a following build the `target` mount cache may contain builds from
other branches, with different code for those dependencies, which have a newer
`mtime`. In this case `cargo` will think it can use the cached dependency
instead of rebuilding (because the code appears older than the cached entry
under `target`).

Avoid this by using `cargo clean` to remove the build artifacts for any local
crate. This should become unnecessary with rust-lang/cargo#14136

This replaces the old behaviour of removing the fingerprints directory. Using
`cargo clean` uses a proper cargo API rather than relying on implementation
details like where the fingerprints live and what the consequence removing them
is. It may also keep the cached data smaller since it removes the build
artifacts which will likely never be reused due to the lack of fingerprint.

Note that the previous fingerprint cleaning was subject to a race where a
different parallel build could reintroduce some fingerprints between `DO
+REMOVE_SOURCE_FINGERPRINTS` and the `RUN ... cargo $args`. For that reason the
calls to `cargo clean` here are made within the same `RUN` command so that the
target cache remains locked.

By switching to `cargo metadata` the requirement for `tomljson` is removed.
  • Loading branch information
ijc committed Aug 19, 2024
1 parent a9b9b6e commit d24d25c
Show file tree
Hide file tree
Showing 2 changed files with 4 additions and 44 deletions.
42 changes: 4 additions & 38 deletions rust/Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ VERSION 0.8
# INIT sets some configuration in the environment (used by following functions), and installs required dependencies.
# Arguments:
# - cache_prefix: Overrides cache prefix for cache IDS. Its value is exported to the build environment under the entry: $EARTHLY_CACHE_PREFIX. By default ${EARTHLY_TARGET_PROJECT_NO_TAG}#${OS_RELEASE}#earthly-cargo-cache
# - keep_fingerprints (false): Instructs the following +CARGO calls to not remove the Cargo fingerprints of the source packages. Use only when source packages have been COPYed with --keep-ts option.
# - sweep_days (4): +CARGO uses cargo-sweep to clean build artifacts that haven't been accessed for this number of days.
INIT:
FUNCTION
Expand All @@ -17,17 +16,14 @@ INIT:
ENV PATH="$PATH:$CARGO_HOME/bin"
END
DO +INSTALL_CARGO_SWEEP
COPY +get-jq/jq /tmp/jq

# $EARTHLY_CACHE_PREFIX
ARG EARTHLY_TARGET_PROJECT_NO_TAG #https://docs.earthly.dev/docs/earthfile/builtin-args
ARG OS_RELEASE=$(md5sum /etc/os-release | cut -d ' ' -f 1)
ARG cache_prefix="${EARTHLY_TARGET_PROJECT_NO_TAG}#${OS_RELEASE}#earthly-cargo-cache"
ENV EARTHLY_CACHE_PREFIX=$cache_prefix

# $EARTHLY_KEEP_FINGERPRINTS
ARG keep_fingerprints=false
ENV EARTHLY_KEEP_FINGERPRINTS=$keep_fingerprints

# $EARTHLY_SWEEP_DAYS
ARG sweep_days=4
ENV EARTHLY_SWEEP_DAYS=$sweep_days
Expand Down Expand Up @@ -56,9 +52,6 @@ CARGO:
ARG --required args
ARG output
DO +SET_CACHE_MOUNTS_ENV
IF [ "$EARTHLY_KEEP_FINGERPRINTS" = "false" ]
DO +REMOVE_SOURCE_FINGERPRINTS
END
RUN --mount=$EARTHLY_RUST_CARGO_HOME_CACHE --mount=$EARTHLY_RUST_TARGET_CACHE \
set -e; \
echo "+CARGO: cargo $args"; \
Expand All @@ -67,7 +60,9 @@ CARGO:
cargo sweep -r -t $EARTHLY_SWEEP_DAYS; \
cargo sweep -r -i; \
echo "+CARGO: copying output"; \
$EARTHLY_FUNCTIONS_HOME/copy-output.sh "$output";
$EARTHLY_FUNCTIONS_HOME/copy-output.sh "$output"; \
echo "+CARGO: removing local crates from target cache"; \
cargo metadata --format-version=1 --no-deps | /tmp/jq -r '.packages[].name' | xargs -I{} cargo clean -p {};
RUN $EARTHLY_FUNCTIONS_HOME/rename-output.sh "$output"

# SET_CACHE_MOUNTS_ENV sets the following entries in the environment, to be used to mount the cargo caches.
Expand Down Expand Up @@ -138,15 +133,6 @@ get-cross:
RUN wget -nv -O- "https://github.com/cross-rs/cross/releases/download/v${version}/cross-x86_64-unknown-linux-musl.tar.gz" | tar -xzf - -C .
SAVE ARTIFACT cross

get-tomljson:
FROM alpine:3.18.3
ARG USERARCH
ARG version=2.1.0
RUN wget -O tomljson.tar.xz https://github.com/pelletier/go-toml/releases/download/v${version}/tomljson_${version}_linux_${USERARCH}.tar.xz && \
tar -xf tomljson.tar.xz; \
chmod +x tomljson
SAVE ARTIFACT tomljson

get-jq:
FROM alpine:3.18.3
ARG USERARCH
Expand Down Expand Up @@ -192,26 +178,6 @@ INSTALL_EARTHLY_FUNCTIONS:
chmod +x $EARTHLY_FUNCTIONS_HOME/rename-output.sh; \
fi;

REMOVE_SOURCE_FINGERPRINTS:
FUNCTION
DO +CHECK_INITED
COPY +get-tomljson/tomljson /tmp/tomljson
COPY +get-jq/jq /tmp/jq
RUN if [ ! -n "$EARTHLY_RUST_TARGET_CACHE" ]; then \
echo "+SET_CACHE_MOUNTS_ENV has not been called yet in this build environment" ; \
exit 1; \
fi;
RUN --mount=$EARTHLY_RUST_TARGET_CACHE \
set -e;\
source_libs=$(find . -name Cargo.toml -exec bash -c '/tmp/tomljson {} | /tmp/jq -r .package.name; printf "\n"' \;) ; \
fingerprint_folders=$(find target -name .fingerprint) ; \
for fingerprint_folder in $fingerprint_folders; do \
cd $fingerprint_folder; \
for source_lib in $source_libs; do \
find . -maxdepth 1 -regex "\./$source_lib-[^-]+" -exec bash -c 'echo "deleting $(readlink -f {})"; rm -rf {}' \; ; \
done \
done;

CHECK_INITED:
FUNCTION
RUN if [ ! -n "$EARTHLY_CACHE_PREFIX" ]; then \
Expand Down
6 changes: 0 additions & 6 deletions rust/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,6 @@ DO rust+INIT ...
Overrides cache prefix for cache IDS. Its value is exported to the build environment under the entry: `$EARTHLY_CACHE_PREFIX`.
By default `${EARTHLY_TARGET_PROJECT_NO_TAG}#${OS_RELEASE}#earthly-cargo-cache`

#### `keep_fingerprints (false)`

By default `+CARGO` removes the [compiler fingerprints](https://doc.rust-lang.org/nightly/nightly-rustc/cargo/core/compiler/fingerprint/struct.Fingerprint.html) of those packages found in your source code (not their dependencies), to force their recompilation and work even when the Earthly `COPY` commands overwrote file mtimes (by default).

Set `keep_fingerprints=true` to keep the source packages fingerprints and avoid their recompilation, when source packages have been copied with `--keep-ts `option.

#### `sweep_days (4)`
`+CARGO` calls use cargo-sweep to clean build artifacts that haven't been accessed for this number of days.

Expand Down

0 comments on commit d24d25c

Please sign in to comment.