Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 0 additions & 1 deletion Cargo.lock

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

1 change: 0 additions & 1 deletion crates/cxx-qt-build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ qt-build-utils = { workspace = true, features = ["serde"] }
codespan-reporting = "0.11"
serde.workspace = true
serde_json = "1.0"
semver.workspace = true

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

pub use qt_build_utils::MocArguments;
use quote::ToTokens;
use semver::Version;
use std::{
collections::HashSet,
env,
Expand Down Expand Up @@ -608,71 +607,6 @@ impl CxxQtBuilder {
self
}

fn define_cfg_variable(key: String, value: Option<&str>) {
if let Some(value) = value {
println!("cargo::rustc-cfg={key}=\"{value}\"");
} else {
println!("cargo::rustc-cfg={key}");
}
let variable_cargo = format!("CARGO_CFG_{key}");
env::set_var(variable_cargo, value.unwrap_or("true"));
}

fn define_cfg_check_variable(key: String, values: Option<Vec<&str>>) {
if let Some(values) = values {
let values = values
.iter()
// Escape and add quotes
.map(|value| format!("\"{}\"", value.escape_default()))
.collect::<Vec<_>>()
.join(", ");
println!("cargo::rustc-check-cfg=cfg({key}, values({values}))");
} else {
println!("cargo::rustc-check-cfg=cfg({key})");
}
}

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(),
Some(vec!["5", "6"]),
);
// Find the Qt version and tell the Rust compiler
// this allows us to have conditional Rust code
CxxQtBuilder::define_cfg_variable(
"cxxqt_qt_version_major".to_owned(),
Some(version.major.to_string().as_str()),
);

// Seed all values from Qt 5.0 through to Qt 7.99
for major in 5..=7 {
CxxQtBuilder::define_cfg_check_variable(
format!("cxxqt_qt_version_at_least_{major}"),
None,
);

for minor in 0..=99 {
CxxQtBuilder::define_cfg_check_variable(
format!("cxxqt_qt_version_at_least_{major}_{minor}"),
None,
);
}
}

for minor in 0..=version.minor {
let qt_version_at_least =
format!("cxxqt_qt_version_at_least_{}_{}", version.major, minor);
CxxQtBuilder::define_cfg_variable(qt_version_at_least, None);
}

// We don't support Qt < 5
for major in 5..=version.major {
let at_least_qt_major_version = format!("cxxqt_qt_version_at_least_{major}");
CxxQtBuilder::define_cfg_variable(at_least_qt_major_version, None);
}
}

fn write_common_headers() {
let header_root = dir::header_root();
// Write cxx headers
Expand Down Expand Up @@ -1103,7 +1037,14 @@ extern "C" bool {init_fun}() {{
let mut qtbuild = qt_build_utils::QtBuild::new(qt_modules.iter().cloned().collect())
.expect("Could not find Qt installation");
qtbuild.cargo_link_libraries(&mut self.cc_builder);
Self::define_qt_version_cfg_variables(qtbuild.version());

// Define the Qt cfg to cargo
//
// TODO: do we have this as a helper on QtBuild too?
qt_build_utils::CfgGenerator::new(qtbuild.version())
.prefix("cxxqt_")
.range_major(5..=7)
.build();

// Ensure that Qt modules and apple framework are linked and searched correctly
let mut include_paths = qtbuild.include_paths();
Expand Down
126 changes: 126 additions & 0 deletions crates/qt-build-utils/src/cfg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// 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 semver::Version;
use std::ops::RangeInclusive;

/// Helper for generating cargo cfg for a version range
pub struct CfgGenerator {
prefix: Option<String>,
range_major: RangeInclusive<u64>,
range_minor: RangeInclusive<u64>,
version: Version,
}

impl CfgGenerator {
/// Construct a new [CfgGenerator]
pub fn new(version: Version) -> Self {
Self {
range_major: (0..=99),
range_minor: (0..=99),
prefix: None,
version,
}
}

/// Specify a prefix for the [CfgGenerator]
pub fn prefix(mut self, prefix: impl Into<String>) -> Self {
self.prefix = Some(prefix.into());
self
}

/// Specify a major range for the [CfgGenerator]
pub fn range_major(mut self, range: RangeInclusive<u64>) -> Self {
self.range_major = range;
self
}

/// Specify a minor range for the [CfgGenerator]
pub fn range_minor(mut self, range: RangeInclusive<u64>) -> Self {
self.range_minor = range;
self
}

/// Generate cargo cfg with any given prefix and ranges for the version
pub fn build(self) {
// Define the major version
self.define_cfg_check_variable(
"qt_version_major".to_string(),
Some(
self.range_major
.clone()
.map(|major| major.to_string())
.collect(),
),
);
self.define_cfg_variable(
"qt_version_major".to_string(),
Some(self.version.major.to_string()),
);

// Tell cargo about all the possible cfg variables
for major in self.range_major.clone() {
self.define_cfg_check_variable(format!("qt_version_at_least_{major}"), None);

for minor in self.range_minor.clone() {
self.define_cfg_check_variable(
format!("qt_version_at_least_{major}_{minor}"),
None,
);
}
}

// Tell cargo which major versions have been reached
for major in *self.range_major.start()..=self.version.major {
self.define_cfg_variable(format!("qt_version_at_least_{major}"), None);
}

// Tell cargo which minor versions with the major have been reached
for minor in *self.range_minor.start()..=self.version.minor {
let major = self.version.major;
self.define_cfg_variable(format!("qt_version_at_least_{major}_{minor}"), None);
}
}
}

impl CfgGenerator {
fn define_cfg_check_variable(&self, key: String, values: Option<Vec<String>>) {
let key = self.key_with_prefix(key);

if let Some(values) = values {
let values = values
.iter()
// Escape and add quotes
.map(|value| format!("\"{}\"", value.escape_default()))
.collect::<Vec<_>>()
.join(", ");

println!("cargo::rustc-check-cfg=cfg({key}, values({values}))");
} else {
println!("cargo::rustc-check-cfg=cfg({key})");
}
}

fn define_cfg_variable(&self, key: String, value: Option<String>) {
let key = self.key_with_prefix(key);

if let Some(value) = &value {
println!("cargo::rustc-cfg={key}=\"{}\"", value.escape_default());
} else {
println!("cargo::rustc-cfg={key}");
}

let variable_cargo = format!("CARGO_CFG_{key}");
std::env::set_var(variable_cargo, value.unwrap_or("true".to_string()));
}

fn key_with_prefix(&self, key: String) -> String {
if let Some(prefix) = &self.prefix {
format!("{prefix}{key}")
} else {
key
}
}
}
3 changes: 3 additions & 0 deletions crates/qt-build-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@

#![allow(clippy::too_many_arguments)]

mod cfg;
pub use cfg::CfgGenerator;

mod error;
pub use error::QtBuildError;

Expand Down
Loading