1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct Target {
11 pub triple: &'static str,
13 pub os: &'static str,
15 pub cpu: &'static str,
17 pub libc: Option<&'static str>,
19 pub wheel_platform: &'static str,
26 pub exe_suffix: &'static str,
28}
29
30impl Target {
31 pub const ALL: &'static [Target] = &[
33 Target {
34 triple: "aarch64-apple-darwin",
35 os: "darwin",
36 cpu: "arm64",
37 libc: None,
38 wheel_platform: "macosx_11_0_arm64",
39 exe_suffix: "",
40 },
41 Target {
42 triple: "aarch64-pc-windows-msvc",
43 os: "win32",
44 cpu: "arm64",
45 libc: None,
46 wheel_platform: "win_arm64",
47 exe_suffix: ".exe",
48 },
49 Target {
50 triple: "aarch64-unknown-linux-gnu",
51 os: "linux",
52 cpu: "arm64",
53 libc: Some("glibc"),
54 wheel_platform: "manylinux_2_17_aarch64",
55 exe_suffix: "",
56 },
57 Target {
58 triple: "aarch64-unknown-linux-musl",
59 os: "linux",
60 cpu: "arm64",
61 libc: Some("musl"),
62 wheel_platform: "musllinux_1_2_aarch64",
63 exe_suffix: "",
64 },
65 Target {
66 triple: "x86_64-apple-darwin",
67 os: "darwin",
68 cpu: "x64",
69 libc: None,
70 wheel_platform: "macosx_10_12_x86_64",
71 exe_suffix: "",
72 },
73 Target {
74 triple: "x86_64-pc-windows-msvc",
75 os: "win32",
76 cpu: "x64",
77 libc: None,
78 wheel_platform: "win_amd64",
79 exe_suffix: ".exe",
80 },
81 Target {
82 triple: "x86_64-unknown-linux-gnu",
83 os: "linux",
84 cpu: "x64",
85 libc: Some("glibc"),
86 wheel_platform: "manylinux_2_17_x86_64",
87 exe_suffix: "",
88 },
89 Target {
90 triple: "x86_64-unknown-linux-musl",
91 os: "linux",
92 cpu: "x64",
93 libc: Some("musl"),
94 wheel_platform: "musllinux_1_2_x86_64",
95 exe_suffix: "",
96 },
97 ];
98
99 pub const DEFAULT: &'static [&'static str] = &[
103 "aarch64-apple-darwin",
104 "aarch64-unknown-linux-gnu",
105 "x86_64-apple-darwin",
106 "x86_64-pc-windows-msvc",
107 "x86_64-unknown-linux-gnu",
108 ];
109
110 pub fn find(triple: &str) -> Option<&'static Target> {
112 Target::ALL.iter().find(|t| t.triple == triple)
113 }
114
115 pub fn matrix(configured: &[String]) -> Result<Vec<&'static Target>, String> {
118 let names: Vec<&str> = if configured.is_empty() {
119 Target::DEFAULT.to_vec()
120 } else {
121 configured.iter().map(String::as_str).collect()
122 };
123 let mut out = Vec::with_capacity(names.len());
124 for name in names {
125 let target = Target::find(name).ok_or_else(|| {
126 let known: Vec<&str> = Target::ALL.iter().map(|t| t.triple).collect();
127 format!(
128 "unknown target {name:?} (expected one of {})",
129 known.join(", ")
130 )
131 })?;
132 if !out.contains(&target) {
133 out.push(target);
134 }
135 }
136 out.sort_by_key(|t| t.triple);
137 Ok(out)
138 }
139
140 pub fn npm_suffix(&self) -> String {
143 match self.libc {
144 Some("musl") => format!("{}-{}-musl", self.os, self.cpu),
145 _ => format!("{}-{}", self.os, self.cpu),
146 }
147 }
148
149 pub fn wheel_tag(&self) -> String {
151 format!("py3-none-{}", self.wheel_platform)
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[test]
160 fn the_table_is_sorted_and_every_triple_is_distinct() {
161 let triples: Vec<&str> = Target::ALL.iter().map(|t| t.triple).collect();
162 let mut sorted = triples.clone();
163 sorted.sort_unstable();
164 sorted.dedup();
165 assert_eq!(triples, sorted, "keep ALL sorted and unique");
166 }
167
168 #[test]
169 fn the_default_matrix_is_all_resolvable() {
170 for triple in Target::DEFAULT {
171 assert!(Target::find(triple).is_some(), "{triple}");
172 }
173 }
174
175 #[test]
176 fn npm_suffixes_match_what_the_ecosystem_publishes() {
177 let suffix = |t: &str| Target::find(t).expect("known").npm_suffix();
178 assert_eq!(suffix("x86_64-unknown-linux-gnu"), "linux-x64");
179 assert_eq!(suffix("aarch64-apple-darwin"), "darwin-arm64");
180 assert_eq!(suffix("x86_64-pc-windows-msvc"), "win32-x64");
181 assert_eq!(suffix("x86_64-unknown-linux-musl"), "linux-x64-musl");
182 }
183
184 #[test]
185 fn glibc_and_musl_linux_never_collide() {
186 let gnu = Target::find("x86_64-unknown-linux-gnu").expect("known");
187 let musl = Target::find("x86_64-unknown-linux-musl").expect("known");
188 assert_ne!(gnu.npm_suffix(), musl.npm_suffix());
189 assert_ne!(gnu.wheel_tag(), musl.wheel_tag());
190 }
191
192 #[test]
193 fn an_empty_matrix_falls_back_to_the_default() {
194 let matrix = Target::matrix(&[]).expect("defaults resolve");
195 assert_eq!(matrix.len(), Target::DEFAULT.len());
196 }
197
198 #[test]
199 fn a_repeated_target_is_only_built_once() {
200 let matrix = Target::matrix(&[
201 "x86_64-unknown-linux-gnu".into(),
202 "x86_64-unknown-linux-gnu".into(),
203 ])
204 .expect("resolves");
205 assert_eq!(matrix.len(), 1);
206 }
207
208 #[test]
209 fn an_unknown_target_lists_the_ones_that_exist() {
210 let err = Target::matrix(&["sparc-unknown-none".into()]).expect_err("rejected");
211 assert!(err.contains("sparc"), "{err}");
212 assert!(err.contains("x86_64-unknown-linux-gnu"), "{err}");
213 }
214}