1#![deny(missing_docs)]
7
8#![allow(clippy::too_many_arguments)]
21
22mod cfg;
23pub use cfg::CfgGenerator;
24
25mod error;
26pub use error::QtBuildError;
27
28mod initializer;
29pub use initializer::Initializer;
30
31mod installation;
32pub use installation::QtInstallation;
33
34#[cfg(feature = "qmake")]
35pub use installation::qmake::QtInstallationQMake;
36
37#[cfg(feature = "qmake")]
38mod parse_cflags;
39
40mod platform;
41pub use platform::QtPlatformLinker;
42
43mod qml;
44pub use qml::{PluginType, QmlDirBuilder, QmlFile, QmlLsIniBuilder, QmlPluginCppBuilder, QmlUri};
45
46mod qrc;
47pub use qrc::{QResource, QResourceFile, QResources};
48
49mod tool;
50pub use tool::{
51 MocArguments, MocProducts, QmlCacheArguments, QmlCacheProducts, QtTool, QtToolMoc,
52 QtToolQmlCacheGen, QtToolQmlTypeRegistrar, QtToolRcc,
53};
54
55mod utils;
56
57use std::{
58 env,
59 ffi::{OsStr, OsString},
60 fs::File,
61 path::{Path, PathBuf},
62};
63
64use semver::Version;
65
66pub struct QmlModuleRegistrationFiles {
68 pub rcc: PathBuf,
72 pub qmlcachegen: Vec<PathBuf>,
74 pub qmltyperegistrar: Option<PathBuf>,
76 pub qmltypes: PathBuf,
79 pub qmldir: PathBuf,
82 pub plugin: PathBuf,
84 pub plugin_init: Initializer,
86 pub include_path: Option<PathBuf>,
88 pub qml_files: Vec<PathBuf>,
90}
91
92pub struct QtBuild {
101 qt_installation: Box<dyn QtInstallation>,
102 qt_modules: Vec<String>,
103 autorcc_options: Vec<OsString>,
104}
105
106impl QtBuild {
107 #[cfg(feature = "qmake")]
114 pub fn new(qt_modules: Vec<String>) -> anyhow::Result<Self> {
115 let qt_installation = Box::new(QtInstallationQMake::new()?);
116 Ok(Self::with_installation(qt_installation, qt_modules))
117 }
118
119 pub fn with_installation(
122 qt_installation: Box<dyn QtInstallation>,
123 mut qt_modules: Vec<String>,
124 ) -> Self {
125 if qt_modules.is_empty() {
126 qt_modules.push("Core".to_owned());
127 }
128
129 Self {
130 qt_installation,
131 qt_modules,
132 autorcc_options: Vec::new(),
133 }
134 }
135
136 pub fn autorcc_options(mut self, options: impl IntoIterator<Item = impl AsRef<OsStr>>) -> Self {
138 self.autorcc_options = options
139 .into_iter()
140 .map(|s| s.as_ref().to_os_string())
141 .collect();
142 self
143 }
144
145 pub fn cargo_link_libraries(&self, builder: &mut cc::Build) {
147 self.qt_installation.link_modules(builder, &self.qt_modules);
148 }
149
150 pub fn framework_paths(&self) -> Vec<PathBuf> {
153 self.qt_installation.framework_paths(&self.qt_modules)
154 }
155
156 pub fn include_paths(&self) -> Vec<PathBuf> {
159 self.qt_installation.include_paths(&self.qt_modules)
160 }
161
162 pub fn version(&self) -> Version {
164 self.qt_installation.version()
165 }
166
167 pub fn moc(&mut self) -> QtToolMoc {
171 QtToolMoc::new(self.qt_installation.as_ref(), &self.qt_modules)
172 }
173
174 pub fn register_qml_module(
182 &mut self,
183 metatypes_json: &[impl AsRef<Path>],
184 uri: &QmlUri,
185 version_major: usize,
186 version_minor: usize,
187 plugin_name: &str,
188 qml_files: &[QmlFile],
189 depends: impl IntoIterator<Item = impl Into<QmlUri>>,
190 plugin_type: PluginType,
191 ) -> QmlModuleRegistrationFiles {
192 let qml_uri_dirs = uri.as_dirs();
193 let qml_uri_underscores = uri.as_underscores();
194 let plugin_type_info = "plugin.qmltypes";
195 let plugin_class_name = format!("{}_plugin", qml_uri_underscores);
196
197 let out_dir = env::var("OUT_DIR").unwrap();
198 let qt_build_utils_dir = PathBuf::from(format!("{out_dir}/qt-build-utils"));
199 std::fs::create_dir_all(&qt_build_utils_dir).expect("Could not create qt_build_utils dir");
200
201 let qml_module_dir = qt_build_utils_dir.join("qml_modules").join(&qml_uri_dirs);
202 std::fs::create_dir_all(&qml_module_dir).expect("Could not create QML module directory");
203
204 let qmltypes_path = qml_module_dir.join(plugin_type_info);
205
206 let qmldir_file_path = qml_module_dir.join("qmldir");
208 {
209 let qml_type_files = qml_files
210 .iter()
211 .filter(|file| {
212 file.get_path()
215 .file_name()
216 .and_then(OsStr::to_str)
217 .and_then(|file_name| file_name.chars().next())
218 .map(char::is_uppercase)
219 .unwrap_or_default()
220 })
221 .cloned();
222 let mut file = File::create(&qmldir_file_path).expect("Could not create qmldir file");
223 QmlDirBuilder::new(uri.clone())
224 .depends(depends)
225 .plugin(plugin_name, true)
226 .class_name(&plugin_class_name)
227 .type_info(plugin_type_info)
228 .qml_files(qml_type_files)
229 .write(&mut file)
230 .expect("Could not write qmldir file");
231 }
232
233 let qrc_path =
236 qml_module_dir.join(format!("qml_module_resources_{qml_uri_underscores}.qrc"));
237 {
238 let qml_module_dir_str = qml_module_dir.to_str().unwrap();
239 let qml_uri_dirs_prefix = format!("/qt/qml/{qml_uri_dirs}");
240 let mut qrc = File::create(&qrc_path).expect("Could not create qrc file");
241 QResources::new()
242 .resource(QResource::new().prefix("/".to_string()).file(
243 QResourceFile::new(qml_module_dir_str).alias(qml_uri_dirs_prefix.clone()),
244 ))
245 .resource({
246 let mut resource = QResource::new().prefix(qml_uri_dirs_prefix.clone()).file(
247 QResourceFile::new(format!("{qml_module_dir_str}/qmldir"))
248 .alias("qmldir".to_string()),
249 );
250
251 fn resource_add_path(resource: QResource, path: &Path) -> QResource {
252 let resolved = std::fs::canonicalize(path)
253 .unwrap_or_else(|_| {
254 panic!("Could not canonicalize path {}", path.display())
255 })
256 .display()
257 .to_string();
258 resource
259 .file(QResourceFile::new(resolved).alias(path.display().to_string()))
260 }
261
262 for file in qml_files {
263 resource = resource_add_path(resource, file.get_path());
264 }
265 resource
266 })
267 .write(&mut qrc)
268 .expect("Could note write qrc file");
269 }
270
271 let mut qmlcachegen_file_paths = Vec::new();
275
276 if self.qt_installation.version().major >= 6 {
278 let qml_cache_args = QmlCacheArguments {
279 uri: uri.clone(),
280 qmldir_path: qmldir_file_path.clone(),
281 qmldir_qrc_path: qrc_path.clone(),
282 };
283 let mut qml_resource_paths = Vec::new();
284 for file in qml_files {
285 let result = QtToolQmlCacheGen::new(self.qt_installation.as_ref())
286 .compile(qml_cache_args.clone(), file.get_path());
287 qmlcachegen_file_paths.push(result.qml_cache_path);
288 qml_resource_paths.push(result.qml_resource_path);
289 }
290
291 if !qml_files.is_empty() {
293 qmlcachegen_file_paths.push(
294 QtToolQmlCacheGen::new(self.qt_installation.as_ref())
295 .compile_loader(qml_cache_args.clone(), &qml_resource_paths),
296 );
297 }
298 }
299
300 let qml_plugin_dir = PathBuf::from(format!("{out_dir}/qt-build-utils/qml_plugin"));
301 std::fs::create_dir_all(&qml_plugin_dir).expect("Could not create qml_plugin dir");
302
303 let qmltyperegistrar_path = self.qmltyperegistrar().compile(
305 metatypes_json,
306 &qmltypes_path,
307 uri,
308 Version::new(version_major as u64, version_minor as u64, 0),
309 );
310
311 let qml_plugin_cpp_path = qml_plugin_dir.join(format!("{plugin_class_name}.cpp"));
313 let include_path;
314 {
315 let mut file = File::create(&qml_plugin_cpp_path)
316 .expect("Could not create plugin definition file");
317 let plugin_init = QmlPluginCppBuilder::new(uri.clone(), plugin_class_name.clone())
318 .qml_cache(!qml_files.is_empty() && !qmlcachegen_file_paths.is_empty())
319 .plugin_type(plugin_type)
320 .write(&mut file)
321 .expect("Failed to write plugin definition");
322
323 let moc_product = self.moc().compile(
324 &qml_plugin_cpp_path,
325 MocArguments::default().uri(uri.to_owned()),
326 );
327 include_path = moc_product.cpp.parent().map(Path::to_path_buf);
329
330 let qml_files = qml_files
331 .iter()
332 .map(|qml_file| qml_file.get_path().to_path_buf())
333 .collect();
334
335 let rcc = self.rcc().compile(&qrc_path);
336 QmlModuleRegistrationFiles {
337 rcc: rcc.file.unwrap(),
340 qmlcachegen: qmlcachegen_file_paths,
341 qmltyperegistrar: qmltyperegistrar_path,
342 qmltypes: qmltypes_path,
343 qmldir: qmldir_file_path,
344 plugin: qml_plugin_cpp_path,
345 plugin_init,
346 include_path,
347 qml_files,
348 }
349 }
350 }
351
352 pub fn rcc(&self) -> QtToolRcc {
356 QtToolRcc::new(self.qt_installation.as_ref()).custom_args(&self.autorcc_options)
357 }
358
359 pub fn qmltyperegistrar(&self) -> QtToolQmlTypeRegistrar {
361 QtToolQmlTypeRegistrar::new(self.qt_installation.as_ref())
362 }
363}