Skip to content

Commit 8c6aedc

Browse files
committed
bindgen::ParseCallbacks: support tracking env variable usage for cargo
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 c94367c commit 8c6aedc

File tree

2 files changed

+59
-27
lines changed

2 files changed

+59
-27
lines changed

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 an environment variable is read (whether or not it has any
106+
/// content), with the name of the env variable.
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: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ pub use crate::regex_set::RegexSet;
7878

7979
use std::borrow::Cow;
8080
use std::env;
81+
use std::ffi::OsStr;
8182
use std::fs::{File, OpenOptions};
8283
use std::io::{self, Write};
8384
use std::path::{Path, PathBuf};
@@ -95,10 +96,10 @@ pub const DEFAULT_ANON_FIELDS_PREFIX: &str = "__bindgen_anon_";
9596
const DEFAULT_NON_EXTERN_FNS_SUFFIX: &str = "__extern";
9697

9798
fn file_is_cpp(name_file: &str) -> bool {
98-
name_file.ends_with(".hpp") ||
99-
name_file.ends_with(".hxx") ||
100-
name_file.ends_with(".hh") ||
101-
name_file.ends_with(".h++")
99+
name_file.ends_with(".hpp")
100+
|| name_file.ends_with(".hxx")
101+
|| name_file.ends_with(".hh")
102+
|| name_file.ends_with(".h++")
102103
}
103104

104105
fn args_are_cpp(clang_args: &[String]) -> bool {
@@ -276,13 +277,18 @@ pub fn builder() -> Builder {
276277
Default::default()
277278
}
278279

279-
fn get_extra_clang_args() -> Vec<String> {
280+
fn get_extra_clang_args(
281+
parse_callbacks: &[Rc<dyn callbacks::ParseCallbacks>],
282+
) -> Vec<String> {
280283
// Add any extra arguments from the environment to the clang command line.
281-
let extra_clang_args =
282-
match get_target_dependent_env_var("BINDGEN_EXTRA_CLANG_ARGS") {
283-
None => return vec![],
284-
Some(s) => s,
285-
};
284+
let extra_clang_args = match get_target_dependent_env_var(
285+
parse_callbacks,
286+
"BINDGEN_EXTRA_CLANG_ARGS",
287+
) {
288+
None => return vec![],
289+
Some(s) => s,
290+
};
291+
286292
// Try to parse it with shell quoting. If we fail, make it one single big argument.
287293
if let Some(strings) = shlex::split(&extra_clang_args) {
288294
return strings;
@@ -421,8 +427,8 @@ impl Builder {
421427
// FIXME(emilio): This is a bit hacky, maybe we should stop re-using the
422428
// RustFeatures to store the "disable_untagged_union" call, and make it
423429
// a different flag that we check elsewhere / in generate().
424-
if !rust_features.untagged_union &&
425-
RustFeatures::from(*rust_target).untagged_union
430+
if !rust_features.untagged_union
431+
&& RustFeatures::from(*rust_target).untagged_union
426432
{
427433
output_vector.push("--disable-untagged-union".into());
428434
}
@@ -1731,7 +1737,9 @@ impl Builder {
17311737
/// Generate the Rust bindings using the options built up thus far.
17321738
pub fn generate(mut self) -> Result<Bindings, BindgenError> {
17331739
// Add any extra arguments from the environment to the clang command line.
1734-
self.options.clang_args.extend(get_extra_clang_args());
1740+
self.options
1741+
.clang_args
1742+
.extend(get_extra_clang_args(&self.options.parse_callbacks));
17351743

17361744
// Transform input headers to arguments on the clang command line.
17371745
self.options.clang_args.extend(
@@ -1814,7 +1822,7 @@ impl Builder {
18141822
cmd.arg(a);
18151823
}
18161824

1817-
for a in get_extra_clang_args() {
1825+
for a in get_extra_clang_args(&self.options.parse_callbacks) {
18181826
cmd.arg(a);
18191827
}
18201828

@@ -2646,8 +2654,8 @@ fn rust_to_clang_target(rust_target: &str) -> String {
26462654
rust_target.strip_suffix("-espidf").unwrap().to_owned();
26472655
clang_target.push_str("-elf");
26482656
if clang_target.starts_with("riscv32imc-") {
2649-
clang_target = "riscv32-".to_owned() +
2650-
clang_target.strip_prefix("riscv32imc-").unwrap();
2657+
clang_target = "riscv32-".to_owned()
2658+
+ clang_target.strip_prefix("riscv32imc-").unwrap();
26512659
}
26522660
return clang_target;
26532661
}
@@ -2742,8 +2750,8 @@ impl Bindings {
27422750
return false;
27432751
}
27442752

2745-
if arg.starts_with("-I") ||
2746-
arg.starts_with("--include-directory=")
2753+
if arg.starts_with("-I")
2754+
|| arg.starts_with("--include-directory=")
27472755
{
27482756
return false;
27492757
}
@@ -2770,8 +2778,8 @@ impl Bindings {
27702778
debug!("Found clang: {:?}", clang);
27712779

27722780
// Whether we are working with C or C++ inputs.
2773-
let is_cpp = args_are_cpp(&options.clang_args) ||
2774-
options.input_headers.iter().any(|h| file_is_cpp(h));
2781+
let is_cpp = args_are_cpp(&options.clang_args)
2782+
|| options.input_headers.iter().any(|h| file_is_cpp(h));
27752783

27762784
let search_paths = if is_cpp {
27772785
clang.cpp_search_paths
@@ -3140,22 +3148,38 @@ pub fn clang_version() -> ClangVersion {
31403148
}
31413149
}
31423150

3151+
fn env_var<K: AsRef<str> + AsRef<OsStr>>(
3152+
parse_callbacks: &[Rc<dyn callbacks::ParseCallbacks>],
3153+
key: K,
3154+
) -> Result<String, std::env::VarError> {
3155+
for callback in parse_callbacks {
3156+
callback.read_env_var(key.as_ref());
3157+
}
3158+
std::env::var(key)
3159+
}
3160+
31433161
/// Looks for the env var `var_${TARGET}`, and falls back to just `var` when it is not found.
3144-
fn get_target_dependent_env_var(var: &str) -> Option<String> {
3145-
if let Ok(target) = env::var("TARGET") {
3146-
if let Ok(v) = env::var(format!("{}_{}", var, target)) {
3162+
fn get_target_dependent_env_var(
3163+
parse_callbacks: &[Rc<dyn callbacks::ParseCallbacks>],
3164+
var: &str,
3165+
) -> Option<String> {
3166+
if let Ok(target) = env_var(parse_callbacks, "TARGET") {
3167+
if let Ok(v) = env_var(parse_callbacks, format!("{}_{}", var, target)) {
31473168
return Some(v);
31483169
}
3149-
if let Ok(v) = env::var(format!("{}_{}", var, target.replace('-', "_")))
3150-
{
3170+
if let Ok(v) = env_var(
3171+
parse_callbacks,
3172+
format!("{}_{}", var, target.replace('-', "_")),
3173+
) {
31513174
return Some(v);
31523175
}
31533176
}
3154-
env::var(var).ok()
3177+
3178+
env_var(parse_callbacks, var).ok()
31553179
}
31563180

31573181
/// A ParseCallbacks implementation that will act on file includes by echoing a rerun-if-changed
3158-
/// line
3182+
/// line and on env variable usage by echoing a rerun-if-env-changed line
31593183
///
31603184
/// When running inside a `build.rs` script, this can be used to make cargo invalidate the
31613185
/// generated bindings whenever any of the files included from the header change:
@@ -3173,6 +3197,10 @@ impl callbacks::ParseCallbacks for CargoCallbacks {
31733197
fn include_file(&self, filename: &str) {
31743198
println!("cargo:rerun-if-changed={}", filename);
31753199
}
3200+
3201+
fn read_env_var(&self, key: &str) {
3202+
println!("cargo:rerun-if-env-changed={}", key);
3203+
}
31763204
}
31773205

31783206
/// Test command_line_flag function.

0 commit comments

Comments
 (0)