如何在不清理整个项目的情况下强制`build.rs`再次运行?

时间:2018-03-02 20:47:41

标签: rust rust-cargo

如何在不清理整个项目的情况下强制build.rs再次运行?我查看了cargo build --help,但找不到与build.rs相关的任何内容。

6 个答案:

答案 0 :(得分:6)

如果您打印

"cargo:rerun-if-changed=<FILE>"

每次文件更改时都会触发构建。

  

rerun-if-changed=PATH是文件或目录的路径,表示如果构建脚本发生更改(应该通过文件中更新的最后修改时间戳检测到),则应重新运行构建脚本。通常,如果crate根目录中的任何文件发生更改,则会重新运行构建脚本,但这可以用于将更改范围仅限于一小组文件。 - source

我不知道没有手动更改文件的解决方案(我只是在我的build.rs中放置了一个空格,但rustfmt将删除它。)

我的项目中有几个构建脚本,大多数这两行给了我一个很好的解决方案:

println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=path/to/Cargo.lock");

但我想您正在寻找命令rustc / cargo命令。无论如何,你可以放入一个小脚本,它将编辑某个文件,这将触发构建过程。

答案 1 :(得分:2)

build.rs注册为板条箱的bin目标:

  1. 将此添加到您的Cargo.toml文件中:

    [package]
    edition = "2018"
    build = "build.rs"
    
    [[bin]]
    name = "force-build"
    path = "build.rs"
    required-features = ["build_deps"]  # only needed for build-dependencies
    
  2. 如果您有[build-dependencies](例如 eg some_crate = "1.2.3"),则需要将其添加到(主要)[dependencies](可悲的是{ {3}}),但您可以将它们设为可选:

    [dependencies]
    some_crate = { version = "1.2.3", optional = true }
    
    [features]
    build_deps = ["some_crate"]
    

然后,您可以使用以下命令运行构建脚本:

$ cargo run --bin force-build --features build_deps

(如果没有$ cargo run --bin force-build,则为[build-dependencies]

  • 您甚至可以通过将build = "build.rs"中的Cargo.toml行替换为build = false

    来禁用构建脚本的自动调用< / li>
  • 注意:由于OUT_DIR目标没有bin环境变量,因此如果您的build.rs脚本使用env!("OUT_DIR"),则可以通过以下方式“解决此问题”:改用concat!(env!("CARGO_MANIFEST_DIR"), "/target/")

答案 2 :(得分:2)

如果build.rs发生变化,则Cargo已经重建了项目:

  

请注意,如果构建脚本本身(或其依赖项之一)发生更改,那么它将无条件地重建和重新运行,因此cargo:rerun-if-changed=build.rs几乎总是多余的(除非您想忽略除{之外的所有其他文件中的更改{1}})。 doc

在Linux上,我只会做build.rs。对于Windows,请参见Windows equivalent of the Linux command 'touch'?

答案 3 :(得分:1)

如果您尝试基于非 Rust 或可能已更改的 include!() 文件进行重建,您可以使用

const _: &[u8] = include_bytes!("foobar.baz");

确保对这些文件的任何更改都会触发新的构建。非常确定此解决方案既不会增加时间也不会增加文件大小。

你也可以把它推到一个宏中,所以很容易做一堆文件。

macro_rules! build_on{($file:literal) => {
    const _: &[u8] = include_bytes!($file);
}

build_on!("foobar.baz");

答案 4 :(得分:0)

如果在gitignore下有目标(应该这样做),那么在开发和测试构建脚本时,这可能对文件的更改很有用。

if Path::new(".git/HEAD").exists() {
    println!("cargo:rerun-if-changed=.git/HEAD");
}

答案 5 :(得分:0)

我可以提供最适合我的解决方案。

将以下内容添加到 build.rs 文件中,以使其每次都必须重新构建:

use failure::{format_err, Error};
use std::env;
use std::path::PathBuf;
use std::process::Command;

/// Just useful trait to run a command
trait RunIt {
    fn run_it(&mut self, err: &str) -> Result<(), Error>;
}

impl RunIt for Command {
    fn run_it(&mut self, err: &str) -> Result<(), Error> {
        let output = self.output()?;
        if !output.status.success() {
            let out = String::from_utf8_lossy(&output.stderr);
            eprintln!("{}", out);
            Err(format_err!("{}", err))
        } else {
            Ok(())
        }
    }
}

fn main() -> Result<(), Error> {

    // Your build stuff

    // Activate this feature to rebuild this dependency everytime
    if cfg!(feature = "refresh") {
        Command::new("touch")
            .args(&["build.rs"])
            .run_it("Can't touch the build file")?;
    }

    Ok(())
}

将功能添加到 Cargo.toml 中以声明功能:

[package]
name = "sensitive-crate"

[features]
default = []
refresh = []

并激活刷新功能,以便每次都重建依赖关系:

[package]
name = "my_project_1"

[dependencies]
sensitive-crate = { path = "../sensitive-crate", features = ["refresh"] }