1use std::fmt;
55
56#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
62pub enum Arch {
63 X86,
65 X86_64,
67 Aarch64,
69 Arm,
71 Riscv32,
73 Riscv64,
75 Wasm32,
77 PowerPc,
79 PowerPc64,
81 S390x,
83 Mips,
85 Mips64,
87 Sparc,
89 Sparc64,
91 LoongArch64,
93}
94
95impl Arch {
96 pub fn as_str(self) -> &'static str {
98 match self {
99 Arch::X86 => "x86",
100 Arch::X86_64 => "x86_64",
101 Arch::Aarch64 => "aarch64",
102 Arch::Arm => "arm",
103 Arch::Riscv32 => "riscv32",
104 Arch::Riscv64 => "riscv64",
105 Arch::Wasm32 => "wasm32",
106 Arch::PowerPc => "powerpc",
107 Arch::PowerPc64 => "powerpc64",
108 Arch::S390x => "s390x",
109 Arch::Mips => "mips",
110 Arch::Mips64 => "mips64",
111 Arch::Sparc => "sparc",
112 Arch::Sparc64 => "sparc64",
113 Arch::LoongArch64 => "loongarch64",
114 }
115 }
116
117 pub fn macros(self) -> &'static [(&'static str, &'static str)] {
125 match self {
126 Arch::X86 => &[("__i386__", "1"), ("__i386", "1")],
127 Arch::X86_64 => &[
150 ("__x86_64__", "1"),
151 ("__x86_64", "1"),
152 ("__amd64__", "1"),
153 ("__amd64", "1"),
154 ("__SSE__", "1"),
155 ("__SSE2__", "1"),
156 ("__SSE_MATH__", "1"),
157 ("__SSE2_MATH__", "1"),
158 ],
159 Arch::Aarch64 => &[("__aarch64__", "1")],
160 Arch::Arm => &[("__arm__", "1")],
161 Arch::Riscv32 => &[("__riscv", "1"), ("__riscv_xlen", "32")],
162 Arch::Riscv64 => &[("__riscv", "1"), ("__riscv_xlen", "64")],
163 Arch::Wasm32 => &[
164 ("__wasm", "1"),
165 ("__wasm__", "1"),
166 ("__wasm32", "1"),
167 ("__wasm32__", "1"),
168 ],
169 Arch::PowerPc => &[("__powerpc__", "1"), ("__PPC__", "1")],
170 Arch::PowerPc64 => &[
171 ("__powerpc__", "1"),
172 ("__powerpc64__", "1"),
173 ("__PPC__", "1"),
174 ("__PPC64__", "1"),
175 ],
176 Arch::S390x => &[("__s390__", "1"), ("__s390x__", "1")],
177 Arch::Mips => &[("__mips__", "1"), ("__mips", "32")],
178 Arch::Mips64 => &[("__mips__", "1"), ("__mips", "64")],
179 Arch::Sparc => &[("__sparc__", "1"), ("__sparc", "1")],
180 Arch::Sparc64 => &[
181 ("__sparc__", "1"),
182 ("__sparc", "1"),
183 ("__sparc64__", "1"),
184 ("__arch64__", "1"),
185 ],
186 Arch::LoongArch64 => &[("__loongarch__", "1"), ("__loongarch64", "1")],
187 }
188 }
189}
190
191#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
198pub enum Os {
199 Linux,
201 Darwin,
203 Windows,
205 FreeBsd,
207 NetBsd,
209 OpenBsd,
211 Wasi,
213 None,
216}
217
218impl Os {
219 pub fn as_str(self) -> &'static str {
221 match self {
222 Os::Linux => "linux",
223 Os::Darwin => "darwin",
224 Os::Windows => "windows",
225 Os::FreeBsd => "freebsd",
226 Os::NetBsd => "netbsd",
227 Os::OpenBsd => "openbsd",
228 Os::Wasi => "wasi",
229 Os::None => "none",
230 }
231 }
232
233 pub fn macros(self) -> &'static [(&'static str, &'static str)] {
238 match self {
239 Os::Linux => &[
240 ("__linux__", "1"),
241 ("__linux", "1"),
242 ("__gnu_linux__", "1"),
243 ("__unix__", "1"),
244 ("__unix", "1"),
245 ],
246 Os::Darwin => &[
247 ("__APPLE__", "1"),
248 ("__MACH__", "1"),
249 ("__unix__", "1"),
250 ("__unix", "1"),
251 ],
252 Os::Windows => &[("_WIN32", "1")],
253 Os::FreeBsd => &[("__FreeBSD__", "1"), ("__unix__", "1"), ("__unix", "1")],
254 Os::NetBsd => &[("__NetBSD__", "1"), ("__unix__", "1"), ("__unix", "1")],
255 Os::OpenBsd => &[("__OpenBSD__", "1"), ("__unix__", "1"), ("__unix", "1")],
256 Os::Wasi => &[("__wasi__", "1")],
257 Os::None => &[],
258 }
259 }
260
261 fn is_elf(self) -> bool {
263 matches!(
264 self,
265 Os::Linux | Os::FreeBsd | Os::NetBsd | Os::OpenBsd | Os::None
266 )
267 }
268}
269
270#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
280pub enum Env {
281 Gnu,
283 Musl,
285 Bionic,
287 Uclibc,
289 Msvc,
291 #[default]
294 None,
295}
296
297impl Env {
298 pub fn as_str(self) -> &'static str {
300 match self {
301 Env::Gnu => "gnu",
302 Env::Musl => "musl",
303 Env::Bionic => "android",
304 Env::Uclibc => "uclibc",
305 Env::Msvc => "msvc",
306 Env::None => "",
307 }
308 }
309
310 fn from_component(component: &str, os: Os) -> Self {
315 if component.starts_with("gnu") {
316 Env::Gnu
317 } else if component.starts_with("musl") {
318 Env::Musl
319 } else if component.starts_with("android") {
320 Env::Bionic
321 } else if component.starts_with("uclibc") {
322 Env::Uclibc
323 } else if component == "msvc" {
324 Env::Msvc
325 } else if os == Os::Linux {
326 Env::Gnu
327 } else {
328 Env::None
329 }
330 }
331}
332
333#[derive(Clone, Debug, PartialEq, Eq, Default)]
335pub enum TargetSource {
336 #[default]
339 Host,
340 Env(String),
342 Pragma(String),
344 Explicit,
347}
348
349impl TargetSource {
350 pub fn triple(&self) -> Option<&str> {
352 match self {
353 TargetSource::Env(t) | TargetSource::Pragma(t) => Some(t),
354 TargetSource::Host | TargetSource::Explicit => None,
355 }
356 }
357
358 pub fn as_str(&self) -> &'static str {
360 match self {
361 TargetSource::Host => "the host",
362 TargetSource::Env(_) => "CINRS_TARGET",
363 TargetSource::Pragma(_) => "#pragma cinrs target",
364 TargetSource::Explicit => "the options given to the front end",
365 }
366 }
367
368 pub fn describe(&self) -> String {
372 match self {
373 TargetSource::Host => "the host, CINRS_TARGET being unset".to_owned(),
374 TargetSource::Env(t) => format!("CINRS_TARGET={t}"),
375 TargetSource::Pragma(t) => format!("#pragma cinrs target \"{t}\""),
376 TargetSource::Explicit => "the options given to the front end".to_owned(),
377 }
378 }
379}
380
381#[derive(Clone, Debug, PartialEq, Eq)]
383pub struct UnknownTarget {
384 pub triple: String,
386 detail: String,
388}
389
390impl UnknownTarget {
391 pub fn message(&self, source: &TargetSource) -> String {
397 let (from, fix) = match source {
398 TargetSource::Env(_) => (
399 "CINRS_TARGET: ",
400 "; set #pragma cinrs target or unset the variable",
401 ),
402 TargetSource::Pragma(_) => ("#pragma cinrs target: ", ""),
403 TargetSource::Host | TargetSource::Explicit => ("", ""),
404 };
405 format!("{from}{}{fix}; {FAMILIES}", self.detail)
406 }
407}
408
409impl fmt::Display for UnknownTarget {
410 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
411 write!(f, "{}; {FAMILIES}", self.detail)
412 }
413}
414
415struct ArchRow {
417 prefix: &'static str,
419 arch: Arch,
420 ptr_bits: u32,
423 big_endian: bool,
424}
425
426impl ArchRow {
427 const fn new(prefix: &'static str, arch: Arch, ptr_bits: u32) -> Self {
428 Self {
429 prefix,
430 arch,
431 ptr_bits,
432 big_endian: false,
433 }
434 }
435
436 const fn big_endian(mut self) -> Self {
437 self.big_endian = true;
438 self
439 }
440}
441
442const ARCHITECTURES: &[ArchRow] = &[
448 ArchRow::new("x86_64", Arch::X86_64, 64),
449 ArchRow::new("i386", Arch::X86, 32),
450 ArchRow::new("i486", Arch::X86, 32),
451 ArchRow::new("i586", Arch::X86, 32),
452 ArchRow::new("i686", Arch::X86, 32),
453 ArchRow::new("aarch64_be", Arch::Aarch64, 64).big_endian(),
454 ArchRow::new("aarch64", Arch::Aarch64, 64),
455 ArchRow::new("arm64", Arch::Aarch64, 64),
456 ArchRow::new("armeb", Arch::Arm, 32).big_endian(),
457 ArchRow::new("arm", Arch::Arm, 32),
458 ArchRow::new("thumb", Arch::Arm, 32),
459 ArchRow::new("riscv32", Arch::Riscv32, 32),
460 ArchRow::new("riscv64", Arch::Riscv64, 64),
461 ArchRow::new("wasm32", Arch::Wasm32, 32),
462 ArchRow::new("powerpc64le", Arch::PowerPc64, 64),
463 ArchRow::new("powerpc64", Arch::PowerPc64, 64).big_endian(),
464 ArchRow::new("powerpc", Arch::PowerPc, 32).big_endian(),
465 ArchRow::new("s390x", Arch::S390x, 64).big_endian(),
466 ArchRow::new("loongarch64", Arch::LoongArch64, 64),
467 ArchRow::new("mipsisa64r6el", Arch::Mips64, 64),
468 ArchRow::new("mipsisa64r6", Arch::Mips64, 64).big_endian(),
469 ArchRow::new("mipsisa32r6el", Arch::Mips, 32),
470 ArchRow::new("mipsisa32r6", Arch::Mips, 32).big_endian(),
471 ArchRow::new("mips64el", Arch::Mips64, 64),
472 ArchRow::new("mips64", Arch::Mips64, 64).big_endian(),
473 ArchRow::new("mipsel", Arch::Mips, 32),
474 ArchRow::new("mips", Arch::Mips, 32).big_endian(),
475 ArchRow::new("sparc64", Arch::Sparc64, 64).big_endian(),
476 ArchRow::new("sparcv9", Arch::Sparc64, 64).big_endian(),
477 ArchRow::new("sparc", Arch::Sparc, 32).big_endian(),
478];
479
480const EXOTIC: &[(&str, &str)] = &[
483 ("avr", "'int' is 16 bits and 'double' is 32"),
484 ("msp430", "'int' is 16 bits"),
485 ("xtensa", "cinrs has no model for it"),
486 ("hexagon", "cinrs has no model for it"),
487 ("csky", "cinrs has no model for it"),
488 ("m68k", "cinrs has no model for it"),
489 ("nvptx64", "cinrs has no model for it"),
490 ("bpfel", "cinrs has no model for it"),
491 ("bpfeb", "cinrs has no model for it"),
492 ("wasm64", "cinrs has no model for it"),
493];
494
495const OPERATING_SYSTEMS: &[(&str, Os)] = &[
498 ("linux", Os::Linux),
499 ("android", Os::Linux),
500 ("androideabi", Os::Linux),
501 ("darwin", Os::Darwin),
502 ("macos", Os::Darwin),
503 ("macosx", Os::Darwin),
504 ("ios", Os::Darwin),
505 ("tvos", Os::Darwin),
506 ("watchos", Os::Darwin),
507 ("visionos", Os::Darwin),
508 ("windows", Os::Windows),
509 ("freebsd", Os::FreeBsd),
510 ("netbsd", Os::NetBsd),
511 ("openbsd", Os::OpenBsd),
512 ("wasi", Os::Wasi),
513 ("wasip1", Os::Wasi),
514 ("wasip2", Os::Wasi),
515 ("none", Os::None),
516 ("elf", Os::None),
517];
518
519const FAMILIES: &str = "the architectures cinrs models are x86, x86_64, aarch64, arm/thumb, \
521 riscv32, riscv64, wasm32, powerpc, powerpc64, s390x, mips, mips64, \
522 sparc, sparc64 and loongarch64, on linux (android included), darwin, \
523 windows, freebsd, netbsd, openbsd, wasi or none";
524
525#[derive(Clone, Copy, Debug, PartialEq, Eq)]
533pub struct TargetModel {
534 pub arch: Arch,
536 pub os: Os,
539 pub env: Env,
542 pub char_signed: bool,
550 pub short_bits: u32,
552 pub int_bits: u32,
554 pub long_bits: u32,
557 pub long_long_bits: u32,
559 pub ptr_bits: u32,
561 pub max_scalar_align: u64,
572 pub int128_align: u64,
579 pub has_int128: bool,
584 pub big_endian: bool,
590 pub wchar_bits: u32,
592 pub wchar_signed: bool,
595 pub wint_bits: u32,
597 pub wint_signed: bool,
600}
601
602impl TargetModel {
603 pub const LP64: Self = Self {
606 arch: Arch::X86_64,
607 os: Os::Linux,
608 env: Env::Gnu,
609 char_signed: true,
610 short_bits: 16,
611 int_bits: 32,
612 long_bits: 64,
613 long_long_bits: 64,
614 ptr_bits: 64,
615 max_scalar_align: 8,
616 int128_align: 16,
617 has_int128: true,
618 big_endian: false,
619 wchar_bits: 32,
620 wchar_signed: true,
621 wint_bits: 32,
622 wint_signed: false,
623 };
624
625 pub const ILP32: Self = Self {
629 arch: Arch::X86,
630 long_bits: 32,
631 ptr_bits: 32,
632 max_scalar_align: 4,
633 has_int128: false,
634 ..Self::LP64
635 };
636
637 pub const LLP64: Self = Self {
640 os: Os::Windows,
641 env: Env::Msvc,
642 long_bits: 32,
643 wchar_bits: 16,
644 wchar_signed: false,
645 wint_bits: 16,
646 ..Self::LP64
647 };
648
649 pub const fn host() -> Self {
654 let char_signed = !(cfg!(any(
660 target_arch = "aarch64",
661 target_arch = "arm",
662 target_arch = "csky",
663 target_arch = "hexagon",
664 target_arch = "msp430",
665 target_arch = "powerpc",
666 target_arch = "powerpc64",
667 target_arch = "riscv32",
668 target_arch = "riscv64",
669 target_arch = "s390x",
670 target_arch = "xtensa",
671 )) && !cfg!(any(windows, target_vendor = "apple", target_os = "vita")));
672 let ptr_bits = if cfg!(target_pointer_width = "64") {
673 64
674 } else if cfg!(target_pointer_width = "32") {
675 32
676 } else {
677 16
678 };
679 Self {
680 arch: host_arch(),
681 os: host_os(),
682 env: host_env(),
683 char_signed,
684 short_bits: 16,
685 int_bits: if cfg!(any(target_arch = "avr", target_arch = "msp430")) {
686 16
687 } else {
688 32
689 },
690 long_bits: if ptr_bits == 64 && !cfg!(windows) {
693 64
694 } else {
695 32
696 },
697 long_long_bits: 64,
698 ptr_bits,
699 max_scalar_align: if cfg!(all(target_arch = "x86", not(windows))) {
700 4
701 } else {
702 8
703 },
704 int128_align: core::mem::align_of::<i128>() as u64,
708 has_int128: ptr_bits == 64,
709 big_endian: cfg!(target_endian = "big"),
710 wchar_bits: if cfg!(windows) { 16 } else { 32 },
711 wchar_signed: !(cfg!(windows)
712 || cfg!(all(
713 any(target_arch = "aarch64", target_arch = "arm"),
714 not(target_vendor = "apple")
715 ))),
716 wint_bits: if cfg!(windows) { 16 } else { 32 },
717 wint_signed: cfg!(target_vendor = "apple"),
718 }
719 }
720
721 pub fn from_triple(triple: &str) -> Result<Self, UnknownTarget> {
736 let unknown = |detail: String| UnknownTarget {
737 triple: triple.to_owned(),
738 detail,
739 };
740 let mut parts = triple.split('-');
741 let Some(arch_name) = parts.next().filter(|a| !a.is_empty()) else {
742 return Err(unknown(format!("unknown target triple '{triple}'")));
743 };
744 let rest: Vec<&str> = parts.collect();
745 if let Some((name, why)) = EXOTIC.iter().find(|(name, _)| arch_name == *name) {
746 return Err(unknown(format!(
747 "the target triple '{triple}' is not supported: on '{name}' {why}"
748 )));
749 }
750 let Some(row) = ARCHITECTURES
751 .iter()
752 .find(|row| arch_name.starts_with(row.prefix))
753 else {
754 return Err(unknown(format!(
755 "unknown architecture '{arch_name}' in the target triple '{triple}'"
756 )));
757 };
758 let os = match rest
763 .iter()
764 .find_map(|c| OPERATING_SYSTEMS.iter().find(|(name, _)| c == name))
765 {
766 Some((_, os)) => *os,
767 None if rest.iter().all(|c| *c == "unknown") => Os::None,
768 None => {
769 let named = rest.last().copied().unwrap_or("");
770 return Err(unknown(format!(
771 "unsupported operating system '{named}' in the target triple '{triple}'"
772 )));
773 }
774 };
775 let env = rest.last().copied().unwrap_or("");
776 let ptr_bits = if env.starts_with("gnux32") || env.starts_with("gnu_ilp32") {
778 32
779 } else {
780 row.ptr_bits
781 };
782 let arch = row.arch;
783 let apple = os == Os::Darwin;
784 let windows = os == Os::Windows;
785 let char_signed = !(matches!(
787 arch,
788 Arch::Aarch64
789 | Arch::Arm
790 | Arch::PowerPc
791 | Arch::PowerPc64
792 | Arch::Riscv32
793 | Arch::Riscv64
794 | Arch::S390x
795 ) && !apple
796 && !windows);
797 let unsigned_wchar = matches!(arch, Arch::Aarch64 | Arch::Arm) && !apple;
798 Ok(Self {
799 arch,
800 os,
801 env: Env::from_component(env, os),
802 char_signed,
803 short_bits: 16,
804 int_bits: 32,
805 long_bits: if (ptr_bits == 64 && !windows) || (arch == Arch::Wasm32 && os == Os::Linux)
809 {
810 64
811 } else {
812 32
813 },
814 long_long_bits: 64,
815 ptr_bits,
816 max_scalar_align: if arch == Arch::X86 && !windows { 4 } else { 8 },
817 int128_align: 16,
818 has_int128: row.ptr_bits == 64,
821 big_endian: row.big_endian,
822 wchar_bits: if windows { 16 } else { 32 },
823 wchar_signed: !windows && !unsigned_wchar,
824 wint_bits: if windows { 16 } else { 32 },
825 wint_signed: apple,
826 })
827 }
828
829 pub fn is_msvc(&self) -> bool {
845 self.os == Os::Windows && self.env == Env::Msvc
846 }
847
848 pub fn platform_long_double_is_double(&self) -> bool {
869 match self.arch {
870 _ if self.is_msvc() => true,
871 Arch::Arm | Arch::Mips => true,
872 Arch::Aarch64 => matches!(self.os, Os::Darwin | Os::Windows),
873 Arch::X86 => self.env == Env::Bionic,
874 Arch::PowerPc | Arch::PowerPc64 => self.env == Env::Musl,
875 _ => false,
876 }
877 }
878
879 pub fn data_model(&self) -> &'static str {
881 match (self.int_bits, self.long_bits, self.ptr_bits) {
882 (32, 64, 64) => "LP64",
883 (32, 32, 64) => "LLP64",
884 (32, 32, 32) => "ILP32",
885 _ => "an unusual data model",
886 }
887 }
888
889 pub fn describe(&self, source: &TargetSource) -> String {
892 format!(
893 "{} ({}-{}, {} 'char', {}-bit 'wchar_t'), chosen from {}",
894 self.data_model(),
895 self.arch.as_str(),
896 self.os.as_str(),
897 if self.char_signed {
898 "signed"
899 } else {
900 "unsigned"
901 },
902 self.wchar_bits,
903 source.describe()
904 )
905 }
906
907 pub fn macros(&self) -> Vec<(&'static str, String)> {
915 let mut out: Vec<(&'static str, String)> = Vec::new();
916 for (name, value) in self.arch.macros() {
917 out.push((name, (*value).to_owned()));
918 }
919 for (name, value) in self.os.macros() {
920 out.push((name, (*value).to_owned()));
921 }
922 if self.os == Os::Windows && self.ptr_bits == 64 {
923 out.push(("_WIN64", "1".to_owned()));
924 }
925 if self.os.is_elf() && self.arch != Arch::Wasm32 {
927 out.push(("__ELF__", "1".to_owned()));
928 }
929 if self.os == Os::Linux {
938 match self.env {
939 Env::Gnu => out.push(("__cinrs_glibc__", "1".to_owned())),
940 Env::Musl => out.push(("__cinrs_musl__", "1".to_owned())),
941 _ => {}
942 }
943 }
944 out
945 }
946}
947
948const fn host_env() -> Env {
950 if cfg!(target_env = "gnu") {
951 Env::Gnu
952 } else if cfg!(target_env = "musl") {
953 Env::Musl
954 } else if cfg!(target_os = "android") {
955 Env::Bionic
956 } else if cfg!(target_env = "uclibc") {
957 Env::Uclibc
958 } else if cfg!(target_env = "msvc") {
959 Env::Msvc
960 } else if cfg!(target_os = "linux") {
961 Env::Gnu
962 } else {
963 Env::None
964 }
965}
966
967const fn host_arch() -> Arch {
974 if cfg!(target_arch = "x86_64") {
975 Arch::X86_64
976 } else if cfg!(target_arch = "x86") {
977 Arch::X86
978 } else if cfg!(target_arch = "aarch64") {
979 Arch::Aarch64
980 } else if cfg!(target_arch = "arm") {
981 Arch::Arm
982 } else if cfg!(target_arch = "riscv32") {
983 Arch::Riscv32
984 } else if cfg!(target_arch = "riscv64") {
985 Arch::Riscv64
986 } else if cfg!(target_arch = "wasm32") {
987 Arch::Wasm32
988 } else if cfg!(target_arch = "powerpc") {
989 Arch::PowerPc
990 } else if cfg!(target_arch = "powerpc64") {
991 Arch::PowerPc64
992 } else if cfg!(target_arch = "s390x") {
993 Arch::S390x
994 } else if cfg!(target_arch = "mips") {
995 Arch::Mips
996 } else if cfg!(target_arch = "mips64") {
997 Arch::Mips64
998 } else if cfg!(target_arch = "sparc") {
999 Arch::Sparc
1000 } else if cfg!(target_arch = "sparc64") {
1001 Arch::Sparc64
1002 } else if cfg!(target_arch = "loongarch64") {
1003 Arch::LoongArch64
1004 } else if cfg!(target_pointer_width = "64") {
1005 Arch::X86_64
1006 } else {
1007 Arch::X86
1008 }
1009}
1010
1011const fn host_os() -> Os {
1013 if cfg!(any(target_os = "linux", target_os = "android")) {
1014 Os::Linux
1015 } else if cfg!(target_vendor = "apple") {
1016 Os::Darwin
1017 } else if cfg!(windows) {
1018 Os::Windows
1019 } else if cfg!(target_os = "freebsd") {
1020 Os::FreeBsd
1021 } else if cfg!(target_os = "netbsd") {
1022 Os::NetBsd
1023 } else if cfg!(target_os = "openbsd") {
1024 Os::OpenBsd
1025 } else if cfg!(target_os = "wasi") {
1026 Os::Wasi
1027 } else {
1028 Os::None
1029 }
1030}
1031
1032impl Default for TargetModel {
1033 fn default() -> Self {
1034 Self::host()
1035 }
1036}
1037
1038#[cfg(test)]
1039mod tests {
1040 use super::*;
1041
1042 fn model(triple: &str) -> TargetModel {
1043 TargetModel::from_triple(triple).unwrap_or_else(|e| panic!("{triple}: {e}"))
1044 }
1045
1046 fn macros(triple: &str) -> Vec<String> {
1047 model(triple)
1048 .macros()
1049 .into_iter()
1050 .map(|(name, value)| format!("{name}={value}"))
1051 .collect()
1052 }
1053
1054 #[test]
1055 fn the_host_model_is_self_consistent() {
1056 let t = TargetModel::host();
1057 assert!(t.short_bits <= t.int_bits);
1058 assert!(t.int_bits <= t.long_bits);
1059 assert!(t.long_bits <= t.long_long_bits);
1060 assert!(t.long_long_bits >= 64);
1061 }
1062
1063 #[test]
1067 fn the_host_model_agrees_with_core_ffi() {
1068 let t = TargetModel::host();
1069 assert_eq!(t.short_bits, 8 * size_of::<core::ffi::c_short>() as u32);
1070 assert_eq!(t.int_bits, 8 * size_of::<core::ffi::c_int>() as u32);
1071 assert_eq!(t.long_bits, 8 * size_of::<core::ffi::c_long>() as u32);
1072 assert_eq!(
1073 t.long_long_bits,
1074 8 * size_of::<core::ffi::c_longlong>() as u32
1075 );
1076 assert_eq!(t.ptr_bits, 8 * size_of::<*const ()>() as u32);
1077 assert_eq!(t.char_signed, core::ffi::c_char::MIN != 0);
1078 assert_eq!(t.int128_align, align_of::<i128>() as u64);
1079 assert_eq!(t.max_scalar_align, align_of::<u64>() as u64);
1080 assert_eq!(t.max_scalar_align, align_of::<f64>() as u64);
1081 }
1082
1083 #[test]
1084 fn a_64_bit_x86_linux_host_is_lp64() {
1085 if cfg!(all(target_os = "linux", target_arch = "x86_64")) {
1086 assert_eq!(TargetModel::host(), TargetModel::LP64);
1087 }
1088 }
1089
1090 #[test]
1093 fn the_table_agrees_with_the_host() {
1094 if cfg!(all(
1095 target_os = "linux",
1096 target_arch = "x86_64",
1097 target_env = "gnu"
1098 )) {
1099 assert_eq!(
1100 model("x86_64-unknown-linux-gnu"),
1101 TargetModel::host(),
1102 "the table and the host disagree"
1103 );
1104 }
1105 }
1106
1107 #[test]
1108 fn the_lp64_family() {
1109 for triple in [
1110 "x86_64-unknown-linux-gnu",
1111 "x86_64-unknown-linux-musl",
1112 "x86_64-unknown-freebsd",
1113 "aarch64-unknown-linux-gnu",
1114 "aarch64-apple-darwin",
1115 "x86_64-apple-darwin",
1116 "riscv64gc-unknown-linux-gnu",
1117 "powerpc64le-unknown-linux-gnu",
1118 "s390x-unknown-linux-gnu",
1119 "loongarch64-unknown-linux-gnu",
1120 "x86_64-unknown-none",
1121 ] {
1122 let t = model(triple);
1123 assert_eq!(t.data_model(), "LP64", "{triple}");
1124 assert_eq!(t.long_bits, 64, "{triple}");
1125 assert_eq!(t.ptr_bits, 64, "{triple}");
1126 assert!(t.has_int128, "{triple}");
1127 }
1128 }
1129
1130 #[test]
1131 fn the_llp64_family() {
1132 for triple in [
1133 "x86_64-pc-windows-msvc",
1134 "x86_64-pc-windows-gnu",
1135 "x86_64-pc-windows-gnullvm",
1136 "aarch64-pc-windows-msvc",
1137 "x86_64-uwp-windows-msvc",
1138 ] {
1139 let t = model(triple);
1140 assert_eq!(t.data_model(), "LLP64", "{triple}");
1141 assert_eq!(t.long_bits, 32, "{triple}");
1142 assert_eq!(t.ptr_bits, 64, "{triple}");
1143 assert_eq!(t.wchar_bits, 16, "{triple}");
1144 assert!(!t.wchar_signed, "{triple}");
1145 assert_eq!(t.wint_bits, 16, "{triple}");
1146 assert!(t.char_signed, "{triple}");
1149 assert_eq!(t.max_scalar_align, 8, "{triple}");
1150 }
1151 }
1152
1153 #[test]
1156 fn the_msvc_environment() {
1157 for triple in [
1158 "x86_64-pc-windows-msvc",
1159 "i686-pc-windows-msvc",
1160 "aarch64-pc-windows-msvc",
1161 "x86_64-uwp-windows-msvc",
1162 "thumbv7a-pc-windows-msvc",
1163 ] {
1164 assert!(model(triple).is_msvc(), "{triple} is MSVC");
1165 }
1166 for triple in [
1169 "x86_64-pc-windows-gnu",
1170 "i686-pc-windows-gnu",
1171 "x86_64-pc-windows-gnullvm",
1172 "aarch64-pc-windows-gnullvm",
1173 "x86_64-unknown-linux-gnu",
1174 "aarch64-apple-darwin",
1175 "x86_64-apple-darwin",
1176 "x86_64-unknown-freebsd",
1177 "wasm32-unknown-unknown",
1178 "thumbv7em-none-eabihf",
1179 ] {
1180 assert!(!model(triple).is_msvc(), "{triple} is not MSVC");
1181 }
1182 assert!(TargetModel::LLP64.is_msvc());
1183 assert!(!TargetModel::LP64.is_msvc());
1184 assert!(!TargetModel::ILP32.is_msvc());
1185 assert_eq!(
1188 TargetModel::host().is_msvc(),
1189 cfg!(all(windows, target_env = "msvc"))
1190 );
1191 }
1192
1193 #[test]
1194 fn the_ilp32_family() {
1195 for triple in [
1196 "i686-unknown-linux-gnu",
1197 "i586-unknown-linux-gnu",
1198 "i686-pc-windows-msvc",
1199 "armv7-unknown-linux-gnueabihf",
1200 "thumbv7em-none-eabihf",
1201 "riscv32imac-unknown-none-elf",
1202 "wasm32-unknown-unknown",
1203 "wasm32-wasip1",
1204 "mips-unknown-linux-gnu",
1205 "powerpc-unknown-linux-gnu",
1206 "sparc-unknown-linux-gnu",
1207 "x86_64-unknown-linux-gnux32",
1208 "aarch64-unknown-linux-gnu_ilp32",
1209 ] {
1210 let t = model(triple);
1211 assert_eq!(t.data_model(), "ILP32", "{triple}");
1212 assert_eq!(t.long_bits, 32, "{triple}");
1213 assert_eq!(t.ptr_bits, 32, "{triple}");
1214 }
1215 }
1216
1217 #[test]
1220 fn int128_follows_the_architecture() {
1221 assert!(model("x86_64-unknown-linux-gnux32").has_int128);
1222 assert!(model("aarch64-unknown-linux-gnu").has_int128);
1223 assert!(!model("i686-unknown-linux-gnu").has_int128);
1224 assert!(!model("wasm32-unknown-unknown").has_int128);
1225 assert!(!model("armv7-unknown-linux-gnueabihf").has_int128);
1226 }
1227
1228 #[test]
1231 fn plain_char_signedness_follows_core_ffi() {
1232 for triple in [
1233 "x86_64-unknown-linux-gnu",
1234 "i686-unknown-linux-gnu",
1235 "loongarch64-unknown-linux-gnu",
1236 "wasm32-unknown-unknown",
1237 "mips-unknown-linux-gnu",
1238 "sparc64-unknown-netbsd",
1239 "aarch64-pc-windows-msvc",
1241 "aarch64-apple-darwin",
1242 "armv7-apple-ios",
1243 ] {
1244 assert!(model(triple).char_signed, "{triple} should be signed");
1245 }
1246 for triple in [
1247 "aarch64-unknown-linux-gnu",
1248 "armv7-unknown-linux-gnueabihf",
1249 "thumbv7em-none-eabihf",
1250 "riscv64gc-unknown-linux-gnu",
1251 "riscv32imac-unknown-none-elf",
1252 "powerpc64le-unknown-linux-gnu",
1253 "powerpc-unknown-linux-gnu",
1254 "s390x-unknown-linux-gnu",
1255 ] {
1256 assert!(!model(triple).char_signed, "{triple} should be unsigned");
1257 }
1258 }
1259
1260 #[test]
1264 fn scalar_alignment_splits_i386_from_i386_on_windows() {
1265 assert_eq!(model("i686-unknown-linux-gnu").max_scalar_align, 4);
1266 assert_eq!(model("i586-unknown-netbsd").max_scalar_align, 4);
1267 assert_eq!(model("i686-pc-windows-msvc").max_scalar_align, 8);
1268 assert_eq!(model("i686-pc-windows-gnu").max_scalar_align, 8);
1269 assert_eq!(model("armv7-unknown-linux-gnueabihf").max_scalar_align, 8);
1270 assert_eq!(model("wasm32-unknown-unknown").max_scalar_align, 8);
1271 }
1272
1273 #[test]
1274 fn endianness() {
1275 for triple in [
1276 "s390x-unknown-linux-gnu",
1277 "powerpc64-unknown-linux-gnu",
1278 "powerpc-unknown-linux-gnu",
1279 "sparc64-unknown-linux-gnu",
1280 "mips-unknown-linux-gnu",
1281 "aarch64_be-unknown-linux-gnu",
1282 ] {
1283 assert!(model(triple).big_endian, "{triple} is big-endian");
1284 }
1285 for triple in [
1286 "x86_64-unknown-linux-gnu",
1287 "powerpc64le-unknown-linux-gnu",
1288 "mipsel-unknown-linux-gnu",
1289 "mips64el-unknown-linux-gnuabi64",
1290 "aarch64-unknown-linux-gnu",
1291 ] {
1292 assert!(!model(triple).big_endian, "{triple} is little-endian");
1293 }
1294 }
1295
1296 #[test]
1300 fn wchar_and_wint() {
1301 let arm = model("aarch64-unknown-linux-gnu");
1302 assert_eq!((arm.wchar_bits, arm.wchar_signed), (32, false));
1303 assert_eq!((arm.wint_bits, arm.wint_signed), (32, false));
1304
1305 let mac = model("aarch64-apple-darwin");
1306 assert_eq!((mac.wchar_bits, mac.wchar_signed), (32, true));
1307 assert_eq!((mac.wint_bits, mac.wint_signed), (32, true));
1308
1309 let win = model("x86_64-pc-windows-msvc");
1310 assert_eq!((win.wchar_bits, win.wchar_signed), (16, false));
1311 assert_eq!((win.wint_bits, win.wint_signed), (16, false));
1312
1313 let linux = model("x86_64-unknown-linux-gnu");
1314 assert_eq!((linux.wchar_bits, linux.wchar_signed), (32, true));
1315 assert_eq!((linux.wint_bits, linux.wint_signed), (32, false));
1316 }
1317
1318 #[test]
1319 fn the_identity_macros() {
1320 let linux = macros("x86_64-unknown-linux-gnu");
1321 for want in [
1322 "__x86_64__=1",
1323 "__amd64__=1",
1324 "__linux__=1",
1325 "__gnu_linux__=1",
1326 "__unix__=1",
1327 "__ELF__=1",
1328 ] {
1329 assert!(linux.contains(&want.to_owned()), "{want} in {linux:?}");
1330 }
1331 assert!(!linux.iter().any(|m| m.starts_with("_WIN")));
1332
1333 let win = macros("x86_64-pc-windows-msvc");
1334 assert!(win.contains(&"_WIN32=1".to_owned()));
1335 assert!(win.contains(&"_WIN64=1".to_owned()));
1336 assert!(!win.iter().any(|m| m.starts_with("__ELF__")));
1337 assert!(!win.iter().any(|m| m.starts_with("__unix")));
1338
1339 let win32 = macros("i686-pc-windows-msvc");
1340 assert!(win32.contains(&"_WIN32=1".to_owned()));
1341 assert!(!win32.contains(&"_WIN64=1".to_owned()));
1342 assert!(win32.contains(&"__i386__=1".to_owned()));
1343
1344 let mac = macros("aarch64-apple-darwin");
1345 assert!(mac.contains(&"__APPLE__=1".to_owned()));
1346 assert!(mac.contains(&"__MACH__=1".to_owned()));
1347 assert!(mac.contains(&"__aarch64__=1".to_owned()));
1348 assert!(!mac.iter().any(|m| m.starts_with("__ELF__")));
1349
1350 let wasm = macros("wasm32-unknown-unknown");
1351 assert!(wasm.contains(&"__wasm32__=1".to_owned()));
1352 assert!(!wasm.iter().any(|m| m.starts_with("__ELF__")));
1353
1354 let riscv = macros("riscv64gc-unknown-linux-gnu");
1355 assert!(riscv.contains(&"__riscv=1".to_owned()));
1356 assert!(riscv.contains(&"__riscv_xlen=64".to_owned()));
1357
1358 let bare = macros("thumbv7em-none-eabihf");
1359 assert!(bare.contains(&"__arm__=1".to_owned()));
1360 assert!(bare.contains(&"__ELF__=1".to_owned()));
1361 assert!(!bare.iter().any(|m| m.starts_with("__linux")));
1362 }
1363
1364 #[test]
1367 fn the_c_library_of_a_linux_triple() {
1368 for (triple, want) in [
1369 ("x86_64-unknown-linux-gnu", Env::Gnu),
1370 ("armv7-unknown-linux-gnueabihf", Env::Gnu),
1371 ("x86_64-unknown-linux-gnux32", Env::Gnu),
1372 ("x86_64-unknown-linux", Env::Gnu),
1375 ("x86_64-unknown-linux-musl", Env::Musl),
1376 ("aarch64-unknown-linux-musl", Env::Musl),
1377 ("aarch64-linux-android", Env::Bionic),
1378 ("armv7-unknown-linux-uclibceabi", Env::Uclibc),
1379 ("x86_64-pc-windows-msvc", Env::Msvc),
1380 ("x86_64-pc-windows-gnu", Env::Gnu),
1383 ("aarch64-apple-darwin", Env::None),
1384 ("x86_64-unknown-freebsd", Env::None),
1385 ("wasm32-unknown-unknown", Env::None),
1386 ] {
1387 assert_eq!(model(triple).env, want, "{triple}");
1388 }
1389
1390 let glibc = "__cinrs_glibc__=1".to_owned();
1391 let musl = "__cinrs_musl__=1".to_owned();
1392 assert!(macros("x86_64-unknown-linux-gnu").contains(&glibc));
1393 assert!(macros("i686-unknown-linux-gnu").contains(&glibc));
1394 assert!(macros("x86_64-unknown-linux-musl").contains(&musl));
1395 for triple in [
1398 "x86_64-unknown-linux-gnu",
1399 "x86_64-unknown-linux-musl",
1400 "aarch64-linux-android",
1401 "x86_64-pc-windows-gnu",
1402 "aarch64-apple-darwin",
1403 "x86_64-unknown-freebsd",
1404 "wasm32-unknown-unknown",
1405 ] {
1406 let macros = macros(triple);
1407 let named = usize::from(macros.contains(&glibc)) + usize::from(macros.contains(&musl));
1408 assert!(named <= 1, "{triple} claims two C libraries");
1409 }
1410 for triple in [
1411 "aarch64-linux-android",
1412 "x86_64-pc-windows-gnu",
1413 "aarch64-apple-darwin",
1414 ] {
1415 let macros = macros(triple);
1416 assert!(!macros.contains(&glibc), "{triple}");
1417 assert!(!macros.contains(&musl), "{triple}");
1418 }
1419 }
1420
1421 #[test]
1422 fn an_unknown_architecture_is_refused() {
1423 let err = TargetModel::from_triple("gizmo-unknown-linux-gnu").unwrap_err();
1424 let message = err.message(&TargetSource::Env("gizmo-unknown-linux-gnu".to_owned()));
1425 assert!(
1426 message.starts_with(
1427 "CINRS_TARGET: unknown architecture 'gizmo' in the target triple \
1428 'gizmo-unknown-linux-gnu'; set #pragma cinrs target or unset the variable; "
1429 ),
1430 "{message}"
1431 );
1432 assert!(message.contains("the architectures cinrs models are x86, x86_64"));
1433 }
1434
1435 #[test]
1436 fn an_unknown_operating_system_is_refused() {
1437 let err = TargetModel::from_triple("x86_64-unknown-plan9").unwrap_err();
1438 let message = err.message(&TargetSource::Pragma("x86_64-unknown-plan9".to_owned()));
1439 assert!(
1440 message.starts_with(
1441 "#pragma cinrs target: unsupported operating system 'plan9' in the target \
1442 triple 'x86_64-unknown-plan9'; "
1443 ),
1444 "{message}"
1445 );
1446 }
1447
1448 #[test]
1449 fn the_exotic_data_models_are_refused_by_name() {
1450 let err = TargetModel::from_triple("avr-none-unknown").unwrap_err();
1451 let message = err.message(&TargetSource::Env("avr-none-unknown".to_owned()));
1452 assert!(
1453 message.starts_with(
1454 "CINRS_TARGET: the target triple 'avr-none-unknown' is not supported: on \
1455 'avr' 'int' is 16 bits and 'double' is 32; set #pragma cinrs target or \
1456 unset the variable; "
1457 ),
1458 "{message}"
1459 );
1460 assert!(TargetModel::from_triple("msp430-none-elf").is_err());
1461 }
1462
1463 #[test]
1464 fn an_empty_triple_is_refused() {
1465 assert!(TargetModel::from_triple("").is_err());
1466 assert!(TargetModel::from_triple("-linux-gnu").is_err());
1467 }
1468
1469 #[test]
1472 fn the_description_names_the_source() {
1473 let t = model("x86_64-pc-windows-msvc");
1474 let said = t.describe(&TargetSource::Env("x86_64-pc-windows-msvc".to_owned()));
1475 assert_eq!(
1476 said,
1477 "LLP64 (x86_64-windows, signed 'char', 16-bit 'wchar_t'), chosen from \
1478 CINRS_TARGET=x86_64-pc-windows-msvc"
1479 );
1480 let host = TargetModel::host().describe(&TargetSource::Host);
1481 assert!(host.contains("CINRS_TARGET being unset"), "{host}");
1482 }
1483}