【问题标题】:Can Cargo download and build dependencies without also building the application?Cargo 可以在不构建应用程序的情况下下载和构建依赖项吗?
【发布时间】:2021-04-26 03:07:02
【问题描述】:

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

我认为cargo install 会这样做,但实际上它也一直用于构建我的应用程序。我想达到一个状态,cargo build 会发现所有可以使用的依赖项,但不接触 /src 目录。


我真正想要实现的目标:

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

构建时间(docker build .):

  1. 导入安装了 rust 工具的 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 能够使用已安装和构建的所有依赖项的缓存映像。因此,在安装依赖项之后之前我不能ADD /src /source/src,因为当我更改自己的代码时,这会使缓存的图像无效。

【问题讨论】:

  • 这是一个有趣的要求!您是否试图通过提供仅列出 extern crate 的“假”lib.rs 来破解自己的方式?它可能会创建一个target 存储库,但您始终可以在执行cargo build 后删除它。
  • 这是一个有趣的想法! ;) 我回家后会试试的。我对 Rust 完全陌生,所以我不太了解内部工作原理或正确构建某些东西所需的内容 - 到目前为止,我的方法一直是反复试验......
  • 我自己在 Cargo 方面没有太多经验,所以这更像是一种试探性的解决方法。据我所知,您想要的命令已经可用。
  • cargo vendor 在这里有用吗?
  • @ChrisEmerson:在努力让cargo vendor 在我的映像上正确安装和运行之后,我注意到这些deps 已下载,但未构建。我正在研究是否可以让它们也进行预编译...

标签: rust rust-cargo


【解决方案1】:

据我所知,仅在 Cargo 中构建依赖项没有本地支持。有an open issue for it。如果你可以向 Cargo 提交一些东西来完成它,或者创建一个第三方 Cargo 插件,我不会感到惊讶。我也想要 cargo doc 的这个功能,当我自己的代码太坏而无法编译时 ;-)

但是,我维护的 Rust playground 确实实现了您的最终目标。有一个 base Docker container 安装 Rustup 并在 Cargo.toml 中复制所有可用于操场的板条箱。 The build steps create a blank project (with a dummy src/lib.rs), then calls cargo build and cargo build --release to compile the crates:

RUN cd / && \
    cargo new playground
WORKDIR /playground

ADD Cargo.toml /playground/Cargo.toml
RUN cargo build
RUN cargo build --release
RUN rm src/*.rs

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

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

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

【讨论】:

  • 在库/二进制文件上通用,可能会使用语法COPY Cargo.* ./(ADD 也可以类似地工作,尽管可能不鼓励nickjanetakis.com/blog/…
  • 嗨,为什么是 cargo buildcargo build --release 而不仅仅是带有发布选项的 cargo build?
  • @UselesssCat playground 允许您在调试和发布模式下编译代码,因此我们需要在这两种模式下预编译所有依赖项。
【解决方案2】:

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

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

我正在使用--volumes,所以到此我就完成了。主机卷进来并吹走虚拟文件,并且货物在我稍后构建源时使用缓存的依赖项。如果您想稍后添加COPY(或ADD)并使用缓存的依赖项,此解决方案也可以正常工作。

【讨论】:

    【解决方案3】:

    基于GitHub comment

    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"]
    

    【讨论】:

      【解决方案4】:

      cargo-chef 工具旨在解决此问题。这是 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"]
      

      【讨论】:

        【解决方案5】:

        我只是想在此处发布此内容,以便其他人看到它。我刚刚开始使用一个名为 cargo-wharf (https://github.com/denzp/cargo-wharf/tree/master/cargo-wharf-frontend) 的 Docker 实验工具。它是一个 Docker BuildKit 前端,可为您缓存构建的货物依赖项。如果您只更改了一个源文件,那么当您调用 docker build 时,唯一需要重建的就是该文件。您可以通过注释 Cargo.toml 文件来使用它,然后将 Docker 定向到 Cargo.toml 而不是 Dockerfile。去看看吧,这正是我想要的。 (我与该项目无关。)

        【讨论】:

          猜你喜欢
          • 2016-11-23
          • 2015-02-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-05-26
          • 1970-01-01
          相关资源
          最近更新 更多