Skip to content

Commit

Permalink
run qmlcachegen on registered QML modules
Browse files Browse the repository at this point in the history
Fixes KDAB#242
  • Loading branch information
Be-ing committed Aug 8, 2023
1 parent d5d8798 commit 6ad56d1
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 3 deletions.
5 changes: 4 additions & 1 deletion crates/cxx-qt-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ where
}

/// Register a QML module at build time. The specified rust_files should contain cxx-qt bridge modules with QObject types annotated
/// with `#[qml_element]`.
/// with `#[qml_element]`. When using Qt 6, this will [run qmlcachegen](https://doc.qt.io/qt-6/qtqml-qtquick-compiler-tech.html) to compile the specified .qml files ahead-of-time.
///
/// ```
/// # use cxx_qt_build::CxxQtBuilder;
Expand Down Expand Up @@ -558,6 +558,9 @@ where
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);
for qmlcachegen_file in qml_type_registration_files.qmlcachegen {
cc_builder_whole_archive.file(qmlcachegen_file);
}
self.cc_builder.define("QT_STATICPLUGIN", None);
cc_builder_whole_archive_files_added = true;
}
Expand Down
99 changes: 97 additions & 2 deletions crates/qt-build-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ pub struct QmlModuleRegistrationFiles {
pub plugin_init: PathBuf,
/// File generated by rcc for the QML plugin. Must be linked with `+whole-archive`.
pub qrc: PathBuf,
/// Files generated by qmlcachegen. Must be linked with `+whole-archive`.
pub qmlcachegen: Vec<PathBuf>,
}

/// Helper for build.rs scripts using Qt
Expand All @@ -186,6 +188,7 @@ pub struct QtBuild {
qmake_executable: String,
moc_executable: Option<String>,
qmltyperegistrar_executable: Option<String>,
qmlcachegen_executable: Option<String>,
rcc_executable: Option<String>,
qt_modules: Vec<String>,
}
Expand Down Expand Up @@ -286,6 +289,7 @@ impl QtBuild {
qmake_executable: executable_name.to_string(),
moc_executable: None,
qmltyperegistrar_executable: None,
qmlcachegen_executable: None,
rcc_executable: None,
version,
qt_modules,
Expand All @@ -309,6 +313,7 @@ impl QtBuild {
qmake_executable: executable_name.to_string(),
moc_executable: None,
qmltyperegistrar_executable: None,
qmlcachegen_executable: None,
rcc_executable: None,
version,
qt_modules,
Expand Down Expand Up @@ -618,6 +623,7 @@ impl QtBuild {
}

/// Generate C++ files to automatically register a QML module at build time using the JSON output from [moc](Self::moc).
/// When using Qt 6, this will [run qmlcachegen](https://doc.qt.io/qt-6/qtqml-qtquick-compiler-tech.html) to compile the specified .qml files ahead-of-time.
pub fn register_qml_module<A, B>(
&mut self,
metatypes_json: &[impl AsRef<Path>],
Expand All @@ -634,6 +640,12 @@ impl QtBuild {
.expect("Could not find qmltyperegistrar"),
);
}
// qmlcachegen has a different CLI in Qt 5, so only support Qt >= 6
if self.qmlcachegen_executable.is_none() && self.version.major >= 6 {
if let Ok(qmlcachegen_executable) = self.get_qt_tool("qmlcachegen") {
self.qmlcachegen_executable = Some(qmlcachegen_executable);
}
}

let uri = &qml_module.uri;
let qml_uri_dirs = uri.replace('.', "/");
Expand All @@ -646,8 +658,8 @@ impl QtBuild {
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");
let qmldir_file_path = format!("{qml_module_dir}/qmldir");
let mut qmldir = File::create(&qmldir_file_path).expect("Could not create qmldir file");
write!(
qmldir,
"module {uri}
Expand Down Expand Up @@ -770,11 +782,94 @@ Q_IMPORT_PLUGIN({plugin_class_name});
)
.unwrap();

let mut qmlcachegen_file_paths = Vec::new();
// qmlcachegen needs to be run once for each .qml file with --resource-path,
// then once for the module with --resource-name.
if let Some(qmlcachegen_executable) = &self.qmlcachegen_executable {
let qmlcachegen_dir = format!("{out_dir}/qmlcachegen/{qml_uri_dirs}");
std::fs::create_dir_all(&qmlcachegen_dir)
.expect("Could not create qmlcachegen directory for QML module");

let common_args = vec![
"-i".to_string(),
qmldir_file_path.to_string(),
"--resource".to_string(),
qrc_path.clone(),
];

let mut qml_file_qrc_paths = Vec::new();
for file in qml_module.qml_files {
let qrc_resource_path =
format!("/qt/qml/{qml_uri_dirs}/{}", file.as_ref().display());

let qml_compiled_file = format!(
"{qmlcachegen_dir}/{}.cpp",
file.as_ref().file_name().unwrap().to_string_lossy()
);
qmlcachegen_file_paths.push(PathBuf::from(&qml_compiled_file));

let specific_args = vec![
"--resource-path".to_string(),
qrc_resource_path.clone(),
"-o".to_string(),
qml_compiled_file,
std::fs::canonicalize(file)
.unwrap()
.to_string_lossy()
.to_string(),
];

let cmd = Command::new(qmlcachegen_executable)
.args(common_args.iter().chain(&specific_args))
.output()
.unwrap_or_else(|_| {
panic!(
"qmlcachegen failed for {} in QML module {uri}",
file.as_ref().display()
)
});
if !cmd.status.success() {
panic!(
"qmlcachegen failed for {} in QML module {uri}:\n{}",
file.as_ref().display(),
String::from_utf8_lossy(&cmd.stderr)
);
}
qml_file_qrc_paths.push(qrc_resource_path);
}

let qmlcachegen_loader = format!("{qmlcachegen_dir}/qmlcache_loader.cpp");
let specific_args = vec![
"--resource-name".to_string(),
format!("qmlcache_{qml_uri_cpp_symbol_safe}"),
"-o".to_string(),
qmlcachegen_loader.clone(),
];

let cmd = Command::new(qmlcachegen_executable)
.args(
common_args
.iter()
.chain(&specific_args)
.chain(&qml_file_qrc_paths),
)
.output()
.unwrap_or_else(|_| panic!("qmlcachegen failed for QML module {uri}"));
if !cmd.status.success() {
panic!(
"qmlcachegen failed for QML module {uri}:\n{}",
String::from_utf8_lossy(&cmd.stderr)
);
}
qmlcachegen_file_paths.push(PathBuf::from(&qmlcachegen_loader));
}

QmlModuleRegistrationFiles {
qmltyperegistrar: output_path,
plugin: qml_plugin_cpp_path,
plugin_init: qml_plugin_init_path,
qrc: qrc_output,
qmlcachegen: qmlcachegen_file_paths,
}
}

Expand Down

0 comments on commit 6ad56d1

Please sign in to comment.