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" } diff --git a/crates/cxx-qt-build/src/lib.rs b/crates/cxx-qt-build/src/lib.rs index 0d1a9e35b..2089a1f05 100644 --- a/crates/cxx-qt-build/src/lib.rs +++ b/crates/cxx-qt-build/src/lib.rs @@ -16,11 +16,13 @@ use diagnostics::{Diagnostic, GeneratedError}; mod opts; pub use opts::CxxQtBuildersOpts; +pub use opts::QObjectHeaderOpts; 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 +305,7 @@ fn panic_duplicate_file_and_qml_module( #[derive(Default)] pub struct CxxQtBuilder { rust_sources: Vec, - qobject_headers: Vec, + qobject_headers: Vec, qrc_files: Vec, qt_modules: HashSet, qml_modules: Vec, @@ -414,10 +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(mut self, path: impl AsRef) -> Self { - let path = path.as_ref(); - self.qobject_headers.push(path.to_owned()); - println!("cargo:rerun-if-changed={}", path.display()); + 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 } @@ -557,13 +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); + self.qobject_headers.push(qobject_header.into()); } } // Run moc on C++ headers with Q_OBJECT macro - for qobject_header in self.qobject_headers { - let moc_products = qtbuild.moc(&qobject_header, None); + for QObjectHeaderOpts { + path, + moc_arguments, + } in self.qobject_headers + { + let moc_products = qtbuild.moc(&path, moc_arguments); self.cc_builder.file(moc_products.cpp); } @@ -581,7 +587,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)); + 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/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 + } + } +} diff --git a/crates/qt-build-utils/src/lib.rs b/crates/qt-build-utils/src/lib.rs index 3eacffa19..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,8 +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 - pub fn moc(&mut self, input_file: impl AsRef, uri: Option<&str>) -> 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")); } @@ -566,14 +593,19 @@ impl QtBuild { let metatypes_json_path = PathBuf::from(&format!("{}.json", output_path.display())); let mut include_args = String::new(); - for include_path in self.include_paths() { + // Qt includes + 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(' ')); @@ -847,7 +879,13 @@ public: "# ) .unwrap(); - self.moc(&qml_plugin_cpp_path, Some(uri)); + 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();