Support emitting Makefile-syntax depfiles like gcc/clang/rustc.

Needed to auto-bindgen with a ninja build without the build graph
going stale.
This commit is contained in:
Adam Perry 2021-04-08 17:26:55 +00:00 committed by Emilio Cobos Álvarez
parent c39c47c2e5
commit 1bb548b7a7
10 changed files with 219 additions and 9 deletions

96
Cargo.lock generated
View File

@ -50,6 +50,7 @@ dependencies = [
"regex",
"rustc-hash",
"shlex",
"tempfile",
"which",
]
@ -125,6 +126,17 @@ dependencies = [
"termcolor",
]
[[package]]
name = "getrandom"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
dependencies = [
"cfg-if 1.0.0",
"libc",
"wasi",
]
[[package]]
name = "glob"
version = "0.3.0"
@ -205,6 +217,12 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "ppv-lite86"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "proc-macro2"
version = "1.0.24"
@ -223,6 +241,55 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_chacha"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
dependencies = [
"rand_core",
]
[[package]]
name = "redox_syscall"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
dependencies = [
"bitflags",
]
[[package]]
name = "regex"
version = "1.4.2"
@ -241,6 +308,15 @@ version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi",
]
[[package]]
name = "rustc-hash"
version = "1.1.0"
@ -259,6 +335,20 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "tempfile"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
dependencies = [
"cfg-if 1.0.0",
"libc",
"rand",
"redox_syscall",
"remove_dir_all",
"winapi",
]
[[package]]
name = "termcolor"
version = "1.1.0"
@ -310,6 +400,12 @@ version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "which"
version = "3.1.1"

View File

@ -43,6 +43,7 @@ required-features = ["clap"]
diff = "0.1"
clap = "2"
shlex = "1"
tempfile = "3"
[dependencies]
bitflags = "1.0.3"

View File

@ -4207,6 +4207,16 @@ pub(crate) fn codegen(
}
}
if let Some(spec) = context.options().depfile.as_ref() {
match spec.write(context.deps()) {
Ok(()) => info!(
"Your depfile was generated successfully into: {}",
spec.depfile_path.display()
),
Err(e) => warn!("{}", e),
}
}
context.resolve_item(context.root_module()).codegen(
context,
&mut result,

20
src/deps.rs Normal file
View File

@ -0,0 +1,20 @@
/// Generating build depfiles from parsed bindings.
use std::{collections::BTreeSet, path::PathBuf};
#[derive(Debug)]
pub(crate) struct DepfileSpec {
pub output_module: String,
pub depfile_path: PathBuf,
}
impl DepfileSpec {
pub fn write(&self, deps: &BTreeSet<String>) -> std::io::Result<()> {
let mut buf = format!("{}:", self.output_module);
for file in deps {
buf = format!("{} {}", buf, file);
}
std::fs::write(&self.depfile_path, &buf)
}
}

View File

@ -29,7 +29,7 @@ use clang_sys;
use proc_macro2::{Ident, Span};
use std::borrow::Cow;
use std::cell::{Cell, RefCell};
use std::collections::HashMap as StdHashMap;
use std::collections::{BTreeSet, HashMap as StdHashMap};
use std::iter::IntoIterator;
use std::mem;
@ -354,6 +354,9 @@ pub struct BindgenContext {
/// This needs to be an std::HashMap because the cexpr API requires it.
parsed_macros: StdHashMap<Vec<u8>, cexpr::expr::EvalResult>,
/// A set of all the included filenames.
deps: BTreeSet<String>,
/// The active replacements collected from replaces="xxx" annotations.
replacements: HashMap<Vec<String>, ItemId>,
@ -545,8 +548,16 @@ If you encounter an error missing from this list, please file an issue or a PR!"
let root_module = Self::build_root_module(ItemId(0));
let root_module_id = root_module.id().as_module_id_unchecked();
// depfiles need to include the explicitly listed headers too
let mut deps = BTreeSet::default();
if let Some(filename) = &options.input_header {
deps.insert(filename.clone());
}
deps.extend(options.extra_input_headers.iter().cloned());
BindgenContext {
items: vec![Some(root_module)],
deps,
types: Default::default(),
type_params: Default::default(),
modules: Default::default(),
@ -632,6 +643,19 @@ If you encounter an error missing from this list, please file an issue or a PR!"
self.options().parse_callbacks.as_ref().map(|t| &**t)
}
/// Add another path to the set of included files.
pub fn include_file(&mut self, filename: String) {
if let Some(cbs) = self.parse_callbacks() {
cbs.include_file(&filename);
}
self.deps.insert(filename);
}
/// Get any included files.
pub fn deps(&self) -> &BTreeSet<String> {
&self.deps
}
/// Define a new item.
///
/// This inserts it into the internal items set, and its type into the

View File

@ -1415,9 +1415,7 @@ impl ClangItemParser for Item {
);
}
Some(filename) => {
if let Some(cb) = ctx.parse_callbacks() {
cb.include_file(&filename)
}
ctx.include_file(filename);
}
}
}

