Skip to content

Commit 3ec71e8

Browse files
committed
qt-build-utils: split Moc into a separate tool
1 parent 7d5c679 commit 3ec71e8

File tree

3 files changed

+133
-94
lines changed

3 files changed

+133
-94
lines changed

crates/qt-build-utils/src/lib.rs

+4-94
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pub use installation::qmake::QtInstallationQMake;
3131
mod parse_cflags;
3232

3333
mod tool;
34-
pub use tool::{QtTool, QtToolRcc};
34+
pub use tool::{MocArguments, MocProducts, QtTool, QtToolMoc, QtToolRcc};
3535

3636
mod utils;
3737

@@ -109,42 +109,6 @@ pub fn setup_linker() {
109109
}
110110
}
111111

112-
/// Paths to files generated by [QtBuild::moc]
113-
pub struct MocProducts {
114-
/// Generated C++ file
115-
pub cpp: PathBuf,
116-
/// Generated JSON file
117-
pub metatypes_json: PathBuf,
118-
}
119-
120-
/// Arguments for a Qt moc invocation.
121-
/// See: [QtBuild::moc]
122-
#[derive(Default, Clone)]
123-
pub struct MocArguments {
124-
uri: Option<String>,
125-
include_paths: Vec<PathBuf>,
126-
}
127-
128-
impl MocArguments {
129-
/// Should be passed if the input_file is part of a QML module
130-
pub fn uri(mut self, uri: String) -> Self {
131-
self.uri = Some(uri);
132-
self
133-
}
134-
135-
/// Additional include path to pass to moc
136-
pub fn include_path(mut self, include_path: PathBuf) -> Self {
137-
self.include_paths.push(include_path);
138-
self
139-
}
140-
141-
/// Additional include paths to pass to moc.
142-
pub fn include_paths(mut self, mut include_paths: Vec<PathBuf>) -> Self {
143-
self.include_paths.append(&mut include_paths);
144-
self
145-
}
146-
}
147-
148112
/// Paths to C++ files generated by [QtBuild::register_qml_module]
149113
pub struct QmlModuleRegistrationFiles {
150114
/// File generated by [rcc](https://doc.qt.io/qt-6/rcc.html) for the QML plugin. The compiled static library
@@ -223,64 +187,10 @@ impl QtBuild {
223187
/// Run moc on a C++ header file and save the output into [cargo's OUT_DIR](https://doc.rust-lang.org/cargo/reference/environment-variables.html).
224188
/// The return value contains the path to the generated C++ file, which can then be passed to [cc::Build::files](https://docs.rs/cc/latest/cc/struct.Build.html#method.file),
225189
/// as well as the path to the generated metatypes.json file, which can be passed to [register_qml_module](Self::register_qml_module).
226-
///
227190
pub fn moc(&mut self, input_file: impl AsRef<Path>, arguments: MocArguments) -> MocProducts {
228-
let moc_executable = self
229-
.qt_installation
230-
.try_find_tool(QtTool::Moc)
231-
.expect("Could not find moc");
232-
let input_path = input_file.as_ref();
233-
234-
// Put all the moc files into one place, this can then be added to the include path
235-
let moc_dir = PathBuf::from(format!(
236-
"{}/qt-build-utils/moc",
237-
env::var("OUT_DIR").unwrap()
238-
));
239-
std::fs::create_dir_all(&moc_dir).expect("Could not create moc dir");
240-
let output_path = moc_dir.join(format!(
241-
"moc_{}.cpp",
242-
input_path.file_name().unwrap().to_str().unwrap()
243-
));
244-
245-
let metatypes_json_path = PathBuf::from(&format!("{}.json", output_path.display()));
246-
247-
let mut include_args = vec![];
248-
// Qt includes
249-
for include_path in self
250-
.include_paths()
251-
.iter()
252-
.chain(arguments.include_paths.iter())
253-
{
254-
include_args.push(format!("-I{}", include_path.display()));
255-
}
256-
257-
let mut cmd = Command::new(moc_executable);
258-
259-
if let Some(uri) = arguments.uri {
260-
cmd.arg(format!("-Muri={uri}"));
261-
}
262-
263-
cmd.args(include_args);
264-
cmd.arg(input_path.to_str().unwrap())
265-
.arg("-o")
266-
.arg(output_path.to_str().unwrap())
267-
.arg("--output-json");
268-
let cmd = cmd
269-
.output()
270-
.unwrap_or_else(|_| panic!("moc failed for {}", input_path.display()));
271-
272-
if !cmd.status.success() {
273-
panic!(
274-
"moc failed for {}:\n{}",
275-
input_path.display(),
276-
String::from_utf8_lossy(&cmd.stderr)
277-
);
278-
}
279-
280-
MocProducts {
281-
cpp: output_path,
282-
metatypes_json: metatypes_json_path,
283-
}
191+
// TODO: do we change this API to just be moc() -> QtToolMoc ?
192+
QtToolMoc::new(self.qt_installation.as_ref(), &self.qt_modules)
193+
.compile(input_file, arguments)
284194
}
285195

286196
/// Generate C++ files to automatically register a QML module at build time using the JSON output from [moc](Self::moc).

crates/qt-build-utils/src/tool/moc.rs

+126
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
2+
// SPDX-FileContributor: Andrew Hayzen <[email protected]>
3+
//
4+
// SPDX-License-Identifier: MIT OR Apache-2.0
5+
6+
use crate::{QtInstallation, QtTool};
7+
8+
use std::{
9+
env,
10+
path::{Path, PathBuf},
11+
process::Command,
12+
};
13+
14+
/// Paths to files generated by [QtBuild::moc]
15+
pub struct MocProducts {
16+
/// Generated C++ file
17+
pub cpp: PathBuf,
18+
/// Generated JSON file
19+
pub metatypes_json: PathBuf,
20+
}
21+
22+
/// Arguments for a Qt moc invocation.
23+
/// See: [QtBuild::moc]
24+
#[derive(Default, Clone)]
25+
pub struct MocArguments {
26+
uri: Option<String>,
27+
include_paths: Vec<PathBuf>,
28+
}
29+
30+
impl MocArguments {
31+
/// Should be passed if the input_file is part of a QML module
32+
pub fn uri(mut self, uri: String) -> Self {
33+
self.uri = Some(uri);
34+
self
35+
}
36+
37+
/// Additional include path to pass to moc
38+
pub fn include_path(mut self, include_path: PathBuf) -> Self {
39+
self.include_paths.push(include_path);
40+
self
41+
}
42+
43+
/// Additional include paths to pass to moc.
44+
pub fn include_paths(mut self, mut include_paths: Vec<PathBuf>) -> Self {
45+
self.include_paths.append(&mut include_paths);
46+
self
47+
}
48+
}
49+
50+
/// A wrapper around the [moc](https://doc.qt.io/qt-6/moc.html) tool
51+
pub struct QtToolMoc {
52+
executable: PathBuf,
53+
qt_include_paths: Vec<PathBuf>,
54+
}
55+
56+
impl QtToolMoc {
57+
/// Construct a [QtToolMoc] from a given [QtInstallation]
58+
pub fn new(qt_installation: &dyn QtInstallation, qt_modules: &[String]) -> Self {
59+
let executable = qt_installation
60+
.try_find_tool(QtTool::Moc)
61+
.expect("Could not find moc");
62+
let qt_include_paths = qt_installation.include_paths(qt_modules);
63+
64+
Self {
65+
executable,
66+
qt_include_paths,
67+
}
68+
}
69+
70+
/// Run moc on a C++ header file and save the output into [cargo's OUT_DIR](https://doc.rust-lang.org/cargo/reference/environment-variables.html).
71+
/// The return value contains the path to the generated C++ file, which can then be passed to [cc::Build::files](https://docs.rs/cc/latest/cc/struct.Build.html#method.file),
72+
/// as well as the path to the generated metatypes.json file, which can be passed to [register_qml_module](Self::register_qml_module).
73+
pub fn compile(&self, input_file: impl AsRef<Path>, arguments: MocArguments) -> MocProducts {
74+
let input_path = input_file.as_ref();
75+
// Put all the moc files into one place, this can then be added to the include path
76+
let moc_dir = PathBuf::from(format!(
77+
"{}/qt-build-utils/moc",
78+
env::var("OUT_DIR").unwrap()
79+
));
80+
std::fs::create_dir_all(&moc_dir).expect("Could not create moc dir");
81+
let output_path = moc_dir.join(format!(
82+
"moc_{}.cpp",
83+
input_path.file_name().unwrap().to_str().unwrap()
84+
));
85+
86+
let metatypes_json_path = PathBuf::from(&format!("{}.json", output_path.display()));
87+
88+
let mut include_args = vec![];
89+
// Qt includes
90+
for include_path in self
91+
.qt_include_paths
92+
.iter()
93+
.chain(arguments.include_paths.iter())
94+
{
95+
include_args.push(format!("-I{}", include_path.display()));
96+
}
97+
98+
let mut cmd = Command::new(&self.executable);
99+
100+
if let Some(uri) = arguments.uri {
101+
cmd.arg(format!("-Muri={uri}"));
102+
}
103+
104+
cmd.args(include_args);
105+
cmd.arg(input_path.to_str().unwrap())
106+
.arg("-o")
107+
.arg(output_path.to_str().unwrap())
108+
.arg("--output-json");
109+
let cmd = cmd
110+
.output()
111+
.unwrap_or_else(|_| panic!("moc failed for {}", input_path.display()));
112+
113+
if !cmd.status.success() {
114+
panic!(
115+
"moc failed for {}:\n{}",
116+
input_path.display(),
117+
String::from_utf8_lossy(&cmd.stderr)
118+
);
119+
}
120+
121+
MocProducts {
122+
cpp: output_path,
123+
metatypes_json: metatypes_json_path,
124+
}
125+
}
126+
}

crates/qt-build-utils/src/tool/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
//
44
// SPDX-License-Identifier: MIT OR Apache-2.0
55

6+
mod moc;
7+
pub use moc::{MocArguments, MocProducts, QtToolMoc};
8+
69
mod rcc;
710
pub use rcc::QtToolRcc;
811

0 commit comments

Comments
 (0)