arm_targets/
lib.rs

1//! Useful cfg helpers for when you are building Arm code
2//!
3//! Hopefully Rust will stabilise these kinds of target features in the
4//! future, and this won't be required. But until this, arm-targets is here to
5//! help you conditionally compile your code based on the specific Arm
6//! platform you are compiling for.
7//!
8//! In your application, do something like this:
9//!
10//! ```console
11//! $ cargo add --build arm-targets
12//! $ cat > build.rs << EOF
13//! fn main() {
14//!     arm_targets::process();
15//! }
16//! EOF
17//! ```
18//!
19//! This will then let you write application code like:
20//!
21//! ```rust
22//! #[cfg(arm_architecture = "armv7m")]
23//! fn only_for_cortex_m3() { }
24//!
25//! #[cfg(arm_isa = "a32")]
26//! fn can_use_arm_32bit_asm_here() { }
27//! ```
28//!
29//! Without this crate, you are limited to `cfg(target_arch = "arm")`, which
30//! isn't all that useful given how many 'Arm' targets there are.
31//!
32//! To see a full list of the features created by this crate, run the CLI tool:
33//!
34//! ```console
35//! $ cargo install arm-targets
36//! $ arm-targets
37//! cargo:rustc-check-cfg=cfg(arm_isa, values("a64", "a32", "t32"))
38//! cargo:rustc-check-cfg=cfg(arm_architecture, values("v4t", "v5te", "v6-m", "v7-m", "v7e-m", "v8-m.base", "v8-m.main", "v7-r", "v8-r", "v7-a", "v8-a"))
39//! cargo:rustc-check-cfg=cfg(arm_profile, values("a", "r", "m", "legacy"))
40//! cargo:rustc-check-cfg=cfg(arm_abi, values("eabi", "eabihf"))
41//! ```
42
43#[derive(Default)]
44pub struct TargetInfo {
45    isa: Option<Isa>,
46    arch: Option<Arch>,
47    profile: Option<Profile>,
48    abi: Option<Abi>,
49}
50
51impl TargetInfo {
52    /// Get the Arm Instruction Set Architecture of the target
53    pub fn isa(&self) -> Option<Isa> {
54        self.isa
55    }
56
57    /// Get the Arm Architecture version of the target
58    pub fn arch(&self) -> Option<Arch> {
59        self.arch
60    }
61
62    /// Get the Arm Architecture Profile of the target
63    pub fn profile(&self) -> Option<Profile> {
64        self.profile
65    }
66
67    /// Get the ABI of the target
68    pub fn abi(&self) -> Option<Abi> {
69        self.abi
70    }
71}
72
73/// Process the ${TARGET} environment variable, and emit cargo configuration to
74/// standard out.
75pub fn process() -> TargetInfo {
76    let target = std::env::var("TARGET").expect("build script TARGET variable");
77    process_target(&target)
78}
79
80/// Process a given target string, and emit cargo configuration to standard out.
81pub fn process_target(target: &str) -> TargetInfo {
82    let mut target_info = TargetInfo::default();
83    if let Some(isa) = Isa::get(target) {
84        println!(r#"cargo:rustc-cfg=arm_isa="{}""#, isa);
85        target_info.isa = Some(isa);
86    }
87    println!(
88        r#"cargo:rustc-check-cfg=cfg(arm_isa, values({}))"#,
89        Isa::values()
90    );
91
92    if let Some(arch) = Arch::get(target) {
93        println!(r#"cargo:rustc-cfg=arm_architecture="{}""#, arch);
94        target_info.arch = Some(arch);
95    }
96    println!(
97        r#"cargo:rustc-check-cfg=cfg(arm_architecture, values({}))"#,
98        Arch::values()
99    );
100
101    if let Some(profile) = Profile::get(target) {
102        println!(r#"cargo:rustc-cfg=arm_profile="{}""#, profile);
103        target_info.profile = Some(profile);
104    }
105    println!(
106        r#"cargo:rustc-check-cfg=cfg(arm_profile, values({}))"#,
107        Profile::values()
108    );
109
110    if let Some(abi) = Abi::get(target) {
111        println!(r#"cargo:rustc-cfg=arm_abi="{}""#, abi);
112        target_info.abi = Some(abi);
113    }
114    println!(
115        r#"cargo:rustc-check-cfg=cfg(arm_abi, values({}))"#,
116        Abi::values()
117    );
118
119    target_info
120}
121
122/// The Arm Instruction Set
123#[derive(Debug, Copy, Clone, PartialEq, Eq)]
124pub enum Isa {
125    /// A64 instructions are executed by Arm processors in Aarch64 mode
126    A64,
127    /// A32 instructions are executed by Arm processors in Aarch32 Arm mode
128    A32,
129    /// T32 instructions are executed by Arm processors in Aarch32 Thumb mode
130    T32,
131}
132
133impl Isa {
134    /// Decode a target string
135    pub fn get(target: &str) -> Option<Isa> {
136        if target.starts_with("arm") {
137            Some(Isa::A32)
138        } else if target.starts_with("thumb") {
139            Some(Isa::T32)
140        } else if target.starts_with("aarch64") {
141            Some(Isa::A64)
142        } else {
143            None
144        }
145    }
146
147    /// Get a comma-separated list of values, suitable for cfg-check
148    pub fn values() -> String {
149        let string_versions: Vec<String> = [Isa::A64, Isa::A32, Isa::T32]
150            .iter()
151            .map(|i| format!(r#""{i}""#))
152            .collect();
153        string_versions.join(", ")
154    }
155}
156
157impl core::fmt::Display for Isa {
158    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159        write!(
160            f,
161            "{}",
162            match self {
163                Isa::A64 => "a64",
164                Isa::A32 => "a32",
165                Isa::T32 => "t32",
166            }
167        )
168    }
169}
170
171/// The Arm Architecture
172///
173/// As defined by a particular revision of the Arm Architecture Reference Manual (ARM).
174#[derive(Debug, Copy, Clone, PartialEq, Eq)]
175pub enum Arch {
176    /// Arm Architecture version 4, with Thumb support (e.g. ARM7TDMI)
177    Armv4T,
178    /// Arm Architecture version 5, with Thumb support and Enhanced DSP Instructions (e.g. ARM926EJ-S)
179    Armv5TE,
180    /// Arm Architecture version 6 (e.g. ARM1176JZF-S)
181    Armv6,
182    /// Armv6-M (e.g. Cortex-M0+)
183    Armv6M,
184    /// Armv7-M (e.g. Cortex-M3)
185    Armv7M,
186    /// Armv7E-M (e.g. Cortex-M4)
187    Armv7EM,
188    /// Armv8-M Baseline (e.g. Cortex-M23)
189    Armv8MBase,
190    /// Armv8-M with Mainline extensions (e.g. Cortex-M33)
191    Armv8MMain,
192    /// Armv7-R (e.g. Cortex-R5)
193    Armv7R,
194    /// Armv8-R (e.g. Cortex-R52)
195    Armv8R,
196    /// Armv7-A (e.g. Cortex-A8)
197    Armv7A,
198    /// Armv8-A (e.g. Cortex-A53)
199    Armv8A,
200}
201
202impl Arch {
203    /// Decode a target string
204    pub fn get(target: &str) -> Option<Arch> {
205        if target.starts_with("armv4t-") || target.starts_with("thumbv4t-") {
206            Some(Arch::Armv4T)
207        } else if target.starts_with("armv5te-") || target.starts_with("thumbv5te-") {
208            Some(Arch::Armv5TE)
209        } else if target.starts_with("thumbv6m-") {
210            Some(Arch::Armv6M)
211        } else if target.starts_with("thumbv7m-") {
212            Some(Arch::Armv7M)
213        } else if target.starts_with("thumbv7em-") {
214            Some(Arch::Armv7EM)
215        } else if target.starts_with("thumbv8m.base-") {
216            Some(Arch::Armv8MBase)
217        } else if target.starts_with("thumbv8m.main-") {
218            Some(Arch::Armv8MMain)
219        } else if target.starts_with("armv7r-") || target.starts_with("armebv7r") {
220            Some(Arch::Armv7R)
221        } else if target.starts_with("armv8r-") {
222            Some(Arch::Armv8R)
223        } else if target.starts_with("armv7a-") {
224            Some(Arch::Armv7A)
225        } else if target.starts_with("aarch64-") || target.starts_with("aarch64be-") {
226            Some(Arch::Armv8A)
227        } else if target.starts_with("arm-") {
228            // If not specified, assume Armv6
229            Some(Arch::Armv6)
230        } else {
231            None
232        }
233    }
234
235    /// Get the Arm Architecture Profile
236    pub fn profile(&self) -> Profile {
237        match self {
238            Arch::Armv6M | Arch::Armv7M | Arch::Armv7EM | Arch::Armv8MBase | Arch::Armv8MMain => {
239                Profile::M
240            }
241            Arch::Armv4T | Arch::Armv5TE | Arch::Armv6 => Profile::Legacy,
242            Arch::Armv7R | Arch::Armv8R => Profile::R,
243            Arch::Armv7A | Arch::Armv8A => Profile::A,
244        }
245    }
246
247    /// Get a comma-separated list of values, suitable for cfg-check
248    pub fn values() -> String {
249        let string_versions: Vec<String> = [
250            Arch::Armv4T,
251            Arch::Armv5TE,
252            Arch::Armv6,
253            Arch::Armv6M,
254            Arch::Armv7M,
255            Arch::Armv7EM,
256            Arch::Armv8MBase,
257            Arch::Armv8MMain,
258            Arch::Armv7R,
259            Arch::Armv8R,
260            Arch::Armv7A,
261            Arch::Armv8A,
262        ]
263        .iter()
264        .map(|i| format!(r#""{i}""#))
265        .collect();
266        string_versions.join(", ")
267    }
268}
269
270impl core::fmt::Display for Arch {
271    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
272        write!(
273            f,
274            "{}",
275            match self {
276                Arch::Armv4T => "v4t",
277                Arch::Armv5TE => "v5te",
278                Arch::Armv6 => "v6",
279                Arch::Armv6M => "v6-m",
280                Arch::Armv7M => "v7-m",
281                Arch::Armv7EM => "v7e-m",
282                Arch::Armv7R => "v7-r",
283                Arch::Armv8R => "v8-r",
284                Arch::Armv8MBase => "v8-m.base",
285                Arch::Armv8MMain => "v8-m.main",
286                Arch::Armv7A => "v7-a",
287                Arch::Armv8A => "v8-a",
288            }
289        )
290    }
291}
292
293/// The Arm Architecture Profile.
294#[derive(Debug, Copy, Clone, PartialEq, Eq)]
295pub enum Profile {
296    /// Microcontrollers
297    M,
298    /// Real-Time
299    R,
300    /// Applications
301    A,
302    /// Legacy
303    Legacy,
304}
305
306impl Profile {
307    /// Decode a target string
308    pub fn get(target: &str) -> Option<Profile> {
309        let arch = Arch::get(target)?;
310        Some(arch.profile())
311    }
312
313    /// Get a comma-separated list of values, suitable for cfg-check
314    pub fn values() -> String {
315        let string_versions: Vec<String> = [Profile::A, Profile::R, Profile::M, Profile::Legacy]
316            .iter()
317            .map(|i| format!(r#""{i}""#))
318            .collect();
319        string_versions.join(", ")
320    }
321}
322
323impl core::fmt::Display for Profile {
324    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
325        write!(
326            f,
327            "{}",
328            match self {
329                Profile::M => "m",
330                Profile::R => "r",
331                Profile::A => "a",
332                Profile::Legacy => "legacy",
333            }
334        )
335    }
336}
337
338/// The ABI
339#[derive(Debug, Copy, Clone, PartialEq, Eq)]
340pub enum Abi {
341    /// Arm Embedded ABI
342    Eabi,
343    /// Arm Embedded ABI with Hard Float
344    EabiHf,
345}
346
347impl Abi {
348    /// Decode a target string
349    pub fn get(target: &str) -> Option<Abi> {
350        if Arch::get(target).is_none() {
351            // Don't give an ABI for non-Arm targets
352            //
353            // e.g. PowerPC also has an ABI called EABI, but it's not the same
354            return None;
355        }
356        if target.ends_with("eabi") {
357            Some(Abi::Eabi)
358        } else if target.ends_with("eabihf") {
359            Some(Abi::EabiHf)
360        } else {
361            None
362        }
363    }
364
365    /// Get a comma-separated list of values, suitable for cfg-check
366    pub fn values() -> String {
367        let string_versions: Vec<String> = [Abi::Eabi, Abi::EabiHf]
368            .iter()
369            .map(|i| format!(r#""{i}""#))
370            .collect();
371        string_versions.join(", ")
372    }
373}
374
375impl core::fmt::Display for Abi {
376    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
377        write!(
378            f,
379            "{}",
380            match self {
381                Abi::Eabi => "eabi",
382                Abi::EabiHf => "eabihf",
383            }
384        )
385    }
386}
387
388#[cfg(test)]
389mod test {
390    use super::*;
391
392    #[test]
393    fn armv4t_none_eabi() {
394        let target = "armv4t-none-eabi";
395        let target_info = process_target(target);
396        assert_eq!(target_info.isa(), Some(Isa::A32));
397        assert_eq!(target_info.arch(), Some(Arch::Armv4T));
398        assert_eq!(target_info.profile(), Some(Profile::Legacy));
399        assert_eq!(target_info.abi(), Some(Abi::Eabi));
400    }
401
402    #[test]
403    fn armv5te_none_eabi() {
404        let target = "armv5te-none-eabi";
405        let target_info = process_target(target);
406        assert_eq!(target_info.isa(), Some(Isa::A32));
407        assert_eq!(target_info.arch(), Some(Arch::Armv5TE));
408        assert_eq!(target_info.profile(), Some(Profile::Legacy));
409        assert_eq!(target_info.abi(), Some(Abi::Eabi));
410    }
411
412    #[test]
413    fn arm_unknown_linux_gnueabi() {
414        let target = "arm-unknown-linux-gnueabi";
415        let target_info = process_target(target);
416        assert_eq!(target_info.isa(), Some(Isa::A32));
417        assert_eq!(target_info.arch(), Some(Arch::Armv6));
418        assert_eq!(target_info.profile(), Some(Profile::Legacy));
419        assert_eq!(target_info.abi(), Some(Abi::Eabi));
420    }
421
422    #[test]
423    fn thumbv6m_none_eabi() {
424        let target = "thumbv6m-none-eabi";
425        let target_info = process_target(target);
426        assert_eq!(target_info.isa(), Some(Isa::T32));
427        assert_eq!(target_info.arch(), Some(Arch::Armv6M));
428        assert_eq!(target_info.profile(), Some(Profile::M));
429        assert_eq!(target_info.abi(), Some(Abi::Eabi));
430    }
431
432    #[test]
433    fn thumbv7m_none_eabi() {
434        let target = "thumbv7m-none-eabi";
435        let target_info = process_target(target);
436        assert_eq!(target_info.isa(), Some(Isa::T32));
437        assert_eq!(target_info.arch(), Some(Arch::Armv7M));
438        assert_eq!(target_info.profile(), Some(Profile::M));
439        assert_eq!(target_info.abi(), Some(Abi::Eabi));
440    }
441
442    #[test]
443    fn thumbv7em_nuttx_eabihf() {
444        let target = "thumbv7em-nuttx-eabihf";
445        let target_info = process_target(target);
446        assert_eq!(target_info.isa(), Some(Isa::T32));
447        assert_eq!(target_info.arch(), Some(Arch::Armv7EM));
448        assert_eq!(target_info.profile(), Some(Profile::M));
449        assert_eq!(target_info.abi(), Some(Abi::EabiHf));
450    }
451
452    #[test]
453    fn thumbv8m_base_none_eabi() {
454        let target = "thumbv8m.base-none-eabi";
455        let target_info = process_target(target);
456        assert_eq!(target_info.isa(), Some(Isa::T32));
457        assert_eq!(target_info.arch(), Some(Arch::Armv8MBase));
458        assert_eq!(target_info.profile(), Some(Profile::M));
459        assert_eq!(target_info.abi(), Some(Abi::Eabi));
460    }
461
462    #[test]
463    fn thumbv8m_main_none_eabihf() {
464        let target = "thumbv8m.main-none-eabihf";
465        let target_info = process_target(target);
466        assert_eq!(target_info.isa(), Some(Isa::T32));
467        assert_eq!(target_info.arch(), Some(Arch::Armv8MMain));
468        assert_eq!(target_info.profile(), Some(Profile::M));
469        assert_eq!(target_info.abi(), Some(Abi::EabiHf));
470    }
471
472    #[test]
473    fn armv7r_none_eabi() {
474        let target = "armv7r-none-eabi";
475        let target_info = process_target(target);
476        assert_eq!(target_info.isa(), Some(Isa::A32));
477        assert_eq!(target_info.arch(), Some(Arch::Armv7R));
478        assert_eq!(target_info.profile(), Some(Profile::R));
479        assert_eq!(target_info.abi(), Some(Abi::Eabi));
480    }
481
482    #[test]
483    fn armv8r_none_eabihf() {
484        let target = "armv8r-none-eabihf";
485        let target_info = process_target(target);
486        assert_eq!(target_info.isa(), Some(Isa::A32));
487        assert_eq!(target_info.arch(), Some(Arch::Armv8R));
488        assert_eq!(target_info.profile(), Some(Profile::R));
489        assert_eq!(target_info.abi(), Some(Abi::EabiHf));
490    }
491
492    #[test]
493    fn armv7a_none_eabi() {
494        let target = "armv7a-none-eabi";
495        let target_info = process_target(target);
496        assert_eq!(target_info.isa(), Some(Isa::A32));
497        assert_eq!(target_info.arch(), Some(Arch::Armv7A));
498        assert_eq!(target_info.profile(), Some(Profile::A));
499        assert_eq!(target_info.abi(), Some(Abi::Eabi));
500    }
501
502    #[test]
503    fn aarch64_none_eabihf() {
504        let target = "aarch64-unknown-none";
505        let target_info = process_target(target);
506        assert_eq!(target_info.isa(), Some(Isa::A64));
507        assert_eq!(target_info.arch(), Some(Arch::Armv8A));
508        assert_eq!(target_info.profile(), Some(Profile::A));
509        assert_eq!(target_info.abi(), None);
510    }
511}