Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bindgen::ParseCallbacks: support tracking env variable usage for cargo #2400

Merged
merged 4 commits into from
Apr 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
- [Fixed](#fixed-13)
- [0.54.1](#0541)
- [Added](#added-12)
- [Changed](#changed-10)
- [Changed](#chainged-10)
- [Fixed](#fixed-14)
- [0.54.0](#0540)
- [Added](#added-13)
Expand Down Expand Up @@ -174,6 +174,8 @@
* Added the `Builder::emit_diagnostics` method and the `--emit-diagnostics`
flag to enable emission of diagnostic messages.
* Added support for the `"efiapi"` calling convention.
* Added the `ParseCallbacks::read_env_var` method which runs everytime
`bindgen` reads and environment variable.

## Changed
* Static functions with no arguments use `void` as their single argument
Expand Down
4 changes: 4 additions & 0 deletions bindgen/callbacks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ pub trait ParseCallbacks: fmt::Debug {
/// This will be called on every file inclusion, with the full path of the included file.
fn include_file(&self, _filename: &str) {}

/// This will be called every time `bindgen` reads an environment variable whether it has any
/// content or not.
fn read_env_var(&self, _key: &str) {}

/// This will be called to determine whether a particular blocklisted type
/// implements a trait or not. This will be used to implement traits on
/// other types containing the blocklisted type.
Expand Down
59 changes: 44 additions & 15 deletions bindgen/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,12 @@ use parse::ParseError;
use std::borrow::Cow;
use std::collections::hash_map::Entry;
use std::env;
use std::ffi::OsStr;
use std::fs::{File, OpenOptions};
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::rc::Rc;
use std::str::FromStr;

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

fn get_extra_clang_args() -> Vec<String> {
fn get_extra_clang_args(
parse_callbacks: &[Rc<dyn callbacks::ParseCallbacks>],
) -> Vec<String> {
// Add any extra arguments from the environment to the clang command line.
let extra_clang_args =
match get_target_dependent_env_var("BINDGEN_EXTRA_CLANG_ARGS") {
None => return vec![],
Some(s) => s,
};
let extra_clang_args = match get_target_dependent_env_var(
parse_callbacks,
"BINDGEN_EXTRA_CLANG_ARGS",
) {
None => return vec![],
Some(s) => s,
};

// Try to parse it with shell quoting. If we fail, make it one single big argument.
if let Some(strings) = shlex::split(&extra_clang_args) {
return strings;
Expand All @@ -296,7 +303,9 @@ impl Builder {
/// Generate the Rust bindings using the options built up thus far.
pub fn generate(mut self) -> Result<Bindings, BindgenError> {
// Add any extra arguments from the environment to the clang command line.
self.options.clang_args.extend(get_extra_clang_args());
self.options
.clang_args
.extend(get_extra_clang_args(&self.options.parse_callbacks));

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

for a in get_extra_clang_args() {
for a in get_extra_clang_args(&self.options.parse_callbacks) {
cmd.arg(a);
}

Expand Down Expand Up @@ -1152,22 +1161,38 @@ pub fn clang_version() -> ClangVersion {
}
}

fn env_var<K: AsRef<str> + AsRef<OsStr>>(
parse_callbacks: &[Rc<dyn callbacks::ParseCallbacks>],
key: K,
) -> Result<String, std::env::VarError> {
for callback in parse_callbacks {
callback.read_env_var(key.as_ref());
}
std::env::var(key)
}

/// Looks for the env var `var_${TARGET}`, and falls back to just `var` when it is not found.
fn get_target_dependent_env_var(var: &str) -> Option<String> {
if let Ok(target) = env::var("TARGET") {
if let Ok(v) = env::var(format!("{}_{}", var, target)) {
fn get_target_dependent_env_var(
parse_callbacks: &[Rc<dyn callbacks::ParseCallbacks>],
var: &str,
) -> Option<String> {
if let Ok(target) = env_var(parse_callbacks, "TARGET") {
if let Ok(v) = env_var(parse_callbacks, format!("{}_{}", var, target)) {
return Some(v);
}
if let Ok(v) = env::var(format!("{}_{}", var, target.replace('-', "_")))
{
if let Ok(v) = env_var(
parse_callbacks,
format!("{}_{}", var, target.replace('-', "_")),
) {
return Some(v);
}
}
env::var(var).ok()

env_var(parse_callbacks, var).ok()
}

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

fn read_env_var(&self, key: &str) {
println!("cargo:rerun-if-env-changed={}", key);
}
}

/// Test command_line_flag function.
Expand Down