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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! Common utilities for the generator and the build script for Qt crates.
//!
//! Qt modules are identified within this crate using snake case names without
//! a prefix, e.g. `core` for QtCore and `ui_tools` for QtUiTools.
//! `sublib_name` argument should be in this form.
//!
//! See [README](https://github.com/rust-qt/cpp_to_rust)
//! for more information.

extern crate cpp_to_rust_common;

use cpp_to_rust_common::utils::get_command_output;
use cpp_to_rust_common::file_utils::PathBufWithAdded;
use cpp_to_rust_common::string_utils::CaseOperations;
use cpp_to_rust_common::errors::Result;
use cpp_to_rust_common::log;

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

/// Makes a query to `qmake`.
fn run_qmake_string_query(property: &str) -> Result<String> {
  let result = get_command_output(Command::new("qmake").arg("-query").arg(property))?;
  Ok(result.trim().to_string())
}


/// Makes a query to `qmake` and interprets its output as a path.
fn run_qmake_query(property: &str) -> Result<PathBuf> {
  Ok(PathBuf::from(run_qmake_string_query(property)?))
}

/// Properties of a Qt installation
pub struct InstallationData {
  /// Qt version.
  pub qt_version: String,
  /// Path to the parent include directory of the installation.
  pub root_include_path: PathBuf,
  /// Path to the include directory of the library that is being processed.
  /// This is a direct subdirectory of `root_include_path`.
  pub lib_include_path: PathBuf,
  /// Path to the directory containing library files for the linker.
  pub lib_path: PathBuf,
  /// Path to the directory containing Qt documentation files.
  pub docs_path: PathBuf,
  /// If true, this Qt library was built as a MacOS framework.
  pub is_framework: bool,
}

/// Detects properties of current Qt installation using `qmake` command line utility.
pub fn get_installation_data(sublib_name: &str) -> Result<InstallationData> {
  let qt_version = run_qmake_string_query("QT_VERSION")?;
  log::status(format!("QT_VERSION = \"{}\"", qt_version));
  log::status("Detecting Qt directories");

  let root_include_path = run_qmake_query("QT_INSTALL_HEADERS")?;
  log::status(format!("QT_INSTALL_HEADERS = \"{}\"", root_include_path.display()));
  let lib_path = run_qmake_query("QT_INSTALL_LIBS")?;
  log::status(format!("QT_INSTALL_LIBS = \"{}\"", lib_path.display()));
  let docs_path = run_qmake_query("QT_INSTALL_DOCS")?;
  log::status(format!("QT_INSTALL_DOCS = \"{}\"", docs_path.display()));
  let folder_name = lib_folder_name(sublib_name);
  let dir = root_include_path.with_added(&folder_name);
  if dir.exists() {
    Ok(InstallationData {
         root_include_path: root_include_path,
         lib_path: lib_path,
         docs_path: docs_path,
         lib_include_path: dir,
         is_framework: false,
         qt_version: qt_version,
       })
  } else {
    let dir2 = lib_path.with_added(format!("{}.framework/Headers", folder_name));
    if dir2.exists() {
      Ok(InstallationData {
           root_include_path: root_include_path,
           lib_path: lib_path,
           docs_path: docs_path,
           lib_include_path: dir2,
           is_framework: true,
           qt_version: qt_version,
         })
    } else {
      Err(format!("extra header dir not found (tried: {}, {})",
                  dir.display(),
                  dir2.display())
              .into())
    }
  }
}

/// Returns library name of the specified module as
/// should be passed to the linker, e.g. `"Qt5Core"`.
pub fn real_lib_name(sublib_name: &str) -> String {
  let sublib_name_capitalized = sublib_name.to_class_case();
  format!("Qt5{}", sublib_name_capitalized)
}

/// Returns name of the module's include directory, e.g. `"QtCore"`.
pub fn lib_folder_name(sublib_name: &str) -> String {
  let sublib_name_capitalized = sublib_name.to_class_case();
  format!("Qt{}", sublib_name_capitalized)
}

/// Returns MacOS framework name of the specified module as
/// should be passed to the linker, e.g. `"QtCore"`.
pub fn framework_name(sublib_name: &str) -> String {
  let sublib_name_capitalized = sublib_name.to_class_case();
  format!("Qt{}", sublib_name_capitalized)
}

/// Returns list of modules this module depends on.
pub fn lib_dependencies(sublib_name: &str) -> Result<&'static [&'static str]> {
  const CORE: &'static [&'static str] = &[];
  const GUI: &'static [&'static str] = &["core"];
  const WIDGETS: &'static [&'static str] = &["core", "gui"];
  const UI_TOOLS: &'static [&'static str] = &["core", "gui", "widgets"];
  const CORE3D: &'static [&'static str] = &["core", "gui"];
  const RENDER3D: &'static [&'static str] = &["core", "gui", "3d_core"];
  const INPUT3D: &'static [&'static str] = &["core", "gui", "3d_core"];
  const LOGIC3D: &'static [&'static str] = &["core", "gui", "3d_core"];
  const EXTRAS3D: &'static [&'static str] = &["core",
                                              "gui",
                                              "3d_core",
                                              "3d_render",
                                              "3d_input",
                                              "3d_logic"];
  Ok(match sublib_name {
       "core" => CORE,
       "gui" => GUI,
       "widgets" => WIDGETS,
       "3d_core" => CORE3D,
       "3d_render" => RENDER3D,
       "3d_input" => INPUT3D,
       "3d_logic" => LOGIC3D,
       "3d_extras" => EXTRAS3D,
       "ui_tools" => UI_TOOLS,
       _ => return Err(format!("Unknown lib name: {}", sublib_name).into()),
     })
}