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:
committed by
Ryan Hunt
parent
fb49cc60a1
commit
33c45a26bb
@@ -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
@@ -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,
|
||||
)?);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
@@ -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"
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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
@@ -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() {
|
||||
}
|
||||
Generated
+4
@@ -0,0 +1,4 @@
|
||||
[[package]]
|
||||
name = "expand"
|
||||
version = "0.1.0"
|
||||
|
||||
@@ -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"
|
||||
@@ -0,0 +1,5 @@
|
||||
[parse]
|
||||
parse_deps = false
|
||||
[parse.expand]
|
||||
crates = ["expand"]
|
||||
features = ["cbindgen"]
|
||||
@@ -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() {
|
||||
}
|
||||
@@ -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() {
|
||||
}
|
||||
Reference in New Issue
Block a user