From ebf91cc3b2461abc9947b80b0f1ee1a6d5903113 Mon Sep 17 00:00:00 2001 From: Jacob Alexander Date: Mon, 4 Sep 2023 09:38:24 -0700 Subject: [PATCH 1/4] Add qobject_header_include() to add support for moc -I - Needed when the moc compiler does not have all the necessary files present in the cwd. --- crates/cxx-qt-build/src/lib.rs | 13 +++++++++++-- crates/qt-build-utils/src/lib.rs | 10 ++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/crates/cxx-qt-build/src/lib.rs b/crates/cxx-qt-build/src/lib.rs index 0d1a9e35b..e865d3ae7 100644 --- a/crates/cxx-qt-build/src/lib.rs +++ b/crates/cxx-qt-build/src/lib.rs @@ -304,6 +304,7 @@ fn panic_duplicate_file_and_qml_module( pub struct CxxQtBuilder { rust_sources: Vec, qobject_headers: Vec, + qobject_header_includes: Vec, qrc_files: Vec, qt_modules: HashSet, qml_modules: Vec, @@ -319,6 +320,7 @@ impl CxxQtBuilder { Self { rust_sources: vec![], qobject_headers: vec![], + qobject_header_includes: vec![], qrc_files: vec![], qt_modules, qml_modules: vec![], @@ -421,6 +423,13 @@ impl CxxQtBuilder { self } + /// Specify additional include paths when running the moc compiler (qobject_header). + pub fn qobject_header_include(mut self, path: impl AsRef) -> Self { + let path = path.as_ref(); + self.qobject_header_includes.push(path.to_owned()); + self + } + /// Use a closure to run additional customization on [CxxQtBuilder]'s internal [cc::Build] /// before calling [CxxQtBuilder::build]. This allows to add extra include paths, compiler flags, /// or anything else available via [cc::Build]'s API. For example, to add an include path for @@ -563,7 +572,7 @@ impl CxxQtBuilder { // Run moc on C++ headers with Q_OBJECT macro for qobject_header in self.qobject_headers { - let moc_products = qtbuild.moc(&qobject_header, None); + let moc_products = qtbuild.moc(&qobject_header, None, &self.qobject_header_includes); self.cc_builder.file(moc_products.cpp); } @@ -581,7 +590,7 @@ impl CxxQtBuilder { if let (Some(qobject), Some(qobject_header)) = (files.qobject, files.qobject_header) { self.cc_builder.file(&qobject); - let moc_products = qtbuild.moc(qobject_header, Some(&qml_module.uri)); + let moc_products = qtbuild.moc(qobject_header, Some(&qml_module.uri), &vec![]); self.cc_builder.file(moc_products.cpp); qml_metatypes_json.push(moc_products.metatypes_json); } diff --git a/crates/qt-build-utils/src/lib.rs b/crates/qt-build-utils/src/lib.rs index 3eacffa19..c7b9e1fcf 100644 --- a/crates/qt-build-utils/src/lib.rs +++ b/crates/qt-build-utils/src/lib.rs @@ -551,7 +551,8 @@ impl QtBuild { /// as well as the path to the generated metatypes.json file, which can be passed to [register_qml_module](Self::register_qml_module). /// /// * uri - Should be passed if the input_file is part of a QML module - pub fn moc(&mut self, input_file: impl AsRef, uri: Option<&str>) -> MocProducts { + /// * include_paths - Additional include paths to pass to moc + pub fn moc(&mut self, input_file: impl AsRef, uri: Option<&str>, include_paths: &Vec) -> MocProducts { if self.moc_executable.is_none() { self.moc_executable = Some(self.get_qt_tool("moc").expect("Could not find moc")); } @@ -566,9 +567,14 @@ impl QtBuild { let metatypes_json_path = PathBuf::from(&format!("{}.json", output_path.display())); let mut include_args = String::new(); + // Qt includes for include_path in self.include_paths() { include_args += &format!("-I {} ", include_path.display()); } + // User includes + for include_path in include_paths { + include_args += &format!("-I {} ", include_path.display()); + } let mut cmd = Command::new(self.moc_executable.as_ref().unwrap()); @@ -847,7 +853,7 @@ public: "# ) .unwrap(); - self.moc(&qml_plugin_cpp_path, Some(uri)); + self.moc(&qml_plugin_cpp_path, Some(uri), &vec![]); // Generate file to load static QQmlExtensionPlugin let mut qml_plugin_init = File::create(&qml_plugin_init_path).unwrap(); From f4533cae2467a68b415b372aa527c70b9bc53880 Mon Sep 17 00:00:00 2001 From: Leon Matthes Date: Wed, 10 Jan 2024 17:40:56 +0100 Subject: [PATCH 2/4] Extract QtBuildUtils::moc() arguments into struct --- crates/cxx-qt-build/src/lib.rs | 29 ++++++++++++----- crates/qt-build-utils/src/lib.rs | 54 +++++++++++++++++++++++++------- 2 files changed, 65 insertions(+), 18 deletions(-) diff --git a/crates/cxx-qt-build/src/lib.rs b/crates/cxx-qt-build/src/lib.rs index e865d3ae7..789198357 100644 --- a/crates/cxx-qt-build/src/lib.rs +++ b/crates/cxx-qt-build/src/lib.rs @@ -21,6 +21,7 @@ mod qml_modules; use qml_modules::OwningQmlModule; pub use qml_modules::QmlModule; +pub use qt_build_utils::MocArguments; use quote::ToTokens; use std::{ collections::HashSet, @@ -303,7 +304,7 @@ fn panic_duplicate_file_and_qml_module( #[derive(Default)] pub struct CxxQtBuilder { rust_sources: Vec, - qobject_headers: Vec, + qobject_headers: Vec<(PathBuf, MocArguments)>, qobject_header_includes: Vec, qrc_files: Vec, qt_modules: HashSet, @@ -416,9 +417,19 @@ impl CxxQtBuilder { /// Specify a C++ header containing a Q_OBJECT macro to run [moc](https://doc.qt.io/qt-6/moc.html) on. /// This allows building QObject C++ subclasses besides the ones autogenerated by cxx-qt. - pub fn qobject_header(mut self, path: impl AsRef) -> Self { + pub fn qobject_header(self, path: impl AsRef) -> Self { + self.qobject_header_with_arguments(path, MocArguments::default()) + } + + /// Specify a C++ header containing a Q_OBJECT macro to run [moc](https://doc.qt.io/qt-6/moc.html) on with the given arguments. + /// This allows building QObject C++ subclasses besides the ones autogenerated by cxx-qt. + pub fn qobject_header_with_arguments( + mut self, + path: impl AsRef, + arguments: MocArguments, + ) -> Self { let path = path.as_ref(); - self.qobject_headers.push(path.to_owned()); + self.qobject_headers.push((path.to_owned(), arguments)); println!("cargo:rerun-if-changed={}", path.display()); self } @@ -566,13 +577,14 @@ impl CxxQtBuilder { self.cc_builder.file(files.plain_cpp); if let (Some(qobject), Some(qobject_header)) = (files.qobject, files.qobject_header) { self.cc_builder.file(&qobject); - self.qobject_headers.push(qobject_header); + self.qobject_headers + .push((qobject_header, MocArguments::default())); } } // Run moc on C++ headers with Q_OBJECT macro - for qobject_header in self.qobject_headers { - let moc_products = qtbuild.moc(&qobject_header, None, &self.qobject_header_includes); + for (qobject_header, moc_arguments) in self.qobject_headers { + let moc_products = qtbuild.moc(&qobject_header, moc_arguments); self.cc_builder.file(moc_products.cpp); } @@ -590,7 +602,10 @@ impl CxxQtBuilder { if let (Some(qobject), Some(qobject_header)) = (files.qobject, files.qobject_header) { self.cc_builder.file(&qobject); - let moc_products = qtbuild.moc(qobject_header, Some(&qml_module.uri), &vec![]); + let moc_products = qtbuild.moc( + qobject_header, + MocArguments::default().uri(qml_module.uri.clone()), + ); self.cc_builder.file(moc_products.cpp); qml_metatypes_json.push(moc_products.metatypes_json); } diff --git a/crates/qt-build-utils/src/lib.rs b/crates/qt-build-utils/src/lib.rs index c7b9e1fcf..bfc269161 100644 --- a/crates/qt-build-utils/src/lib.rs +++ b/crates/qt-build-utils/src/lib.rs @@ -136,6 +136,34 @@ pub struct MocProducts { pub metatypes_json: PathBuf, } +/// Arguments for a Qt moc invocation. +/// See: [QtBuild::moc] +#[derive(Default, Clone)] +pub struct MocArguments { + uri: Option, + include_paths: Vec, +} + +impl MocArguments { + /// Should be passed if the input_file is part of a QML module + pub fn uri(mut self, uri: String) -> Self { + self.uri = Some(uri); + self + } + + /// Additional include path to pass to moc + pub fn include_path(mut self, include_path: PathBuf) -> Self { + self.include_paths.push(include_path); + self + } + + /// Additional include paths to pass to moc. + pub fn include_paths(mut self, mut include_paths: Vec) -> Self { + self.include_paths.append(&mut include_paths); + self + } +} + /// Paths to C++ files generated by [QtBuild::register_qml_module] pub struct QmlModuleRegistrationFiles { /// File generated by [rcc](https://doc.qt.io/qt-6/rcc.html) for the QML plugin. The compiled static library @@ -550,9 +578,7 @@ impl QtBuild { /// The return value contains the path to the generated C++ file, which can then be passed to [cc::Build::files](https://docs.rs/cc/latest/cc/struct.Build.html#method.file), /// as well as the path to the generated metatypes.json file, which can be passed to [register_qml_module](Self::register_qml_module). /// - /// * uri - Should be passed if the input_file is part of a QML module - /// * include_paths - Additional include paths to pass to moc - pub fn moc(&mut self, input_file: impl AsRef, uri: Option<&str>, include_paths: &Vec) -> MocProducts { + pub fn moc(&mut self, input_file: impl AsRef, arguments: MocArguments) -> MocProducts { if self.moc_executable.is_none() { self.moc_executable = Some(self.get_qt_tool("moc").expect("Could not find moc")); } @@ -568,18 +594,18 @@ impl QtBuild { let mut include_args = String::new(); // Qt includes - for include_path in self.include_paths() { - include_args += &format!("-I {} ", include_path.display()); - } - // User includes - for include_path in include_paths { + for include_path in self + .include_paths() + .iter() + .chain(arguments.include_paths.iter()) + { include_args += &format!("-I {} ", include_path.display()); } let mut cmd = Command::new(self.moc_executable.as_ref().unwrap()); - if let Some(uri) = uri { - cmd.arg(&format!("-Muri={}", uri)); + if let Some(uri) = arguments.uri { + cmd.arg(&format!("-Muri={uri}")); } cmd.args(include_args.trim_end().split(' ')); @@ -853,7 +879,13 @@ public: "# ) .unwrap(); - self.moc(&qml_plugin_cpp_path, Some(uri), &vec![]); + self.moc( + &qml_plugin_cpp_path, + MocArguments { + uri: Some(uri.to_owned()), + ..Default::default() + }, + ); // Generate file to load static QQmlExtensionPlugin let mut qml_plugin_init = File::create(&qml_plugin_init_path).unwrap(); From 8aa6e6c8ceb27fb72a65025aa27313511b8d5134 Mon Sep 17 00:00:00 2001 From: Leon Matthes Date: Wed, 13 Mar 2024 10:59:53 +0100 Subject: [PATCH 3/4] Add cxx_qt_build::QObjectHeaderOpts This allows us to expose more options in future, should that be required. --- crates/cxx-qt-build/src/lib.rs | 41 ++++++++++-------------------- crates/cxx-qt-build/src/opts.rs | 44 ++++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 29 deletions(-) diff --git a/crates/cxx-qt-build/src/lib.rs b/crates/cxx-qt-build/src/lib.rs index 789198357..2089a1f05 100644 --- a/crates/cxx-qt-build/src/lib.rs +++ b/crates/cxx-qt-build/src/lib.rs @@ -16,6 +16,7 @@ use diagnostics::{Diagnostic, GeneratedError}; mod opts; pub use opts::CxxQtBuildersOpts; +pub use opts::QObjectHeaderOpts; mod qml_modules; use qml_modules::OwningQmlModule; @@ -304,8 +305,7 @@ fn panic_duplicate_file_and_qml_module( #[derive(Default)] pub struct CxxQtBuilder { rust_sources: Vec, - qobject_headers: Vec<(PathBuf, MocArguments)>, - qobject_header_includes: Vec, + qobject_headers: Vec, qrc_files: Vec, qt_modules: HashSet, qml_modules: Vec, @@ -321,7 +321,6 @@ impl CxxQtBuilder { Self { rust_sources: vec![], qobject_headers: vec![], - qobject_header_includes: vec![], qrc_files: vec![], qt_modules, qml_modules: vec![], @@ -417,27 +416,10 @@ impl CxxQtBuilder { /// Specify a C++ header containing a Q_OBJECT macro to run [moc](https://doc.qt.io/qt-6/moc.html) on. /// This allows building QObject C++ subclasses besides the ones autogenerated by cxx-qt. - pub fn qobject_header(self, path: impl AsRef) -> Self { - self.qobject_header_with_arguments(path, MocArguments::default()) - } - - /// Specify a C++ header containing a Q_OBJECT macro to run [moc](https://doc.qt.io/qt-6/moc.html) on with the given arguments. - /// This allows building QObject C++ subclasses besides the ones autogenerated by cxx-qt. - pub fn qobject_header_with_arguments( - mut self, - path: impl AsRef, - arguments: MocArguments, - ) -> Self { - let path = path.as_ref(); - self.qobject_headers.push((path.to_owned(), arguments)); - println!("cargo:rerun-if-changed={}", path.display()); - self - } - - /// Specify additional include paths when running the moc compiler (qobject_header). - pub fn qobject_header_include(mut self, path: impl AsRef) -> Self { - let path = path.as_ref(); - self.qobject_header_includes.push(path.to_owned()); + pub fn qobject_header(mut self, opts: impl Into) -> Self { + let opts = opts.into(); + println!("cargo:rerun-if-changed={}", opts.path.display()); + self.qobject_headers.push(opts); self } @@ -577,14 +559,17 @@ impl CxxQtBuilder { self.cc_builder.file(files.plain_cpp); if let (Some(qobject), Some(qobject_header)) = (files.qobject, files.qobject_header) { self.cc_builder.file(&qobject); - self.qobject_headers - .push((qobject_header, MocArguments::default())); + self.qobject_headers.push(qobject_header.into()); } } // Run moc on C++ headers with Q_OBJECT macro - for (qobject_header, moc_arguments) in self.qobject_headers { - let moc_products = qtbuild.moc(&qobject_header, moc_arguments); + for QObjectHeaderOpts { + path, + moc_arguments, + } in self.qobject_headers + { + let moc_products = qtbuild.moc(&path, moc_arguments); self.cc_builder.file(moc_products.cpp); } diff --git a/crates/cxx-qt-build/src/opts.rs b/crates/cxx-qt-build/src/opts.rs index 9459e555e..4800dce47 100644 --- a/crates/cxx-qt-build/src/opts.rs +++ b/crates/cxx-qt-build/src/opts.rs @@ -3,7 +3,12 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 -use std::collections::HashSet; +use std::{ + collections::HashSet, + path::{Path, PathBuf}, +}; + +use crate::MocArguments; /// Options for external crates to use #[derive(Default)] @@ -42,3 +47,40 @@ impl CxxQtBuildersOpts { self } } + +/// Options for qobject_headers +/// +/// QObjectHeaderOpts can be created using the `From>` trait. +/// ``` +/// # use cxx_qt_build::{QObjectHeaderOpts, MocArguments}; +/// QObjectHeaderOpts::from("path/to/header.h") +/// .moc_arguments(MocArguments::default()); +/// ``` +pub struct QObjectHeaderOpts { + pub(crate) path: PathBuf, + pub(crate) moc_arguments: MocArguments, +} + +impl From for QObjectHeaderOpts +where + T: AsRef, +{ + fn from(path: T) -> Self { + Self { + path: path.as_ref().to_owned(), + moc_arguments: MocArguments::default(), + } + } +} + +impl QObjectHeaderOpts { + /// Set the moc arguments for this header + /// + /// By default this is `MocArguments::default()` + pub fn moc_arguments(self, moc_arguments: MocArguments) -> Self { + Self { + moc_arguments, + ..self + } + } +} From 173c96b8524ec62b9ff9013a82de8bef303d8e01 Mon Sep 17 00:00:00 2001 From: Laurent Montel Date: Tue, 12 Mar 2024 14:29:04 +0100 Subject: [PATCH 4/4] Add version for cxx-qt-build/cxx-qt-lib --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 397a99d4e..884c77096 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,9 +34,9 @@ version = "0.6.0" [workspace.dependencies] cxx-qt = { path = "crates/cxx-qt" } cxx-qt-macro = { path = "crates/cxx-qt-macro", version = "0.6.0" } -cxx-qt-build = { path = "crates/cxx-qt-build" } +cxx-qt-build = { path = "crates/cxx-qt-build", version = "0.6.0" } cxx-qt-gen = { path = "crates/cxx-qt-gen", version = "0.6.0" } -cxx-qt-lib = { path = "crates/cxx-qt-lib" } +cxx-qt-lib = { path = "crates/cxx-qt-lib", version = "0.6.0" } cxx-qt-lib-headers = { path = "crates/cxx-qt-lib-headers", version = "0.6.0" } qt-build-utils = { path = "crates/qt-build-utils", version = "0.6.0" }