diff --git a/crates/cxx-qt-build/src/lib.rs b/crates/cxx-qt-build/src/lib.rs index 013903a80..9edc27829 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,17 @@ 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 } /// Run cxx-qt's C++ code generator on Rust modules marked with the `cxx_qt::bridge` macro, compile @@ -283,9 +285,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 +306,7 @@ impl CxxQtBuilder { qobject_headers: vec![], qrc_files: vec![], qt_modules, + qml_modules: vec![], cc_builder: cc::Build::new(), } } @@ -310,9 +314,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!("CXX-Qt bridge Rust file {} specified in QML module {} (version {}.{}), but also specified via CxxQtBridge::file. Bridge files must be specified via CxxQtBuilder::file or CxxQtBuilder::qml_module, but not both.", rust_source.display(), 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 +360,36 @@ 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 { + for path in rust_files { + if self.rust_sources.contains(&path.as_ref().to_path_buf()) { + 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.as_ref().display()); + } + } + self.qml_modules.push(QmlModule { + uri: uri.to_owned(), + version_major, + version_minor, + rust_files: rust_files + .iter() + .map(|p| p.as_ref().to_path_buf()) + .collect(), + }); + 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 +498,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;