View File

@ -51,6 +51,7 @@ macro_rules! doc_mod {
mod clang;
mod codegen;
mod deps;
mod features;
mod ir;
mod parse;
@ -604,6 +605,19 @@ impl Builder {
self
}
/// Add a depfile output which will be written alongside the generated bindings.
pub fn depfile<H: Into<String>, D: Into<PathBuf>>(
mut self,
output_module: H,
depfile: D,
) -> Builder {
self.options.depfile = Some(deps::DepfileSpec {
output_module: output_module.into(),
depfile_path: depfile.into(),
});
self
}
/// Add `contents` as an input C/C++ header named `name`.
///
/// The file `name` will be added to the clang arguments.
@ -1417,11 +1431,13 @@ impl Builder {
// Transform input headers to arguments on the clang command line.
self.options.input_header = self.input_headers.pop();
self.options
.clang_args
.extend(self.input_headers.drain(..).flat_map(|header| {
iter::once("-include".into()).chain(iter::once(header))
}));
self.options.extra_input_headers = self.input_headers;
self.options.clang_args.extend(
self.options.extra_input_headers.iter().flat_map(|header| {
iter::once("-include".into())
.chain(iter::once(header.to_string()))
}),
);
self.options.input_unsaved_files.extend(
self.input_header_contents
@ -1624,6 +1640,9 @@ struct BindgenOptions {
/// The explicit rustfmt path.
rustfmt_path: Option<PathBuf>,
/// The path to which we should write a Makefile-syntax depfile (if any).
depfile: Option<deps::DepfileSpec>,
/// The set of types that we should have bindings for in the generated
/// code.
///
@ -1785,6 +1804,9 @@ struct BindgenOptions {
/// The input header file.
input_header: Option<String>,
/// Any additional input header files.
extra_input_headers: Vec<String>,
/// Unsaved files for input.
input_unsaved_files: Vec<clang::UnsavedFile>,
@ -1963,6 +1985,7 @@ impl Default for BindgenOptions {
blocklisted_items: Default::default(),
opaque_types: Default::default(),
rustfmt_path: Default::default(),
depfile: Default::default(),
allowlisted_types: Default::default(),
allowlisted_functions: Default::default(),
allowlisted_vars: Default::default(),
@ -2008,6 +2031,7 @@ impl Default for BindgenOptions {
module_lines: HashMap::default(),
clang_args: vec![],
input_header: None,
extra_input_headers: vec![],
input_unsaved_files: vec![],
parse_callbacks: None,
codegen_config: CodegenConfig::all(),

View File

@ -30,6 +30,10 @@ where
Arg::with_name("header")
.help("C or C++ header file")
.required(true),
Arg::with_name("depfile")
.long("depfile")
.takes_value(true)
.help("Path to write depfile to"),
Arg::with_name("default-enum-style")
.long("default-enum-style")
.help("The default style of code used to generate enums.")
@ -848,8 +852,14 @@ where
let output = if let Some(path) = matches.value_of("output") {
let file = File::create(path)?;
if let Some(depfile) = matches.value_of("depfile") {
builder = builder.depfile(path, depfile);
}
Box::new(io::BufWriter::new(file)) as Box<dyn io::Write>
} else {
if let Some(depfile) = matches.value_of("depfile") {
builder = builder.depfile("-", depfile);
}
Box::new(io::BufWriter::new(io::stdout())) as Box<dyn io::Write>
};

View File

@ -0,0 +1 @@
tests/expectations/tests/enum-default-rust.rs: tests/headers/enum-default-rust.h tests/headers/enum.h

View File

@ -578,6 +578,32 @@ fn no_system_header_includes() {
.success());
}
#[test]
fn emit_depfile() {
let header = PathBuf::from("tests/headers/enum-default-rust.h");
let expected_depfile = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests")
.join("expectations")
.join("tests")
.join("enum-default-rust.d");
let observed_depfile = tempfile::NamedTempFile::new().unwrap();
let mut builder = create_bindgen_builder(&header).unwrap();
builder.builder = builder.builder.depfile(
"tests/expectations/tests/enum-default-rust.rs",
observed_depfile.path(),
);
let check_roundtrip =
env::var_os("BINDGEN_DISABLE_ROUNDTRIP_TEST").is_none();
let (builder, _roundtrip_builder) =
builder.into_builder(check_roundtrip).unwrap();
let _bindings = builder.generate().unwrap();
let observed = std::fs::read_to_string(observed_depfile).unwrap();
let expected = std::fs::read_to_string(expected_depfile).unwrap();
assert_eq!(observed.trim(), expected.trim());
}
#[test]
fn dump_preprocessed_input() {
let arg_keyword =