diff options
Diffstat (limited to 'src/bindgen/config.rs')
-rw-r--r-- | src/bindgen/config.rs | 1114 |
1 files changed, 1114 insertions, 0 deletions
diff --git a/src/bindgen/config.rs b/src/bindgen/config.rs new file mode 100644 index 0000000..5012414 --- /dev/null +++ b/src/bindgen/config.rs @@ -0,0 +1,1114 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use std::collections::{BTreeMap, HashMap}; +use std::default::Default; +use std::str::FromStr; +use std::{fmt, fs, path::Path as StdPath, path::PathBuf as StdPathBuf}; + +use serde::de::value::{MapAccessDeserializer, SeqAccessDeserializer}; +use serde::de::{Deserialize, Deserializer, MapAccess, SeqAccess, Visitor}; + +use crate::bindgen::ir::annotation::AnnotationSet; +use crate::bindgen::ir::path::Path; +use crate::bindgen::ir::repr::ReprAlign; +pub use crate::bindgen::rename::RenameRule; + +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); + +/// A language type to generate bindings for. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Language { + Cxx, + C, + Cython, +} + +impl FromStr for Language { + type Err = String; + + fn from_str(s: &str) -> Result<Language, Self::Err> { + match s { + "cxx" => Ok(Language::Cxx), + "Cxx" => Ok(Language::Cxx), + "CXX" => Ok(Language::Cxx), + "cpp" => Ok(Language::Cxx), + "Cpp" => Ok(Language::Cxx), + "CPP" => Ok(Language::Cxx), + "c++" => Ok(Language::Cxx), + "C++" => Ok(Language::Cxx), + "c" => Ok(Language::C), + "C" => Ok(Language::C), + "cython" => Ok(Language::Cython), + "Cython" => Ok(Language::Cython), + _ => Err(format!("Unrecognized Language: '{}'.", s)), + } + } +} + +deserialize_enum_str!(Language); + +impl Language { + pub(crate) fn typedef(self) -> &'static str { + match self { + Language::Cxx | Language::C => "typedef", + Language::Cython => "ctypedef", + } + } +} + +/// Controls what type of line endings are used in the generated code. +#[derive(Debug, Clone, Copy)] +#[allow(clippy::upper_case_acronyms)] +#[derive(Default)] +pub enum LineEndingStyle { + /// Use Unix-style linefeed characters + #[default] + LF, + /// Use classic Mac-style carriage-return characters + CR, + /// Use Windows-style carriage-return and linefeed characters + CRLF, + /// Use the native mode for the platform: CRLF on Windows, LF everywhere else. + Native, +} + +impl LineEndingStyle { + pub fn as_str(&self) -> &'static str { + match self { + Self::LF => "\n", + Self::CR => "\r", + Self::CRLF => "\r\n", + Self::Native => { + #[cfg(target_os = "windows")] + { + Self::CRLF.as_str() + } + #[cfg(not(target_os = "windows"))] + { + Self::LF.as_str() + } + } + } + } +} + +impl FromStr for LineEndingStyle { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s.to_lowercase().as_ref() { + "native" => Ok(Self::Native), + "lf" => Ok(Self::LF), + "crlf" => Ok(Self::CRLF), + "cr" => Ok(Self::CR), + _ => Err(format!("Unrecognized line ending style: '{}'.", s)), + } + } +} + +deserialize_enum_str!(LineEndingStyle); + +/// A style of braces to use for generating code. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Braces { + SameLine, + NextLine, +} + +impl FromStr for Braces { + type Err = String; + + fn from_str(s: &str) -> Result<Braces, Self::Err> { + match s { + "SameLine" => Ok(Braces::SameLine), + "same_line" => Ok(Braces::SameLine), + "NextLine" => Ok(Braces::NextLine), + "next_line" => Ok(Braces::NextLine), + _ => Err(format!("Unrecognized Braces: '{}'.", s)), + } + } +} + +deserialize_enum_str!(Braces); + +/// A type of layout to use when generating long lines of code. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Layout { + Horizontal, + Vertical, + Auto, +} + +impl FromStr for Layout { + type Err = String; + + fn from_str(s: &str) -> Result<Layout, Self::Err> { + match s { + "Horizontal" => Ok(Layout::Horizontal), + "horizontal" => Ok(Layout::Horizontal), + "Vertical" => Ok(Layout::Vertical), + "vertical" => Ok(Layout::Vertical), + "Auto" => Ok(Layout::Auto), + "auto" => Ok(Layout::Auto), + _ => Err(format!("Unrecognized Layout: '{}'.", s)), + } + } +} + +deserialize_enum_str!(Layout); + +/// How the comments containing documentation should be styled. +#[derive(Debug, Clone, PartialEq, Eq, Copy)] +pub enum DocumentationStyle { + C, + C99, + Doxy, + Cxx, + Auto, +} + +impl FromStr for DocumentationStyle { + type Err = String; + + fn from_str(s: &str) -> Result<DocumentationStyle, Self::Err> { + match s.to_lowercase().as_ref() { + "c" => Ok(DocumentationStyle::C), + "c99" => Ok(DocumentationStyle::C99), + "cxx" => Ok(DocumentationStyle::Cxx), + "c++" => Ok(DocumentationStyle::Cxx), + "doxy" => Ok(DocumentationStyle::Doxy), + "auto" => Ok(DocumentationStyle::Auto), + _ => Err(format!("Unrecognized documentation style: '{}'.", s)), + } + } +} + +deserialize_enum_str!(DocumentationStyle); + +/// How much of the documentation to include in the header file. +#[derive(Debug, Clone, Copy)] +pub enum DocumentationLength { + Short, + Full, +} + +impl FromStr for DocumentationLength { + type Err = String; + + fn from_str(s: &str) -> Result<DocumentationLength, Self::Err> { + match s.to_lowercase().as_ref() { + "short" => Ok(DocumentationLength::Short), + "full" => Ok(DocumentationLength::Full), + _ => Err(format!("Unrecognized documentation style: '{}'.", s)), + } + } +} + +deserialize_enum_str!(DocumentationLength); + +/// A style of Style to use when generating structs and enums. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] +pub enum Style { + #[default] + Both, + Tag, + Type, +} + +impl Style { + pub fn generate_tag(self) -> bool { + match self { + Style::Both | Style::Tag => true, + Style::Type => false, + } + } + + pub fn generate_typedef(self) -> bool { + match self { + Style::Both | Style::Type => true, + Style::Tag => false, + } + } + + // https://cython.readthedocs.io/en/latest/src/userguide/external_C_code.html#styles-of-struct-union-and-enum-declaration + pub fn cython_def(self) -> &'static str { + if self.generate_tag() { + "cdef " + } else { + "ctypedef " + } + } +} + +impl FromStr for Style { + type Err = String; + + fn from_str(s: &str) -> Result<Style, Self::Err> { + match s { + "Both" => Ok(Style::Both), + "both" => Ok(Style::Both), + "Tag" => Ok(Style::Tag), + "tag" => Ok(Style::Tag), + "Type" => Ok(Style::Type), + "type" => Ok(Style::Type), + _ => Err(format!("Unrecognized Style: '{}'.", s)), + } + } +} + +deserialize_enum_str!(Style); + +/// Different item types that we can generate and filter. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ItemType { + Constants, + Globals, + Enums, + Structs, + Unions, + Typedefs, + OpaqueItems, + Functions, +} + +impl FromStr for ItemType { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + use self::ItemType::*; + Ok(match &*s.to_lowercase() { + "constants" => Constants, + "globals" => Globals, + "enums" => Enums, + "structs" => Structs, + "unions" => Unions, + "typedefs" => Typedefs, + "opaque" => OpaqueItems, + "functions" => Functions, + _ => return Err(format!("Unrecognized Style: '{}'.", s)), + }) + } +} + +deserialize_enum_str!(ItemType); + +/// Type which specifies the sort order of functions +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SortKey { + Name, + None, +} + +impl FromStr for SortKey { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + use self::SortKey::*; + Ok(match &*s.to_lowercase() { + "name" => Name, + "none" => None, + _ => return Err(format!("Unrecognized sort option: '{}'.", s)), + }) + } +} + +deserialize_enum_str!(SortKey); + +/// Settings to apply when exporting items. +#[derive(Debug, Clone, Deserialize, Default)] +#[serde(rename_all = "snake_case")] +#[serde(deny_unknown_fields)] +#[serde(default)] +pub struct ExportConfig { + /// A list of additional items not used by exported functions to include in + /// the generated bindings + pub include: Vec<String>, + /// A list of items to not include in the generated bindings + pub exclude: Vec<String>, + /// Table of name conversions to apply to item names + pub rename: HashMap<String, String>, + /// Table of raw strings to prepend to the body of items. + pub pre_body: HashMap<String, String>, + /// Table of raw strings to append to the body of items. + pub body: HashMap<String, String>, + /// A prefix to add before the name of every item + pub prefix: Option<String>, + /// Types of items to generate. + pub item_types: Vec<ItemType>, + /// Whether renaming overrides or extends prefixing. + pub renaming_overrides_prefixing: bool, + /// Mangling configuration. + pub mangle: MangleConfig, +} + +/// Mangling-specific configuration. +#[derive(Debug, Clone, Deserialize, Default)] +#[serde(rename_all = "snake_case")] +#[serde(deny_unknown_fields)] +#[serde(default)] +pub struct MangleConfig { + /// The rename rule to apply to the type names mangled. + pub rename_types: RenameRule, + /// Remove the underscores used for name mangling. + pub remove_underscores: bool, +} + +impl ExportConfig { + pub(crate) fn should_generate(&self, item_type: ItemType) -> bool { + self.item_types.is_empty() || self.item_types.contains(&item_type) + } + + pub(crate) fn pre_body(&self, path: &Path) -> Option<&str> { + self.pre_body.get(path.name()).map(|s| s.trim_matches('\n')) + } + + pub(crate) fn post_body(&self, path: &Path) -> Option<&str> { + self.body.get(path.name()).map(|s| s.trim_matches('\n')) + } + + pub(crate) fn rename(&self, item_name: &mut String) { + if let Some(name) = self.rename.get(item_name) { + *item_name = name.clone(); + if self.renaming_overrides_prefixing { + return; + } + } + if let Some(ref prefix) = self.prefix { + item_name.insert_str(0, prefix); + } + } +} + +/// Settings to apply to generated types with layout modifiers. +#[derive(Debug, Default, Clone, Deserialize)] +#[serde(rename_all = "snake_case")] +#[serde(deny_unknown_fields)] +#[serde(default)] +pub struct LayoutConfig { + /// The way to annotate C types as #[repr(packed)]. + pub packed: Option<String>, + /// The way to annotate C types as #[repr(align(...))]. This is assumed to be a functional + /// macro which takes a single argument (the alignment). + pub aligned_n: Option<String>, +} + +impl LayoutConfig { + pub(crate) fn ensure_safe_to_represent(&self, align: &ReprAlign) -> Result<(), String> { + match (align, &self.packed, &self.aligned_n) { + (ReprAlign::Packed, None, _) => Err("Cannot safely represent #[repr(packed)] type without configured 'packed' annotation.".to_string()), + (ReprAlign::Align(_), _, None) => Err("Cannot safely represent #[repr(aligned(...))] type without configured 'aligned_n' annotation.".to_string()), + _ => Ok(()), + } + } +} + +/// Settings to apply to generated functions. +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "snake_case")] +#[serde(deny_unknown_fields)] +#[serde(default)] +pub struct FunctionConfig { + /// Optional text to output before each function declaration + pub prefix: Option<String>, + /// Optional text to output after each function declaration + pub postfix: Option<String>, + /// The way to annotation this function as #[must_use] + pub must_use: Option<String>, + /// The way to annotation this function as #[deprecated] without notes + pub deprecated: Option<String>, + /// The way to annotation this function as #[deprecated] with notes + pub deprecated_with_note: Option<String>, + /// The style to layout the args + pub args: Layout, + /// The rename rule to apply to function args + pub rename_args: RenameRule, + /// An optional macro to use when generating Swift function name attributes + pub swift_name_macro: Option<String>, + /// Sort key for functions + pub sort_by: Option<SortKey>, + /// Optional text to output after functions which return `!`. + pub no_return: Option<String>, +} + +impl Default for FunctionConfig { + fn default() -> FunctionConfig { + FunctionConfig { + prefix: None, + postfix: None, + must_use: None, + deprecated: None, + deprecated_with_note: None, + args: Layout::Auto, + rename_args: RenameRule::None, + swift_name_macro: None, + sort_by: None, + no_return: None, + } + } +} + +impl FunctionConfig { + pub(crate) fn prefix(&self, annotations: &AnnotationSet) -> Option<String> { + if let Some(x) = annotations.atom("prefix") { + return x; + } + self.prefix.clone() + } + + pub(crate) fn postfix(&self, annotations: &AnnotationSet) -> Option<String> { + if let Some(x) = annotations.atom("postfix") { + return x; + } + self.postfix.clone() + } +} + +/// Settings to apply to generated structs. +#[derive(Debug, Default, Clone, Deserialize)] +#[serde(rename_all = "snake_case")] +#[serde(deny_unknown_fields)] +#[serde(default)] +pub struct StructConfig { + /// The rename rule to apply to the name of struct fields + pub rename_fields: RenameRule, + /// Whether to generate a constructor for the struct (which takes + /// arguments to initialize all the members) + pub derive_constructor: bool, + /// Whether to generate a piecewise equality operator + pub derive_eq: bool, + /// Whether to generate a piecewise inequality operator + pub derive_neq: bool, + /// Whether to generate a less than operator on structs with one field + pub derive_lt: bool, + /// Whether to generate a less than or equal to operator on structs with one field + pub derive_lte: bool, + /// Whether to generate a greater than operator on structs with one field + pub derive_gt: bool, + /// Whether to generate a greater than or equal to operator on structs with one field + pub derive_gte: bool, + /// Whether to generate a ostream serializer for the struct + pub derive_ostream: bool, + /// Whether associated constants should be in the body. Only applicable to + /// non-transparent structs, and in C++-only. + pub associated_constants_in_body: bool, + /// The way to annotate this struct as #[must_use]. + pub must_use: Option<String>, + /// The way to annotation this function as #[deprecated] without notes + pub deprecated: Option<String>, + /// The way to annotation this function as #[deprecated] with notes + pub deprecated_with_note: Option<String>, +} + +impl StructConfig { + pub(crate) fn derive_constructor(&self, annotations: &AnnotationSet) -> bool { + if let Some(x) = annotations.bool("derive-constructor") { + return x; + } + self.derive_constructor + } + pub(crate) fn derive_eq(&self, annotations: &AnnotationSet) -> bool { + if let Some(x) = annotations.bool("derive-eq") { + return x; + } + self.derive_eq + } + pub(crate) fn derive_neq(&self, annotations: &AnnotationSet) -> bool { + if let Some(x) = annotations.bool("derive-neq") { + return x; + } + self.derive_neq + } + pub(crate) fn derive_lt(&self, annotations: &AnnotationSet) -> bool { + if let Some(x) = annotations.bool("derive-lt") { + return x; + } + self.derive_lt + } + pub(crate) fn derive_lte(&self, annotations: &AnnotationSet) -> bool { + if let Some(x) = annotations.bool("derive-lte") { + return x; + } + self.derive_lte + } + pub(crate) fn derive_gt(&self, annotations: &AnnotationSet) -> bool { + if let Some(x) = annotations.bool("derive-gt") { + return x; + } + self.derive_gt + } + pub(crate) fn derive_gte(&self, annotations: &AnnotationSet) -> bool { + if let Some(x) = annotations.bool("derive-gte") { + return x; + } + self.derive_gte + } + pub(crate) fn derive_ostream(&self, annotations: &AnnotationSet) -> bool { + if let Some(x) = annotations.bool("derive-ostream") { + return x; + } + self.derive_ostream + } +} + +/// Settings to apply to generated enums. +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "snake_case")] +#[serde(deny_unknown_fields)] +#[serde(default)] +pub struct EnumConfig { + /// The rename rule to apply to the name of enum variants + pub rename_variants: RenameRule, + /// The rename rule to apply to the names of the union fields in C/C++ + /// generated from the Rust enum. Applied before rename_variants + /// rename rule. Defaults to SnakeCase. + pub rename_variant_name_fields: RenameRule, + /// Whether to add a `Sentinel` value at the end of every enum + /// This is useful in Gecko for IPC serialization + pub add_sentinel: bool, + /// Whether the enum variants should be prefixed with the enum name + pub prefix_with_name: bool, + /// Whether to generate static `::X(..)` constructors and `IsX()` + /// methods for tagged enums. + pub derive_helper_methods: bool, + /// Whether to generate `AsX() const` methods for tagged enums. + pub derive_const_casts: bool, + /// Whether to generate `AsX()` methods for tagged enums. + pub derive_mut_casts: bool, + /// The name of the macro to use for `derive_{const,mut}casts`. If custom, you're + /// responsible to provide the necessary header, otherwise `assert` will be + /// used, and `<cassert>` will be included. + pub cast_assert_name: Option<String>, + /// The way to annotation this enum as #[must_use]. + pub must_use: Option<String>, + /// The way to annotation this function as #[deprecated] without notes + pub deprecated: Option<String>, + /// The way to annotation this function as #[deprecated] with notes + pub deprecated_with_note: Option<String>, + /// Whether to generate destructors of tagged enums. + pub derive_tagged_enum_destructor: bool, + /// Whether to generate copy-constructors of tagged enums. + pub derive_tagged_enum_copy_constructor: bool, + /// Whether to generate copy-assignment operators of tagged enums. + /// + /// This is only generated if a copy constructor for the same tagged enum is + /// generated as well. + pub derive_tagged_enum_copy_assignment: bool, + /// Whether to generate a ostream serializer for the struct + pub derive_ostream: bool, + /// Declare the enum as an enum class. + /// Only relevant when targeting C++. + pub enum_class: bool, + /// Whether to generate empty, private default-constructors for tagged + /// enums. + pub private_default_tagged_enum_constructor: bool, +} + +impl Default for EnumConfig { + fn default() -> EnumConfig { + EnumConfig { + rename_variants: RenameRule::None, + rename_variant_name_fields: RenameRule::SnakeCase, + add_sentinel: false, + prefix_with_name: false, + derive_helper_methods: false, + derive_const_casts: false, + derive_mut_casts: false, + cast_assert_name: None, + must_use: None, + deprecated: None, + deprecated_with_note: None, + derive_tagged_enum_destructor: false, + derive_tagged_enum_copy_constructor: false, + derive_tagged_enum_copy_assignment: false, + derive_ostream: false, + enum_class: true, + private_default_tagged_enum_constructor: false, + } + } +} + +impl EnumConfig { + pub(crate) fn add_sentinel(&self, annotations: &AnnotationSet) -> bool { + if let Some(x) = annotations.bool("add-sentinel") { + return x; + } + self.add_sentinel + } + pub(crate) fn derive_helper_methods(&self, annotations: &AnnotationSet) -> bool { + if let Some(x) = annotations.bool("derive-helper-methods") { + return x; + } + self.derive_helper_methods + } + pub(crate) fn derive_const_casts(&self, annotations: &AnnotationSet) -> bool { + if let Some(x) = annotations.bool("derive-const-casts") { + return x; + } + self.derive_const_casts + } + pub(crate) fn derive_mut_casts(&self, annotations: &AnnotationSet) -> bool { + if let Some(x) = annotations.bool("derive-mut-casts") { + return x; + } + self.derive_mut_casts + } + pub(crate) fn derive_tagged_enum_destructor(&self, annotations: &AnnotationSet) -> bool { + if let Some(x) = annotations.bool("derive-tagged-enum-destructor") { + return x; + } + self.derive_tagged_enum_destructor + } + pub(crate) fn derive_tagged_enum_copy_constructor(&self, annotations: &AnnotationSet) -> bool { + if let Some(x) = annotations.bool("derive-tagged-enum-copy-constructor") { + return x; + } + self.derive_tagged_enum_copy_constructor + } + pub(crate) fn derive_tagged_enum_copy_assignment(&self, annotations: &AnnotationSet) -> bool { + if let Some(x) = annotations.bool("derive-tagged-enum-copy-assignment") { + return x; + } + self.derive_tagged_enum_copy_assignment + } + pub(crate) fn derive_ostream(&self, annotations: &AnnotationSet) -> bool { + if let Some(x) = annotations.bool("derive-ostream") { + return x; + } + self.derive_ostream + } + pub(crate) fn enum_class(&self, annotations: &AnnotationSet) -> bool { + if let Some(x) = annotations.bool("enum-class") { + return x; + } + self.enum_class + } + pub(crate) fn private_default_tagged_enum_constructor( + &self, + annotations: &AnnotationSet, + ) -> bool { + if let Some(x) = annotations.bool("private-default-tagged-enum-constructor") { + return x; + } + self.private_default_tagged_enum_constructor + } +} + +/// Settings to apply to generated constants. +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "snake_case")] +#[serde(deny_unknown_fields)] +#[serde(default)] +pub struct ConstantConfig { + /// Whether a generated constant can be a static const in C++ mode. + pub allow_static_const: bool, + /// Whether a generated constant should be constexpr in C++ mode. + pub allow_constexpr: bool, + /// Sort key for constants + pub sort_by: Option<SortKey>, +} + +impl Default for ConstantConfig { + fn default() -> ConstantConfig { + ConstantConfig { + allow_static_const: true, + allow_constexpr: true, + sort_by: None, + } + } +} + +/// Settings for custom macro expansion. +#[derive(Debug, Clone, Deserialize, Default)] +#[serde(rename_all = "snake_case")] +#[serde(deny_unknown_fields)] +#[serde(default)] +pub struct MacroExpansionConfig { + /// Whether the `bitflags` macro should be expanded. + pub bitflags: bool, +} + +/// Controls which Cargo profile is used for macro expansion. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Profile { + Debug, + Release, +} + +impl FromStr for Profile { + type Err = String; + + fn from_str(s: &str) -> Result<Profile, Self::Err> { + match s { + "debug" | "Debug" => Ok(Profile::Debug), + "release" | "Release" => Ok(Profile::Release), + _ => Err(format!("Unrecognized Profile: '{}'.", s)), + } + } +} + +deserialize_enum_str!(Profile); + +/// Settings to apply when running `rustc -Zunpretty=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 -Zunpretty=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>>, + /// Controls whether or not to pass `--release` when expanding. + pub profile: Profile, +} + +impl Default for ParseExpandConfig { + fn default() -> ParseExpandConfig { + ParseExpandConfig { + crates: Vec::new(), + all_features: false, + default_features: true, + features: None, + profile: Profile::Debug, + } + } +} + +// 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, + profile: Profile::Debug, + }) + } + + 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, Default, Clone, Deserialize)] +#[serde(rename_all = "snake_case")] +#[serde(deny_unknown_fields)] +#[serde(default)] +pub struct ParseConfig { + /// Whether to parse dependencies when generating bindings. When this is true, + /// each dependent crate is found using a combination of `cargo metadata` and + /// `Cargo.lock`. To further control this behavior, crates can be whitelisted or + /// blacklisted using `include` and `exclude` respectively. Additionally in cases + /// where crates have types to expose in bindings hidden in macros, a crate can + /// be marked in `expand` and `cargo expand` will be used to expand the macros + /// before parsing. A crate marked in `expand` doesn't need to be added to any + /// whitelist. + pub parse_deps: bool, + /// An optional whitelist of names of crates to parse + pub include: Option<Vec<String>>, + /// The names of crates to not parse + pub exclude: Vec<String>, + /// The configuration options for `rustc -Zunpretty=expanded` + #[serde(deserialize_with = "retrocomp_parse_expand_config_deserialize")] + pub expand: ParseExpandConfig, + /// Whether to use a new temporary target directory when running `rustc -Zunpretty=expanded`. + /// This may be required for some build processes. + pub clean: bool, + /// List of crate names which generate consts, statics, and fns. By default + /// no dependent crates generate them. + pub extra_bindings: Vec<String>, +} + +impl ParseConfig { + pub(crate) fn should_generate_top_level_item( + &self, + crate_name: &str, + binding_crate_name: &str, + ) -> bool { + if crate_name == binding_crate_name { + // Always generate items for the binding crate. + return true; + } + + self.extra_bindings.iter().any(|dep| dep == crate_name) + } +} + +/// Settings to apply to pointers +#[derive(Debug, Clone, Default, Deserialize)] +#[serde(rename_all = "snake_case")] +#[serde(deny_unknown_fields)] +#[serde(default)] +pub struct PtrConfig { + /// Optional attribute to apply to pointers that are required to not be null + pub non_null_attribute: Option<String>, +} + +/// Settings specific to Cython bindings. +#[derive(Debug, Clone, Default, Deserialize)] +#[serde(rename_all = "snake_case")] +#[serde(deny_unknown_fields)] +#[serde(default)] +pub struct CythonConfig { + /// Header specified in the top level `cdef extern from header:` declaration. + pub header: Option<String>, + /// `from module cimport name1, name2, ...` declarations added in the same place + /// where you'd get includes in C. + pub cimports: BTreeMap<String, Vec<String>>, +} + +/// A collection of settings to customize the generated bindings. +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "snake_case")] +#[serde(deny_unknown_fields)] +#[serde(default)] +pub struct Config { + /// Optional text to output at the beginning of the file + pub header: Option<String>, + /// A list of additional includes to put at the beginning of the generated header + pub includes: Vec<String>, + /// A list of additional system includes to put at the beginning of the generated header + pub sys_includes: Vec<String>, + /// Optional verbatim code added after the include blocks + pub after_includes: Option<String>, + /// Optional text to output at the end of the file + pub trailer: Option<String>, + /// Optional name to use for an include guard + pub include_guard: Option<String>, + /// Add a `#pragma once` guard + pub pragma_once: bool, + /// Generates no includes at all. Overrides all other include options + /// + /// This option is useful when using cbindgen with tools such as python's cffi which + /// doesn't understand include directives + pub no_includes: bool, + /// Optional text to output at major sections to deter manual editing + pub autogen_warning: Option<String>, + /// Include a comment with the version of cbindgen used to generate the file + pub include_version: bool, + /// An optional name for the root namespace. Only applicable when language="C++" + pub namespace: Option<String>, + /// An optional list of namespaces. Only applicable when language="C++" + pub namespaces: Option<Vec<String>>, + /// An optional list of namespaces to declare as using. Only applicable when language="C++" + pub using_namespaces: Option<Vec<String>>, + /// The style to use for braces + pub braces: Braces, + /// The preferred length of a line, used for auto breaking function arguments + pub line_length: usize, + /// The amount of spaces in a tab + pub tab_width: usize, + /// The type of line endings to generate + pub line_endings: LineEndingStyle, + /// The language to output bindings for + pub language: Language, + /// Include preprocessor defines in C bindings to ensure C++ compatibility + pub cpp_compat: bool, + /// The style to declare structs, enums and unions in for C + pub style: Style, + /// Default sort key for functions and constants. + pub sort_by: SortKey, + /// If this option is true `usize` and `isize` will be converted into `size_t` and `ptrdiff_t` + /// instead of `uintptr_t` and `intptr_t` respectively. + pub usize_is_size_t: bool, + /// The configuration options for parsing + pub parse: ParseConfig, + /// The configuration options for exporting + pub export: ExportConfig, + /// The configuration options for macros. + pub macro_expansion: MacroExpansionConfig, + /// The configuration options for type layouts. + pub layout: LayoutConfig, + /// The configuration options for functions + #[serde(rename = "fn")] + pub function: FunctionConfig, + /// The configuration options for structs + #[serde(rename = "struct")] + pub structure: StructConfig, + /// The configuration options for enums + #[serde(rename = "enum")] + pub enumeration: EnumConfig, + /// The configuration options for constants + #[serde(rename = "const")] + pub constant: ConstantConfig, + /// Preprocessor defines to use when generating #ifdef's for #[cfg] + pub defines: HashMap<String, String>, + /// Include doc comments from Rust as documentation + pub documentation: bool, + /// How documentation comments should be styled. + pub documentation_style: DocumentationStyle, + /// How much of the documentation should be output for each item. + pub documentation_length: DocumentationLength, + /// Configuration options for pointers + #[serde(rename = "ptr")] + pub pointer: PtrConfig, + /// Only download sources for dependencies needed for the target platform. + /// + /// By default, cbindgen will fetch sources for dependencies used on any platform so that if a + /// type is defined in terms of a type from a dependency on another target (probably behind a + /// `#[cfg]`), cbindgen will be able to generate the appropriate binding as it can see the + /// nested type's definition. However, this makes calling cbindgen slower, as it may have to + /// download a number of additional dependencies. + /// + /// As an example, consider this Cargo.toml: + /// + /// ```toml + /// [target.'cfg(windows)'.dependencies] + /// windows = "0.7" + /// ``` + /// + /// with this declaration in one of the `.rs` files that cbindgen is asked to generate bindings + /// for: + /// + /// ```rust,ignore + /// #[cfg(windows)] + /// pub struct Error(windows::ErrorCode); + /// ``` + /// + /// With the default value (`false`), cbindgen will download the `windows` dependency even when + /// not compiling for Windows, and will thus be able to generate the binding for `Error` + /// (behind a `#define`). + /// + /// If this value is instead to `true`, cbindgen will _not_ download the `windows` dependency + /// if it's not compiling for Windows, but will also fail to generate a Windows binding for + /// `Error` as it does not know the definition for `ErrorCode`. + /// + /// The target can be chosen via the `TARGET` environment variable (if used + /// via the CLI, when ran from a build script cargo sets this variable + /// appropriately). + pub only_target_dependencies: bool, + /// Configuration options specific to Cython. + pub cython: CythonConfig, + #[serde(skip)] + pub(crate) config_path: Option<StdPathBuf>, +} + +impl Default for Config { + fn default() -> Config { + Config { + header: None, + includes: Vec::new(), + sys_includes: Vec::new(), + after_includes: None, + trailer: None, + include_guard: None, + pragma_once: false, + autogen_warning: None, + include_version: false, + no_includes: false, + namespace: None, + namespaces: None, + using_namespaces: None, + braces: Braces::SameLine, + line_length: 100, + tab_width: 2, + line_endings: LineEndingStyle::default(), + language: Language::Cxx, + cpp_compat: false, + style: Style::default(), + usize_is_size_t: false, + sort_by: SortKey::None, + macro_expansion: Default::default(), + parse: ParseConfig::default(), + export: ExportConfig::default(), + layout: LayoutConfig::default(), + function: FunctionConfig::default(), + structure: StructConfig::default(), + enumeration: EnumConfig::default(), + constant: ConstantConfig::default(), + defines: HashMap::new(), + documentation: true, + documentation_style: DocumentationStyle::Auto, + documentation_length: DocumentationLength::Full, + pointer: PtrConfig::default(), + only_target_dependencies: false, + cython: CythonConfig::default(), + config_path: None, + } + } +} + +impl Config { + pub(crate) fn cpp_compatible_c(&self) -> bool { + self.language == Language::C && self.cpp_compat + } + + pub(crate) fn include_guard(&self) -> Option<&str> { + if self.language == Language::Cython { + None + } else { + self.include_guard.as_deref() + } + } + + pub(crate) fn includes(&self) -> &[String] { + if self.language == Language::Cython { + &[] + } else { + &self.includes + } + } + + pub(crate) fn sys_includes(&self) -> &[String] { + if self.language == Language::Cython { + &[] + } else { + &self.sys_includes + } + } + + pub fn from_file<P: AsRef<StdPath>>(file_name: P) -> Result<Config, String> { + let config_text = fs::read_to_string(file_name.as_ref()).map_err(|_| { + format!( + "Couldn't open config file: {}.", + file_name.as_ref().display() + ) + })?; + + let mut config = toml::from_str::<Config>(&config_text) + .map_err(|e| format!("Couldn't parse config file: {}.", e))?; + config.config_path = Some(StdPathBuf::from(file_name.as_ref())); + Ok(config) + } + + pub fn from_root_or_default<P: AsRef<StdPath>>(root: P) -> Config { + let c = root.as_ref().join("cbindgen.toml"); + + if c.exists() { + Config::from_file(c).unwrap() + } else { + Config::default() + } + } +} |