From 10a1bd91a16083a4cf7460510c2963e7be0ae1c9 Mon Sep 17 00:00:00 2001 From: Be Wilson Date: Tue, 25 Jul 2023 15:45:36 -0500 Subject: [PATCH] move QML element registration to build.rs instead of bridge This avoids the need to repeatedly specify the URI and version of the QML module in the bridges. QObjects that are registered as QML elements are now marked by [cxx_qt::qobject(qml_element = "OptionalName")] This is a precursor to generating qmldir files which are required for qmlcachegen and qmltc (https://github.com/KDAB/cxx-qt/issues/242) --- crates/cxx-qt-build/src/lib.rs | 169 ++++++++++-------- .../cxx-qt-gen/src/generator/cpp/qobject.rs | 6 +- .../cxx-qt-gen/src/generator/rust/qobject.rs | 2 +- crates/cxx-qt-gen/src/parser/qobject.rs | 119 ++---------- crates/qt-build-utils/src/lib.rs | 21 +-- examples/cargo_without_cmake/build.rs | 3 +- examples/demo_threading/rust/build.rs | 4 +- examples/demo_threading/rust/src/lib.rs | 2 +- examples/qml_features/rust/build.rs | 33 ++-- examples/qml_features/rust/src/containers.rs | 2 +- .../rust/src/custom_base_class.rs | 6 +- examples/qml_features/rust/src/invokables.rs | 2 +- .../rust/src/multiple_qobjects.rs | 4 +- .../qml_features/rust/src/nested_qobjects.rs | 4 +- examples/qml_features/rust/src/properties.rs | 2 +- .../qml_features/rust/src/serialisation.rs | 2 +- examples/qml_features/rust/src/signals.rs | 2 +- examples/qml_features/rust/src/singleton.rs | 2 +- examples/qml_features/rust/src/threading.rs | 2 +- examples/qml_features/rust/src/types.rs | 2 +- examples/qml_features/rust/src/uncreatable.rs | 2 +- examples/qml_minimal/rust/build.rs | 4 +- examples/qml_minimal/rust/src/cxxqt_object.rs | 2 +- 23 files changed, 168 insertions(+), 229 deletions(-) diff --git a/crates/cxx-qt-build/src/lib.rs b/crates/cxx-qt-build/src/lib.rs index 013903a80..7967193f3 100644 --- a/crates/cxx-qt-build/src/lib.rs +++ b/crates/cxx-qt-build/src/lib.rs @@ -17,7 +17,7 @@ use diagnostics::{Diagnostic, GeneratedError}; use convert_case::{Case, Casing}; use quote::ToTokens; use std::{ - collections::{HashMap, HashSet}, + collections::HashSet, env, fs::File, io::Write, @@ -26,7 +26,7 @@ use std::{ use cxx_qt_gen::{ parse_qt_file, write_cpp, write_rust, CppFragment, CxxQtItem, GeneratedCppBlocks, - GeneratedRustBlocks, Parser, QmlElementMetadata, + GeneratedRustBlocks, Parser, }; // TODO: we need to eventually support having multiple modules defined in a single file. This @@ -40,19 +40,25 @@ use cxx_qt_gen::{ struct GeneratedCppFilePaths { plain_cpp: PathBuf, qobject: Option, - qobject_header: Option, -} - -struct QObjectHeader { - path: PathBuf, - qml_metadata: Vec, + qobject_header: Option, } struct GeneratedCpp { cxx_qt: Option, cxx: cxx_gen::GeneratedCode, file_ident: String, - qml_metadata: Vec, +} + +/// Metadata for registering a QML module +struct QmlModule { + /// The URI of the QML module + pub uri: String, + /// The minor version of the QML module + pub version_minor: usize, + /// The major version of the QML module + pub version_major: usize, + /// The .rs files with #[qml_element] attribute(s) + pub rust_files: Vec, } impl GeneratedCpp { @@ -67,7 +73,6 @@ impl GeneratedCpp { .map_err(to_diagnostic)?; let mut cxx_qt = None; - let mut qml_metadata = Vec::new(); // TODO: later change how the resultant filename is chosen, can we match the input file like // CXX does? // @@ -120,11 +125,6 @@ impl GeneratedCpp { .map_err(to_diagnostic)?; let rust_tokens = write_rust(&generated_rust); file_ident = parser.cxx_file_stem.clone(); - for (_, qobject) in parser.cxx_qt_data.qobjects { - if let Some(q) = qobject.qml_metadata { - qml_metadata.push(q); - } - } // We need to do this and can't rely on the macro, as we need to generate the // CXX bridge Rust code that is then fed into the cxx_gen generation. @@ -145,7 +145,6 @@ impl GeneratedCpp { cxx_qt, cxx, file_ident, - qml_metadata, }) } @@ -183,10 +182,7 @@ impl GeneratedCpp { header .write_all(header_generated.as_bytes()) .expect("Could not write cxx-qt header file"); - cpp_file_paths.qobject_header = Some(QObjectHeader { - path: header_path, - qml_metadata: self.qml_metadata, - }); + cpp_file_paths.qobject_header = Some(header_path); let cpp_path = PathBuf::from(format!( "{}/{}.cxxqt.cpp", @@ -232,7 +228,7 @@ impl GeneratedCpp { fn generate_cxxqt_cpp_files( rs_source: &[PathBuf], header_dir: impl AsRef, -) -> Result, Diagnostic> { +) -> Vec { let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); let mut generated_file_paths: Vec = Vec::with_capacity(rs_source.len()); @@ -241,11 +237,21 @@ fn generate_cxxqt_cpp_files( let path = format!("{manifest_dir}/{}", rs_path.display()); println!("cargo:rerun-if-changed={path}"); - let generated_code = GeneratedCpp::new(&path)?; + let generated_code = match GeneratedCpp::new(&path) { + Ok(v) => v, + Err(diagnostic) => { + diagnostic.report(); + std::process::exit(1); + } + }; generated_file_paths.push(generated_code.write_to_directories(cpp_directory, &header_dir)); } - Ok(generated_file_paths) + generated_file_paths +} + +fn panic_duplicate_file_and_qml_module(path: &Path, uri: &str, version_major: usize, version_minor: usize) { + panic!("CXX-Qt bridge Rust file {} specified in QML module {uri} (version {version_major}.{version_minor}), but also specified via CxxQtBuilder::file. Bridge files must be specified via CxxQtBuilder::file or CxxQtBuilder::qml_module, but not both.", path.display()); } /// Run cxx-qt's C++ code generator on Rust modules marked with the `cxx_qt::bridge` macro, compile @@ -283,9 +289,10 @@ fn generate_cxxqt_cpp_files( #[derive(Default)] pub struct CxxQtBuilder { rust_sources: Vec, - qobject_headers: Vec, + qobject_headers: Vec, qrc_files: Vec, qt_modules: HashSet, + qml_modules: Vec, cc_builder: cc::Build, } @@ -303,6 +310,7 @@ impl CxxQtBuilder { qobject_headers: vec![], qrc_files: vec![], qt_modules, + qml_modules: vec![], cc_builder: cc::Build::new(), } } @@ -310,9 +318,14 @@ impl CxxQtBuilder { /// Specify rust file paths to parse through the cxx-qt marco /// Relative paths are treated as relative to the path of your crate's Cargo.toml file pub fn file(mut self, rust_source: impl AsRef) -> Self { - let rust_source = rust_source.as_ref(); - self.rust_sources.push(rust_source.to_path_buf()); + let rust_source = rust_source.as_ref().to_path_buf(); + for qml_module in &self.qml_modules { + if qml_module.rust_files.contains(&rust_source) { + panic_duplicate_file_and_qml_module(&rust_source, &qml_module.uri, qml_module.version_major, qml_module.version_minor); + } + } println!("cargo:rerun-if-changed={}", rust_source.display()); + self.rust_sources.push(rust_source); self } @@ -351,14 +364,34 @@ impl CxxQtBuilder { self } + /// Register a QML module at build time + pub fn qml_module( + mut self, + uri: &str, + version_major: usize, + version_minor: usize, + rust_files: &[impl AsRef], + ) -> Self { + let rust_files: Vec = rust_files.iter().map(|p| p.as_ref().to_path_buf()).collect(); + for path in &rust_files { + if self.rust_sources.contains(&path) { + panic_duplicate_file_and_qml_module(&path, &uri, version_major, version_minor); + } + } + self.qml_modules.push(QmlModule { + uri: uri.to_owned(), + version_major, + version_minor, + rust_files, + }); + self + } + /// 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(QObjectHeader { - path: path.to_owned(), - qml_metadata: Vec::new(), - }); + self.qobject_headers.push(path.to_owned()); println!("cargo:rerun-if-changed={}", path.display()); self } @@ -467,62 +500,52 @@ impl CxxQtBuilder { } // Generate files - match generate_cxxqt_cpp_files(&self.rust_sources, &generated_header_dir) { - Ok(generated_files) => { - for files in generated_files { - 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); - } - } - } - Err(diagnostic) => { - // When CXX-Qt fails in the build script, we shouldn't panic, as the Rust backtrace - // probably isn't useful. We can instead report the error nicely, using - // codespan_reporting and then just exit the build script with a non-zero exit code. - // This will make for a cleaner build-script output than panicing. - diagnostic.report(); - std::process::exit(1); + for files in generate_cxxqt_cpp_files(&self.rust_sources, &generated_header_dir) { + 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); } } - // To support multiple QML elements with the same import URI, qmltyperegistrar must be run - // only once for each QML module (URI). So, collect the metadata for all QML elements within - // each module, regardless of which Rust QObject they are from. - let mut qml_modules = HashMap::<(String, usize, usize), Vec>::new(); - let mut cc_builder_whole_archive_files_added = false; // Run moc on C++ headers with Q_OBJECT macro for qobject_header in self.qobject_headers { - let uris = qobject_header - .qml_metadata - .iter() - .map(|qml_metadata| qml_metadata.uri.as_str()); - let moc_products = qtbuild.moc(&qobject_header.path, uris); + let moc_products = qtbuild.moc(&qobject_header, None); self.cc_builder.file(moc_products.cpp); - for qml_metadata in qobject_header.qml_metadata { - self.cc_builder.define("QT_STATICPLUGIN", None); - qml_modules - .entry(( - qml_metadata.uri.clone(), - qml_metadata.version_major, - qml_metadata.version_minor, - )) - .or_default() - .push(moc_products.metatypes_json.clone()); - } } - for ((uri, version_major, version_minor), paths) in qml_modules { - let qml_type_registration_files = - qtbuild.register_qml_types(&paths, version_major, version_minor, &uri); + + let mut cc_builder_whole_archive_files_added = false; + + // Bridges for QML modules are handled separately because + // the metatypes_json generated by moc needs to be passed to qmltyperegistrar + for qml_module in self.qml_modules { + let mut qml_metatypes_json = Vec::new(); + + for files in generate_cxxqt_cpp_files(&qml_module.rust_files, &generated_header_dir) { + self.cc_builder.file(files.plain_cpp); + 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)); + self.cc_builder.file(moc_products.cpp); + qml_metatypes_json.push(moc_products.metatypes_json); + } + } + + let qml_type_registration_files = qtbuild.register_qml_types( + &qml_metatypes_json, + qml_module.version_major, + qml_module.version_minor, + &qml_module.uri, + ); self.cc_builder .file(qml_type_registration_files.qmltyperegistrar); self.cc_builder.file(qml_type_registration_files.plugin); cc_builder_whole_archive.file(qml_type_registration_files.plugin_init); + self.cc_builder.define("QT_STATICPLUGIN", None); cc_builder_whole_archive_files_added = true; } + for qrc_file in self.qrc_files { cc_builder_whole_archive.file(qtbuild.qrc(&qrc_file)); cc_builder_whole_archive_files_added = true; diff --git a/crates/cxx-qt-gen/src/generator/cpp/qobject.rs b/crates/cxx-qt-gen/src/generator/cpp/qobject.rs index cc9dbf4c0..636b1f47a 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/qobject.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/qobject.rs @@ -214,7 +214,7 @@ mod tests { #[cxx_qt::bridge(namespace = "cxx_qt")] mod ffi { extern "RustQt" { - #[cxx_qt::qobject(qml_uri = "com.kdab", qml_version = "1.0", qml_name = "MyQmlElement")] + #[cxx_qt::qobject(qml_element = "MyQmlElement")] type MyNamedObject = super::MyNamedObjectRust; } } @@ -240,7 +240,7 @@ mod tests { #[cxx_qt::bridge(namespace = "cxx_qt")] mod ffi { extern "RustQt" { - #[cxx_qt::qobject(qml_uri = "com.kdab", qml_version = "1.0", qml_singleton)] + #[cxx_qt::qobject(qml_element, qml_singleton)] type MyObject = super::MyObjectRust; } } @@ -267,7 +267,7 @@ mod tests { #[cxx_qt::bridge(namespace = "cxx_qt")] mod ffi { extern "RustQt" { - #[cxx_qt::qobject(qml_uri = "com.kdab", qml_version = "1.0", qml_uncreatable)] + #[cxx_qt::qobject(qml_element, qml_uncreatable)] type MyObject = super::MyObjectRust; } } diff --git a/crates/cxx-qt-gen/src/generator/rust/qobject.rs b/crates/cxx-qt-gen/src/generator/rust/qobject.rs index 43514c218..488024510 100644 --- a/crates/cxx-qt-gen/src/generator/rust/qobject.rs +++ b/crates/cxx-qt-gen/src/generator/rust/qobject.rs @@ -259,7 +259,7 @@ mod tests { #[cxx_qt::bridge(namespace = "cxx_qt")] mod ffi { extern "RustQt" { - #[cxx_qt::qobject(qml_uri = "com.kdab", qml_version = "1.0", qml_singleton)] + #[cxx_qt::qobject(qml_element, qml_singleton)] type MyObject = super::MyObjectRust; } } diff --git a/crates/cxx-qt-gen/src/parser/qobject.rs b/crates/cxx-qt-gen/src/parser/qobject.rs index 4f41b2424..cb812ec2f 100644 --- a/crates/cxx-qt-gen/src/parser/qobject.rs +++ b/crates/cxx-qt-gen/src/parser/qobject.rs @@ -19,10 +19,7 @@ use syn::{Attribute, Error, Ident, ImplItem, Item, ItemImpl, LitStr, Result}; /// Metadata for registering QML element #[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct QmlElementMetadata { - pub uri: String, pub name: String, - pub version_major: usize, - pub version_minor: usize, pub uncreatable: bool, pub singleton: bool, } @@ -124,63 +121,22 @@ impl ParsedQObject { &qobject_ty.attrs[attr_index], AttributeDefault::Some(|span| LitStr::new("", span)), )?; - let qml_uri = attrs_map.get("e::format_ident!("qml_uri")); - let qml_version = attrs_map.get("e::format_ident!("qml_version")); - let qml_name = attrs_map.get("e::format_ident!("qml_name")); - let qml_uncreatable = attrs_map.get("e::format_ident!("qml_uncreatable")); - let qml_singleton = attrs_map.get("e::format_ident!("qml_singleton")); - match (qml_uri, qml_version) { - (Some(qml_uri), Some(qml_version)) => { - let qml_version = qml_version.value(); - let version_parts: Vec<_> = qml_version.split('.').collect(); - let version_major = version_parts[0] - .parse() - .expect("Could not parse major version from qml_version"); - let version_minor = version_parts.get(1).unwrap_or(&"0").parse().unwrap_or(0); - - let name = match qml_name { - Some(qml_name) => qml_name.value(), - None => qobject_ty.ident_left.to_string(), + match attrs_map.get("e::format_ident!("qml_element")) { + Some(qml_element) => { + let name = if qml_element.value().is_empty() { + qobject_ty.ident_left.to_string() + } else { + qml_element.value() }; - + let qml_uncreatable = attrs_map.get("e::format_ident!("qml_uncreatable")); + let qml_singleton = attrs_map.get("e::format_ident!("qml_singleton")); Ok(Some(QmlElementMetadata { - uri: qml_uri.value(), name, - version_major, - version_minor, uncreatable: qml_uncreatable.is_some(), singleton: qml_singleton.is_some(), })) } - (Some(uri), None) => Err(Error::new( - uri.span(), - "qml_uri specified but no qml_version specified", - )), - (None, Some(version)) => Err(Error::new( - version.span(), - "qml_version specified but no qml_uri specified", - )), - (None, None) => { - if let Some(qml_name) = qml_name { - return Err(Error::new( - qml_name.span(), - "qml_name specified but qml_uri and qml_version unspecified", - )); - } - if let Some(qml_uncreatable) = qml_uncreatable { - return Err(Error::new( - qml_uncreatable.span(), - "qml_uncreatable specified but qml_uri and qml_version unspecified", - )); - } - if let Some(qml_singleton) = qml_singleton { - return Err(Error::new( - qml_singleton.span(), - "qml_singleton specified but qml_uri and qml_version unspecified", - )); - } - Ok(None) - } + None => Ok(None), } } @@ -394,17 +350,14 @@ pub mod tests { #[test] fn test_qml_metadata() { let item: ForeignTypeIdentAlias = parse_quote! { - #[cxx_qt::qobject(qml_uri = "foo.bar", qml_version = "1.0")] + #[cxx_qt::qobject(qml_element)] type MyObject = super::MyObjectRust; }; let qobject = ParsedQObject::from_foreign_item_type(&item, 0).unwrap(); assert_eq!( qobject.qml_metadata, Some(QmlElementMetadata { - uri: "foo.bar".to_owned(), - name: "MyObject".to_owned(), - version_major: 1, - version_minor: 0, + name: "MyObject".to_string(), uncreatable: false, singleton: false, }) @@ -414,17 +367,14 @@ pub mod tests { #[test] fn test_qml_metadata_named() { let item: ForeignTypeIdentAlias = parse_quote! { - #[cxx_qt::qobject(qml_uri = "foo.bar", qml_version = "1", qml_name = "MyQmlElement")] - type MyNamedObject = super::MyObjectRust; + #[cxx_qt::qobject(qml_element = "OtherName")] + type MyObject = super::MyObjectRust; }; let qobject = ParsedQObject::from_foreign_item_type(&item, 0).unwrap(); assert_eq!( qobject.qml_metadata, Some(QmlElementMetadata { - uri: "foo.bar".to_owned(), - name: "MyQmlElement".to_owned(), - version_major: 1, - version_minor: 0, + name: "OtherName".to_string(), uncreatable: false, singleton: false, }) @@ -434,17 +384,14 @@ pub mod tests { #[test] fn test_qml_metadata_singleton() { let item: ForeignTypeIdentAlias = parse_quote! { - #[cxx_qt::qobject(qml_uri = "foo.bar", qml_version = "1", qml_singleton)] + #[cxx_qt::qobject(qml_element, qml_singleton)] type MyObject = super::MyObjectRust; }; let qobject = ParsedQObject::from_foreign_item_type(&item, 0).unwrap(); assert_eq!( qobject.qml_metadata, Some(QmlElementMetadata { - uri: "foo.bar".to_owned(), - name: "MyObject".to_owned(), - version_major: 1, - version_minor: 0, + name: "MyObject".to_string(), uncreatable: false, singleton: true, }) @@ -454,47 +401,17 @@ pub mod tests { #[test] fn test_qml_metadata_uncreatable() { let item: ForeignTypeIdentAlias = parse_quote! { - #[cxx_qt::qobject(qml_uri = "foo.bar", qml_version = "1", qml_uncreatable)] + #[cxx_qt::qobject(qml_element, qml_uncreatable)] type MyObject = super::MyObjectRust; }; let qobject = ParsedQObject::from_foreign_item_type(&item, 0).unwrap(); assert_eq!( qobject.qml_metadata, Some(QmlElementMetadata { - uri: "foo.bar".to_owned(), - name: "MyObject".to_owned(), - version_major: 1, - version_minor: 0, + name: "MyObject".to_string(), uncreatable: true, singleton: false, }) ); } - - #[test] - fn test_qml_metadata_no_version() { - let item: ForeignTypeIdentAlias = parse_quote! { - #[cxx_qt::qobject(qml_uri = "foo.bar")] - type MyObject = super::MyObjectRust; - }; - assert!(ParsedQObject::from_foreign_item_type(&item, 0).is_err()); - } - - #[test] - fn test_qml_metadata_no_uri() { - let item: ForeignTypeIdentAlias = parse_quote! { - #[cxx_qt::qobject(qml_version = "1.0")] - type MyObject = super::MyObjectRust; - }; - assert!(ParsedQObject::from_foreign_item_type(&item, 0).is_err()); - } - - #[test] - fn test_qml_metadata_only_name_no_version_no_uri() { - let item: ForeignTypeIdentAlias = parse_quote! { - #[cxx_qt::qobject(qml_name = "MyQmlElement")] - type MyObject = super::MyObjectRust; - }; - assert!(ParsedQObject::from_foreign_item_type(&item, 0).is_err()); - } } diff --git a/crates/qt-build-utils/src/lib.rs b/crates/qt-build-utils/src/lib.rs index aab95a0f8..4b0f39787 100644 --- a/crates/qt-build-utils/src/lib.rs +++ b/crates/qt-build-utils/src/lib.rs @@ -521,12 +521,8 @@ 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_types](Self::register_qml_types). /// - /// * uris - An iterator of uri's that the moc compiler is working on. This is required because some moc compilers require this to be specified. - pub fn moc<'a>( - &mut self, - input_file: impl AsRef, - uris: impl Iterator, - ) -> MocProducts { + /// * 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 { if self.moc_executable.is_none() { self.moc_executable = Some(self.get_qt_tool("moc").expect("Could not find moc")); } @@ -545,16 +541,13 @@ impl QtBuild { include_args += &format!("-I {} ", include_path.display()); } - let mut uri_args = String::new(); - for uri in uris { - uri_args += &format!("-Muri={} ", uri); + let mut cmd = Command::new(self.moc_executable.as_ref().unwrap()); + + if let Some(uri) = uri { + cmd.arg(&format!("-Muri={} ", uri)); } - let mut cmd = Command::new(self.moc_executable.as_ref().unwrap()); cmd.args(include_args.trim_end().split(' ')); - if !uri_args.is_empty() { - cmd.args(uri_args.trim_end().split(' ')); - } cmd.arg(input_path.to_str().unwrap()) .arg("-o") .arg(output_path.to_str().unwrap()) @@ -658,7 +651,7 @@ public: "# ) .unwrap(); - self.moc(&qml_plugin_cpp_path, std::iter::once(import_name)); + self.moc(&qml_plugin_cpp_path, Some(import_name)); let qml_plugin_init_path = PathBuf::from(format!("{out_dir}/{plugin_class_name}_init.cpp")); let mut qml_plugin_init = File::create(&qml_plugin_init_path).unwrap(); diff --git a/examples/cargo_without_cmake/build.rs b/examples/cargo_without_cmake/build.rs index cfea8b066..178a6be9d 100644 --- a/examples/cargo_without_cmake/build.rs +++ b/examples/cargo_without_cmake/build.rs @@ -14,8 +14,7 @@ fn main() { // - Qt Qml is linked by enabling the qt_qml Cargo feature (default). // - Qt Qml requires linking Qt Network on macOS .qt_module("Network") - // Generate C++ from the `#[cxx_qt::bridge]` module - .file("src/cxxqt_object.rs") + .qml_module("com.kdab.cxx_qt.demo", 1, 0, &["src/cxxqt_object.rs"]) // Generate C++ code from the .qrc file with the rcc tool // https://doc.qt.io/qt-6/resources.html .qrc("qml/qml.qrc") diff --git a/examples/demo_threading/rust/build.rs b/examples/demo_threading/rust/build.rs index 5076cfd0c..e99d9a387 100644 --- a/examples/demo_threading/rust/build.rs +++ b/examples/demo_threading/rust/build.rs @@ -5,5 +5,7 @@ use cxx_qt_build::CxxQtBuilder; fn main() { - CxxQtBuilder::new().file("src/lib.rs").build(); + CxxQtBuilder::new() + .qml_module("com.kdab.energy", 1, 0, &["src/lib.rs"]) + .build(); } diff --git a/examples/demo_threading/rust/src/lib.rs b/examples/demo_threading/rust/src/lib.rs index a969eafe8..5d81b3db1 100644 --- a/examples/demo_threading/rust/src/lib.rs +++ b/examples/demo_threading/rust/src/lib.rs @@ -18,7 +18,7 @@ mod qobject { } extern "RustQt" { - #[cxx_qt::qobject(qml_uri = "com.kdab.energy", qml_version = "1.0")] + #[cxx_qt::qobject(qml_element)] #[qproperty(f64, average_use)] #[qproperty(u32, sensors)] #[qproperty(f64, total_use)] diff --git a/examples/qml_features/rust/build.rs b/examples/qml_features/rust/build.rs index d305d5763..15683b8ea 100644 --- a/examples/qml_features/rust/build.rs +++ b/examples/qml_features/rust/build.rs @@ -9,19 +9,26 @@ use cxx_qt_build::CxxQtBuilder; fn main() { CxxQtBuilder::new() - .file("src/containers.rs") - .file("src/custom_base_class.rs") - .file("src/custom_parent_class.rs") - .file("src/invokables.rs") - .file("src/multiple_qobjects.rs") - .file("src/nested_qobjects.rs") - .file("src/serialisation.rs") - .file("src/signals.rs") - .file("src/singleton.rs") - .file("src/properties.rs") - .file("src/threading.rs") - .file("src/types.rs") - .file("src/uncreatable.rs") + .qml_module( + "com.kdab.cxx_qt.demo", + 1, + 0, + &[ + "src/containers.rs", + "src/custom_base_class.rs", + "src/custom_parent_class.rs", + "src/invokables.rs", + "src/multiple_qobjects.rs", + "src/nested_qobjects.rs", + "src/serialisation.rs", + "src/signals.rs", + "src/singleton.rs", + "src/properties.rs", + "src/threading.rs", + "src/types.rs", + "src/uncreatable.rs", + ], + ) // custom_object.cpp/h need to be handled here rather than CMakeLists.txt, // otherwise linking cargo tests fails because the symbols from those files are not found. .cc_builder(|cc| { diff --git a/examples/qml_features/rust/src/containers.rs b/examples/qml_features/rust/src/containers.rs index 9f89be82a..ad620990e 100644 --- a/examples/qml_features/rust/src/containers.rs +++ b/examples/qml_features/rust/src/containers.rs @@ -33,7 +33,7 @@ pub mod qobject { } unsafe extern "RustQt" { - #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0")] + #[cxx_qt::qobject(qml_element)] #[qproperty(QString, string_hash)] #[qproperty(QString, string_list)] #[qproperty(QString, string_map)] diff --git a/examples/qml_features/rust/src/custom_base_class.rs b/examples/qml_features/rust/src/custom_base_class.rs index 9af29ea04..893084129 100644 --- a/examples/qml_features/rust/src/custom_base_class.rs +++ b/examples/qml_features/rust/src/custom_base_class.rs @@ -34,11 +34,7 @@ pub mod qobject { // ANCHOR: book_inherit_qalm // ANCHOR: book_qobject_base extern "RustQt" { - #[cxx_qt::qobject( - base = "QAbstractListModel", - qml_uri = "com.kdab.cxx_qt.demo", - qml_version = "1.0" - )] + #[cxx_qt::qobject(base = "QAbstractListModel", qml_element)] type CustomBaseClass = super::CustomBaseClassRust; } // ANCHOR_END: book_qobject_base diff --git a/examples/qml_features/rust/src/invokables.rs b/examples/qml_features/rust/src/invokables.rs index 69eedd3cc..618751eb2 100644 --- a/examples/qml_features/rust/src/invokables.rs +++ b/examples/qml_features/rust/src/invokables.rs @@ -16,7 +16,7 @@ pub mod qobject { } unsafe extern "RustQt" { - #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0")] + #[cxx_qt::qobject(qml_element)] type RustInvokables = super::RustInvokablesRust; } diff --git a/examples/qml_features/rust/src/multiple_qobjects.rs b/examples/qml_features/rust/src/multiple_qobjects.rs index ed011b3f1..ea1729735 100644 --- a/examples/qml_features/rust/src/multiple_qobjects.rs +++ b/examples/qml_features/rust/src/multiple_qobjects.rs @@ -18,7 +18,7 @@ pub mod qobject { } extern "RustQt" { - #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0")] + #[cxx_qt::qobject(qml_element)] #[qproperty(i32, counter)] #[qproperty(QColor, color)] type FirstObject = super::FirstObjectRust; @@ -44,7 +44,7 @@ pub mod qobject { } extern "RustQt" { - #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0")] + #[cxx_qt::qobject(qml_element)] #[qproperty(i32, counter)] #[qproperty(QUrl, url)] type SecondObject = super::SecondObjectRust; diff --git a/examples/qml_features/rust/src/nested_qobjects.rs b/examples/qml_features/rust/src/nested_qobjects.rs index 4fad35970..2ab825173 100644 --- a/examples/qml_features/rust/src/nested_qobjects.rs +++ b/examples/qml_features/rust/src/nested_qobjects.rs @@ -11,7 +11,7 @@ pub mod qobject { // ANCHOR: book_extern_block extern "RustQt" { - #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0")] + #[cxx_qt::qobject(qml_element)] #[qproperty(i32, counter)] type InnerObject = super::InnerObjectRust; } @@ -24,7 +24,7 @@ pub mod qobject { } extern "RustQt" { - #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0")] + #[cxx_qt::qobject(qml_element)] #[qproperty(*mut InnerObject, inner)] type OuterObject = super::OuterObjectRust; diff --git a/examples/qml_features/rust/src/properties.rs b/examples/qml_features/rust/src/properties.rs index 6ebd1e862..348d5d5c9 100644 --- a/examples/qml_features/rust/src/properties.rs +++ b/examples/qml_features/rust/src/properties.rs @@ -19,7 +19,7 @@ pub mod qobject { unsafe extern "RustQt" { // ANCHOR: book_properties_struct - #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0")] + #[cxx_qt::qobject(qml_element)] #[qproperty(bool, connected)] #[qproperty(QUrl, connected_url)] #[qproperty(QUrl, previous_connected_url)] diff --git a/examples/qml_features/rust/src/serialisation.rs b/examples/qml_features/rust/src/serialisation.rs index bd175adc5..f0e47fe7e 100644 --- a/examples/qml_features/rust/src/serialisation.rs +++ b/examples/qml_features/rust/src/serialisation.rs @@ -36,7 +36,7 @@ pub mod qobject { } unsafe extern "RustQt" { - #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0")] + #[cxx_qt::qobject(qml_element)] #[qproperty(i32, number)] #[qproperty(QString, string)] type Serialisation = super::SerialisationRust; diff --git a/examples/qml_features/rust/src/signals.rs b/examples/qml_features/rust/src/signals.rs index 85a311501..1896ff426 100644 --- a/examples/qml_features/rust/src/signals.rs +++ b/examples/qml_features/rust/src/signals.rs @@ -36,7 +36,7 @@ pub mod qobject { unsafe extern "RustQt" { // ANCHOR: book_signals_struct - #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0")] + #[cxx_qt::qobject(qml_element)] #[qproperty(bool, logging_enabled)] type RustSignals = super::RustSignalsRust; } diff --git a/examples/qml_features/rust/src/singleton.rs b/examples/qml_features/rust/src/singleton.rs index 124dc5ccf..01052d577 100644 --- a/examples/qml_features/rust/src/singleton.rs +++ b/examples/qml_features/rust/src/singleton.rs @@ -10,7 +10,7 @@ #[cxx_qt::bridge(cxx_file_stem = "rust_singleton")] pub mod qobject { unsafe extern "RustQt" { - #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0", qml_singleton)] + #[cxx_qt::qobject(qml_element, qml_singleton)] #[qproperty(i32, persistent_value)] type RustSingleton = super::RustSingletonRust; diff --git a/examples/qml_features/rust/src/threading.rs b/examples/qml_features/rust/src/threading.rs index ea52f4bec..b03a660cb 100644 --- a/examples/qml_features/rust/src/threading.rs +++ b/examples/qml_features/rust/src/threading.rs @@ -22,7 +22,7 @@ pub mod qobject { } extern "RustQt" { - #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0")] + #[cxx_qt::qobject(qml_element)] #[qproperty(QString, title)] #[qproperty(QUrl, url)] type ThreadingWebsite = super::ThreadingWebsiteRust; diff --git a/examples/qml_features/rust/src/types.rs b/examples/qml_features/rust/src/types.rs index 444d2a0b7..b753d77df 100644 --- a/examples/qml_features/rust/src/types.rs +++ b/examples/qml_features/rust/src/types.rs @@ -74,7 +74,7 @@ pub mod ffi { } unsafe extern "RustQt" { - #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0")] + #[cxx_qt::qobject(qml_element)] #[qproperty(bool, boolean)] #[qproperty(QPointF, point)] #[qproperty(QUrl, url)] diff --git a/examples/qml_features/rust/src/uncreatable.rs b/examples/qml_features/rust/src/uncreatable.rs index c36d4c9a3..33ab655a1 100644 --- a/examples/qml_features/rust/src/uncreatable.rs +++ b/examples/qml_features/rust/src/uncreatable.rs @@ -10,7 +10,7 @@ #[cxx_qt::bridge(cxx_file_stem = "rust_uncreatable")] pub mod ffi { extern "RustQt" { - #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0", qml_uncreatable)] + #[cxx_qt::qobject(qml_element, qml_uncreatable)] #[qproperty(i32, value)] type RustUncreatable = super::RustUncreatableRust; } diff --git a/examples/qml_minimal/rust/build.rs b/examples/qml_minimal/rust/build.rs index deaf8075f..7130c28f9 100644 --- a/examples/qml_minimal/rust/build.rs +++ b/examples/qml_minimal/rust/build.rs @@ -8,6 +8,8 @@ use cxx_qt_build::CxxQtBuilder; fn main() { - CxxQtBuilder::new().file("src/cxxqt_object.rs").build(); + CxxQtBuilder::new() + .qml_module("com.kdab.cxx_qt.demo", 1, 0, &["src/cxxqt_object.rs"]) + .build(); } // ANCHOR_END: book_build_rs diff --git a/examples/qml_minimal/rust/src/cxxqt_object.rs b/examples/qml_minimal/rust/src/cxxqt_object.rs index 32cb8506d..38f4649de 100644 --- a/examples/qml_minimal/rust/src/cxxqt_object.rs +++ b/examples/qml_minimal/rust/src/cxxqt_object.rs @@ -23,7 +23,7 @@ pub mod qobject { // ANCHOR: book_rustobj_struct_signature unsafe extern "RustQt" { - #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0")] + #[cxx_qt::qobject(qml_element)] #[qproperty(i32, number)] #[qproperty(QString, string)] type MyObject = super::MyObjectRust;