Cargo可以在不构建应用程序的情况下下载和构建依赖项吗



有没有办法告诉Cargo安装并构建我的所有依赖项,但不尝试构建我的应用程序?

我原以为cargo install会这么做,但实际上它也一直在构建我的应用程序。我想达到这样一种状态,即cargo build可以找到所有可以使用的依赖项,但不需要接触/src目录。


我真正想完成的事情:

我正在尝试为Rust应用程序构建一个Docker映像,我想在其中执行以下步骤:

构建时间(docker build .):

  1. 导入安装了防锈工具的docker图像
  2. 添加我的Cargo.toml和Cargo.lock文件
  3. 下载并构建所有依赖项
  4. 将我的源目录添加到映像中
  5. 构建我的源代码

运行时间(docker run ...):

  1. 运行应用程序

我已经尝试了以下Dockerfile,但指示的步骤也构建了我的应用程序(当然失败了,因为源目录还没有):

FROM jimmycuadra/rust
ADD Cargo.toml /source
ADD Cargo.lock /source
RUN cargo install # <-- failure here
ADD src /source/src
RUN cargo build
ENTRYPOINT cargo run

我想将安装依赖关系步骤与实际构建应用程序分开的原因是,如果我不更改依赖关系,我希望Docker能够使用已安装和构建的所有依赖关系的缓存映像。因此,在安装dependencies之后的之前,我不能ADD /src /source/src,因为当我更改自己的代码时,这会使缓存的映像无效。

据我所知,Cargo中没有本地支持只构建依赖项。这是一个悬而未决的问题。如果你能向Cargo提交一些东西来完成它,或者创建一个第三方Cargo插件,我不会感到惊讶。当我自己的代码太坏而无法编译时,我也希望cargo doc具有此功能;-)

然而,我维护的Rust游乐场确实实现了你的最终目标。有一个基本的Docker容器,可以安装Rustup并在Cargo.toml中复制,所有的板条箱都可以用于游乐场。构建步骤创建一个空白项目(带有一个伪src/lib.rs),然后调用cargo buildcargo build --release来编译板条箱:

RUN cd / && 
cargo new playground
WORKDIR /playground
ADD Cargo.toml /playground/Cargo.toml
RUN cargo build
RUN cargo build --release
RUN rm src/*.rs

所有下载的板条箱存储在Docker镜像的$HOME/.cargo目录中,所有构建的板条箱都存储在应用程序target/{debug,release}目录中。

稍后,真实的源文件被复制到容器中,并且cargo build/cargo run可以使用现在编译的crates再次执行。

如果您正在构建一个可执行项目,那么您也需要在Cargo.lock中进行复制。

如果添加一个伪main或lib文件,则可以使用cargo build来下拉依赖项。我目前正在为我的基于Docker的项目使用这个解决方案:

COPY Cargo.toml .
RUN mkdir src 
&& echo "// dummy file" > src/lib.rs 
&& cargo build

我正在使用--volumes,所以在这一点上我已经完成了。主机卷进入并吹走伪文件,当我稍后构建源文件时,货物使用缓存的依赖项。如果您想稍后添加COPY(或ADD)并使用缓存的依赖项,则此解决方案也同样有效。

基于GitHub注释

FROM rust:1.37
WORKDIR /usr/src
# Create blank project
RUN USER=root cargo new PROJ
# We want dependencies cached, so copy those first.
COPY Cargo.toml /usr/src/PROJ/
COPY Cargo.lock /usr/src/PROJ/
WORKDIR /usr/src/PROJ
# This is a dummy build to get the dependencies cached.
RUN cargo build --release
# Now copy in the rest of the sources
COPY MyPROJECT/src /usr/src/PROJ/src/
# This is the actual build.
RUN cargo build --release 
&& mv target/release/appname /bin 
&& rm -rf /usr/src/PROJ
WORKDIR /
EXPOSE 8888
CMD ["/bin/appname"]

货物厨师工具就是为了解决这个问题而设计的。以下是README中关于如何在Dockerfile:中使用它的示例

FROM lukemathwalker/cargo-chef as planner
WORKDIR app
COPY . .
RUN cargo chef prepare  --recipe-path recipe.json
FROM lukemathwalker/cargo-chef as cacher
WORKDIR app
COPY --from=planner /app/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json
FROM rust as builder
WORKDIR app
COPY . .
# Copy over the cached dependencies
COPY --from=cacher /app/target target
COPY --from=cacher $CARGO_HOME $CARGO_HOME
RUN cargo build --release --bin app
FROM rust as runtime
WORKDIR app
COPY --from=builder /app/target/release/app /usr/local/bin
ENTRYPOINT ["/usr/local/bin/app"]

我只是想在这里发布这篇文章,这样其他人就会看到它的发展。Docker有一个我刚开始使用的实验工具,叫做货运码头(https://github.com/denzp/cargo-wharf/tree/master/cargo-wharf-frontend)。它是一个Docker BuildKit前端,为您缓存已构建的货物依赖关系。如果您只更改了一个源文件,那么在调用docker build时,这是唯一会重新生成的文件。您可以通过注释您的Cargo.toml文件来使用它,然后将Docker指向您的Cargo.toml,而不是Dockerfile。去看看,这正是我想要的。(我与该项目没有任何关系。)

可以通过cargo initcargo buildcargo install完成。例如,对于名为foo的项目,定义以下Dockerfile:

FROM rust:slim-bullseye
# Build dependencies only.
RUN cargo init foo
COPY Cargo.toml foo/
RUN cargo build --release; 
rm -rf foo
# Install `foo`.
COPY . .
RUN echo "// force Cargo cache invalidation" >> foo/src/main.rs; 
cargo install --path foo
CMD ["foo"]

这里,cargo init创建Cargo期望的占位符文件,cargo build构建在Cargo.toml中指定的依赖项,cargo install创建foo二进制文件。出于某种原因,Docker一直在构建cargo init foo创建的默认项目。上面通过附加// force Cargo cache invalidation来强制更新main.rs来解决这个问题。

为了避免由于大型构建上下文和大型层而导致的缓慢构建,请确保通过.dockerignore忽略target等不重要的文件夹。例如,定义以下.dockerignore:

**/*.lock
LICENSE
README.md
target

相关内容

最新更新