summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTravis Finkenauer <tmfinken@gmail.com>2017-08-13 13:12:21 -0700
committerTravis Finkenauer <tmfinken@gmail.com>2017-08-13 13:12:21 -0700
commite7fa2895582435b59dfb1ac960167b0876f8da55 (patch)
tree321361d58ee698eea089eed18fbd62341345cf13 /src
parentfdee9f1c83f8bf7cc554a59c2f66d9cbbb09eda4 (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.rs30
-rw-r--r--src/features.rs185
-rw-r--r--src/ir/analysis/derive_copy.rs2
-rw-r--r--src/ir/analysis/derive_debug.rs2
-rw-r--r--src/ir/analysis/derive_default.rs2
-rw-r--r--src/ir/analysis/derive_hash.rs2
-rw-r--r--src/ir/comp.rs17
-rw-r--r--src/lib.rs59
-rw-r--r--src/options.rs30
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 {
diff --git a/src/lib.rs b/src/lib.rs
index 17259782..e72800c0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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();
}