@@ -35,6 +35,7 @@ use qml_modules::OwningQmlModule;
3535pub use qml_modules:: QmlModule ;
3636
3737pub use qt_build_utils:: MocArguments ;
38+ use qt_build_utils:: MocProducts ;
3839use quote:: ToTokens ;
3940use semver:: Version ;
4041use std:: {
@@ -324,15 +325,6 @@ fn qml_module_init_key(module_uri: &str) -> String {
324325 format ! ( "qml_module_{}" , module_name_from_uri( module_uri) )
325326}
326327
327- fn panic_duplicate_file_and_qml_module (
328- path : impl AsRef < Path > ,
329- uri : & str ,
330- version_major : usize ,
331- version_minor : usize ,
332- ) {
333- 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( ) ) ;
334- }
335-
336328/// Run cxx-qt's C++ code generator on Rust modules marked with the `cxx_qt::bridge` macro, compile
337329/// the code, and link to Qt. This is the complement of the `cxx_qt::bridge` macro, which the Rust
338330/// compiler uses to generate the corresponding Rust code. No dependencies besides Qt, a C++17 compiler,
@@ -372,7 +364,7 @@ pub struct CxxQtBuilder {
372364 qrc_files : Vec < PathBuf > ,
373365 init_files : Vec < qt_build_utils:: Initializer > ,
374366 qt_modules : HashSet < String > ,
375- qml_modules : Vec < OwningQmlModule > ,
367+ qml_module : Option < OwningQmlModule > ,
376368 cc_builder : cc:: Build ,
377369 include_prefix : String ,
378370 crate_include_root : Option < String > ,
@@ -413,7 +405,7 @@ impl CxxQtBuilder {
413405 qrc_files : vec ! [ ] ,
414406 init_files : vec ! [ ] ,
415407 qt_modules,
416- qml_modules : vec ! [ ] ,
408+ qml_module : None ,
417409 cc_builder : cc:: Build :: new ( ) ,
418410 include_prefix : crate_name ( ) ,
419411 crate_include_root : Some ( String :: new ( ) ) ,
@@ -425,21 +417,24 @@ impl CxxQtBuilder {
425417 /// Relative paths are treated as relative to the path of your crate's Cargo.toml file
426418 pub fn file ( mut self , rust_source : impl AsRef < Path > ) -> Self {
427419 let rust_source = rust_source. as_ref ( ) . to_path_buf ( ) ;
428- for qml_module in & self . qml_modules {
429- if qml_module. rust_files . contains ( & rust_source) {
430- panic_duplicate_file_and_qml_module (
431- & rust_source,
432- & qml_module. uri ,
433- qml_module. version_major ,
434- qml_module. version_minor ,
435- ) ;
436- }
437- }
438420 println ! ( "cargo::rerun-if-changed={}" , rust_source. display( ) ) ;
439421 self . rust_sources . push ( rust_source) ;
440422 self
441423 }
442424
425+ /// Specify multiple rust file paths to parse through the cxx-qt marco.
426+ ///
427+ /// See also: [Self::file]
428+ pub fn files ( mut self , rust_source : impl IntoIterator < Item = impl AsRef < Path > > ) -> Self {
429+ let rust_sources = rust_source. into_iter ( ) . map ( |p| {
430+ let p = p. as_ref ( ) . to_path_buf ( ) ;
431+ println ! ( "cargo::rerun-if-changed={}" , p. display( ) ) ;
432+ p
433+ } ) ;
434+ self . rust_sources . extend ( rust_sources) ;
435+ self
436+ }
437+
443438 #[ doc( hidden) ]
444439 pub fn initializer ( mut self , initializer : qt_build_utils:: Initializer ) -> Self {
445440 if let Some ( ref init_file) = initializer. file {
@@ -552,30 +547,29 @@ impl CxxQtBuilder {
552547 /// use cxx_qt_build::{CxxQtBuilder, QmlModule};
553548 ///
554549 /// CxxQtBuilder::new()
555- /// .qml_module(QmlModule {
550+ /// .qml_module(QmlModule::<&str, &str> {
556551 /// uri: "com.kdab.cxx_qt.demo",
557- /// rust_files: &["src/cxxqt_object.rs"],
558552 /// qml_files: &["qml/main.qml"],
559553 /// ..Default::default()
560554 /// })
555+ /// .files(["src/cxxqt_object.rs"])
561556 /// .build();
562557 /// ```
563558 pub fn qml_module < A : AsRef < Path > , B : AsRef < Path > > (
564559 mut self ,
565560 qml_module : QmlModule < A , B > ,
566561 ) -> CxxQtBuilder {
567- let qml_module = OwningQmlModule :: from ( qml_module) ;
568- for path in & qml_module. rust_files {
569- if self . rust_sources . contains ( path) {
570- panic_duplicate_file_and_qml_module (
571- path,
572- & qml_module. uri ,
573- qml_module. version_major ,
574- qml_module. version_minor ,
575- ) ;
576- }
562+ if let Some ( module) = & self . qml_module {
563+ panic ! (
564+ "Duplicate QML module registration!\n \
565+ The QML module with URI '{}' (version {}.{}) was already registered.\n \
566+ Only one QML module can be registered per crate.",
567+ module. uri, module. version_major, module. version_minor
568+ ) ;
577569 }
578- self . qml_modules . push ( qml_module) ;
570+
571+ let qml_module = OwningQmlModule :: from ( qml_module) ;
572+ self . qml_module = Some ( qml_module) ;
579573 self
580574 }
581575
@@ -728,19 +722,42 @@ impl CxxQtBuilder {
728722 }
729723 }
730724
731- fn moc_qobject_headers ( & mut self , qtbuild : & mut qt_build_utils:: QtBuild ) {
732- for QObjectHeaderOpts {
725+ /// Returns the list of Moc products. Especially the qml_metatypes.json files are needed for
726+ /// the QML module generation later
727+ fn moc_qobject_headers ( & mut self , qtbuild : & mut qt_build_utils:: QtBuild ) -> Vec < MocProducts > {
728+ self . qobject_headers . iter ( ) . map ( |QObjectHeaderOpts {
733729 path,
734730 moc_arguments,
735- } in & self . qobject_headers
731+ } |
736732 {
737- let moc_products = qtbuild. moc ( ) . compile ( path, moc_arguments. clone ( ) ) ;
733+
734+ let mut moc_arguments = moc_arguments. clone ( ) ;
735+ if let Some ( qml_module) = & self . qml_module {
736+ // Ensure that the generated QObject header is in the include path
737+ // so that qmltyperegistar can include them later
738+ if let Some ( dir) = path. parent ( ) {
739+ self . cc_builder . include ( dir) ;
740+ }
741+
742+ if let Some ( uri) = moc_arguments. get_uri ( ) {
743+ if uri != qml_module. uri {
744+ panic ! (
745+ "URI for QObject header {path} ({uri}) conflicts with QML Module URI ({qml_module_uri})" ,
746+ path = path. display( ) ,
747+ qml_module_uri = qml_module. uri) ;
748+ }
749+ }
750+ moc_arguments = moc_arguments. uri ( qml_module. uri . clone ( ) ) ;
751+ }
752+ let moc_products = qtbuild. moc ( ) . compile ( path, moc_arguments) ;
738753 // Include the moc folder
739754 if let Some ( dir) = moc_products. cpp . parent ( ) {
740755 self . cc_builder . include ( dir) ;
741756 }
742- self . cc_builder . file ( moc_products. cpp ) ;
743- }
757+ self . cc_builder . file ( moc_products. cpp . clone ( ) ) ;
758+ moc_products
759+ } )
760+ . collect ( )
744761 }
745762
746763 fn generate_cpp_files_from_cxxqt_bridges (
@@ -791,19 +808,15 @@ impl CxxQtBuilder {
791808 fn build_qml_modules (
792809 & mut self ,
793810 qtbuild : & mut qt_build_utils:: QtBuild ,
794- generated_header_dir : impl AsRef < Path > ,
795- header_prefix : & str ,
811+ moc_products : & [ MocProducts ] ,
796812 ) -> Vec < qt_build_utils:: Initializer > {
797813 let mut initializer_functions = Vec :: new ( ) ;
798814 // Extract qml_modules out of self so we don't have to hold onto `self` for the duration of
799815 // the loop.
800- let qml_modules: Vec < _ > = self . qml_modules . drain ( ..) . collect ( ) ;
801- for qml_module in qml_modules {
816+ if let Some ( qml_module) = self . qml_module . take ( ) {
802817 dir:: clean ( dir:: module_target ( & qml_module. uri ) )
803818 . expect ( "Failed to clean qml module export directory!" ) ;
804819
805- let mut qml_metatypes_json = Vec :: new ( ) ;
806-
807820 // Check that all rust files are within the same directory
808821 //
809822 // Note we need to do this as moc generates an inputFile which only
@@ -813,8 +826,8 @@ impl CxxQtBuilder {
813826 // This can also be observed when using qt_add_qml_module, if a class
814827 // has a QML_ELEMENT the file must be in the same directory as the
815828 // CMakeLists and cannot be a relative path to a sub directory.
816- let dirs = qml_module
817- . rust_files
829+ let dirs = self
830+ . rust_sources
818831 . iter ( )
819832 . map ( |file| {
820833 if let Some ( parent) = file. parent ( ) {
@@ -840,34 +853,10 @@ impl CxxQtBuilder {
840853 let cc_builder = & mut self . cc_builder ;
841854 qtbuild. cargo_link_libraries ( cc_builder) ;
842855
843- let mut moc_include_paths = HashSet :: new ( ) ;
844- for files in generate_cxxqt_cpp_files (
845- & qml_module. rust_files ,
846- & generated_header_dir,
847- header_prefix,
848- ) {
849- cc_builder. file ( files. plain_cpp ) ;
850- if let ( Some ( qobject) , Some ( qobject_header) ) = ( files. qobject , files. qobject_header )
851- {
852- // Ensure that the generated QObject header is in the include path
853- // so that qmltyperegistar can include them later
854- if let Some ( dir) = qobject_header. parent ( ) {
855- moc_include_paths. insert ( dir. to_path_buf ( ) ) ;
856- }
857-
858- cc_builder. file ( & qobject) ;
859- let moc_products = qtbuild. moc ( ) . compile (
860- qobject_header,
861- MocArguments :: default ( ) . uri ( qml_module. uri . clone ( ) ) ,
862- ) ;
863- // Include the moc folder
864- if let Some ( dir) = moc_products. cpp . parent ( ) {
865- moc_include_paths. insert ( dir. to_path_buf ( ) ) ;
866- }
867- cc_builder. file ( moc_products. cpp ) ;
868- qml_metatypes_json. push ( moc_products. metatypes_json ) ;
869- }
870- }
856+ let qml_metatypes_json: Vec < PathBuf > = moc_products
857+ . iter ( )
858+ . map ( |products| products. metatypes_json . clone ( ) )
859+ . collect ( ) ;
871860
872861 let qml_module_registration_files = qtbuild. register_qml_module (
873862 & qml_metatypes_json,
@@ -895,11 +884,6 @@ impl CxxQtBuilder {
895884 // Add any include paths the qml module registration needs
896885 // this is most likely the moc folder for the plugin
897886 if let Some ( include_path) = qml_module_registration_files. include_path {
898- moc_include_paths. insert ( include_path) ;
899- }
900-
901- // Ensure that all include paths from moc folders that are required
902- for include_path in & moc_include_paths {
903887 cc_builder. include ( include_path) ;
904888 }
905889
@@ -910,12 +894,11 @@ impl CxxQtBuilder {
910894 cc_builder. define ( "QT_STATICPLUGIN" , None ) ;
911895
912896 // If any of the files inside the qml module change, then trigger a rerun
913- for path in qml_module. qml_files . iter ( ) . chain (
914- qml_module
915- . rust_files
916- . iter ( )
917- . chain ( qml_module. qrc_files . iter ( ) ) ,
918- ) {
897+ for path in qml_module
898+ . qml_files
899+ . iter ( )
900+ . chain ( qml_module. qrc_files . iter ( ) )
901+ {
919902 println ! ( "cargo::rerun-if-changed={}" , path. display( ) ) ;
920903 }
921904
@@ -1149,12 +1132,11 @@ extern "C" bool {init_fun}() {{
11491132 // Generate files
11501133 self . generate_cpp_files_from_cxxqt_bridges ( & header_root, & self . include_prefix . clone ( ) ) ;
11511134
1152- self . moc_qobject_headers ( & mut qtbuild) ;
1135+ let moc_products = self . moc_qobject_headers ( & mut qtbuild) ;
11531136
11541137 // Bridges for QML modules are handled separately because
11551138 // the metatypes_json generated by moc needs to be passed to qmltyperegistrar
1156- let module_initializers =
1157- self . build_qml_modules ( & mut qtbuild, & header_root, & self . include_prefix . clone ( ) ) ;
1139+ let module_initializers = self . build_qml_modules ( & mut qtbuild, & moc_products) ;
11581140
11591141 let qrc_files = self . generate_cpp_from_qrc_files ( & mut qtbuild) ;
11601142
0 commit comments