summaryrefslogtreecommitdiffstats
path: root/src/bindgen/ir/cfg.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/bindgen/ir/cfg.rs')
-rw-r--r--src/bindgen/ir/cfg.rs365
1 files changed, 365 insertions, 0 deletions
diff --git a/src/bindgen/ir/cfg.rs b/src/bindgen/ir/cfg.rs
new file mode 100644
index 0000000..e8aa412
--- /dev/null
+++ b/src/bindgen/ir/cfg.rs
@@ -0,0 +1,365 @@
+/* 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::fmt;
+use std::io::Write;
+
+use crate::bindgen::cargo::cargo_metadata::Dependency;
+use crate::bindgen::config::{Config, Language};
+use crate::bindgen::writer::SourceWriter;
+
+#[derive(PartialEq, Eq)]
+enum DefineKey<'a> {
+ Boolean(&'a str),
+ Named(&'a str, &'a str),
+}
+
+impl<'a> DefineKey<'a> {
+ fn load(key: &str) -> DefineKey {
+ // TODO: dirty parser
+ if !key.contains('=') {
+ return DefineKey::Boolean(key);
+ }
+
+ let mut splits = key.trim().split('=');
+
+ let name = match splits.next() {
+ Some(n) => n.trim(),
+ None => return DefineKey::Boolean(key),
+ };
+
+ let value = match splits.next() {
+ Some(v) => v.trim(),
+ None => return DefineKey::Boolean(key),
+ };
+
+ if splits.next().is_some() {
+ return DefineKey::Boolean(key);
+ }
+
+ DefineKey::Named(name, value)
+ }
+}
+
+#[derive(Debug, Clone)]
+pub enum Cfg {
+ Boolean(String),
+ Named(String, String),
+ Any(Vec<Cfg>),
+ All(Vec<Cfg>),
+ Not(Box<Cfg>),
+}
+
+impl fmt::Display for Cfg {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Cfg::Boolean(key) => write!(f, "{}", key),
+ Cfg::Named(key, value) => write!(f, "{} = {:?}", key, value),
+ Cfg::Any(cfgs) => {
+ write!(f, "any(")?;
+ for (index, cfg) in cfgs.iter().enumerate() {
+ if index > 0 {
+ write!(f, ", ")?;
+ }
+ write!(f, "{}", cfg)?;
+ }
+ write!(f, ")")
+ }
+ Cfg::All(cfgs) => {
+ write!(f, "all(")?;
+ for (index, cfg) in cfgs.iter().enumerate() {
+ if index > 0 {
+ write!(f, ", ")?;
+ }
+ write!(f, "{}", cfg)?;
+ }
+ write!(f, ")")
+ }
+ Cfg::Not(cfg) => write!(f, "not({})", cfg),
+ }
+ }
+}
+
+impl Cfg {
+ pub fn join(cfgs: &[Cfg]) -> Option<Cfg> {
+ if cfgs.is_empty() {
+ None
+ } else {
+ Some(Cfg::All(cfgs.to_owned()))
+ }
+ }
+
+ pub fn append(parent: Option<&Cfg>, child: Option<Cfg>) -> Option<Cfg> {
+ match (parent, child) {
+ (None, None) => None,
+ (None, Some(child)) => Some(child),
+ (Some(parent), None) => Some(parent.clone()),
+ (Some(parent), Some(child)) => Some(Cfg::All(vec![parent.clone(), child])),
+ }
+ }
+
+ pub fn load(attrs: &[syn::Attribute]) -> Option<Cfg> {
+ let mut configs = Vec::new();
+
+ for attr in attrs {
+ if let Ok(syn::Meta::List(syn::MetaList { path, nested, .. })) = attr.parse_meta() {
+ if !path.is_ident("cfg") || nested.len() != 1 {
+ continue;
+ }
+
+ if let Some(config) = Cfg::load_single(nested.first().unwrap()) {
+ configs.push(config);
+ }
+ }
+ }
+
+ match configs.len() {
+ 0 => None,
+ 1 => Some(configs.pop().unwrap()),
+ _ => Some(Cfg::All(configs)),
+ }
+ }
+
+ pub fn load_metadata(dependency: &Dependency) -> Option<Cfg> {
+ let target = dependency.target.as_ref()?;
+ match syn::parse_str::<syn::Meta>(target) {
+ Ok(target) => {
+ // Parsing succeeded using the #[cfg] syntax
+ if let syn::Meta::List(syn::MetaList { path, nested, .. }) = target {
+ if !path.is_ident("cfg") || nested.len() != 1 {
+ return None;
+ }
+ Cfg::load_single(nested.first().unwrap())
+ } else {
+ None
+ }
+ }
+ Err(_) => {
+ // Parsing failed using #[cfg], this may be a literal target
+ // name
+ Cfg::load_single(&syn::NestedMeta::Lit(syn::Lit::Str(syn::LitStr::new(
+ target,
+ proc_macro2::Span::call_site(),
+ ))))
+ }
+ }
+ }
+
+ fn load_single(item: &syn::NestedMeta) -> Option<Cfg> {
+ Some(match *item {
+ syn::NestedMeta::Meta(syn::Meta::Path(ref path)) => {
+ Cfg::Boolean(format!("{}", path.segments.first().unwrap().ident))
+ }
+ syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
+ ref path,
+ lit: syn::Lit::Str(ref value),
+ ..
+ })) => Cfg::Named(
+ format!("{}", path.segments.first().unwrap().ident),
+ value.value(),
+ ),
+ syn::NestedMeta::Meta(syn::Meta::List(syn::MetaList {
+ ref path,
+ ref nested,
+ ..
+ })) => {
+ if path.is_ident("any") {
+ Cfg::Any(Cfg::load_list(nested.iter())?)
+ } else if path.is_ident("all") {
+ Cfg::All(Cfg::load_list(nested.iter())?)
+ } else if path.is_ident("not") {
+ if nested.len() != 1 {
+ return None;
+ }
+
+ Cfg::Not(Box::new(Cfg::load_single(&nested[0])?))
+ } else {
+ return None;
+ }
+ }
+ _ => return None,
+ })
+ }
+
+ fn load_list<'a, I: Iterator<Item = &'a syn::NestedMeta>>(attrs: I) -> Option<Vec<Cfg>> {
+ let mut configs = Vec::new();
+
+ for attr in attrs {
+ configs.push(Cfg::load_single(attr)?);
+ }
+
+ if configs.is_empty() {
+ None
+ } else {
+ Some(configs)
+ }
+ }
+}
+
+pub trait ToCondition: Sized {
+ fn to_condition(&self, config: &Config) -> Option<Condition>;
+}
+
+impl ToCondition for Option<Cfg> {
+ fn to_condition(&self, config: &Config) -> Option<Condition> {
+ self.as_ref()?.to_condition(config)
+ }
+}
+
+impl ToCondition for Cfg {
+ fn to_condition(&self, config: &Config) -> Option<Condition> {
+ match *self {
+ Cfg::Boolean(ref cfg_name) => {
+ let define = config
+ .defines
+ .iter()
+ .find(|(key, ..)| DefineKey::Boolean(cfg_name) == DefineKey::load(key));
+ if let Some((_, define)) = define {
+ Some(Condition::Define(define.to_owned()))
+ } else {
+ warn!(
+ "Missing `[defines]` entry for `{}` in cbindgen config.",
+ self,
+ );
+ None
+ }
+ }
+ Cfg::Named(ref cfg_name, ref cfg_value) => {
+ let define = config.defines.iter().find(|(key, ..)| {
+ DefineKey::Named(cfg_name, cfg_value) == DefineKey::load(key)
+ });
+ if let Some((_, define)) = define {
+ Some(Condition::Define(define.to_owned()))
+ } else {
+ warn!(
+ "Missing `[defines]` entry for `{}` in cbindgen config.",
+ self,
+ );
+ None
+ }
+ }
+ Cfg::Any(ref children) => {
+ let conditions: Vec<_> = children
+ .iter()
+ .filter_map(|x| x.to_condition(config))
+ .collect();
+ match conditions.len() {
+ 0 => None,
+ 1 => conditions.into_iter().next(),
+ _ => Some(Condition::Any(conditions)),
+ }
+ }
+ Cfg::All(ref children) => {
+ let cfgs: Vec<_> = children
+ .iter()
+ .filter_map(|x| x.to_condition(config))
+ .collect();
+ match cfgs.len() {
+ 0 => None,
+ 1 => cfgs.into_iter().next(),
+ _ => Some(Condition::All(cfgs)),
+ }
+ }
+ Cfg::Not(ref child) => child
+ .to_condition(config)
+ .map(|cfg| Condition::Not(Box::new(cfg))),
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub enum Condition {
+ Define(String),
+ Any(Vec<Condition>),
+ All(Vec<Condition>),
+ Not(Box<Condition>),
+}
+
+impl Condition {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ match *self {
+ Condition::Define(ref define) => {
+ if config.language == Language::Cython {
+ write!(out, "{}", define);
+ } else {
+ out.write("defined(");
+ write!(out, "{}", define);
+ out.write(")");
+ }
+ }
+ Condition::Any(ref conditions) => {
+ out.write("(");
+ for (i, condition) in conditions.iter().enumerate() {
+ if i != 0 {
+ out.write(if config.language == Language::Cython {
+ " or "
+ } else {
+ " || "
+ });
+ }
+ condition.write(config, out);
+ }
+ out.write(")");
+ }
+ Condition::All(ref conditions) => {
+ out.write("(");
+ for (i, condition) in conditions.iter().enumerate() {
+ if i != 0 {
+ out.write(if config.language == Language::Cython {
+ " and "
+ } else {
+ " && "
+ });
+ }
+ condition.write(config, out);
+ }
+ out.write(")");
+ }
+ Condition::Not(ref condition) => {
+ out.write(if config.language == Language::Cython {
+ "not "
+ } else {
+ "!"
+ });
+ condition.write(config, out);
+ }
+ }
+ }
+}
+
+pub trait ConditionWrite {
+ fn write_before<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>);
+ fn write_after<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>);
+}
+
+impl ConditionWrite for Option<Condition> {
+ fn write_before<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ if let Some(ref cfg) = *self {
+ if config.language == Language::Cython {
+ out.write("IF ");
+ cfg.write(config, out);
+ out.open_brace();
+ } else {
+ out.push_set_spaces(0);
+ out.write("#if ");
+ cfg.write(config, out);
+ out.pop_set_spaces();
+ out.new_line();
+ }
+ }
+ }
+
+ fn write_after<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ if self.is_some() {
+ if config.language == Language::Cython {
+ out.close_brace(false);
+ } else {
+ out.new_line();
+ out.push_set_spaces(0);
+ out.write("#endif");
+ out.pop_set_spaces();
+ }
+ }
+ }
+}