From db0f7ad5bf83f681e5dfb5b59038804a6cb5a196 Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Thu, 9 Apr 2020 22:45:15 -0700 Subject: [PATCH] Separate layout tests (wip) --- src/codegen/mod.rs | 53 ++++++++++++++++++++++++++++++++++--------- src/lib.rs | 56 +++++++++++++++++++++++++++++++++++++++++----- src/options.rs | 5 +++++ 3 files changed, 98 insertions(+), 16 deletions(-) diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 73cc2f2577..2b9565d033 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -13,7 +13,7 @@ mod bitfield_unit_tests; use self::helpers::attributes; use self::struct_layout::StructLayoutTracker; -use super::BindgenOptions; +use super::{BindgenOptions, LayoutTests}; use ir::analysis::{HasVtable, Sizedness}; use ir::annotations::FieldAccessorKind; @@ -98,6 +98,12 @@ fn root_import( struct CodegenResult<'a> { items: Vec, + /// Just the layout tests. + /// + /// Used to implement `LayoutTests::EmitOnly`, and empty if that setting is + /// disabled (note that conversely `items` is always populted). + only_tests: Vec, + /// A monotonic counter used to add stable unique id's to stuff that doesn't /// need to be referenced by anything. codegen_id: &'a Cell, @@ -147,6 +153,7 @@ impl<'a> CodegenResult<'a> { fn new(codegen_id: &'a Cell) -> Self { CodegenResult { items: vec![], + only_tests: vec![], saw_bindgen_union: false, saw_incomplete_array: false, saw_objc: false, @@ -214,7 +221,10 @@ impl<'a> CodegenResult<'a> { self.vars_seen.insert(name.into()); } - fn inner(&mut self, cb: F) -> Vec + fn inner( + &mut self, + cb: F, + ) -> (Vec, Vec) where F: FnOnce(&mut Self), { @@ -228,7 +238,7 @@ impl<'a> CodegenResult<'a> { self.saw_bitfield_unit |= new.saw_bitfield_unit; self.saw_bindgen_union |= new.saw_bindgen_union; - new.items + (new.items, new.only_tests) } } @@ -435,7 +445,7 @@ impl CodeGenerator for Module { } let mut found_any = false; - let inner_items = result.inner(|result| { + let (inner_items, inner_tests) = result.inner(|result| { result.push(root_import(ctx, item)); let path = item.namespace_aware_canonical_path(ctx).join("::"); @@ -457,7 +467,7 @@ impl CodeGenerator for Module { } let name = item.canonical_name(ctx); - let ident = ctx.rust_ident(name); + let ident = ctx.rust_ident(&name); result.push(if item.id() == ctx.root_module() { quote! { #[allow(non_snake_case, non_camel_case_types, non_upper_case_globals)] @@ -472,6 +482,19 @@ impl CodeGenerator for Module { } } }); + if ctx.options().layout_tests == LayoutTests::EmitOnly && + !result.only_tests.is_empty() + { + // XXX the following appears to work? Or should this just be unsupported. + let test_mod = + ctx.rust_ident(format!("__bindgen_test_mod_{}", name)); + result.only_tests.push(quote! { + mod #test_mod { + use super::#ident::*; + #( #inner_tests )* + } + }); + } } } @@ -959,7 +982,7 @@ impl CodeGenerator for TemplateInstantiation { // instantiation is opaque, then its presumably because we don't // properly understand it (maybe because of specializations), and so we // shouldn't emit layout tests either. - if !ctx.options().layout_tests || self.is_opaque(ctx, item) { + if !ctx.options().layout_tests.needed() || self.is_opaque(ctx, item) { return; } @@ -1006,7 +1029,9 @@ impl CodeGenerator for TemplateInstantiation { stringify!(#ident))); } }; - + if ctx.options().layout_tests == LayoutTests::EmitOnly { + result.only_tests.push(item.clone()); + } result.push(item); } } @@ -1899,7 +1924,9 @@ impl CodeGenerator for CompInfo { } } - if ctx.options().layout_tests && !self.is_forward_declaration() { + if ctx.options().layout_tests.needed() && + !self.is_forward_declaration() + { if let Some(layout) = layout { let fn_name = format!("bindgen_test_layout_{}", canonical_ident); @@ -1982,6 +2009,9 @@ impl CodeGenerator for CompInfo { #( #check_field_offset )* } }; + if ctx.options().layout_tests == LayoutTests::EmitOnly { + result.only_tests.push(item.clone()); + } result.push(item); } } @@ -3848,8 +3878,11 @@ pub(crate) fn codegen( &mut result, &(), ); - - result.items + if LayoutTests::EmitOnly == context.options().layout_tests { + result.only_tests + } else { + result.items + } }) } diff --git a/src/lib.rs b/src/lib.rs index df5b129200..5937dfd641 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -374,8 +374,14 @@ impl Builder { }) .count(); - if !self.options.layout_tests { - output_vector.push("--no-layout-tests".into()); + match self.options.layout_tests { + LayoutTests::Emit => {} + LayoutTests::EmitNone => { + output_vector.push("--no-layout-tests".into()) + } + LayoutTests::EmitOnly => { + output_vector.push("--only-layout-tests".into()) + } } if self.options.impl_debug { @@ -1088,8 +1094,8 @@ impl Builder { } /// Set whether layout tests should be generated. - pub fn layout_tests(mut self, doit: bool) -> Self { - self.options.layout_tests = doit; + pub fn layout_tests(mut self, doit: impl Into) -> Self { + self.options.layout_tests = doit.into(); self } @@ -1551,6 +1557,44 @@ impl Builder { } } +/// Setting for layout test generation. +#[derive(Debug, Clone, PartialEq)] +pub enum LayoutTests { + /// Include layout tests in the generated bindings. The default. + Emit, + /// Don't include any layout tests. + EmitNone, + /// Only emit the layout tests. + /// + /// The intended use case for this is for separating tests from the + /// generated bindings (as the tests are by nature non-portable, even if the + /// bindings otherwise would be). + /// + /// When used in this manner, you're encouraged to provide an an otherwise + /// identical set of options (even though many of them are effectively + /// ignored when this is set). + EmitOnly, +} + +impl LayoutTests { + pub(crate) fn needed(&self) -> bool { + match self { + LayoutTests::Emit | LayoutTests::EmitOnly => true, + LayoutTests::EmitNone => false, + } + } +} + +impl From for LayoutTests { + fn from(value: bool) -> Self { + if value { + LayoutTests::Emit + } else { + LayoutTests::EmitNone + } + } +} + /// Configuration options for generated bindings. #[derive(Debug)] struct BindgenOptions { @@ -1649,7 +1693,7 @@ struct BindgenOptions { disable_nested_struct_naming: bool, /// True if we should generate layout tests for generated structures. - layout_tests: bool, + layout_tests: LayoutTests, /// True if we should implement the Debug trait for C/C++ structures and types /// that do not support automatically deriving Debug. @@ -1894,7 +1938,7 @@ impl Default for BindgenOptions { emit_ast: false, emit_ir: false, emit_ir_graphviz: None, - layout_tests: true, + layout_tests: LayoutTests::Emit, impl_debug: false, impl_partialeq: false, derive_copy: true, diff --git a/src/options.rs b/src/options.rs index b630bb4bed..406ad1b096 100644 --- a/src/options.rs +++ b/src/options.rs @@ -149,6 +149,11 @@ where .takes_value(true) .multiple(true) .number_of_values(1), + Arg::with_name("only-layout-tests") + .long("only-layout-tests") + .help( + "Only emit layout tests. Allows separating the tests from the bindings." + ), Arg::with_name("no-layout-tests") .long("no-layout-tests") .help("Avoid generating layout tests for any type."),