Skip to content

qt-build-utils: build system changes to have a QtInstallation #1286

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
57e0b78
qt-build-utils: use u64 as major type this allows us to move to SemVer
ahayzen-kdab Mar 14, 2025
72bba13
qt-build-utils: add anyhow and semver crates for later
ahayzen-kdab Mar 14, 2025
095156e
qt-build-utils: add a QtTool enum for later
ahayzen-kdab Apr 22, 2025
dffb242
qt-build-utils: add QtInstallation trait
ahayzen-kdab Mar 14, 2025
9cc3405
qt-build-utils: add qmake implementation of QtInstallation
ahayzen-kdab Mar 14, 2025
dd7f0d1
qt-build-utils: create a QtInstallation but don't use it
ahayzen-kdab Mar 14, 2025
53a39b6
cxx-qt-build: use version from QtInstallation
ahayzen-kdab Apr 22, 2025
e188bd5
qt-build-utils: ask QtInstallation for location of Qt tools
ahayzen-kdab Apr 22, 2025
a4effb3
qt-build-utils: split out common is_*_target
ahayzen-kdab Apr 22, 2025
6f3770c
qt-build-utils: move linking of Qt modules to Qt Installation
ahayzen-kdab Apr 22, 2025
02c4341
qt-build-utils: move include paths to qmake installation
ahayzen-kdab Apr 22, 2025
112871a
qt-build-utils: remove qmake executable path as it is in installation
ahayzen-kdab Apr 22, 2025
84e18cd
qt-build-utils: parse_cflags is now only needed for qmake builds
ahayzen-kdab Apr 22, 2025
8f48c03
qt-build-utils: fix docs and add new method with QtInstallation arg
ahayzen-kdab Apr 22, 2025
3b1b3f3
qt-build-utils: add back a cache for finding the Qt tool
ahayzen-kdab Apr 24, 2025
025ae24
qt-build-utils: split error into separate module
ahayzen-kdab May 1, 2025
42599e2
qt-build-utils: split initialiser into module
ahayzen-kdab May 1, 2025
a601367
qt-build-utils: move tool to a folder
ahayzen-kdab May 1, 2025
481c7f4
qt-build-utils: split out rcc into a tool
ahayzen-kdab May 1, 2025
bdb9e4c
qt-build-utils: split Moc into a separate tool
ahayzen-kdab May 1, 2025
4b1873f
qt-build-utils: have a command writable path method for tools
ahayzen-kdab May 1, 2025
ec39afe
qt-build-utils: split out qmltyperegistrar into a separate tool
ahayzen-kdab May 5, 2025
88dad8f
qt-build-utils: split out qmlcachegen into a separate tool
ahayzen-kdab May 9, 2025
e0e4072
cxx-qt-build: use new rcc method instead of qrc
ahayzen-kdab May 13, 2025
42b24e9
cxx-qt-build: use moc() method to retrieve the tool not compile
ahayzen-kdab May 13, 2025
e4b8567
qt-build-utils: Return Result in try_find_tool
LeonMatthesKDAB May 22, 2025
6c2ce2f
qt-build-utils: new_with_default/new -> new/with_installation
LeonMatthesKDAB May 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 15 additions & 49 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ cxx-gen = "0.7.121"
proc-macro2 = "1.0"
syn = { version = "2.0", features = ["extra-traits", "full"] }
quote = "1.0"
semver = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"
Expand Down
2 changes: 1 addition & 1 deletion crates/cxx-qt-build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ proc-macro2.workspace = true
quote.workspace = true
qt-build-utils = { workspace = true, features = ["serde"] }
codespan-reporting = "0.11"
version_check = "0.9"
serde.workspace = true
serde_json = "1.0"
semver.workspace = true

[features]
link_qt_object_files = ["qt-build-utils/link_qt_object_files"]
Expand Down
16 changes: 9 additions & 7 deletions crates/cxx-qt-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ use qml_modules::OwningQmlModule;
pub use qml_modules::QmlModule;

