Issues With Docker Multi Stage Build and Cache Invalidation in Rust
FleetingContext: I want to build a wasm package from a project consisting on a rust library.
Layout
- ./common
- ./Cargo.lock
- ./Cargo.toml
- ./Earthfile
- ./src
- ./wasm-client
- ./Cargo.lock
- ./Cargo.toml
- ./Earthfile
- ./src
first try
In common
src:
FROM rust
RUN rustup component add clippy rustfmt
WORKDIR /app
COPY Cargo.lock Cargo.toml .
RUN mkdir -p src && touch src/lib.rs
RUN cargo build
COPY --dir src .
SAVE ARTIFACT /app src
In wasm-client
wasm:
FROM rust
RUN rustup component add clippy rustfmt
WORKDIR /app
RUN apt update && apt install -y build-essential
WORKDIR /app/wasm-client
RUN cargo install wasm-pack wasm-opt
COPY Cargo.lock Cargo.toml .
COPY ../common+deps/common ../common
RUN mkdir -p src && touch src/lib.rs
RUN wasm-pack build --target web --release # to have a cache of the dependencies
COPY --dir src /app/wasm-client
COPY ../common+src/src /app/common
RUN wasm-pack build --target web --release
This is what happens
- when COPYing stuff, the dates are put to “Apr 16 2020”.
- when running the caching build, the target files have the date of now.
- then, when copying the actual sources, they get the date “apr 16 2020” and
- the build will ignore them.
second try, with –keep-ts
I can add a –keep-ts instruction when moving the sources.
In common
src:
FROM rust
RUN rustup component add clippy rustfmt
WORKDIR /app
COPY Cargo.lock Cargo.toml .
RUN mkdir -p src && touch src/lib.rs
RUN cargo build
COPY --keep-ts --dir src .
SAVE ARTIFACT --keep-ts /app src
In wasm-client
wasm:
FROM rust
RUN rustup component add clippy rustfmt
WORKDIR /app
RUN apt update && apt install -y build-essential
WORKDIR /app/wasm-client
RUN cargo install wasm-pack wasm-opt
COPY Cargo.lock Cargo.toml .
COPY ../common+deps/common ../common
RUN mkdir -p src && touch src/lib.rs
RUN wasm-pack build --target web --release # to have a cache of the dependencies
COPY --keep-ts --dir src /app/wasm-client
COPY --keep-ts ../common+src/src /app/common
RUN wasm-pack build --target web --release
Consider that the sources have a date like “now-1h” this is what happens:
- when COPYing stuff, the dates are put to “now-1h”.
- when running the caching build, the target files have the date of now.
- then, when copying the actual sources, they get the date “now-1h” that is still < now and
- the build will ignore them.
trying using a target dating to “Apr 16 2020”
src:
FROM rust
RUN rustup component add clippy rustfmt
WORKDIR /app
COPY Cargo.lock Cargo.toml .
RUN mkdir -p src && touch src/lib.rs
RUN cargo build
COPY --keep-ts --dir src .
SAVE ARTIFACT --keep-ts /app src
In wasm-client
cached-target:
FROM rust
RUN rustup component add clippy rustfmt
WORKDIR /app
RUN apt update && apt install -y build-essential
WORKDIR /app/wasm-client
RUN cargo install wasm-pack wasm-opt
COPY Cargo.lock Cargo.toml .
COPY ../common+deps/common ../common
RUN mkdir -p src && touch src/lib.rs
RUN wasm-pack build --target web --release # to have a cache of the dependencies
SAVE ARTIFACT target
wasm:
FROM rust
COPY --keep-ts --dir src /app/wasm-client
COPY --keep-ts ../common+src/src /app/common
COPY --dir +cached-target/target .
RUN wasm-pack build --target web --release
This happens
- when COPYing stuff, the dates are put to “now-1h”,
- when running the caching build, the target files have the date of now,
- when copying its artifact, the target gets the date “Apr 16 2020”,
- then, when copying the actual sources, they get the date “now-1h” that is always > “Apr 16 2020”
- the build will be triggered everytime, but at least the dependencies won’t be built again.
trying with the cache instruction
src:
FROM rust
RUN rustup component add clippy rustfmt
WORKDIR /app
COPY Cargo.lock Cargo.toml .
RUN mkdir -p src && touch src/lib.rs
RUN cargo build
COPY --keep-ts --dir src .
SAVE ARTIFACT --keep-ts /app src
In wasm-client
cached-target:
FROM rust
RUN rustup component add clippy rustfmt
WORKDIR /app
RUN apt update && apt install -y build-essential
WORKDIR /app/wasm-client
RUN cargo install wasm-pack wasm-opt
COPY Cargo.lock Cargo.toml .
COPY ../common+deps/common ../common
RUN mkdir -p src && touch src/lib.rs
RUN cargo fetch
COPY --keep-ts --dir src /app/wasm-client
COPY --keep-ts ../common+src/src /app/common
RUN --mount=type=cache,target=/app/wasm-client/target wasm-pack build --target web --release
Note that we only fetched the dependencies before hand. We did not run the build.
This happens:
- when COPYing stuff, the dates are put to “now-1h”,
- when building, the cache will keep the dates
- when building again, only the new things are built
With this, we loose a bit of control about where the data resides, as there is a cache somewhere that we need to deal with. We also cannot take a look at this cache as easily as using the trick of RUN false and earthly -i .
But it provides the most correct build-when-needed behavior.
Note also that doing this, we NEED to –keep-ts every sources, or the build system will always think that the target is up-to-date.