Add ability to specify features to use for macro expansion

Currently, `cbindgen` uses the `--all-features` flag when expanding a
dependent crate. However, this may not be desirable in a number of
cases:

 - Some C APIs may be gated by a feature flag and would not be present
   in the final cdylib depending on the features provided (for instance
   one could want to have the ability to build a "debug" version of the
   library which provides extra unstable hooks). In such cases, a
   programmatic `cbindgen` call in a build script would want to use only
   the features that will get used in the current build.

 - Some features may bring in large dependencies and/or potentially
   increase compilation time without affecting the FFI surface, and it
   would be faster and more efficient to disable them when running
   `cbindgen`.

 - Some features may require external libraries and/or hardware (e.g.
   dependencies on GPU libraries such as CUDA) that may not be available
   on the current machine without affecting the FFI surface.

To alleviate this problem, this PR adds an extended version of the
`parse.expand` configuration key, allowing control over the features
used when expanding in a way similar to the way cargo handles extended
dependencies (although note that there is a single version of each key,
since the features refer to the features of the current crate). So for
instance instead of writing `expand = ["euclid"]` one would write:

```
[parse.expand]
crates = ["euclid"]
```

which is equivalent to:

```
[parse.expand]
crates = ["euclid"]
all_features = false
default_features = true
features = ["feature1", "feature2"]
```

Note that `all_features` is set to `false` by default in order to match
cargo's behavior.

For backwards compatibility, the old syntax `expand = ["euclid"]` is
still supported and is equivalent to:

```
[parse.expand]
crates = ["euclid"]
all_features = true
default_features = true
features = null
```

In this case, `all_features` is set to `true` in order to match the
previous behavior of cbindgen.
This commit is contained in:
Basile Clement
2018-07-27 17:05:31 +02:00
committed by Ryan Hunt
parent fb49cc60a1
commit 33c45a26bb
33 changed files with 467 additions and 12 deletions
+18 -3
View File
@@ -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
+32 -3
View File
@@ -173,7 +173,30 @@ impl Builder {
#[allow(unused)]
pub fn with_parse_expand<S: AsRef<str>>(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<S: AsRef<str>>(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,
)?);
}
+10 -1
View File
@@ -197,12 +197,21 @@ impl Cargo {
None
}
pub(crate) fn expand_crate(&self, package: &PackageRef) -> Result<String, cargo_expand::Error> {
pub(crate) fn expand_crate(
&self,
package: &PackageRef,
expand_all_features: bool,
expand_default_features: bool,
expand_features: &Option<Vec<String>>,
) -> Result<String, cargo_expand::Error> {
cargo_expand::expand(
&self.manifest_path,
&package.name,
&package.version,
self.clean,
expand_all_features,
expand_default_features,
expand_features,
)
}
}
+20 -1
View File
@@ -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<Vec<String>>,
) -> Result<String, Error> {
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("--");
+74 -3
View File
@@ -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<String>,
/// 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<Vec<String>>,
}
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<ParseExpandConfig, D::Error> {
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<A: SeqAccess<'de>>(self, seq: A) -> Result<Self::Value, A::Error> {
let crates =
<Vec<String> as Deserialize>::deserialize(SeqAccessDeserializer::new(seq))?;
Ok(ParseExpandConfig {
crates,
all_features: true,
default_features: true,
features: None,
})
}
fn visit_map<A: MapAccess<'de>>(self, map: A) -> Result<Self::Value, A::Error> {
<ParseExpandConfig as Deserialize>::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<Vec<String>>,
/// The names of crates to not parse
pub exclude: Vec<String>,
/// The names of crates to parse with `rustc --pretty=expanded`
pub expand: Vec<String>,
/// 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,
}
}
+18 -1
View File
@@ -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<Vec<String>>,
exclude: &Vec<String>,
expand: &Vec<String>,
expand_all_features: bool,
expand_default_features: bool,
expand_features: &Option<Vec<String>>,
) -> 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<Vec<String>>,
exclude: Vec<String>,
expand: Vec<String>,
expand_all_features: bool,
expand_default_features: bool,
expand_features: Option<Vec<String>>,
parsed_crates: HashSet<String>,
cache_src: HashMap<PathBuf, Vec<syn::Item>>,
@@ -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(),
@@ -0,0 +1,11 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct Foo {
} Foo;
void extra_debug_fn(void);
void root(Foo a);
+13
View File
@@ -0,0 +1,13 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct Foo {
} Foo;
void cbindgen(void);
void extra_debug_fn(void);
void root(Foo a);
@@ -0,0 +1,9 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct Foo {
} Foo;
void root(Foo a);
@@ -0,0 +1,11 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct {
} Foo;
void extra_debug_fn(void);
void root(Foo a);
@@ -0,0 +1,14 @@
#include <cstdint>
#include <cstdlib>
struct Foo {
};
extern "C" {
void extra_debug_fn();
void root(Foo a);
} // extern "C"
+13
View File
@@ -0,0 +1,13 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct {
} Foo;
void cbindgen(void);
void extra_debug_fn(void);
void root(Foo a);
+16
View File
@@ -0,0 +1,16 @@
#include <cstdint>
#include <cstdlib>
struct Foo {
};
extern "C" {
void cbindgen();
void extra_debug_fn();
void root(Foo a);
} // extern "C"
@@ -0,0 +1,9 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct {
} Foo;
void root(Foo a);
@@ -0,0 +1,12 @@
#include <cstdint>
#include <cstdlib>
struct Foo {
};
extern "C" {
void root(Foo a);
} // extern "C"
@@ -0,0 +1,11 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
struct Foo {
};
void extra_debug_fn(void);
void root(struct Foo a);
+13
View File
@@ -0,0 +1,13 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
struct Foo {
};
void cbindgen(void);
void extra_debug_fn(void);
void root(struct Foo a);
@@ -0,0 +1,9 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
struct Foo {
};
void root(struct Foo a);
+4
View File
@@ -0,0 +1,4 @@
[[package]]
name = "expand"
version = "0.1.0"
@@ -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"
@@ -0,0 +1,5 @@
[parse]
parse_deps = false
[parse.expand]
crates = ["expand"]
all_features = false
@@ -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) {
}
@@ -0,0 +1,2 @@
fn main() {
}
+4
View File
@@ -0,0 +1,4 @@
[[package]]
name = "expand"
version = "0.1.0"
+18
View File
@@ -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"
+5
View File
@@ -0,0 +1,5 @@
[parse]
parse_deps = false
[parse.expand]
crates = ["expand"]
features = ["cbindgen"]
+23
View File
@@ -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) {
}
+2
View File
@@ -0,0 +1,2 @@
fn main() {
}
+4
View File
@@ -0,0 +1,4 @@
[[package]]
name = "expand"
version = "0.1.0"
@@ -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"
@@ -0,0 +1,5 @@
[parse]
parse_deps = false
[parse.expand]
crates = ["expand"]
default_features = false
@@ -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) {
}
@@ -0,0 +1,2 @@
fn main() {
}