pub use qt_build_utils::MocArguments;
use qt_build_utils::SemVer;
use quote::ToTokens;
use semver::Version;
use std::{
collections::HashSet,
env,
Expand Down Expand Up @@ -600,7 +600,7 @@ impl CxxQtBuilder {
}
}

fn define_qt_version_cfg_variables(version: &SemVer) {
fn define_qt_version_cfg_variables(version: Version) {
// Allow for Qt 5 or Qt 6 as valid values
CxxQtBuilder::define_cfg_check_variable(
"cxxqt_qt_version_major".to_owned(),
Expand Down Expand Up @@ -702,7 +702,7 @@ impl CxxQtBuilder {
moc_arguments,
} in &self.qobject_headers
{
let moc_products = qtbuild.moc(path, moc_arguments.clone());
let moc_products = qtbuild.moc().compile(path, moc_arguments.clone());
// Include the moc folder
if let Some(dir) = moc_products.cpp.parent() {
self.cc_builder.include(dir);
Expand Down Expand Up @@ -827,7 +827,7 @@ impl CxxQtBuilder {
}

cc_builder.file(&qobject);
let moc_products = qtbuild.moc(
let moc_products = qtbuild.moc().compile(
qobject_header,
MocArguments::default().uri(qml_module.uri.clone()),
);
Expand All @@ -852,8 +852,10 @@ impl CxxQtBuilder {
&qml_module.qml_files,
&qml_module.qrc_files,
);
if let Some(qmltyperegistrar) = qml_module_registration_files.qmltyperegistrar {
cc_builder.file(qmltyperegistrar);
}
cc_builder
.file(qml_module_registration_files.qmltyperegistrar)
.file(qml_module_registration_files.plugin)
// In comparison to the other RCC files, we don't need to link this with whole-archive or
// anything like that.
Expand Down Expand Up @@ -1030,12 +1032,12 @@ extern "C" bool {init_fun}() {{
.iter()
.map(|qrc_file| {
// Also ensure that each of the files in the qrc can cause a change
for qrc_inner_file in qtbuild.qrc_list(&qrc_file) {
for qrc_inner_file in qtbuild.rcc().list(qrc_file) {
println!("cargo::rerun-if-changed={}", qrc_inner_file.display());
}
// We need to link this using an object file or +whole-achive, the static initializer of
// the qrc file isn't lost.
qtbuild.qrc(&qrc_file)
qtbuild.rcc().compile(qrc_file)
})
.collect()
}
Expand Down
6 changes: 5 additions & 1 deletion crates/qt-build-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@ repository.workspace = true
rust-version.workspace = true

[dependencies]
anyhow = "1.0"
cc.workspace = true
semver.workspace = true
serde = { workspace = true, optional = true }
versions = "6.3"
thiserror.workspace = true

[features]
# TODO: should we default to qmake or let downstream crates specify, such as cxx-qt-build
default = ["qmake"]
# When Cargo links an executable, whether a bin crate or test executable,
# and Qt 6 is linked statically, this feature must be enabled to link
# unarchived .o files with static symbols that Qt ships (for example
Expand All @@ -31,6 +34,7 @@ thiserror.workspace = true
#
# When linking Qt dynamically, this makes no difference.
link_qt_object_files = []
qmake = []
serde = ["dep:serde"]

[lints]
Expand Down
41 changes: 41 additions & 0 deletions crates/qt-build-utils/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
// SPDX-FileContributor: Andrew Hayzen <[email protected]>
//
// SPDX-License-Identifier: MIT OR Apache-2.0

use thiserror::Error;

#[derive(Error, Debug)]
/// Errors that can occur while using [crate::QtBuild]
pub enum QtBuildError {
/// `QMAKE` environment variable was set but Qt was not detected
#[error("QMAKE environment variable specified as {qmake_env_var} but could not detect Qt: {error:?}")]
QMakeSetQtMissing {
/// The value of the qmake environment variable when the error occurred
qmake_env_var: String,
/// The inner error that occurred
error: Box<anyhow::Error>,
},
/// Qt was not found
#[error("Could not find Qt")]
QtMissing,
/// Executing `qmake -query` failed
#[error("Executing `qmake -query` failed: {0:?}")]
QmakeFailed(#[from] std::io::Error),
/// `QT_VERSION_MAJOR` environment variable was specified but could not be parsed as an integer
#[error("QT_VERSION_MAJOR environment variable specified as {qt_version_major_env_var} but could not parse as integer: {source:?}")]
QtVersionMajorInvalid {
/// The Qt major version from `QT_VERSION_MAJOR`
qt_version_major_env_var: String,
/// The [std::num::ParseIntError] when parsing the `QT_VERSION_MAJOR`
source: std::num::ParseIntError,
},
/// `QT_VERSION_MAJOR` environment variable was specified but the Qt version specified by `qmake -query QT_VERSION` did not match
#[error("qmake version ({qmake_version}) does not match version specified by QT_VERSION_MAJOR ({qt_version_major})")]
QtVersionMajorDoesNotMatch {
/// The qmake version
qmake_version: u64,
/// The Qt major version from `QT_VERSION_MAJOR`
qt_version_major: u64,
},
}
35 changes: 35 additions & 0 deletions crates/qt-build-utils/src/initializer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
// SPDX-FileContributor: Andrew Hayzen <[email protected]>
//
// SPDX-License-Identifier: MIT OR Apache-2.0

use std::path::PathBuf;

#[doc(hidden)]
#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Initializer {
pub file: Option<PathBuf>,
pub init_call: Option<String>,
pub init_declaration: Option<String>,
}

impl Initializer {
#[doc(hidden)]
pub fn default_signature(name: &str) -> Self {
Self {
file: None,
init_call: Some(format!("{name}();")),
init_declaration: Some(format!("extern \"C\" bool {name}();")),
}
}

#[doc(hidden)]
// Strip the init files from the public initializers
// For downstream dependencies, it's often enough to just declare the init function and
// call it.
pub fn strip_file(mut self) -> Self {
self.file = None;
self
}
}
42 changes: 42 additions & 0 deletions crates/qt-build-utils/src/installation/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
// SPDX-FileContributor: Andrew Hayzen <[email protected]>
//
// SPDX-License-Identifier: MIT OR Apache-2.0

#[cfg(feature = "qmake")]
pub(crate) mod qmake;

use semver::Version;
use std::path::PathBuf;

use crate::QtTool;

/// A Qt Installation that can be used by cxx-qt-build to run Qt related tasks
///
/// Note that it is the responsbility of the QtInstallation implementation
/// to print any cargo::rerun-if-changed lines
pub trait QtInstallation {
/// Return the include paths for Qt, including Qt module subdirectories.
///
/// This is intended to be passed to whichever tool you are using to invoke the C++ compiler.
fn include_paths(&self, qt_modules: &[String]) -> Vec<PathBuf>;
/// Configure the given cc::Build and cargo to link to the given Qt modules
///
// TODO: should we hand in a cc::Build or should we instead return a struct
// with details of the rustc-link-lib / search paths ? and then have the
// calling function apply those and any flags to the cc::Build?
// eg return the following?
//
// pub struct LinkArgs {
// builder_flag_if_supported: Vec<String>,
// builder_object: Vec<String>,
// rustc_link_arg: Vec<String>,
// rustc_link_lib: Vec<String>,
// rustc_link_search: Vec<String>,
// }
fn link_modules(&self, builder: &mut cc::Build, qt_modules: &[String]);
/// Find the path to a given Qt tool for the Qt installation
fn try_find_tool(&self, tool: QtTool) -> anyhow::Result<PathBuf>;
/// Version of the detected Qt installation
fn version(&self) -> Version;
}
Loading
Loading