qt_build_utils/tool/
qmlcachegen.rs

1// SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
2// SPDX-FileContributor: Andrew Hayzen <andrew.hayzen@kdab.com>
3//
4// SPDX-License-Identifier: MIT OR Apache-2.0
5
6use crate::{QmlUri, QtInstallation, QtTool};
7use std::{
8    path::{Path, PathBuf},
9    process::Command,
10};
11
12/// Arguments for a [QtToolQmlCacheGen]
13#[derive(Clone)]
14pub struct QmlCacheArguments {
15    /// The URI for the QML module
16    pub uri: QmlUri,
17    /// The path to the qmldir
18    pub qmldir_path: PathBuf,
19    /// The path to the qrc file that contains a qmldir
20    pub qmldir_qrc_path: PathBuf,
21}
22
23/// Paths to files generated by [QtToolQmlCacheGen]
24pub struct QmlCacheProducts {
25    /// The path of the generated cache file
26    pub qml_cache_path: PathBuf,
27    /// The Qt resource path for qml file
28    pub qml_resource_path: String,
29}
30
31/// A wrapper around the [qmlcachegen](https://www.qt.io/blog/qml-type-registration-in-qt-5.15) tool
32pub struct QtToolQmlCacheGen {
33    executable: PathBuf,
34}
35
36impl QtToolQmlCacheGen {
37    /// Construct a [QtToolQmlCacheGen] from a given [QtInstallation]
38    pub fn new(qt_installation: &dyn QtInstallation) -> Self {
39        let executable = qt_installation
40            .try_find_tool(QtTool::QmlCacheGen)
41            .expect("Could not find qmlcachegen");
42
43        Self { executable }
44    }
45
46    /// Run qmlcachegen for a given qml file
47    pub fn compile(
48        &self,
49        common_args: QmlCacheArguments,
50        file: impl AsRef<Path>,
51    ) -> QmlCacheProducts {
52        let uri = common_args.uri;
53        let qml_uri_dirs = uri.as_dirs();
54
55        let qmlcachegen_dir = QtTool::QmlCacheGen.writable_path().join(&qml_uri_dirs);
56        std::fs::create_dir_all(&qmlcachegen_dir)
57            .expect("Could not create qmlcachegen directory for QML module");
58
59        let common_args = [
60            "-i".to_owned(),
61            common_args.qmldir_path.to_string_lossy().into_owned(),
62            "--resource".to_owned(),
63            common_args.qmldir_qrc_path.to_string_lossy().into_owned(),
64        ];
65
66        let qml_cache_path = qmlcachegen_dir.join(format!(
67            "{}.cpp",
68            file.as_ref().file_name().unwrap().to_string_lossy()
69        ));
70
71        let qml_resource_path = format!("/qt/qml/{qml_uri_dirs}/{}", file.as_ref().display());
72
73        let specific_args = vec![
74            "--resource-path".to_owned(),
75            qml_resource_path.clone(),
76            "-o".to_owned(),
77            qml_cache_path.to_string_lossy().into_owned(),
78            std::fs::canonicalize(&file)
79                .unwrap()
80                .to_string_lossy()
81                .into_owned(),
82        ];
83
84        let cmd = Command::new(&self.executable)
85            .args(common_args.iter().chain(&specific_args))
86            .output()
87            .unwrap_or_else(|_| {
88                panic!(
89                    "qmlcachegen failed for {} in QML module {uri}",
90                    file.as_ref().display()
91                )
92            });
93        if !cmd.status.success() {
94            panic!(
95                "qmlcachegen failed for {} in QML module {uri}:\n{}",
96                file.as_ref().display(),
97                String::from_utf8_lossy(&cmd.stderr)
98            );
99        }
100
101        QmlCacheProducts {
102            qml_cache_path,
103            qml_resource_path,
104        }
105    }
106
107    /// Compile a loader for given qml resource paths
108    pub fn compile_loader(
109        &self,
110        common_args: QmlCacheArguments,
111        qml_resource_paths: &[String],
112    ) -> PathBuf {
113        let uri = common_args.uri;
114        let qml_uri_dirs = uri.as_dirs();
115        let qml_uri_underscores = uri.as_underscores();
116
117        let qmlcachegen_dir = QtTool::QmlCacheGen.writable_path().join(qml_uri_dirs);
118        std::fs::create_dir_all(&qmlcachegen_dir)
119            .expect("Could not create qmlcachegen directory for QML module");
120
121        let common_args = [
122            "-i".to_owned(),
123            common_args.qmldir_path.to_string_lossy().into_owned(),
124            "--resource".to_owned(),
125            common_args.qmldir_qrc_path.to_string_lossy().into_owned(),
126        ];
127
128        let qmlcachegen_loader = qmlcachegen_dir.join("qmlcache_loader.cpp");
129        let specific_args = vec![
130            "--resource-name".to_owned(),
131            format!("qmlcache_{qml_uri_underscores}"),
132            "-o".to_owned(),
133            qmlcachegen_loader.to_string_lossy().into_owned(),
134        ];
135
136        let cmd = Command::new(&self.executable)
137            .args(
138                common_args
139                    .iter()
140                    .chain(&specific_args)
141                    .chain(qml_resource_paths),
142            )
143            .output()
144            .unwrap_or_else(|_| panic!("qmlcachegen failed for QML module {uri}"));
145        if !cmd.status.success() {
146            panic!(
147                "qmlcachegen failed for QML module {uri}:\n{}",
148                String::from_utf8_lossy(&cmd.stderr)
149            );
150        }
151
152        qmlcachegen_loader
153    }
154}