1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
extern crate cpp_to_rust;
use cpp_to_rust::config::Config;
use cpp_to_rust::errors::{Result, ChainErr};
use cpp_to_rust::log;
use cpp_to_rust::utils::run_command;
use cpp_to_rust::file_utils::PathBufWithAdded;

mod fix_header_names;

use std::path::PathBuf;
use std::process::Command;
use std::env;

fn run_qmake_query(arg: &str) -> Result<PathBuf> {
  let result = try!(run_command(Command::new("qmake").arg("-query").arg(arg), true, true));
  Ok(PathBuf::from(result.trim()))
}

pub trait QtConfig {
  fn setup_qt_library(&mut self) -> Result<()>;
  fn exclude_qlist_eq_based_methods<S: AsRef<str>, I: IntoIterator<Item=S>>(&mut self, types: I);
  fn exclude_qvector_eq_based_methods<S: AsRef<str>, I: IntoIterator<Item=S>>(&mut self, types: I);
}


impl QtConfig for Config {
  fn setup_qt_library(&mut self) -> Result<()> {
    let qt_lib_name = try!(env::var("CARGO_MANIFEST_LINKS")
      .chain_err(|| "no environment variable: CARGO_MANIFEST_LINKS"));
    if !qt_lib_name.starts_with("Qt5") {
      return Err(format!("Invalid library name (must start with \"Qt5\"): {}",
                         qt_lib_name)
        .into());
    }
    let folder_name = format!("Qt{}", &qt_lib_name[3..]);

    log::info("Detecting Qt directories...");

    let qt_install_headers_path = try!(run_qmake_query("QT_INSTALL_HEADERS"));
    log::info(format!("QT_INSTALL_HEADERS = \"{}\"",
                      qt_install_headers_path.display()));
    let qt_install_libs_path = try!(run_qmake_query("QT_INSTALL_LIBS"));
    log::info(format!("QT_INSTALL_LIBS = \"{}\"", qt_install_libs_path.display()));
    self.add_include_path(&qt_install_headers_path);

    let dir = qt_install_headers_path.with_added(&folder_name);
    let qt_this_lib_headers_dir = if dir.exists() {
      self.add_include_path(&dir);
      self.add_target_include_path(&dir);

      self.add_lib_path(&qt_install_libs_path);
      self.add_linked_lib(qt_lib_name);
      dir
    } else {
      let dir2 = qt_install_libs_path.with_added(format!("{}.framework/Headers", folder_name));
      if dir2.exists() {
        self.add_include_path(&dir2);
        self.add_target_include_path(&dir2);

        self.add_framework_path(&qt_install_libs_path);
        self.add_linked_framework(folder_name.clone());
        dir2
      } else {
        return Err(format!("extra header dir not found (tried: {}, {})",
                           dir.display(),
                           dir2.display())
          .into());
      }
    };
    self.add_include_directive(&folder_name);
    self.add_cpp_data_filter(Box::new(move |cpp_data| {
      fix_header_names::fix_header_names(cpp_data, &qt_this_lib_headers_dir)
    }));

    self.add_cpp_parser_blocked_name("qt_check_for_QGADGET_macro");
    Ok(())
  }

  fn exclude_qlist_eq_based_methods<S: AsRef<str>, I: IntoIterator<Item=S>>(&mut self, types: I) {
    let types: Vec<String> = types.into_iter().map(|x| x.as_ref().to_string()).collect();
    self.add_cpp_ffi_generator_filter(Box::new(move |method| {
      if let Some(ref info) = method.class_membership {
        if info.class_type.name == "QList" {
          let args = try!(info.class_type.template_arguments.as_ref()
              .chain_err(|| "failed to get QList args"));
          let arg = try!(args.get(0).chain_err(|| "failed to get QList arg"));
          let arg_text = arg.to_cpp_pseudo_code();
          if types.iter().any(|x| x == &arg_text) {
            match method.name.as_ref() {
              "operator==" | "operator!=" | "indexOf" | "lastIndexOf" | "contains" |
              "startsWith" | "endsWith" | "removeOne" | "removeAll" | "value" |
              "toVector" | "toSet" => return Ok(false),
              "count" => if method.arguments.len() == 1 { return Ok(false); },
              _ => {}
            }
          }
        }
      }
      Ok(true)
    }));
  }

  fn exclude_qvector_eq_based_methods<S: AsRef<str>, I: IntoIterator<Item=S>>(&mut self, types: I) {
    let types: Vec<String> = types.into_iter().map(|x| x.as_ref().to_string()).collect();
    self.add_cpp_ffi_generator_filter(Box::new(move |method| {
      if let Some(ref info) = method.class_membership {
        if info.class_type.name == "QVector" {
          let args = try!(info.class_type.template_arguments.as_ref()
              .chain_err(|| "failed to get QVector args"));
          let arg = try!(args.get(0).chain_err(|| "failed to get QVector arg"));
          let arg_text = arg.to_cpp_pseudo_code();
          if types.iter().any(|x| x == &arg_text) {
            match method.name.as_ref() {
              "operator==" | "operator!=" | "indexOf" | "lastIndexOf" | "contains" |
              "startsWith" | "endsWith" | "removeOne" | "removeAll" | "toList" => return Ok(false),
              "count" => if method.arguments.len() == 1 { return Ok(false); },
              _ => {}
            }
          }
        }
      }
      Ok(true)
    }));
  }
}