Skip to content

Commit

Permalink
[WIP] register QML modules at build time
Browse files Browse the repository at this point in the history
This is a prerequisite for using qmlcachegen
KDAB#242
  • Loading branch information
Be-ing committed Aug 3, 2023
1 parent 3454e1d commit 1c5c906
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 24 deletions.
20 changes: 18 additions & 2 deletions crates/cxx-qt-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ struct QmlModule {
pub version_major: usize,
/// The .rs files with #[qml_element] attribute(s)
pub rust_files: Vec<PathBuf>,
/// .qml files included in the module
pub qml_files: Vec<PathBuf>,
/// Other QRC resources (such as images) included in the module
pub qrc_files: Vec<PathBuf>,
}

impl GeneratedCpp {
Expand Down Expand Up @@ -381,6 +385,8 @@ impl CxxQtBuilder {
version_major: usize,
version_minor: usize,
rust_files: &[impl AsRef<Path>],
qml_files: &[impl AsRef<Path>],
qrc_files: &[impl AsRef<Path>],
) -> Self {
let rust_files: Vec<PathBuf> = rust_files
.iter()
Expand All @@ -391,11 +397,15 @@ impl CxxQtBuilder {
panic_duplicate_file_and_qml_module(path, uri, version_major, version_minor);
}
}
let qml_files: Vec<PathBuf> = qml_files.iter().map(|p| p.as_ref().to_path_buf()).collect();
let qrc_files: Vec<PathBuf> = qrc_files.iter().map(|p| p.as_ref().to_path_buf()).collect();
self.qml_modules.push(QmlModule {
uri: uri.to_owned(),
version_major,
version_minor,
rust_files,
qml_files,
qrc_files,
});
self
}
Expand Down Expand Up @@ -532,6 +542,8 @@ impl CxxQtBuilder {

let mut cc_builder_whole_archive_files_added = false;

let lib_name = "cxx-qt-generated";

// 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 {
Expand All @@ -548,16 +560,20 @@ impl CxxQtBuilder {
}
}

let qml_type_registration_files = qtbuild.register_qml_types(
let qml_type_registration_files = qtbuild.register_qml_module(
&qml_metatypes_json,
&qml_module.qml_files,
&qml_module.qrc_files,
qml_module.version_major,
qml_module.version_minor,
&qml_module.uri,
lib_name,
);
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);
cc_builder_whole_archive.file(qml_type_registration_files.qrc);
self.cc_builder.define("QT_STATICPLUGIN", None);
cc_builder_whole_archive_files_added = true;
}
Expand Down Expand Up @@ -599,6 +615,6 @@ impl CxxQtBuilder {
if cc_builder_whole_archive_files_added {
cc_builder_whole_archive.compile("qt-static-initializers");
}
self.cc_builder.compile("cxx-qt-gen");
self.cc_builder.compile(lib_name);
}
}
56 changes: 54 additions & 2 deletions crates/qt-build-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ pub struct QmlTypeRegistrationFiles {
/// The compiled static library must be linked with [+whole-archive](https://doc.rust-lang.org/rustc/command-line-arguments.html#linking-modifiers-whole-archive)
/// or the linker will discard the generated static variables because they are not referenced from `main`.
pub plugin_init: PathBuf,
/// File generated by rcc for the QML plugin
pub qrc: PathBuf,
}

/// Helper for build.rs scripts using Qt
Expand Down Expand Up @@ -571,12 +573,15 @@ impl QtBuild {
}

/// Generate C++ files to automatically register a QML element at build time using the JSON output from [moc](Self::moc)
pub fn register_qml_types(
pub fn register_qml_module<P: AsRef<Path>>(
&mut self,
metatypes_json: &[impl AsRef<Path>],
qml_files: &[P],
qrc_files: &[P],
major_version: usize,
minor_version: usize,
import_name: &str,
library_name: &str,
) -> QmlTypeRegistrationFiles {
if self.qmltyperegistrar_executable.is_none() {
self.qmltyperegistrar_executable = Some(
Expand All @@ -585,9 +590,56 @@ impl QtBuild {
);
}

let qml_uri_dirs = import_name.replace('.', "/");

let out_dir = env::var("OUT_DIR").unwrap();
let qml_module_dir = format!("{out_dir}/qml_modules/{qml_uri_dirs}");
std::fs::create_dir_all(&qml_module_dir).expect("Could not create QML module directory");

let qml_uri_cpp_symbol_safe = import_name.replace('.', "_");
let typeinfo_path = format!("{qml_uri_cpp_symbol_safe}.qmltypes");
let plugin_class_name = format!("{qml_uri_cpp_symbol_safe}_plugin");

let mut qmldir =
File::create(format!("{qml_module_dir}/qmldir")).expect("Could not create qmldir file");
write!(
qmldir,
"module {import_name}
optional plugin {library_name}
classname {plugin_class_name}
typeinfo {typeinfo_path}
prefer :/qt/qml/{qml_uri_dirs}/
"
)
.expect("Could not write qmldir file");

let mut qml_files_qrc = String::new();
for file_path in qml_files.iter().chain(qrc_files.iter()) {
qml_files_qrc.push_str(&format!(
" <file alias=\"{}\">{}</file>\n",
file_path.as_ref().display(),
std::fs::canonicalize(file_path).unwrap().display()
));
}

let qrc_path = format!("{qml_module_dir}/qml_module_resources.qrc");
let mut qrc = File::create(&qrc_path).expect("Could not create qrc file");
write!(
qrc,
r#"<RCC>
<qresource prefix="/">
<file alias="/qt/qml/{qml_uri_dirs}">{qml_module_dir}</file>
</qresource>
<qresource prefix="/qt/qml/{qml_uri_dirs}">
{qml_files_qrc}
<file alias="qmldir">{qml_module_dir}/qmldir</file>
</qresource>
</RCC>
"#
)
.expect("Could note write qrc file");
let qrc_output = self.qrc(&qrc_path);

let output_path = PathBuf::from(&format!(
"{out_dir}/{qml_uri_cpp_symbol_safe}_qmltyperegistration.cpp"
));
Expand Down Expand Up @@ -620,7 +672,6 @@ impl QtBuild {
);
}

let plugin_class_name = format!("{qml_uri_cpp_symbol_safe}_plugin");
// This function is generated by qmltyperegistrar
let register_types_function = format!("qml_register_types_{qml_uri_cpp_symbol_safe}");

Expand Down Expand Up @@ -668,6 +719,7 @@ Q_IMPORT_PLUGIN({plugin_class_name});
qmltyperegistrar: output_path,
plugin: qml_plugin_cpp_path,
plugin_init: qml_plugin_init_path,
qrc: qrc_output,
}
}

Expand Down
12 changes: 8 additions & 4 deletions examples/cargo_without_cmake/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@ 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")
.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")
.qml_module(
"com.kdab.cxx_qt.demo",
1,
0,
&["src/cxxqt_object.rs"],
&["qml/main.qml"],
&[] as &[&str; 0],
)
.build();
}
// ANCHOR_END: book_cargo_executable_build_rs
2 changes: 1 addition & 1 deletion examples/cargo_without_cmake/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ fn main() {

// Load the QML path into the engine
if let Some(engine) = engine.as_mut() {
engine.load(&QUrl::from("qrc:/main.qml"));
engine.load(&QUrl::from("qrc:/qt/qml/com/kdab/cxx_qt/demo/qml/main.qml"));
}

// Start the app
Expand Down
15 changes: 0 additions & 15 deletions examples/qml_minimal/qml/qml.qrc

This file was deleted.

0 comments on commit 1c5c906

Please sign in to comment.