When building valet I required a couple pieces of configuration values but didn't want to read them from a file and didn't want them to be forever present in my environment variables so that I can read from there on runtime. Neither did I want to put them in code anywhere, so I looked for if there was a way where I read a value from somewhere (environment variable in this case) at compile-time and then use that value during run-time without needing to have that source available at run-time.

That's where I first encountered how to configure your build process using build.rs. So let's take this example that you need to read VALET_CONFIG_SOMETHING environment variable at build-time and then have its value available at run-time you need to put this in your build.rs.

fn main() {
    let compile_time_config = std::env::var("VALET_CONFIG_SOMETHING").expect("Please provide config");
    println!("cargo:rustc-env=CONFIG_SOMETHING={}", compile_time_config);

    // Rebuild when these change
    println!("cargo:rerun-if-changed=build.rs");
    println!("cargo:rerun-if-env-changed=VALET_CONFIG_SOMETHING");
}

And then to use it in your program you need to do something like this:

fn main() {
    println!("Config found at run-time: {}", env!("CONFIG_SOMETHING"));
}

To ensure cargo uses your build-script, you need to add this line to your Cargo.toml:

build = "build.rs"

Then you need to ensure that the variable is available at build-time:

$ VALET_CONFIG_SOMETHING="A value for config" cargo build
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s

And then check that the value is available later at run-time:

$ ./target/debug/valet
Config found at run-time: A value for config

Articles related to project valet

  1. Project valet announcement
  2. Read SMB share password from MacOS Keychain
  3. Rust read build-time environment variables at run-time (this article)