How can I force `build.rs` to run again without cleaning my whole project?

坚强是说给别人听的谎言 提交于 2019-12-06 02:56:24

问题


How can I force build.rs to run again without cleaning my whole project? I checked cargo build --help but I couldn't find anything related to build.rs.


回答1:


If you print

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

the build will be triggered every time the file has changed.

rerun-if-changed=PATH is a path to a file or directory which indicates that the build script should be re-run if it changes (detected by a more-recent last-modified timestamp on the file). Normally build scripts are re-run if any file inside the crate root changes, but this can be used to scope changes to just a small set of files. -- source

I'm not aware of a solution without changing a file manually (I just put a whitespace anywhere in my build.rs, it will be removed by rustfmt though).

I have several buildscripts in my projects, and mostly these two lines give me a nice solution:

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

but I guess you are looking for a command rustc/cargo command. Anyway, you can put in a small script, which will edit a certain file, which will trigger the build-process.




回答2:


If build.rs changes, Cargo already rebuilds the project:

Note that if the build script itself (or one of its dependencies) changes, then it's rebuilt and rerun unconditionally, so cargo:rerun-if-changed=build.rs is almost always redundant (unless you want to ignore changes in all other files except for build.rs). doc

On Linux, I will just do touch build.rs && cargo build. For Windows, see Windows equivalent of the Linux command 'touch'?




回答3:


Register build.rs as a crate's bin target:

  1. Add this to your Cargo.toml file:

    [package]
    edition = "2018"
    build = "build.rs"
    
    [[bin]]
    name = "force-build"
    path = "build.rs"
    required-features = ["build_deps"]  # only needed for build-dependencies
    
  2. If you have any [build-dependencies] (e.g. some_crate = "1.2.3"), you need to add those to (the main) [dependencies] (sadly no [bin-dependencies] as of yet), but you can make them optional:

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

Then you can run the build script with:

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

(or $ cargo run --bin force-build when no [build-dependencies])

  • You can even disable the automatic call of the build script by replacing the build = "build.rs" line in Cargo.toml with build = false

  • Note: since the OUT_DIR env var is not present for bin targets, if your build.rs script uses env!("OUT_DIR"), you may "fix this" by using concat!(env!("CARGO_MANIFEST_DIR"), "/target/") instead.




回答4:


If you got target under gitignore (which you should) this might be useful for any file changes when you're developing and testing the build script.

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



回答5:


I can offer the solution that works for me well.

Add the following to build.rs file to a dependency that have to be rebuilt everytime:

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(())
}

Add the feature to Cargo.toml to declare the feature:

[package]
name = "sensitive-crate"

[features]
default = []
refresh = []

And activate refresh feature to rebuild the dependency everytime:

[package]
name = "my_project_1"

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


来源:https://stackoverflow.com/questions/49077147/how-can-i-force-build-rs-to-run-again-without-cleaning-my-whole-project

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!