1use crate::driver::EmitKind;
9use crate::llvm::{FreestandingOptions, Runtime};
10
11#[derive(Debug, Clone, PartialEq, Eq)]
13pub enum RuntimeAbi {
14 Hosted,
16
17 Freestanding(FreestandingOptions),
19}
20
21impl RuntimeAbi {
22 pub fn is_freestanding(&self) -> bool {
24 matches!(self, Self::Freestanding(_))
25 }
26
27 pub fn to_llvm_runtime(&self) -> Runtime {
29 match self {
30 Self::Hosted => Runtime::Hosted,
31 Self::Freestanding(options) => Runtime::Freestanding(options.clone()),
32 }
33 }
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum RuntimeAbiKind {
39 Hosted,
41
42 Freestanding,
44}
45
46impl RuntimeAbiKind {
47 fn into_abi(self) -> RuntimeAbi {
48 match self {
49 Self::Hosted => RuntimeAbi::Hosted,
50 Self::Freestanding => RuntimeAbi::Freestanding(FreestandingOptions::default()),
51 }
52 }
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum TargetImageFormat {
58 Gba,
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub struct TargetPreset {
65 pub name: &'static str,
67
68 pub description: &'static str,
70
71 pub llvm_triple: Option<&'static str>,
73
74 pub runtime_abi: RuntimeAbiKind,
76
77 pub clang_args: &'static [&'static str],
79
80 pub default_emit: EmitKind,
82
83 pub image_format: Option<TargetImageFormat>,
85}
86
87impl TargetPreset {
88 pub fn profile(self) -> TargetProfile {
90 TargetProfile {
91 name: self.name.to_string(),
92 description: self.description.to_string(),
93 llvm_triple: self.llvm_triple.map(ToString::to_string),
94 runtime_abi: self.runtime_abi.into_abi(),
95 clang_args: self.clang_args.iter().map(ToString::to_string).collect(),
96 default_emit: self.default_emit,
97 image_format: self.image_format,
98 }
99 }
100}
101
102#[derive(Debug, Clone, PartialEq, Eq)]
104pub struct TargetProfile {
105 name: String,
106 description: String,
107 llvm_triple: Option<String>,
108 runtime_abi: RuntimeAbi,
109 clang_args: Vec<String>,
110 default_emit: EmitKind,
111 image_format: Option<TargetImageFormat>,
112}
113
114impl TargetProfile {
115 pub fn native() -> Self {
117 known_targets()[0].profile()
118 }
119
120 pub fn resolve(value: &str) -> Self {
122 find_target(value)
123 .map(TargetPreset::profile)
124 .unwrap_or_else(|| Self::raw_llvm_triple(value))
125 }
126
127 pub fn raw_llvm_triple(triple: &str) -> Self {
129 Self {
130 name: triple.to_string(),
131 description: "raw LLVM target triple".to_string(),
132 llvm_triple: Some(triple.to_string()),
133 runtime_abi: RuntimeAbi::Hosted,
134 clang_args: Vec::new(),
135 default_emit: EmitKind::Executable,
136 image_format: None,
137 }
138 }
139
140 pub fn custom(
142 name: impl Into<String>,
143 description: impl Into<String>,
144 llvm_triple: Option<String>,
145 runtime_abi: RuntimeAbi,
146 clang_args: Vec<String>,
147 default_emit: EmitKind,
148 ) -> Self {
149 Self {
150 name: name.into(),
151 description: description.into(),
152 llvm_triple,
153 runtime_abi,
154 clang_args,
155 default_emit,
156 image_format: None,
157 }
158 }
159
160 pub fn name(&self) -> &str {
162 &self.name
163 }
164
165 pub fn description(&self) -> &str {
167 &self.description
168 }
169
170 pub fn llvm_triple(&self) -> Option<&str> {
172 self.llvm_triple.as_deref()
173 }
174
175 pub fn runtime_abi(&self) -> &RuntimeAbi {
177 &self.runtime_abi
178 }
179
180 pub fn is_freestanding(&self) -> bool {
182 self.runtime_abi.is_freestanding()
183 }
184
185 pub fn clang_args(&self) -> &[String] {
187 &self.clang_args
188 }
189
190 pub fn default_emit(&self) -> EmitKind {
192 self.default_emit
193 }
194
195 pub fn image_format(&self) -> Option<TargetImageFormat> {
197 self.image_format
198 }
199
200 pub fn set_runtime_abi(&mut self, runtime_abi: RuntimeAbi) {
202 self.runtime_abi = runtime_abi;
203 if self.runtime_abi.is_freestanding() && self.default_emit == EmitKind::Executable {
204 self.default_emit = EmitKind::Object;
205 }
206 }
207
208 pub fn with_runtime_abi(mut self, runtime_abi: RuntimeAbi) -> Self {
210 self.set_runtime_abi(runtime_abi);
211 self
212 }
213
214 pub fn with_image_format(mut self, image_format: Option<TargetImageFormat>) -> Self {
216 self.image_format = image_format;
217 self
218 }
219}
220
221pub fn known_targets() -> &'static [TargetPreset] {
223 &TARGETS
224}
225
226pub fn find_target(name: &str) -> Option<TargetPreset> {
228 known_targets()
229 .iter()
230 .copied()
231 .find(|target| target.name == name)
232}
233
234const TARGETS: [TargetPreset; 5] = [
235 TargetPreset {
236 name: "native",
237 description: "hosted executable/JIT on the host LLVM default target",
238 llvm_triple: None,
239 runtime_abi: RuntimeAbiKind::Hosted,
240 clang_args: &[],
241 default_emit: EmitKind::Executable,
242 image_format: None,
243 },
244 TargetPreset {
245 name: "x86_64-none",
246 description: "x86_64 freestanding object for a caller-provided runtime",
247 llvm_triple: Some("x86_64-unknown-none"),
248 runtime_abi: RuntimeAbiKind::Freestanding,
249 clang_args: &[],
250 default_emit: EmitKind::Object,
251 image_format: None,
252 },
253 TargetPreset {
254 name: "i386-none",
255 description: "32-bit x86 freestanding object for tiny boot/runtime layers",
256 llvm_triple: Some("i386-unknown-none"),
257 runtime_abi: RuntimeAbiKind::Freestanding,
258 clang_args: &[],
259 default_emit: EmitKind::Object,
260 image_format: None,
261 },
262 TargetPreset {
263 name: "nds-arm9",
264 description: "Nintendo DS ARM9 freestanding payload object",
265 llvm_triple: Some("armv5te-none-eabi"),
266 runtime_abi: RuntimeAbiKind::Freestanding,
267 clang_args: &["-mcpu=arm946e-s"],
268 default_emit: EmitKind::Object,
269 image_format: None,
270 },
271 TargetPreset {
272 name: "gba",
273 description: "Game Boy Advance ARM7TDMI/Thumb ROM image",
274 llvm_triple: Some("thumbv4t-none-eabi"),
275 runtime_abi: RuntimeAbiKind::Freestanding,
276 clang_args: &["-mcpu=arm7tdmi", "-mthumb"],
277 default_emit: EmitKind::Image,
278 image_format: Some(TargetImageFormat::Gba),
279 },
280];
281
282#[cfg(test)]
283mod tests {
284 use super::*;
285
286 #[test]
287 fn resolves_known_native_target() {
288 let target = TargetProfile::resolve("native");
289
290 assert_eq!(target.name(), "native");
291 assert_eq!(target.llvm_triple(), None);
292 assert!(!target.is_freestanding());
293 assert_eq!(target.default_emit(), EmitKind::Executable);
294 }
295
296 #[test]
297 fn resolves_raw_triples_as_hosted_targets() {
298 let target = TargetProfile::resolve("x86_64-unknown-linux-gnu");
299
300 assert_eq!(target.name(), "x86_64-unknown-linux-gnu");
301 assert_eq!(target.llvm_triple(), Some("x86_64-unknown-linux-gnu"));
302 assert!(!target.is_freestanding());
303 assert_eq!(target.default_emit(), EmitKind::Executable);
304 }
305
306 #[test]
307 fn gba_is_freestanding_thumb_target() {
308 let target = TargetProfile::resolve("gba");
309
310 assert_eq!(target.llvm_triple(), Some("thumbv4t-none-eabi"));
311 assert!(target.is_freestanding());
312 assert_eq!(target.default_emit(), EmitKind::Image);
313 assert_eq!(target.image_format(), Some(TargetImageFormat::Gba));
314 assert!(target.clang_args().iter().any(|arg| arg == "-mthumb"));
315 }
316
317 #[test]
318 fn nds_arm9_is_freestanding_object_target() {
319 let target = TargetProfile::resolve("nds-arm9");
320
321 assert_eq!(target.llvm_triple(), Some("armv5te-none-eabi"));
322 assert!(target.is_freestanding());
323 assert_eq!(target.default_emit(), EmitKind::Object);
324 assert_eq!(target.image_format(), None);
325 assert!(
326 target
327 .clang_args()
328 .iter()
329 .any(|arg| arg == "-mcpu=arm946e-s")
330 );
331 }
332
333 #[test]
334 fn builds_custom_freestanding_target() {
335 let target = TargetProfile::custom(
336 "weird-board",
337 "custom board",
338 Some("thumbv7em-none-eabi".to_string()),
339 RuntimeAbi::Freestanding(FreestandingOptions::default()),
340 vec!["-mcpu=cortex-m4".to_string()],
341 EmitKind::Object,
342 );
343
344 assert_eq!(target.name(), "weird-board");
345 assert_eq!(target.llvm_triple(), Some("thumbv7em-none-eabi"));
346 assert!(target.is_freestanding());
347 assert_eq!(target.clang_args(), ["-mcpu=cortex-m4"]);
348 }
349}