Skip to content

Commit 5d1c79a

Browse files
authored
bindgen::ParseCallbacks: support tracking env variable usage for cargo (#2400)
bindgen currently has a `bindgen/build.rs` which attempts to have cargo rebuild bindgen when TARGET specific env variables change, specifically `BINDGEN_EXTRA_CLANG_ARGS_<target>`. Unfortunately, this doesn't have the desired effect in most cases. Specifically, when a crate `A` has `bindgen` in `build-dependencies`, and we're cross compiling `A` for target `T` on a host `H`, `bindgen`'s `build.rs` will observe `TARGET` set to `H` (the host target name) instead of `T` (because `bindgen` itself is being built for `H` and not `T`). Then, within the build script of crate `A`, one would use `bindgen` to generate bindings, and now `TARGET` is set to `T`, so different env variables are used. Allow crates using `bindgen` in build scripts to correctly handle env variable changes by adding `ParseCallbacks::read_env_var()` to track env var reads and adding a basic implementation to `CargoCallbacks` to emit `cargo:rerun-if-env-changed` lines.
1 parent 1e3e25f commit 5d1c79a

File tree

3 files changed

+51
-16
lines changed

3 files changed

+51
-16
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
- [Fixed](#fixed-13)
6666
- [0.54.1](#0541)
6767
- [Added](#added-12)
68-
- [Changed](#changed-10)
68+
- [Changed](#chainged-10)
6969
- [Fixed](#fixed-14)
7070
- [0.54.0](#0540)
7171
- [Added](#added-13)
@@ -174,6 +174,8 @@
174174
* Added the `Builder::emit_diagnostics` method and the `--emit-diagnostics`
175175
flag to enable emission of diagnostic messages.
176176
* Added support for the `"efiapi"` calling convention.
177+
* Added the `ParseCallbacks::read_env_var` method which runs everytime
178+
`bindgen` reads and environment variable.
177179

178180
## Changed
179181
* Static functions with no arguments use `void` as their single argument

bindgen/callbacks.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ pub trait ParseCallbacks: fmt::Debug {
102102
/// This will be called on every file inclusion, with the full path of the included file.
103103
fn include_file(&self, _filename: &str) {}
104104

105+
/// This will be called every time `bindgen` reads an environment variable whether it has any
106+
/// content or not.
107+
fn read_env_var(&self, _key: &str) {}
108+
105109
/// This will be called to determine whether a particular blocklisted type
106110
/// implements a trait or not. This will be used to implement traits on
107111
/// other types containing the blocklisted type.

bindgen/lib.rs

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,12 @@ use parse::ParseError;
6868
use std::borrow::Cow;
6969
use std::collections::hash_map::Entry;
7070
use std::env;
71+
use std::ffi::OsStr;
7172
use std::fs::{File, OpenOptions};
7273
use std::io::{self, Write};
7374
use std::path::{Path, PathBuf};
7475
use std::process::{Command, Stdio};
76+
use std::rc::Rc;
7577
use std::str::FromStr;
7678

7779
// Some convenient typedefs for a fast hash map and hash set.
@@ -278,13 +280,18 @@ pub fn builder() -> Builder {
278280
Default::default()
279281
}
280282

281-
fn get_extra_clang_args() -> Vec<String> {
283+
fn get_extra_clang_args(
284+
parse_callbacks: &[Rc<dyn callbacks::ParseCallbacks>],
285+
) -> Vec<String> {
282286
// Add any extra arguments from the environment to the clang command line.
283-
let extra_clang_args =
284-
match get_target_dependent_env_var("BINDGEN_EXTRA_CLANG_ARGS") {
285-
None => return vec![],
286-
Some(s) => s,
287-
};
287+
let extra_clang_args = match get_target_dependent_env_var(
288+
parse_callbacks,
289+
"BINDGEN_EXTRA_CLANG_ARGS",
290+
) {
291+
None => return vec![],
292+
Some(s) => s,
293+
};
294+
288295
// Try to parse it with shell quoting. If we fail, make it one single big argument.
289296
if let Some(strings) = shlex::split(&extra_clang_args) {
290297
return strings;
@@ -296,7 +303,9 @@ impl Builder {
296303
/// Generate the Rust bindings using the options built up thus far.
297304
pub fn generate(mut self) -> Result<Bindings, BindgenError> {
298305
// Add any extra arguments from the environment to the clang command line.
299-
self.options.clang_args.extend(get_extra_clang_args());
306+
self.options
307+
.clang_args
308+
.extend(get_extra_clang_args(&self.options.parse_callbacks));
300309

301310
// Transform input headers to arguments on the clang command line.
302311
self.options.clang_args.extend(
@@ -379,7 +388,7 @@ impl Builder {
379388
cmd.arg(a);
380389
}
381390

382-
for a in get_extra_clang_args() {
391+
for a in get_extra_clang_args(&self.options.parse_callbacks) {
383392
cmd.arg(a);
384393
}
385394

@@ -1152,22 +1161,38 @@ pub fn clang_version() -> ClangVersion {
11521161
}
11531162
}
11541163

1164+
fn env_var<K: AsRef<str> + AsRef<OsStr>>(
1165+
parse_callbacks: &[Rc<dyn callbacks::ParseCallbacks>],
1166+
key: K,
1167+
) -> Result<String, std::env::VarError> {
1168+
for callback in parse_callbacks {
1169+
callback.read_env_var(key.as_ref());
1170+
}
1171+
std::env::var(key)
1172+
}
1173+
11551174
/// Looks for the env var `var_${TARGET}`, and falls back to just `var` when it is not found.
1156-
fn get_target_dependent_env_var(var: &str) -> Option<String> {
1157-
if let Ok(target) = env::var("TARGET") {
1158-
if let Ok(v) = env::var(format!("{}_{}", var, target)) {
1175+
fn get_target_dependent_env_var(
1176+
parse_callbacks: &[Rc<dyn callbacks::ParseCallbacks>],
1177+
var: &str,
1178+
) -> Option<String> {
1179+
if let Ok(target) = env_var(parse_callbacks, "TARGET") {
1180+
if let Ok(v) = env_var(parse_callbacks, format!("{}_{}", var, target)) {
11591181
return Some(v);
11601182
}
1161-
if let Ok(v) = env::var(format!("{}_{}", var, target.replace('-', "_")))
1162-
{
1183+
if let Ok(v) = env_var(
1184+
parse_callbacks,
1185+
format!("{}_{}", var, target.replace('-', "_")),
1186+
) {
11631187
return Some(v);
11641188
}
11651189
}
1166-
env::var(var).ok()
1190+
1191+
env_var(parse_callbacks, var).ok()
11671192
}
11681193

11691194
/// A ParseCallbacks implementation that will act on file includes by echoing a rerun-if-changed
1170-
/// line
1195+
/// line and on env variable usage by echoing a rerun-if-env-changed line
11711196
///
11721197
/// When running inside a `build.rs` script, this can be used to make cargo invalidate the
11731198
/// generated bindings whenever any of the files included from the header change:
@@ -1185,6 +1210,10 @@ impl callbacks::ParseCallbacks for CargoCallbacks {
11851210
fn include_file(&self, filename: &str) {
11861211
println!("cargo:rerun-if-changed={}", filename);
11871212
}
1213+
1214+
fn read_env_var(&self, key: &str) {
1215+
println!("cargo:rerun-if-env-changed={}", key);
1216+
}
11881217
}
11891218

11901219
/// Test command_line_flag function.

0 commit comments

Comments
 (0)