【问题标题】:Can I prevent cargo from rebuilding libraries with every new project?我可以阻止货物在每个新项目中重建库吗?
【发布时间】:2022-04-30 00:59:08
【问题描述】:

假设我执行cargo new one --bincargo new two --bin 然后将相同的依赖项添加到每个项目的Cargo.toml 并构建它们。

现在有两组完全相同的库:

/one/target/debug/deps/*.rlib

/two/target/debug/deps/*.rlib

它们是相同的文件并浪费存储空间,但真正的问题是我必须为每个项目重新编译这些库。这需要很长时间。 cargo install也有同样的问题。

我可以指定一个存放已编译库的位置以避免重新编译吗?

【问题讨论】:

    标签: rust rust-cargo


    【解决方案1】:

    多个 Cargo 项目可能会使用相同的目标目录共享库。

    .cargo/config

    在项目中放置一个“.cargo”文件夹并在其中创建一个“config”文件,其中包含:

    [build]
    target-dir = "/path/to/your/shared/target/dir"
    

    在 Unix 上,这可能看起来像:

    mkdir ~/shared_rust_target
    mkdir .cargo
    echo "[build]" > .cargo/config
    echo "target-dir = \"$HOME/shared_rust_target\"" >> .cargo/config
    

    CARGO_TARGET_DIR

    设置CARGO_TARGET_DIRenvironment variable

    在 Unix 上,这可能看起来像:

    export CARGO_TARGET_DIR = "$HOME/shared_rust_target"
    

    请参阅 this commit 以获取一些额外的 target-dir 文档。

    特别是,在 Cargo 1.9 之前,您不应该同时将多个项目构建到同一个目标目录中。 (Here's more Cargo 1.9 如何支持并发构建)。

    target-dirCargo docs 中也有提及。

    请注意,我个人仅使用target-dir 功能将构建重定向到不同的位置,因此我从未尝试过共享构建。但根据this issue,它应该可以工作。


    附:现在还可以使用workspaces 实现 crate 重用。

    【讨论】:

      【解决方案2】:

      即使有办法做到这一点,您也可能不想这样做。仅仅因为您碰巧使用相同的库并不意味着它们的编译方式相同。例如,Cargo supports the concept of features,编译时配置会更改 crate 的编译方式。

      同样,您可能需要支持多个版本的 Rust,例如 nightly 和 stable。或者您可能需要针对不同的架构进行交叉编译。每一个都会产生不同的代码。

      Cargo 缓存单个项目的构建产品,所以我发现开销并不是很明显,而且我编译了很多来自在 Stack Overflow 上提问的人的项目! :-)

      【讨论】:

      • 我可以配置缓存这些库的位置吗?我正在使用 Docker 构建我的项目,我认为它不允许缓存,因此构建时间更长。我可以使用其他答案中提到的target-dir 吗?
      • @RajeevRanjan 我已经用您可以选择在 Docker 中使用的环境变量更新了另一个答案。但是,您不需要,如 Can Cargo download and build dependencies without also building the application? 中所述
      • 感谢@Shepmaster
      • 这是一个奇怪的不回答。当然,您会希望它缓存事物并冗余编译世界。它只需要智能地完成它,工件必须键入影响构建的所有相关因素。目标、编译器、特性等。这些通常是相同的。
      【解决方案3】:

      我设法从几个答案中拼凑出代码,但主要是this one。 这个 Dockerfile 不仅应该缓存 Cargo 依赖下载,还应该缓存它们的编译和 crates.io 索引。我能找到的所有其他答案都只缓存了下载或索引,而不是两者。

      FROM arm64v8/rust as builder
      
      # Capture dependencies
      COPY Cargo.toml Cargo.lock /app/
      
      # We create a dummy main.rs to build deps
      WORKDIR /app
      RUN mkdir src && echo "fn main() {}" > src/main.rs
      # RUN rustup install nightly && rustup default nightly
      
      # This step compiles only our dependencies and saves them in a layer. This is the most impactful time savings
      # Note the use of --mount=type=cache. On subsequent runs, we'll have the crates already downloaded
      RUN --mount=type=cache,target=/usr/local/cargo/registry cargo build --release && rm src/main.rs
      
      # Copy our sources
      COPY ./src /app/src
      
      # A bit of magic here!
      # * We're mounting that cache again to use during the build, otherwise it's not present and we'll have to download those again - bad!
      # * Rust here is a bit fiddly, so we'll touch the files (even though we copied over them) to force a new build
      RUN --mount=type=cache,target=/usr/local/cargo/registry \
          set -e && \
          # update timestamps to force a new build &&
          touch /app/src/main.rs && \
          cargo build --release
      
      # Again, our final image is the same - a slim base and just our app
      FROM debian:buster-slim as app
      COPY --from=builder /app/target/release/app/app
      CMD ["/app"]
      

      注意FROM arm64v8,如果您的目标是x86,请将builderapp FROM 替换为各自的x86 版本。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-01-16
        • 1970-01-01
        • 2020-06-30
        • 2013-08-07
        • 1970-01-01
        • 1970-01-01
        • 2013-03-22
        • 2020-08-03
        相关资源
        最近更新 更多