Add function to choose what Qt modules to link to

master
Jos van den Oever 2019-01-21 23:29:13 +01:00
parent 0fb6e84077
commit 7c8bb6a535
1 changed files with 79 additions and 51 deletions

View File

@ -1,11 +1,11 @@
extern crate cc; extern crate cc;
use super::{generate_bindings, read_bindings_file, Config};
use regex::Regex; use regex::Regex;
use serde_xml_rs::deserialize; use serde_xml_rs::deserialize;
use std::io::{self, Write};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::Command; use std::process::Command;
use std::io::{self, Write};
use super::{Config, generate_bindings, read_bindings_file};
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -22,7 +22,8 @@ struct QResource {
/// Parse the qrc file, panic if it fails. /// Parse the qrc file, panic if it fails.
fn read_qrc(qrc: &Path) -> RCC { fn read_qrc(qrc: &Path) -> RCC {
let bytes = ::std::fs::read(qrc).expect(&format!("Could not read {}", qrc.display())); let bytes = ::std::fs::read(qrc).expect(&format!("Could not read {}", qrc.display()));
let mut rcc: RCC = deserialize(&bytes[..]).expect(&format!("could not parse {}", qrc.display())); let mut rcc: RCC =
deserialize(&bytes[..]).expect(&format!("could not parse {}", qrc.display()));
for qresource in &mut rcc.qresource { for qresource in &mut rcc.qresource {
for file in &mut qresource.file { for file in &mut qresource.file {
let mut p = qrc.parent().unwrap().to_path_buf(); let mut p = qrc.parent().unwrap().to_path_buf();
@ -50,18 +51,14 @@ fn qrc_to_input_list<'a>(qrc: &'a Path, rcc: &'a RCC) -> Vec<&'a Path> {
fn run(cmd: &str, command: &mut Command) -> Vec<u8> { fn run(cmd: &str, command: &mut Command) -> Vec<u8> {
eprintln!("running: {:?}", command); eprintln!("running: {:?}", command);
match command.output() { match command.output() {
Err(e) => { Err(e) => eprintln!(
eprintln!( "Could not run {}. Make sure {} is in your path: {}",
"Could not run {}. Make sure {} is in your path: {}", cmd, cmd, e
cmd, ),
cmd,
e
)
}
Ok(output) => { Ok(output) => {
io::stderr().write(&output.stderr).expect( io::stderr()
"Could not write to stderr.", .write(&output.stderr)
); .expect("Could not write to stderr.");
if output.status.success() { if output.status.success() {
return output.stdout; return output.stdout;
} }
@ -95,17 +92,16 @@ fn parse_qt_version(qt_version: &str) -> Version {
let re = Regex::new(r"(\d)\.(\d{1,2})(\.(\d{1,2}))").unwrap(); let re = Regex::new(r"(\d)\.(\d{1,2})(\.(\d{1,2}))").unwrap();
match re.captures(&qt_version) { match re.captures(&qt_version) {
None => panic!("Cannot parse Qt version number {}", qt_version), None => panic!("Cannot parse Qt version number {}", qt_version),
Some(cap) => { Some(cap) => Version {
Version { major: cap[1].parse::<u8>().unwrap(),
major: cap[1].parse::<u8>().unwrap(), minor: cap[2].parse::<u8>().unwrap(),
minor: cap[2].parse::<u8>().unwrap(), patch: cap
patch: cap.get(4) .get(4)
.map(|m| m.as_str()) .map(|m| m.as_str())
.unwrap_or("0") .unwrap_or("0")
.parse::<u8>() .parse::<u8>()
.unwrap(), .unwrap(),
} },
}
} }
} }
@ -126,20 +122,49 @@ fn parse_qt_version(qt_version: &str) -> Version {
pub fn require_qt_version(major: u8, minor: u8, patch: u8) { pub fn require_qt_version(major: u8, minor: u8, patch: u8) {
let qt_version = qmake_query("QT_VERSION"); let qt_version = qmake_query("QT_VERSION");
let version = parse_qt_version(&qt_version); let version = parse_qt_version(&qt_version);
if version.major < major || if version.major < major
(version.major == major && || (version.major == major
(version.minor < minor || (version.minor == minor && version.patch < patch))) && (version.minor < minor || (version.minor == minor && version.patch < patch)))
{ {
panic!( panic!(
"Please use a version of Qt >= {}.{}.{}, not {}", "Please use a version of Qt >= {}.{}.{}, not {}",
major, major, minor, patch, qt_version
minor,
patch,
qt_version
); );
} }
} }
#[derive(Debug)]
pub enum QtModule {
Core,
Gui,
Multimedia,
MultimediaWidgets,
Network,
Qml,
Quick,
QuickTest,
Sql,
Test,
Widgets,
Concurrent,
DBus,
Help,
Location,
OpenGL,
Positioning,
PrintSupport,
Svg,
WebChannel,
WebEngine,
WaylandCompositor,
X11Extras,
Xml,
XmlPatterns,
Charts,
}
/// A builder for binding generation and compilation of a Qt application. /// A builder for binding generation and compilation of a Qt application.
/// ///
/// Pass options into this `Build` and then run `build` to generate binddings /// Pass options into this `Build` and then run `build` to generate binddings
@ -153,6 +178,7 @@ pub struct Build {
qrc: Vec<PathBuf>, qrc: Vec<PathBuf>,
h: Vec<PathBuf>, h: Vec<PathBuf>,
cpp: Vec<PathBuf>, cpp: Vec<PathBuf>,
modules: Vec<QtModule>,
} }
impl Build { impl Build {
@ -177,10 +203,10 @@ impl Build {
pub fn new<P: AsRef<Path>>(out_dir: P) -> Build { pub fn new<P: AsRef<Path>>(out_dir: P) -> Build {
let qt_include_path = qmake_query("QT_INSTALL_HEADERS"); let qt_include_path = qmake_query("QT_INSTALL_HEADERS");
let mut build = cc::Build::new(); let mut build = cc::Build::new();
build.cpp(true).include(out_dir.as_ref()).include( build
qt_include_path .cpp(true)
.trim(), .include(out_dir.as_ref())
); .include(qt_include_path.trim());
Build { Build {
qt_library_path: qmake_query("QT_INSTALL_LIBS").trim().into(), qt_library_path: qmake_query("QT_INSTALL_LIBS").trim().into(),
out_dir: out_dir.as_ref().to_path_buf(), out_dir: out_dir.as_ref().to_path_buf(),
@ -189,6 +215,7 @@ impl Build {
qrc: Vec::new(), qrc: Vec::new(),
h: Vec::new(), h: Vec::new(),
cpp: Vec::new(), cpp: Vec::new(),
modules: vec![QtModule::Core],
} }
} }
/// Add a bindings file to be processed. /// Add a bindings file to be processed.
@ -217,6 +244,11 @@ impl Build {
self.cpp.push(path.as_ref().to_path_buf()); self.cpp.push(path.as_ref().to_path_buf());
self self
} }
/// Add a Qt module to be linked to the executable
pub fn module(&mut self, module: QtModule) -> &mut Build {
self.modules.push(module);
self
}
/// Compile the static library. /// Compile the static library.
/// ///
/// # Panics /// # Panics
@ -249,10 +281,9 @@ impl Build {
print_cpp_link_stdlib(); print_cpp_link_stdlib();
} }
println!("cargo:rustc-link-search={}", self.qt_library_path.display()); println!("cargo:rustc-link-search={}", self.qt_library_path.display());
println!("cargo:rustc-link-lib=Qt5Core"); for module in &self.modules {
println!("cargo:rustc-link-lib=Qt5Network"); println!("cargo:rustc-link-lib=Qt5{:?}", module);
println!("cargo:rustc-link-lib=Qt5Gui"); }
println!("cargo:rustc-link-lib=Qt5Qml");
} }
} }
@ -262,9 +293,7 @@ fn print_cpp_link_stdlib() {
let target = ::std::env::var("TARGET").unwrap(); let target = ::std::env::var("TARGET").unwrap();
let stdlib = if target.contains("msvc") { let stdlib = if target.contains("msvc") {
None None
} else if target.contains("apple") } else if target.contains("apple") || target.contains("freebsd") || target.contains("openbsd") {
|| target.contains("freebsd")
|| target.contains("openbsd") {
Some("c++") Some("c++")
} else { } else {
Some("stdc++") Some("stdc++")
@ -299,9 +328,10 @@ fn are_outputs_up_to_date(paths: &[&Path], input: SystemTime) -> bool {
fn get_youngest_mtime(paths: &[&Path]) -> Result<SystemTime, String> { fn get_youngest_mtime(paths: &[&Path]) -> Result<SystemTime, String> {
let mut max = UNIX_EPOCH; let mut max = UNIX_EPOCH;
for path in paths { for path in paths {
let mt = path.metadata().and_then(|m| m.modified()).map_err(|e| { let mt = path
format!("Error reading file {}: {}.", path.display(), e) .metadata()
})?; .and_then(|m| m.modified())
.map_err(|e| format!("Error reading file {}: {}.", path.display(), e))?;
if mt > max { if mt > max {
max = mt; max = mt;
} }
@ -344,9 +374,8 @@ fn handle_binding(
h: &mut Vec<PathBuf>, h: &mut Vec<PathBuf>,
cpp: &mut Vec<PathBuf>, cpp: &mut Vec<PathBuf>,
) { ) {
let mut config = read_bindings_file(&bindings_json).unwrap_or_else(|e| { let mut config = read_bindings_file(&bindings_json)
panic!("Could not parse {}: {}", bindings_json.display(), e) .unwrap_or_else(|e| panic!("Could not parse {}: {}", bindings_json.display(), e));
});
let bindings_cpp = out_dir.join(&config.cpp_file); let bindings_cpp = out_dir.join(&config.cpp_file);
let mut bindings_h = bindings_cpp.clone(); let mut bindings_h = bindings_cpp.clone();
bindings_h.set_extension("h"); bindings_h.set_extension("h");
@ -356,8 +385,7 @@ fn handle_binding(
if should_run( if should_run(
&[&bindings_json], &[&bindings_json],
&[&bindings_h, &bindings_cpp, &interface_rs], &[&bindings_h, &bindings_cpp, &interface_rs],
) ) {
{
generate_bindings(&config).unwrap(); generate_bindings(&config).unwrap();
} }
h.push(bindings_h); h.push(bindings_h);