A build script is a Rust program that Cargo compiles and runs before compiling a package. Cargo recognizes a file named `build.rs` at the crate root. Because it runs first, the script can read build-time settings and generate files that the package then compiles. section 4.3.1
Generate a greeting at build time
A simple `build.rs` reads `GREET_LANG` and writes a Rust function to `src/greet.rs`. The application uses `include!("greet.rs")` to compile that function and call it. Running with `GREET_LANG=el` generates a Greek greeting; omitting the variable makes this script panic. This is a teaching example, not a recommended internationalization strategy. section 4.3.1
Listing 4.3 Build script writing code with environment variables
use std::fs::File;
use std::io::Write;
fn main() {
let language = std::env::var("GREET_LANG").unwrap();
let greeting = match language.as_ref() { #1
"en" => "Hello!",
"es" => "¡Hola!",
"el" => "γεια σας",
"de" => "Hallo!",
x => panic!("Unsupported language code {}", x),
};
let rust_code = format!("fn greet() {{
println!(\"{}\"); }}", greeting); #2
let mut file = File::create("src/greet.rs").unwrap();
file.write_all(rust_code.as_bytes()).unwrap();
}
The script selects a greeting, formats it as Rust code, and writes the generated file. section 4.3.1
The same pattern supports C interoperability. A build script can run `bindgen` over a C header to generate Rust bindings before the Rust library compiles. Put `bindgen` under Cargo's `[build-dependencies]`: the script needs it, but the finished binary does not need it as a normal dependency. section 4.3 section 4.3.2
For the NGINX binding, the build script:
- Reads `wrapper.h`, supplying paths to the NGINX headers needed for parsing. section 4.3.2
- Restricts generated declarations to `ngx_`-prefixed items rather than keeping an unnecessarily large API surface. section 4.3.2
- Writes `nginx.rs` under Cargo's `OUT_DIR`; the Rust source includes it with `include!(concat!(env!("OUT_DIR"), "/nginx.rs"))`. section 4.3.2
Listing 4.5 Build script creating NGINX bindings for Rust
fn main() {
let nginx_dir = "nginx-1.19.3";
let bindings = bindgen::builder()
.header("wrapper.h") #1
.clang_args(vec![
format!("-I{}/src/core", nginx_dir), #2
format!("-I{}/src/event", nginx_dir), #2
format!("-I{}/src/event/modules", nginx_dir), #2
format!("-I{}/src/os/unix", nginx_dir), #2
format!("-I{}/objs", nginx_dir), #2
format!("-I{}/src/http", nginx_dir), #2
format!("-I{}/src/http/v2", nginx_dir), #2
format!("-I{}/src/http/modules", nginx_dir), #2
]) #2
.generate() #2
.unwrap(); #2
bindings
.write_to_file("nginx.rs") #3
.unwrap();
}
The initial NGINX script shows the `bindgen` call and header search paths; the later version filters declarations and writes to `OUT_DIR`. section 4.3.2
