aboutsummaryrefslogtreecommitdiff
path: root/embassy-executor/build_common.rs
blob: af6bb061801535c9b1180b96347a776962541aff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// NOTE: this file is copy-pasted between several Embassy crates, because there is no
// straightforward way to share this code:
// - it cannot be placed into the root of the repo and linked from each build.rs using `#[path =
// "../build_common.rs"]`, because `cargo publish` requires that all files published with a crate
// reside in the crate's directory,
// - it cannot be symlinked from `embassy-xxx/build_common.rs` to `../build_common.rs`, because
// symlinks don't work on Windows.

use std::collections::HashSet;
use std::env;

/// Helper for emitting cargo instruction for enabling configs (`cargo:rustc-cfg=X`) and declaring
/// them (`cargo:rust-check-cfg=cfg(X)`).
#[derive(Debug)]
pub struct CfgSet {
    enabled: HashSet<String>,
    declared: HashSet<String>,
}

impl CfgSet {
    pub fn new() -> Self {
        Self {
            enabled: HashSet::new(),
            declared: HashSet::new(),
        }
    }

    /// Enable a config, which can then be used in `#[cfg(...)]` for conditional compilation.
    ///
    /// All configs that can potentially be enabled should be unconditionally declared using
    /// [`Self::declare()`].
    pub fn enable(&mut self, cfg: impl AsRef<str>) {
        if self.enabled.insert(cfg.as_ref().to_owned()) {
            println!("cargo:rustc-cfg={}", cfg.as_ref());
        }
    }

    pub fn enable_all(&mut self, cfgs: &[impl AsRef<str>]) {
        for cfg in cfgs.iter() {
            self.enable(cfg.as_ref());
        }
    }

    /// Declare a valid config for conditional compilation, without enabling it.
    ///
    /// This enables rustc to check that the configs in `#[cfg(...)]` attributes are valid.
    pub fn declare(&mut self, cfg: impl AsRef<str>) {
        if self.declared.insert(cfg.as_ref().to_owned()) {
            println!("cargo:rustc-check-cfg=cfg({})", cfg.as_ref());
        }
    }

    pub fn declare_all(&mut self, cfgs: &[impl AsRef<str>]) {
        for cfg in cfgs.iter() {
            self.declare(cfg.as_ref());
        }
    }

    pub fn set(&mut self, cfg: impl Into<String>, enable: bool) {
        let cfg = cfg.into();
        if enable {
            self.enable(cfg.clone());
        }
        self.declare(cfg);
    }
}

/// Sets configs that describe the target platform.
pub fn set_target_cfgs(cfgs: &mut CfgSet) {
    let target = env::var("TARGET").unwrap();

    if target.starts_with("thumbv6m-") {
        cfgs.enable_all(&["cortex_m", "armv6m"]);
    } else if target.starts_with("thumbv7m-") {
        cfgs.enable_all(&["cortex_m", "armv7m"]);
    } else if target.starts_with("thumbv7em-") {
        cfgs.enable_all(&["cortex_m", "armv7m", "armv7em"]);
    } else if target.starts_with("thumbv8m.base") {
        cfgs.enable_all(&["cortex_m", "armv8m", "armv8m_base"]);
    } else if target.starts_with("thumbv8m.main") {
        cfgs.enable_all(&["cortex_m", "armv8m", "armv8m_main"]);
    }
    cfgs.declare_all(&[
        "cortex_m",
        "armv6m",
        "armv7m",
        "armv7em",
        "armv8m",
        "armv8m_base",
        "armv8m_main",
    ]);

    cfgs.set("has_fpu", target.ends_with("-eabihf"));
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct CompilerDate {
    year: u16,
    month: u8,
    day: u8,
}

impl CompilerDate {
    fn parse(date: &str) -> Option<Self> {
        let mut parts = date.split('-');
        let year = parts.next()?.parse().ok()?;
        let month = parts.next()?.parse().ok()?;
        let day = parts.next()?.parse().ok()?;
        Some(Self { year, month, day })
    }
}

impl PartialEq<&str> for CompilerDate {
    fn eq(&self, other: &&str) -> bool {
        let Some(other) = Self::parse(other) else {
            return false;
        };
        self.eq(&other)
    }
}

impl PartialOrd<&str> for CompilerDate {
    fn partial_cmp(&self, other: &&str) -> Option<std::cmp::Ordering> {
        Self::parse(other).map(|other| self.cmp(&other))
    }
}

pub struct CompilerInfo {
    #[allow(unused)]
    pub version: rustc_version::Version,
    pub channel: rustc_version::Channel,
    pub commit_date: Option<CompilerDate>,
}

pub fn compiler_info() -> Option<CompilerInfo> {
    let Ok(meta) = rustc_version::version_meta() else {
        return None;
    };

    Some(CompilerInfo {
        version: meta.semver,
        channel: meta.channel,
        commit_date: meta.commit_date.as_deref().and_then(CompilerDate::parse),
    })
}