qt_build_utils/
cfg.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 semver::Version;
7use std::ops::RangeInclusive;
8
9/// Helper for generating cargo cfg for a version range
10pub struct CfgGenerator {
11    prefix: Option<String>,
12    range_major: RangeInclusive<u64>,
13    range_minor: RangeInclusive<u64>,
14    version: Version,
15}
16
17impl CfgGenerator {
18    /// Construct a new [CfgGenerator]
19    pub fn new(version: Version) -> Self {
20        Self {
21            range_major: (0..=99),
22            range_minor: (0..=99),
23            prefix: None,
24            version,
25        }
26    }
27
28    /// Specify a prefix for the [CfgGenerator]
29    pub fn prefix(mut self, prefix: impl Into<String>) -> Self {
30        self.prefix = Some(prefix.into());
31        self
32    }
33
34    /// Specify a major range for the [CfgGenerator]
35    pub fn range_major(mut self, range: RangeInclusive<u64>) -> Self {
36        self.range_major = range;
37        self
38    }
39
40    /// Specify a minor range for the [CfgGenerator]
41    pub fn range_minor(mut self, range: RangeInclusive<u64>) -> Self {
42        self.range_minor = range;
43        self
44    }
45
46    /// Generate cargo cfg with any given prefix and ranges for the version
47    pub fn build(self) {
48        // Define the major version
49        self.define_cfg_check_variable(
50            "qt_version_major".to_string(),
51            Some(
52                self.range_major
53                    .clone()
54                    .map(|major| major.to_string())
55                    .collect(),
56            ),
57        );
58        self.define_cfg_variable(
59            "qt_version_major".to_string(),
60            Some(self.version.major.to_string()),
61        );
62
63        // Tell cargo about all the possible cfg variables
64        for major in self.range_major.clone() {
65            self.define_cfg_check_variable(format!("qt_version_at_least_{major}"), None);
66
67            for minor in self.range_minor.clone() {
68                self.define_cfg_check_variable(
69                    format!("qt_version_at_least_{major}_{minor}"),
70                    None,
71                );
72            }
73        }
74
75        // Tell cargo which major versions have been reached
76        for major in *self.range_major.start()..=self.version.major {
77            self.define_cfg_variable(format!("qt_version_at_least_{major}"), None);
78        }
79
80        // Tell cargo which minor versions with the major have been reached
81        for minor in *self.range_minor.start()..=self.version.minor {
82            let major = self.version.major;
83            self.define_cfg_variable(format!("qt_version_at_least_{major}_{minor}"), None);
84        }
85    }
86}
87
88impl CfgGenerator {
89    fn define_cfg_check_variable(&self, key: String, values: Option<Vec<String>>) {
90        let key = self.key_with_prefix(key);
91
92        if let Some(values) = values {
93            let values = values
94                .iter()
95                // Escape and add quotes
96                .map(|value| format!("\"{}\"", value.escape_default()))
97                .collect::<Vec<_>>()
98                .join(", ");
99
100            println!("cargo::rustc-check-cfg=cfg({key}, values({values}))");
101        } else {
102            println!("cargo::rustc-check-cfg=cfg({key})");
103        }
104    }
105
106    fn define_cfg_variable(&self, key: String, value: Option<String>) {
107        let key = self.key_with_prefix(key);
108
109        if let Some(value) = &value {
110            println!("cargo::rustc-cfg={key}=\"{}\"", value.escape_default());
111        } else {
112            println!("cargo::rustc-cfg={key}");
113        }
114
115        let variable_cargo = format!("CARGO_CFG_{key}");
116        std::env::set_var(variable_cargo, value.unwrap_or("true".to_string()));
117    }
118
119    fn key_with_prefix(&self, key: String) -> String {
120        if let Some(prefix) = &self.prefix {
121            format!("{prefix}{key}")
122        } else {
123            key
124        }
125    }
126}