diff options
author | Travis Finkenauer <tmfinken@gmail.com> | 2017-08-13 13:12:21 -0700 |
---|---|---|
committer | Travis Finkenauer <tmfinken@gmail.com> | 2017-08-13 13:12:21 -0700 |
commit | e7fa2895582435b59dfb1ac960167b0876f8da55 (patch) | |
tree | 321361d58ee698eea089eed18fbd62341345cf13 /src | |
parent | fdee9f1c83f8bf7cc554a59c2f66d9cbbb09eda4 (diff) |
Re-add --rust-target to replace --unstable-rust
Instead of specifying whether or not to use stable, specify the Rust
release to support (one of several stable/beta releases or nightly).
The `--unstable-rust` option is still accepted and implies nightly.
The definitions of `RustTarget` and `RustFeatures` are created with
macros.
For each test that uses unions, there is a version that uses the latest
stable and 1.0.
This also fixes the bug where unions were generated with non-Copy
fields.
Diffstat (limited to 'src')
-rw-r--r-- | src/codegen/mod.rs | 30 | ||||
-rw-r--r-- | src/features.rs | 185 | ||||
-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/ir/analysis/derive_hash.rs | 2 | ||||
-rw-r--r-- | src/ir/comp.rs | 17 | ||||
-rw-r--r-- | src/lib.rs | 59 | ||||
-rw-r--r-- | src/options.rs | 30 |
9 files changed, 297 insertions, 32 deletions
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 3fb4453e..3f5ceaaa 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -84,6 +84,9 @@ struct CodegenResult<'a> { /// need to be referenced by anything. codegen_id: &'a Cell<usize>, + /// Whether a bindgen union has been generated at least once. + saw_bindgen_union: bool, + /// Whether an union has been generated at least once. saw_union: bool, @@ -124,6 +127,7 @@ impl<'a> CodegenResult<'a> { CodegenResult { items: vec![], saw_union: false, + saw_bindgen_union: false, saw_incomplete_array: false, saw_objc: false, codegen_id: codegen_id, @@ -138,6 +142,11 @@ impl<'a> CodegenResult<'a> { self.saw_union = true; } + fn saw_bindgen_union(&mut self) { + self.saw_union(); + self.saw_bindgen_union = true; + } + fn saw_incomplete_array(&mut self) { self.saw_incomplete_array = true; } @@ -360,7 +369,7 @@ impl CodeGenerator for Module { } if item.id() == ctx.root_module() { - if result.saw_union && !ctx.options().unstable_rust { + if result.saw_bindgen_union { utils::prepend_union_types(ctx, &mut *result); } if result.saw_incomplete_array { @@ -943,8 +952,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() && !parent.can_be_rust_union(ctx) { if ctx.options().enable_cxx_namespaces { quote_ty!(ctx.ext_cx(), root::__BindgenUnionField<$ty>) } else { @@ -1077,8 +1086,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) @@ -1138,8 +1147,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) @@ -1491,7 +1500,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 && self.can_be_rust_union(ctx) { aster::AstBuilder::new() .item() .pub_() @@ -1571,6 +1580,9 @@ impl CodeGenerator for CompInfo { } if is_union { result.saw_union(); + if !self.can_be_rust_union(ctx) { + result.saw_bindgen_union(); + } } let layout = item.kind().expect_type().layout(ctx); @@ -1600,7 +1612,7 @@ impl CodeGenerator for CompInfo { ); } - if is_union && !ctx.options().unstable_rust { + if is_union && !self.can_be_rust_union(ctx) { 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..3f59c6b9 --- /dev/null +++ b/src/features.rs @@ -0,0 +1,185 @@ +//! 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; + + /// 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 + 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); } diff --git a/src/ir/analysis/derive_hash.rs b/src/ir/analysis/derive_hash.rs index 2456143c..e879bd22 100644 --- a/src/ir/analysis/derive_hash.rs +++ b/src/ir/analysis/derive_hash.rs @@ -232,7 +232,7 @@ impl<'ctx, 'gen> MonotoneFramework for CannotDeriveHash<'ctx, 'gen> { ); if info.kind() == CompKind::Union { - if self.ctx.options().unstable_rust { + if self.ctx.options().rust_features().untagged_union() { trace!(" cannot derive Hash for Rust unions"); return self.insert(id); } diff --git a/src/ir/comp.rs b/src/ir/comp.rs index 81bb8d8c..ecdfb5de 100644 --- a/src/ir/comp.rs +++ b/src/ir/comp.rs @@ -10,6 +10,7 @@ use super::traversal::{EdgeKind, Trace, Tracer}; use super::template::TemplateParameters; use clang; use codegen::struct_layout::{align_to, bytes_from_bits_pow2}; +use ir::derive::CanDeriveCopy; use parse::{ClangItemParser, ParseError}; use peeking_take_while::PeekableExt; use std::cell::Cell; @@ -1316,6 +1317,22 @@ impl CompInfo { pub fn compute_bitfield_units(&mut self, ctx: &BindgenContext) { self.fields.compute_bitfield_units(ctx); } + + /// Returns whether the current union can be represented as a Rust `union` + /// + /// Requirements: + /// 1. Current RustTarget allows for `untagged_union` + /// 2. Each field can derive `Copy` + pub fn can_be_rust_union(&self, ctx: &BindgenContext) -> bool { + ctx.options().rust_features().untagged_union() && + self.fields().iter().all(|f| + match *f { + Field::DataMember(ref field_data) => field_data.ty().can_derive_copy(ctx), + Field::Bitfields(_) => false, + } + ) + } + } impl DotAttributes for CompInfo { @@ -59,6 +59,7 @@ macro_rules! doc_mod { } mod clang; +mod features; mod ir; mod parse; mod regex_set; @@ -69,6 +70,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); @@ -77,6 +79,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}; @@ -185,6 +189,8 @@ impl Builder { output_vector.push(header); } + output_vector.push(self.options.rust_target.into()); + self.options .bitfield_enums .get_items() @@ -357,10 +363,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() @@ -492,6 +494,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(); @@ -789,9 +799,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. @@ -1016,10 +1027,6 @@ pub struct BindgenOptions { /// and types. pub derive_hash: 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, @@ -1086,6 +1093,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 @@ -1104,11 +1117,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(), @@ -1129,7 +1165,6 @@ impl Default for BindgenOptions { derive_hash: 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 8fce0586..28faea99 100644 --- a/src/options.rs +++ b/src/options.rs @@ -1,7 +1,9 @@ -use bindgen::{builder, Builder, CodegenConfig}; + +use bindgen::{Builder, CodegenConfig, RUST_TARGET_STRINGS, RustTarget, builder}; use clap::{App, Arg}; use std::fs::File; -use std::io::{self, Error, ErrorKind}; +use std::io::{self, Error, ErrorKind, Write, stderr}; +use std::str::FromStr; /// Construct a new [`Builder`](./struct.Builder.html) from command line flags. pub fn builder_from_flags<I>( @@ -10,6 +12,10 @@ pub fn builder_from_flags<I>( 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.") @@ -148,7 +154,7 @@ where .help("Do not prepend the enum name to bitfield or constant variants."), Arg::with_name("unstable-rust") .long("unstable-rust") - .help("Generate unstable Rust code.") + .help("Generate unstable Rust code (deprecated; use --rust-target instead).") .multiple(true), // FIXME: Pass legacy test suite Arg::with_name("opaque-type") .long("opaque-type") @@ -168,6 +174,10 @@ where .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.") @@ -233,6 +243,16 @@ where return Err(Error::new(ErrorKind::Other, "Header not found")); } + if matches.is_present("unstable-rust") { + builder = builder.rust_target(RustTarget::Nightly); + writeln!(&mut stderr(), "warning: the `--unstable-rust` option is deprecated") + .expect("Unable to write error message"); + } + + 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); @@ -353,10 +373,6 @@ where 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(); } |