diff --git a/README.md b/README.md index 8f67536..b99783f 100644 --- a/README.md +++ b/README.md @@ -98,13 +98,28 @@ parse_deps = true include = ["webrender", "webrender_traits"] # A black list of crate names that are not allowed to be parsed exclude = ["libc"] -# A list of crate names that should be run through `cargo expand` before -# parsing to expand any macros -expand = ["euclid"] # Whether to use a new temporary target directory when running `rustc --pretty=expanded`. # This may be required for some build processes. clean = false +[parse.expand] +# A list of crate names that should be run through `cargo expand` before +# parsing to expand any macros +crates = ["euclid"] +# If enabled, use the `--all-features` option when expanding. Ignored when +# `expand_features` is set. Disabled by default, except when using the +# `expand = ["euclid"]` shorthand for backwards-compatibility. +all_features = false +# When `enable_all_features` is disabled and this is also disabled, use the +# `--no-default-features` option when expanding. Enabled by default. +default_features = true +# A list of feature names that should be used when running `cargo expand`. This +# combines with `default_features` like in `Cargo.toml`. Note that the features +# listed here are features for the current crate being built, *not* the crates +# being expanded. The crate's `Cargo.toml` must take care of enabling the +# appropriate features in its dependencies +features = ["cbindgen"] + [export] # A list of additional items not used by exported functions to include in # the generated bindings diff --git a/src/bindgen/builder.rs b/src/bindgen/builder.rs index 51c1773..6b9de0c 100644 --- a/src/bindgen/builder.rs +++ b/src/bindgen/builder.rs @@ -173,7 +173,30 @@ impl Builder { #[allow(unused)] pub fn with_parse_expand>(mut self, expand: &[S]) -> Builder { - self.config.parse.expand = expand.iter().map(|x| String::from(x.as_ref())).collect(); + self.config.parse.expand.crates = expand.iter().map(|x| String::from(x.as_ref())).collect(); + self + } + + #[allow(unused)] + pub fn with_parse_expand_all_features(mut self, expand_all_features: bool) -> Builder { + self.config.parse.expand.all_features = expand_all_features; + self + } + + #[allow(unused)] + pub fn with_parse_expand_default_features(mut self, expand_default_features: bool) -> Builder { + self.config.parse.expand.default_features = expand_default_features; + self + } + + #[allow(unused)] + pub fn with_parse_expand_features>(mut self, expand_features: &[S]) -> Builder { + self.config.parse.expand.features = Some( + expand_features + .iter() + .map(|x| String::from(x.as_ref())) + .collect(), + ); self } @@ -295,7 +318,10 @@ impl Builder { self.config.parse.parse_deps, &self.config.parse.include, &self.config.parse.exclude, - &self.config.parse.expand, + &self.config.parse.expand.crates, + self.config.parse.expand.all_features, + self.config.parse.expand.default_features, + &self.config.parse.expand.features, )?); } else if let Some(cargo) = self.lib_cargo.clone() { result.extend_with(&parser::parse_lib( @@ -303,7 +329,10 @@ impl Builder { self.config.parse.parse_deps, &self.config.parse.include, &self.config.parse.exclude, - &self.config.parse.expand, + &self.config.parse.expand.crates, + self.config.parse.expand.all_features, + self.config.parse.expand.default_features, + &self.config.parse.expand.features, )?); } diff --git a/src/bindgen/cargo/cargo.rs b/src/bindgen/cargo/cargo.rs index a35d42d..812bc5b 100644 --- a/src/bindgen/cargo/cargo.rs +++ b/src/bindgen/cargo/cargo.rs @@ -197,12 +197,21 @@ impl Cargo { None } - pub(crate) fn expand_crate(&self, package: &PackageRef) -> Result { + pub(crate) fn expand_crate( + &self, + package: &PackageRef, + expand_all_features: bool, + expand_default_features: bool, + expand_features: &Option>, + ) -> Result { cargo_expand::expand( &self.manifest_path, &package.name, &package.version, self.clean, + expand_all_features, + expand_default_features, + expand_features, ) } } diff --git a/src/bindgen/cargo/cargo_expand.rs b/src/bindgen/cargo/cargo_expand.rs index 492932f..6ebac39 100644 --- a/src/bindgen/cargo/cargo_expand.rs +++ b/src/bindgen/cargo/cargo_expand.rs @@ -40,6 +40,9 @@ pub fn expand( crate_name: &str, version: &str, use_tempdir: bool, + expand_all_features: bool, + expand_default_features: bool, + expand_features: &Option>, ) -> Result { let cargo = env::var("CARGO").unwrap_or_else(|_| String::from("cargo")); let mut cmd = Command::new(cargo); @@ -56,7 +59,23 @@ pub fn expand( cmd.arg("--lib"); cmd.arg("--manifest-path"); cmd.arg(manifest_path); - cmd.arg("--all-features"); + if let Some(features) = expand_features { + cmd.arg("--features"); + let mut features_str = String::new(); + for (index, feature) in features.iter().enumerate() { + if index != 0 { + features_str.push_str(" "); + } + features_str.push_str(feature); + } + cmd.arg(features_str); + } + if expand_all_features { + cmd.arg("--all-features"); + } + if !expand_default_features { + cmd.arg("--no-default-features"); + } cmd.arg("-p"); cmd.arg(&format!("{}:{}", crate_name, version)); cmd.arg("--"); diff --git a/src/bindgen/config.rs b/src/bindgen/config.rs index fe64f1b..94cee19 100644 --- a/src/bindgen/config.rs +++ b/src/bindgen/config.rs @@ -4,12 +4,16 @@ use std::collections::HashMap; use std::default::Default; +use std::fmt; use std::fs::File; use std::io::prelude::*; use std::io::{self, BufReader}; use std::path::Path; use std::str::FromStr; +use serde::de::value::{MapAccessDeserializer, SeqAccessDeserializer}; +use serde::de::{Deserialize, Deserializer, MapAccess, SeqAccess, Visitor}; + use toml; use bindgen::ir::annotation::AnnotationSet; @@ -409,6 +413,72 @@ impl Default for ConstantConfig { } } +/// Settings to apply when running `rustc --pretty=expanded` +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "snake_case")] +#[serde(deny_unknown_fields)] +#[serde(default)] +pub struct ParseExpandConfig { + /// The names of crates to parse with `rustc --pretty=expanded` + pub crates: Vec, + /// Whether to enable all the features when expanding. + pub all_features: bool, + /// Whether to use the default feature set when expanding. + pub default_features: bool, + /// List of features to use when expanding. Combines with `default_features` like in + /// `Cargo.toml`. + pub features: Option>, +} + +impl Default for ParseExpandConfig { + fn default() -> ParseExpandConfig { + ParseExpandConfig { + crates: Vec::new(), + all_features: false, + default_features: true, + features: None, + } + } +} + +// Backwards-compatibility deserializer for ParseExpandConfig. This allows accepting both the +// simple `expand = ["crate"]` and the more complex `expand = {"crates": ["crate"], +// "default_features": false}` format for the `expand` key. +// +// Note that one (major) difference between the two forms is that, for backwards-compatibility +// reasons, the `expand = ["crate"]` form will enable the `--all-features` flag by default while +// the `expand = {"crates": ["crate"]}` form will use the default feature set by default. +fn retrocomp_parse_expand_config_deserialize<'de, D: Deserializer<'de>>( + deserializer: D, +) -> Result { + struct ParseExpandVisitor; + + impl<'de> Visitor<'de> for ParseExpandVisitor { + type Value = ParseExpandConfig; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a map or sequence of string") + } + + fn visit_seq>(self, seq: A) -> Result { + let crates = + as Deserialize>::deserialize(SeqAccessDeserializer::new(seq))?; + Ok(ParseExpandConfig { + crates, + all_features: true, + default_features: true, + features: None, + }) + } + + fn visit_map>(self, map: A) -> Result { + ::deserialize(MapAccessDeserializer::new(map)) + } + } + + deserializer.deserialize_any(ParseExpandVisitor) +} + /// Settings to apply when parsing. #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "snake_case")] @@ -428,8 +498,9 @@ pub struct ParseConfig { pub include: Option>, /// The names of crates to not parse pub exclude: Vec, - /// The names of crates to parse with `rustc --pretty=expanded` - pub expand: Vec, + /// The configuration options for `rustc --pretty=expanded` + #[serde(deserialize_with = "retrocomp_parse_expand_config_deserialize")] + pub expand: ParseExpandConfig, /// Whether to use a new temporary target directory when running `rustc --pretty=expanded`. /// This may be required for some build processes. pub clean: bool, @@ -441,7 +512,7 @@ impl Default for ParseConfig { parse_deps: false, include: None, exclude: Vec::new(), - expand: Vec::new(), + expand: ParseExpandConfig::default(), clean: false, } } diff --git a/src/bindgen/parser.rs b/src/bindgen/parser.rs index 29ba09a..ccdb692 100644 --- a/src/bindgen/parser.rs +++ b/src/bindgen/parser.rs @@ -37,6 +37,9 @@ pub fn parse_src(src_file: &Path) -> ParseResult { include: None, exclude: Vec::new(), expand: Vec::new(), + expand_all_features: true, + expand_default_features: true, + expand_features: None, parsed_crates: HashSet::new(), cache_src: HashMap::new(), cache_expanded_crate: HashMap::new(), @@ -64,6 +67,9 @@ pub(crate) fn parse_lib( include: &Option>, exclude: &Vec, expand: &Vec, + expand_all_features: bool, + expand_default_features: bool, + expand_features: &Option>, ) -> ParseResult { let mut context = Parser { binding_crate_name: lib.binding_crate_name().to_owned(), @@ -72,6 +78,9 @@ pub(crate) fn parse_lib( include: include.clone(), exclude: exclude.clone(), expand: expand.clone(), + expand_all_features, + expand_default_features, + expand_features: expand_features.clone(), parsed_crates: HashSet::new(), cache_src: HashMap::new(), cache_expanded_crate: HashMap::new(), @@ -93,6 +102,9 @@ struct Parser { include: Option>, exclude: Vec, expand: Vec, + expand_all_features: bool, + expand_default_features: bool, + expand_features: Option>, parsed_crates: HashSet, cache_src: HashMap>, @@ -163,7 +175,12 @@ impl Parser { .lib .as_ref() .unwrap() - .expand_crate(pkg) + .expand_crate( + pkg, + self.expand_all_features, + self.expand_default_features, + &self.expand_features, + ) .map_err(|x| Error::CargoExpand(pkg.name.clone(), x))?; let i = syn::parse_file(&s).map_err(|x| Error::ParseSyntaxError { crate_name: pkg.name.clone(), diff --git a/tests/expectations/both/expand_default_features.c b/tests/expectations/both/expand_default_features.c new file mode 100644 index 0000000..1b5c5ce --- /dev/null +++ b/tests/expectations/both/expand_default_features.c @@ -0,0 +1,11 @@ +#include +#include +#include + +typedef struct Foo { + +} Foo; + +void extra_debug_fn(void); + +void root(Foo a); diff --git a/tests/expectations/both/expand_features.c b/tests/expectations/both/expand_features.c new file mode 100644 index 0000000..22beed1 --- /dev/null +++ b/tests/expectations/both/expand_features.c @@ -0,0 +1,13 @@ +#include +#include +#include + +typedef struct Foo { + +} Foo; + +void cbindgen(void); + +void extra_debug_fn(void); + +void root(Foo a); diff --git a/tests/expectations/both/expand_no_default_features.c b/tests/expectations/both/expand_no_default_features.c new file mode 100644 index 0000000..d29cc3c --- /dev/null +++ b/tests/expectations/both/expand_no_default_features.c @@ -0,0 +1,9 @@ +#include +#include +#include + +typedef struct Foo { + +} Foo; + +void root(Foo a); diff --git a/tests/expectations/expand_default_features.c b/tests/expectations/expand_default_features.c new file mode 100644 index 0000000..de28a4f --- /dev/null +++ b/tests/expectations/expand_default_features.c @@ -0,0 +1,11 @@ +#include +#include +#include + +typedef struct { + +} Foo; + +void extra_debug_fn(void); + +void root(Foo a); diff --git a/tests/expectations/expand_default_features.cpp b/tests/expectations/expand_default_features.cpp new file mode 100644 index 0000000..9d67b2c --- /dev/null +++ b/tests/expectations/expand_default_features.cpp @@ -0,0 +1,14 @@ +#include +#include + +struct Foo { + +}; + +extern "C" { + +void extra_debug_fn(); + +void root(Foo a); + +} // extern "C" diff --git a/tests/expectations/expand_features.c b/tests/expectations/expand_features.c new file mode 100644 index 0000000..2968f14 --- /dev/null +++ b/tests/expectations/expand_features.c @@ -0,0 +1,13 @@ +#include +#include +#include + +typedef struct { + +} Foo; + +void cbindgen(void); + +void extra_debug_fn(void); + +void root(Foo a); diff --git a/tests/expectations/expand_features.cpp b/tests/expectations/expand_features.cpp new file mode 100644 index 0000000..fae1d12 --- /dev/null +++ b/tests/expectations/expand_features.cpp @@ -0,0 +1,16 @@ +#include +#include + +struct Foo { + +}; + +extern "C" { + +void cbindgen(); + +void extra_debug_fn(); + +void root(Foo a); + +} // extern "C" diff --git a/tests/expectations/expand_no_default_features.c b/tests/expectations/expand_no_default_features.c new file mode 100644 index 0000000..afbb0aa --- /dev/null +++ b/tests/expectations/expand_no_default_features.c @@ -0,0 +1,9 @@ +#include +#include +#include + +typedef struct { + +} Foo; + +void root(Foo a); diff --git a/tests/expectations/expand_no_default_features.cpp b/tests/expectations/expand_no_default_features.cpp new file mode 100644 index 0000000..cc9f7cc --- /dev/null +++ b/tests/expectations/expand_no_default_features.cpp @@ -0,0 +1,12 @@ +#include +#include + +struct Foo { + +}; + +extern "C" { + +void root(Foo a); + +} // extern "C" diff --git a/tests/expectations/tag/expand_default_features.c b/tests/expectations/tag/expand_default_features.c new file mode 100644 index 0000000..752cfee --- /dev/null +++ b/tests/expectations/tag/expand_default_features.c @@ -0,0 +1,11 @@ +#include +#include +#include + +struct Foo { + +}; + +void extra_debug_fn(void); + +void root(struct Foo a); diff --git a/tests/expectations/tag/expand_features.c b/tests/expectations/tag/expand_features.c new file mode 100644 index 0000000..2ce69ca --- /dev/null +++ b/tests/expectations/tag/expand_features.c @@ -0,0 +1,13 @@ +#include +#include +#include + +struct Foo { + +}; + +void cbindgen(void); + +void extra_debug_fn(void); + +void root(struct Foo a); diff --git a/tests/expectations/tag/expand_no_default_features.c b/tests/expectations/tag/expand_no_default_features.c new file mode 100644 index 0000000..16782fd --- /dev/null +++ b/tests/expectations/tag/expand_no_default_features.c @@ -0,0 +1,9 @@ +#include +#include +#include + +struct Foo { + +}; + +void root(struct Foo a); diff --git a/tests/rust/expand_default_features/Cargo.lock b/tests/rust/expand_default_features/Cargo.lock new file mode 100644 index 0000000..39194fa --- /dev/null +++ b/tests/rust/expand_default_features/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "expand" +version = "0.1.0" + diff --git a/tests/rust/expand_default_features/Cargo.toml b/tests/rust/expand_default_features/Cargo.toml new file mode 100644 index 0000000..98a968d --- /dev/null +++ b/tests/rust/expand_default_features/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "expand" +version = "0.1.0" +authors = ["cbindgen"] + +[features] +default = ["extra_headers"] +extra_headers = [] +no_parse = [] + +[lib] +name = "expand" +crate-type = ["lib", "dylib"] + +[[bin]] +name = "expand" +path = "src/main.rs" diff --git a/tests/rust/expand_default_features/cbindgen.toml b/tests/rust/expand_default_features/cbindgen.toml new file mode 100644 index 0000000..5741ca7 --- /dev/null +++ b/tests/rust/expand_default_features/cbindgen.toml @@ -0,0 +1,5 @@ +[parse] +parse_deps = false +[parse.expand] +crates = ["expand"] +all_features = false diff --git a/tests/rust/expand_default_features/src/lib.rs b/tests/rust/expand_default_features/src/lib.rs new file mode 100644 index 0000000..ee51de9 --- /dev/null +++ b/tests/rust/expand_default_features/src/lib.rs @@ -0,0 +1,23 @@ +#[repr(C)] +struct Foo { + +} + +#[cfg(feature = "extra_headers")] +#[no_mangle] +pub extern "C" fn extra_debug_fn() { +} + +#[cfg(feature = "no_parse")] +pub extern "C" fn no_parse() { + x; +} + +#[cfg(feature = "cbindgen")] +#[no_mangle] +pub extern "C" fn cbindgen() { +} + +#[no_mangle] +pub extern "C" fn root(a: Foo) { +} diff --git a/tests/rust/expand_default_features/src/main.rs b/tests/rust/expand_default_features/src/main.rs new file mode 100644 index 0000000..f79c691 --- /dev/null +++ b/tests/rust/expand_default_features/src/main.rs @@ -0,0 +1,2 @@ +fn main() { +} diff --git a/tests/rust/expand_features/Cargo.lock b/tests/rust/expand_features/Cargo.lock new file mode 100644 index 0000000..39194fa --- /dev/null +++ b/tests/rust/expand_features/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "expand" +version = "0.1.0" + diff --git a/tests/rust/expand_features/Cargo.toml b/tests/rust/expand_features/Cargo.toml new file mode 100644 index 0000000..f1dc6d4 --- /dev/null +++ b/tests/rust/expand_features/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "expand" +version = "0.1.0" +authors = ["cbindgen"] + +[features] +default = ["extra_headers"] +extra_headers = [] +no_parse = [] +cbindgen = [] + +[lib] +name = "expand" +crate-type = ["lib", "dylib"] + +[[bin]] +name = "expand" +path = "src/main.rs" diff --git a/tests/rust/expand_features/cbindgen.toml b/tests/rust/expand_features/cbindgen.toml new file mode 100644 index 0000000..3d8686e --- /dev/null +++ b/tests/rust/expand_features/cbindgen.toml @@ -0,0 +1,5 @@ +[parse] +parse_deps = false +[parse.expand] +crates = ["expand"] +features = ["cbindgen"] diff --git a/tests/rust/expand_features/src/lib.rs b/tests/rust/expand_features/src/lib.rs new file mode 100644 index 0000000..ee51de9 --- /dev/null +++ b/tests/rust/expand_features/src/lib.rs @@ -0,0 +1,23 @@ +#[repr(C)] +struct Foo { + +} + +#[cfg(feature = "extra_headers")] +#[no_mangle] +pub extern "C" fn extra_debug_fn() { +} + +#[cfg(feature = "no_parse")] +pub extern "C" fn no_parse() { + x; +} + +#[cfg(feature = "cbindgen")] +#[no_mangle] +pub extern "C" fn cbindgen() { +} + +#[no_mangle] +pub extern "C" fn root(a: Foo) { +} diff --git a/tests/rust/expand_features/src/main.rs b/tests/rust/expand_features/src/main.rs new file mode 100644 index 0000000..f79c691 --- /dev/null +++ b/tests/rust/expand_features/src/main.rs @@ -0,0 +1,2 @@ +fn main() { +} diff --git a/tests/rust/expand_no_default_features/Cargo.lock b/tests/rust/expand_no_default_features/Cargo.lock new file mode 100644 index 0000000..39194fa --- /dev/null +++ b/tests/rust/expand_no_default_features/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "expand" +version = "0.1.0" + diff --git a/tests/rust/expand_no_default_features/Cargo.toml b/tests/rust/expand_no_default_features/Cargo.toml new file mode 100644 index 0000000..98a968d --- /dev/null +++ b/tests/rust/expand_no_default_features/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "expand" +version = "0.1.0" +authors = ["cbindgen"] + +[features] +default = ["extra_headers"] +extra_headers = [] +no_parse = [] + +[lib] +name = "expand" +crate-type = ["lib", "dylib"] + +[[bin]] +name = "expand" +path = "src/main.rs" diff --git a/tests/rust/expand_no_default_features/cbindgen.toml b/tests/rust/expand_no_default_features/cbindgen.toml new file mode 100644 index 0000000..53b00d9 --- /dev/null +++ b/tests/rust/expand_no_default_features/cbindgen.toml @@ -0,0 +1,5 @@ +[parse] +parse_deps = false +[parse.expand] +crates = ["expand"] +default_features = false diff --git a/tests/rust/expand_no_default_features/src/lib.rs b/tests/rust/expand_no_default_features/src/lib.rs new file mode 100644 index 0000000..ee51de9 --- /dev/null +++ b/tests/rust/expand_no_default_features/src/lib.rs @@ -0,0 +1,23 @@ +#[repr(C)] +struct Foo { + +} + +#[cfg(feature = "extra_headers")] +#[no_mangle] +pub extern "C" fn extra_debug_fn() { +} + +#[cfg(feature = "no_parse")] +pub extern "C" fn no_parse() { + x; +} + +#[cfg(feature = "cbindgen")] +#[no_mangle] +pub extern "C" fn cbindgen() { +} + +#[no_mangle] +pub extern "C" fn root(a: Foo) { +} diff --git a/tests/rust/expand_no_default_features/src/main.rs b/tests/rust/expand_no_default_features/src/main.rs new file mode 100644 index 0000000..f79c691 --- /dev/null +++ b/tests/rust/expand_no_default_features/src/main.rs @@ -0,0 +1,2 @@ +fn main() { +}