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

Add --depfile option #820

Merged
merged 2 commits into from
May 29, 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
57 changes: 42 additions & 15 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,6 @@ fn generate_tests() {

println!("cargo:rerun-if-changed={}", tests_dir.display());

// Try to make a decent guess at where our binary will end up in.
//
// TODO(emilio): Ideally running tests will just use the library-version of
// cbindgen instead of the built binary.
let cbindgen_path = out_dir
.parent()
.unwrap()
.parent()
.unwrap()
.parent()
.unwrap()
.join("cbindgen");

for entry in entries {
let path_segment = if entry.file_type().unwrap().is_file() {
match entry.path().extension().and_then(OsStr::to_str) {
Expand All @@ -53,8 +40,47 @@ fn generate_tests() {

writeln!(
dst,
"test_file!({:?}, test_{}, {:?}, {:?});",
cbindgen_path,
"test_file!(test_{}, {:?}, {:?});",
identifier,
path_segment,
entry.path(),
)
.unwrap();
}

dst.flush().unwrap();
}

fn generate_depfile_tests() {
use std::env;
use std::fs::{self, File};
use std::io::Write;
use std::path::{Path, PathBuf};

let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let mut dst = File::create(Path::new(&out_dir).join("depfile_tests.rs")).unwrap();

let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let tests_dir = manifest_dir.join("tests").join("depfile");
let tests = fs::read_dir(&tests_dir).unwrap();

let entries = tests.map(|t| t.expect("Couldn't read test file"));

println!("cargo:rerun-if-changed={}", tests_dir.display());

for entry in entries {
if entry.file_type().unwrap().is_file() {
continue;
};
let path_segment = entry.file_name().to_str().unwrap().to_owned();

let identifier = path_segment
.replace(|c| !char::is_alphanumeric(c), "_")
.replace("__", "_");

writeln!(
dst,
"test_file!(test_depfile_{}, {:?}, {:?});",
identifier,
path_segment,
entry.path(),
Expand All @@ -67,4 +93,5 @@ fn generate_tests() {

fn main() {
generate_tests();
generate_depfile_tests();
}
44 changes: 44 additions & 0 deletions src/bindgen/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub struct Bindings {
constants: Vec<Constant>,
items: Vec<ItemContainer>,
functions: Vec<Function>,
source_files: Vec<path::PathBuf>,
/// Bindings are generated by a recursive call to cbindgen
/// and shouldn't do anything when written anywhere.
noop: bool,
Expand All @@ -50,6 +51,7 @@ impl Bindings {
globals: Vec<Static>,
items: Vec<ItemContainer>,
functions: Vec<Function>,
source_files: Vec<path::PathBuf>,
noop: bool,
) -> Bindings {
Bindings {
Expand All @@ -61,6 +63,7 @@ impl Bindings {
constants,
items,
functions,
source_files,
noop,
}
}
Expand Down Expand Up @@ -128,6 +131,47 @@ impl Bindings {
fields
}

pub fn generate_depfile<P: AsRef<path::Path>>(&self, header_path: P, depfile_path: P) {
if let Some(dir) = depfile_path.as_ref().parent() {
if !dir.exists() {
std::fs::create_dir_all(dir).unwrap()
}
}
let canon_header_path = header_path.as_ref().canonicalize().unwrap();
let mut canon_source_files: Vec<_> = self
.source_files
.iter()
.chain(self.config.config_path.as_ref().into_iter())
.map(|p| p.canonicalize().unwrap())
.collect();
// Sorting makes testing easier by ensuring the output is ordered.
canon_source_files.sort_unstable();

// When writing the depfile we must escape whitespace in paths to avoid it being interpreted
// as a seperator.
// It is not clear how to otherwise _correctly_ replace whitespace in a non-unicode
// compliant slice, without knowing the encoding, so we lossy convert such cases,
// to avoid panics.
let mut depfile = File::create(depfile_path).unwrap();
write!(
&mut depfile,
"{}:",
canon_header_path.to_string_lossy().replace(' ', "\\ ")
)
.expect("Writing header name to depfile failed");
canon_source_files.into_iter().for_each(|source_file| {
// Add line-continue and line-break and then indent with 4 spaces.
// This makes the output more human-readable.
depfile.write_all(b" \\\n ").unwrap();
let escaped_path = source_file.to_string_lossy().replace(' ', "\\ ");
depfile.write_all(escaped_path.as_bytes()).unwrap();
});

writeln!(&mut depfile).unwrap();

depfile.flush().unwrap();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't closing the file flush as well? I doubt this is needed right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the file is dropped, errors when closing are ignored. Manually flushing and unwrapping results in a panic for errors. I don't have a strong opinion here, since errors when closing the file should be extremely rare. I can remove this line if you prefer.

}

pub fn write_to_file<P: AsRef<path::Path>>(&self, path: P) -> bool {
if self.noop {
return false;
Expand Down
4 changes: 4 additions & 0 deletions src/bindgen/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ impl Builder {
Default::default(),
Default::default(),
Default::default(),
Default::default(),
true,
));
}
Expand Down Expand Up @@ -391,6 +392,8 @@ impl Builder {
result.extend_with(&parser::parse_lib(cargo, &self.config)?);
}

result.source_files.extend_from_slice(self.srcs.as_slice());

Library::new(
self.config,
result.constants,
Expand All @@ -401,6 +404,7 @@ impl Builder {
result.opaque_items,
result.typedefs,
result.functions,
result.source_files,
)
.generate()
}
Expand Down
13 changes: 8 additions & 5 deletions src/bindgen/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use std::collections::{BTreeMap, HashMap};
use std::default::Default;
use std::str::FromStr;
use std::{fmt, fs, path::Path as StdPath};
use std::{fmt, fs, path::Path as StdPath, path::PathBuf as StdPathBuf};

use serde::de::value::{MapAccessDeserializer, SeqAccessDeserializer};
use serde::de::{Deserialize, Deserializer, MapAccess, SeqAccess, Visitor};
Expand Down Expand Up @@ -1003,6 +1003,8 @@ pub struct Config {
pub only_target_dependencies: bool,
/// Configuration options specific to Cython.
pub cython: CythonConfig,
#[serde(skip)]
pub(crate) config_path: Option<StdPathBuf>,
}

impl Default for Config {
Expand Down Expand Up @@ -1045,6 +1047,7 @@ impl Default for Config {
pointer: PtrConfig::default(),
only_target_dependencies: false,
cython: CythonConfig::default(),
config_path: None,
}
}
}
Expand Down Expand Up @@ -1086,10 +1089,10 @@ impl Config {
)
})?;

match toml::from_str::<Config>(&config_text) {
Ok(x) => Ok(x),
Err(e) => Err(format!("Couldn't parse config file: {}.", e)),
}
let mut config = toml::from_str::<Config>(&config_text)
.map_err(|e| format!("Couldn't parse config file: {}.", e))?;
config.config_path = Some(StdPathBuf::from(file_name.as_ref()));
Ok(config)
}

pub fn from_root_or_default<P: AsRef<StdPath>>(root: P) -> Config {
Expand Down
5 changes: 5 additions & 0 deletions src/bindgen/library.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::collections::HashMap;
use std::path::PathBuf;

use crate::bindgen::bindings::Bindings;
use crate::bindgen::config::{Config, Language, SortKey};
Expand All @@ -25,6 +26,7 @@ pub struct Library {
opaque_items: ItemMap<OpaqueItem>,
typedefs: ItemMap<Typedef>,
functions: Vec<Function>,
source_files: Vec<PathBuf>,
}

impl Library {
Expand All @@ -39,6 +41,7 @@ impl Library {
opaque_items: ItemMap<OpaqueItem>,
typedefs: ItemMap<Typedef>,
functions: Vec<Function>,
source_files: Vec<PathBuf>,
) -> Library {
Library {
config,
Expand All @@ -50,6 +53,7 @@ impl Library {
opaque_items,
typedefs,
functions,
source_files,
}
}

Expand Down Expand Up @@ -135,6 +139,7 @@ impl Library {
globals,
items,
functions,
self.source_files,
false,
))
}
Expand Down
5 changes: 5 additions & 0 deletions src/bindgen/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ pub fn parse_src(src_file: &FilePath, config: &Config) -> ParseResult {
};

context.parse_mod(&pkg_ref, src_file, 0)?;
context.out.source_files = context.cache_src.keys().map(|k| k.to_owned()).collect();
Ok(context.out)
}

Expand All @@ -79,6 +80,7 @@ pub(crate) fn parse_lib(lib: Cargo, config: &Config) -> ParseResult {

let binding_crate = context.lib.as_ref().unwrap().binding_crate_ref();
context.parse_crate(&binding_crate)?;
context.out.source_files = context.cache_src.keys().map(|k| k.to_owned()).collect();
emilio marked this conversation as resolved.
Show resolved Hide resolved
Ok(context.out)
}

Expand Down Expand Up @@ -406,6 +408,7 @@ pub struct Parse {
pub opaque_items: ItemMap<OpaqueItem>,
pub typedefs: ItemMap<Typedef>,
pub functions: Vec<Function>,
pub source_files: Vec<FilePathBuf>,
}

impl Parse {
Expand All @@ -419,6 +422,7 @@ impl Parse {
opaque_items: ItemMap::default(),
typedefs: ItemMap::default(),
functions: Vec::new(),
source_files: Vec::new(),
}
}

Expand Down Expand Up @@ -466,6 +470,7 @@ impl Parse {
self.opaque_items.extend_with(&other.opaque_items);
self.typedefs.extend_with(&other.typedefs);
self.functions.extend_from_slice(&other.functions);
self.source_files.extend_from_slice(&other.source_files);
}

fn load_syn_crate_mod<'a>(
Expand Down
17 changes: 17 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,20 @@ fn main() {
.help("Report errors only (overrides verbosity options).")
.required(false),
)
.arg(
Arg::new("depfile")
.value_name("PATH")
.long("depfile")
.takes_value(true)
.min_values(1)
.max_values(1)
.required(false)
.help("Generate a depfile at the given Path listing the source files \
cbindgen traversed when generating the bindings. Useful when \
integrating cbindgen into 3rd party build-systems. \
This option is ignored if `--out` is missing."
)
)
.get_matches();

if !matches.is_present("out") && matches.is_present("verify") {
Expand Down Expand Up @@ -306,6 +320,9 @@ fn main() {
error!("Bindings changed: {}", file);
std::process::exit(2);
}
if let Some(depfile) = matches.value_of("depfile") {
bindings.generate_depfile(file, depfile)
}
}
_ => {
bindings.write(io::stdout());
Expand Down
Loading