diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/codegen/mod.rs | 18 | ||||
-rw-r--r-- | src/features.rs | 187 | ||||
-rw-r--r-- | src/ir/analysis/derive_copy.rs | 2 | ||||
-rw-r--r-- | src/ir/analysis/derive_debug.rs | 2 | ||||
-rw-r--r-- | src/ir/analysis/derive_default.rs | 2 | ||||
-rw-r--r-- | src/lib.rs | 59 | ||||
-rw-r--r-- | src/options.rs | 20 |
7 files changed, 261 insertions, 29 deletions
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 5c00a53d..ef6f031f 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -350,7 +350,7 @@ impl CodeGenerator for Module { } if item.id() == ctx.root_module() { - if result.saw_union && !ctx.options().unstable_rust { + if result.saw_union && !ctx.options().rust_features().untagged_union() { utils::prepend_union_types(ctx, &mut *result); } if result.saw_incomplete_array { @@ -911,8 +911,8 @@ impl<'a> FieldCodegen<'a> for FieldData { let field_ty = ctx.resolve_type(self.ty()); let ty = self.ty().to_rust_ty_or_opaque(ctx, &()); - // NB: In unstable rust we use proper `union` types. - let ty = if parent.is_union() && !ctx.options().unstable_rust { + // NB: If supported, we use proper `union` types. + let ty = if parent.is_union() && !ctx.options().rust_features().untagged_union() { if ctx.options().enable_cxx_namespaces { quote_ty!(ctx.ext_cx(), root::__BindgenUnionField<$ty>) } else { @@ -1052,8 +1052,8 @@ impl BitfieldUnit { -> P<ast::Item> { let ctor_name = self.ctor_name(ctx); - // If we're generating unstable Rust, add the const. - let fn_prefix = if ctx.options().unstable_rust { + // If supported, add the const. + let fn_prefix = if ctx.options().rust_features().const_fn() { quote_tokens!(ctx.ext_cx(), pub const fn) } else { quote_tokens!(ctx.ext_cx(), pub fn) @@ -1115,8 +1115,8 @@ impl Bitfield { let offset = self.offset_into_unit(); let mask = self.mask(); - // If we're generating unstable Rust, add the const. - let fn_prefix = if ctx.options().unstable_rust { + // If supported, add the const. + let fn_prefix = if ctx.options().rust_features().const_fn() { quote_tokens!(ctx.ext_cx(), pub const fn) } else { quote_tokens!(ctx.ext_cx(), pub fn) @@ -1445,7 +1445,7 @@ impl CodeGenerator for CompInfo { } let canonical_name = item.canonical_name(ctx); - let builder = if is_union && ctx.options().unstable_rust { + let builder = if is_union && ctx.options().rust_features().untagged_union() { aster::AstBuilder::new() .item() .pub_() @@ -1552,7 +1552,7 @@ impl CodeGenerator for CompInfo { ()); } - if is_union && !ctx.options().unstable_rust { + if is_union && !ctx.options().rust_features().untagged_union() { let layout = layout.expect("Unable to get layout information?"); let ty = BlobTyBuilder::new(layout).build(); let field = StructFieldBuilder::named("bindgen_union_field") diff --git a/src/features.rs b/src/features.rs new file mode 100644 index 00000000..ba2f11cf --- /dev/null +++ b/src/features.rs @@ -0,0 +1,187 @@ +//! Contains code for selecting features + +#![deny(missing_docs)] +#![deny(warnings)] +#![deny(unused_extern_crates)] + +use std::io; +use std::str::FromStr; + +/// Define RustTarget struct definition, Default impl, and conversions +/// between RustTarget and String. +macro_rules! rust_target_def { + ( $( $( #[$attr:meta] )* => $release:ident => $value:expr; )* ) => { + /// Represents the version of the Rust language to target. + /// + /// To support a beta release, use the corresponding stable release. + /// + /// This enum will have more variants added as necessary. + #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Hash)] + #[allow(non_camel_case_types)] + pub enum RustTarget { + $( + $( + #[$attr] + )* + $release, + )* + } + + impl Default for RustTarget { + /// Gives the latest stable Rust version + fn default() -> RustTarget { + LATEST_STABLE_RUST + } + } + + impl FromStr for RustTarget { + type Err = io::Error; + + #[allow(dead_code)] + /// Create a `RustTarget` from a string. + /// + /// * The stable/beta versions of Rust are of the form "1.0", + /// "1.19", etc. + /// * The nightly version should be specified with "nightly". + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s.as_ref() { + $( + stringify!($value) => Ok(RustTarget::$release), + )* + _ => Err( + io::Error::new( + io::ErrorKind::InvalidInput, + concat!( + "Got an invalid rust target. Accepted values ", + "are of the form ", + "\"1.0\" or \"nightly\"."))), + } + } + } + + impl From<RustTarget> for String { + fn from(target: RustTarget) -> Self { + match target { + $( + RustTarget::$release => stringify!($value), + )* + }.into() + } + } + } +} + +/// Defines an array slice with all RustTarget values +macro_rules! rust_target_values_def { + ( $( $( #[$attr:meta] )* => $release:ident => $value:expr; )* ) => { + /// Strings of allowed `RustTarget` values + #[allow(dead_code)] + pub static RUST_TARGET_STRINGS: &'static [&str] = &[ + $( + stringify!($value), + )* + ]; + } +} + +/// Defines macro which takes a macro +macro_rules! rust_target_base { + ( $x_macro:ident ) => { + $x_macro!( + /// Rust stable 1.0 + => Stable_1_0 => 1.0; + /// Rust stable 1.19 + => Stable_1_19 => 1.19; + /// Nightly rust + => Nightly => nightly; + ); + } +} + +rust_target_base!(rust_target_def); +rust_target_base!(rust_target_values_def); + +/// Latest stable release of Rust +pub const LATEST_STABLE_RUST: RustTarget = RustTarget::Stable_1_19; + +/// Create RustFeatures struct definition, new(), and a getter for each field +macro_rules! rust_feature_def { + ( $( $( #[$attr:meta] )* => $feature:ident; )* ) => { + /// Features supported by a rust target + #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] + pub struct RustFeatures { + $( + $feature: bool, + )* + } + + impl RustFeatures { + /// Gives a RustFeatures struct with all features disabled + fn new() -> Self { + RustFeatures { + $( + $feature: false, + )* + } + } + + $( + $( + #[$attr] + )* + pub fn $feature(&self) -> bool { + self.$feature + } + )* + } + } +} + +rust_feature_def!( + /// Untagged unions ([RFC 1444](https://github.com/rust-lang/rfcs/blob/master/text/1444-union.md)) + => untagged_union; + /// Constant function ([RFC 911](https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md)) + => const_fn; +); + +impl From<RustTarget> for RustFeatures { + fn from(rust_target: RustTarget) -> Self { + let mut features = RustFeatures::new(); + + if rust_target >= RustTarget::Stable_1_19 { + features.untagged_union = true; + } + + if rust_target >= RustTarget::Nightly { + features.const_fn = true; + } + + features + } +} + +impl Default for RustFeatures { + fn default() -> Self { + let default_rust_target: RustTarget = Default::default(); + Self::from(default_rust_target) + } +} + +#[cfg(test)] +mod test { + #![allow(unused_imports)] + use super::*; + + fn test_target(target_str: &str, target: RustTarget) { + let target_string: String = target.into(); + assert_eq!(target_str, target_string); + assert_eq!(target, RustTarget::from_str(target_str).unwrap()); + } + + #[test] + fn str_to_target() { + test_target("1.0", RustTarget::Stable_1_0); + test_target("1.19", RustTarget::Stable_1_19); + test_target("nightly", RustTarget::Nightly); + } +} diff --git a/src/ir/analysis/derive_copy.rs b/src/ir/analysis/derive_copy.rs index f4997632..dfd0343c 100644 --- a/src/ir/analysis/derive_copy.rs +++ b/src/ir/analysis/derive_copy.rs @@ -207,7 +207,7 @@ impl<'ctx, 'gen> MonotoneFramework for CannotDeriveCopy<'ctx, 'gen> { } if info.kind() == CompKind::Union { - if !self.ctx.options().unstable_rust { + if !self.ctx.options().rust_features().untagged_union() { // NOTE: If there's no template parameters we can derive copy // unconditionally, since arrays are magical for rustc, and // __BindgenUnionField always implements copy. diff --git a/src/ir/analysis/derive_debug.rs b/src/ir/analysis/derive_debug.rs index 8990d1cc..8f2be22a 100644 --- a/src/ir/analysis/derive_debug.rs +++ b/src/ir/analysis/derive_debug.rs @@ -208,7 +208,7 @@ impl<'ctx, 'gen> MonotoneFramework for CannotDeriveDebug<'ctx, 'gen> { ); if info.kind() == CompKind::Union { - if self.ctx.options().unstable_rust { + if self.ctx.options().rust_features().untagged_union() { trace!(" cannot derive Debug for Rust unions"); return self.insert(id); } diff --git a/src/ir/analysis/derive_default.rs b/src/ir/analysis/derive_default.rs index fe429e2a..4c2cad25 100644 --- a/src/ir/analysis/derive_default.rs +++ b/src/ir/analysis/derive_default.rs @@ -242,7 +242,7 @@ impl<'ctx, 'gen> MonotoneFramework for CannotDeriveDefault<'ctx, 'gen> { ); if info.kind() == CompKind::Union { - if self.ctx.options().unstable_rust { + if self.ctx.options().rust_features().untagged_union() { trace!(" cannot derive Default for Rust unions"); return self.insert(id); } @@ -62,6 +62,7 @@ macro_rules! doc_mod { } mod clang; +mod features; mod ir; mod parse; mod regex_set; @@ -72,6 +73,7 @@ pub mod callbacks; mod codegen; doc_mod!(clang, clang_docs); +doc_mod!(features, features_docs); doc_mod!(ir, ir_docs); doc_mod!(parse, parse_docs); doc_mod!(regex_set, regex_set_docs); @@ -80,6 +82,8 @@ mod codegen { include!(concat!(env!("OUT_DIR"), "/codegen.rs")); } +pub use features::{RustTarget, LATEST_STABLE_RUST, RUST_TARGET_STRINGS}; +use features::RustFeatures; use ir::context::{BindgenContext, ItemId}; use ir::item::Item; use parse::{ClangItemParser, ParseError}; @@ -188,6 +192,8 @@ impl Builder { output_vector.push(header); } + output_vector.push(self.options.rust_target.into()); + self.options .bitfield_enums .get_items() @@ -340,10 +346,6 @@ impl Builder { output_vector.push("--no-prepend-enum-name".into()); } - if !self.options.unstable_rust { - output_vector.push("--unstable-rust".into()); - } - self.options .opaque_types .get_items() @@ -463,6 +465,14 @@ impl Builder { self } + /// Specify the rust target + /// + /// The default is the latest stable Rust version + pub fn rust_target(mut self, rust_target: RustTarget) -> Self { + self.options.set_rust_target(rust_target); + self + } + /// Set the output graphviz file. pub fn emit_ir_graphviz<T: Into<String>>(mut self, path: T) -> Builder { let path = path.into(); @@ -744,9 +754,10 @@ impl Builder { } /// Avoid generating any unstable Rust, such as Rust unions, in the generated bindings. - pub fn unstable_rust(mut self, doit: bool) -> Self { - self.options.unstable_rust = doit; - self + #[deprecated(note="please use `rust_target` instead")] + pub fn unstable_rust(self, doit: bool) -> Self { + let rust_target = if doit { RustTarget::Nightly } else { LATEST_STABLE_RUST }; + self.rust_target(rust_target) } /// Use core instead of libstd in the generated bindings. @@ -955,10 +966,6 @@ pub struct BindgenOptions { /// and types. pub derive_default: bool, - /// True if we can use unstable Rust code in the bindings, false if we - /// cannot. - pub unstable_rust: bool, - /// True if we should avoid using libstd to use libcore instead. pub use_core: bool, @@ -1025,6 +1032,12 @@ pub struct BindgenOptions { /// Whether to prepend the enum name to bitfield or constant variants. pub prepend_enum_name: bool, + + /// Version of the Rust compiler to target + rust_target: RustTarget, + + /// Features to enable, derived from `rust_target` + rust_features: RustFeatures, } /// TODO(emilio): This is sort of a lie (see the error message that results from @@ -1043,11 +1056,34 @@ impl BindgenOptions { self.constified_enum_modules.build(); self.constified_enums.build(); } + + /// Update rust target version + pub fn set_rust_target(&mut self, rust_target: RustTarget) { + self.rust_target = rust_target; + + // Keep rust_features synced with rust_target + self.rust_features = rust_target.into(); + } + + /// Get target Rust version + pub fn rust_target(&self) -> RustTarget { + self.rust_target + } + + /// Get features supported by target Rust version + pub fn rust_features(&self) -> RustFeatures { + self.rust_features + } + } impl Default for BindgenOptions { fn default() -> BindgenOptions { + let rust_target = RustTarget::default(); + BindgenOptions { + rust_target: rust_target, + rust_features: rust_target.into(), hidden_types: Default::default(), opaque_types: Default::default(), whitelisted_types: Default::default(), @@ -1066,7 +1102,6 @@ impl Default for BindgenOptions { derive_default: false, enable_cxx_namespaces: false, disable_name_namespacing: false, - unstable_rust: false, use_core: false, ctypes_prefix: None, namespaced_constants: true, diff --git a/src/options.rs b/src/options.rs index f2ed5494..e2e1b277 100644 --- a/src/options.rs +++ b/src/options.rs @@ -1,7 +1,9 @@ -use bindgen::{Builder, CodegenConfig, builder}; +use bindgen::{Builder, CodegenConfig, RUST_TARGET_STRINGS, RustTarget, builder}; use clap::{App, Arg}; +// use super::features::RUST_TARGET_STRINGS; use std::fs::File; use std::io::{self, Error, ErrorKind}; +use std::str::FromStr; /// Construct a new [`Builder`](./struct.Builder.html) from command line flags. pub fn builder_from_flags<I> @@ -9,6 +11,10 @@ pub fn builder_from_flags<I> -> Result<(Builder, Box<io::Write>, bool), io::Error> where I: Iterator<Item = String>, { + let rust_target_help = format!( + "Version of the Rust compiler to target. Valid options are: {:?}.", + RUST_TARGET_STRINGS); + let matches = App::new("bindgen") .version(env!("CARGO_PKG_VERSION")) .about("Generates Rust bindings from C/C++ headers.") @@ -161,6 +167,10 @@ pub fn builder_from_flags<I> .takes_value(true) .multiple(true) .number_of_values(1), + Arg::with_name("rust-target") + .long("rust-target") + .help(&rust_target_help) + .takes_value(true), Arg::with_name("static") .long("static-link") .help("Link to static library.") @@ -226,6 +236,10 @@ pub fn builder_from_flags<I> return Err(Error::new(ErrorKind::Other, "Header not found")); } + if let Some(rust_target) = matches.value_of("rust-target") { + builder = builder.rust_target(RustTarget::from_str(rust_target)?); + } + if let Some(bitfields) = matches.values_of("bitfield-enum") { for regex in bitfields { builder = builder.bitfield_enum(regex); @@ -337,10 +351,6 @@ pub fn builder_from_flags<I> builder = builder.ignore_methods(); } - if matches.is_present("unstable-rust") { - builder = builder.unstable_rust(true); - } - if matches.is_present("no-convert-floats") { builder = builder.no_convert_floats(); } |