1# Define the list of system with their properties.
2#
3# See https://clang.llvm.org/docs/CrossCompilation.html and
4# http://llvm.org/docs/doxygen/html/Triple_8cpp_source.html especially
5# Triple::normalize. Parsing should essentially act as a more conservative
6# version of that last function.
7#
8# Most of the types below come in "open" and "closed" pairs. The open ones
9# specify what information we need to know about systems in general, and the
10# closed ones are sub-types representing the whitelist of systems we support in
11# practice.
12#
13# Code in the remainder of nixpkgs shouldn't rely on the closed ones in
14# e.g. exhaustive cases. Its more a sanity check to make sure nobody defines
15# systems that overlap with existing ones and won't notice something amiss.
16#
17{ lib }:
18
19let
20 inherit (lib)
21 all
22 any
23 attrValues
24 elem
25 elemAt
26 hasPrefix
27 id
28 length
29 mapAttrs
30 mergeOneOption
31 optionalString
32 splitString
33 versionAtLeast
34 ;
35
36 inherit (lib.strings) match;
37
38 inherit (lib.systems.inspect.predicates)
39 isAarch32
40 isBigEndian
41 isDarwin
42 isLinux
43 isPower64
44 isWindows
45 ;
46
47 inherit (lib.types)
48 enum
49 float
50 isType
51 mkOptionType
52 number
53 setType
54 string
55 types
56 ;
57
58 setTypes =
59 type:
60 mapAttrs (
61 name: value:
62 assert type.check value;
63 setType type.name ({ inherit name; } // value)
64 );
65
66 # gnu-config will ignore the portion of a triple matching the
67 # regex `e?abi.*$` when determining the validity of a triple. In
68 # other words, `i386-linuxabichickenlips` is a valid triple.
69 removeAbiSuffix =
70 x:
71 let
72 found = match "(.*)e?abi.*" x;
73 in
74 if found == null then x else elemAt found 0;
75
76in
77
78rec {
79
80 ################################################################################
81
82 types.openSignificantByte = mkOptionType {
83 name = "significant-byte";
84 description = "Endianness";
85 merge = mergeOneOption;
86 };
87
88 types.significantByte = enum (attrValues significantBytes);
89
90 significantBytes = setTypes types.openSignificantByte {
91 bigEndian = { };
92 littleEndian = { };
93 };
94
95 ################################################################################
96
97 # Reasonable power of 2
98 types.bitWidth = enum [
99 8
100 16
101 32
102 64
103 128
104 ];
105
106 ################################################################################
107
108 types.openCpuType = mkOptionType {
109 name = "cpu-type";
110 description = "instruction set architecture name and information";
111 merge = mergeOneOption;
112 check =
113 x:
114 types.bitWidth.check x.bits
115 && (if 8 < x.bits then types.significantByte.check x.significantByte else !(x ? significantByte));
116 };
117
118 types.cpuType = enum (attrValues cpuTypes);
119
120 cpuTypes =
121 let
122 inherit (significantBytes) bigEndian littleEndian;
123 in
124 setTypes types.openCpuType {
125 arm = {
126 bits = 32;
127 significantByte = littleEndian;
128 family = "arm";
129 };
130 armv5tel = {
131 bits = 32;
132 significantByte = littleEndian;
133 family = "arm";
134 version = "5";
135 arch = "armv5t";
136 };
137 armv6m = {
138 bits = 32;
139 significantByte = littleEndian;
140 family = "arm";
141 version = "6";
142 arch = "armv6-m";
143 };
144 armv6l = {
145 bits = 32;
146 significantByte = littleEndian;
147 family = "arm";
148 version = "6";
149 arch = "armv6";
150 };
151 armv7a = {
152 bits = 32;
153 significantByte = littleEndian;
154 family = "arm";
155 version = "7";
156 arch = "armv7-a";
157 };
158 armv7r = {
159 bits = 32;
160 significantByte = littleEndian;
161 family = "arm";
162 version = "7";
163 arch = "armv7-r";
164 };
165 armv7m = {
166 bits = 32;
167 significantByte = littleEndian;
168 family = "arm";
169 version = "7";
170 arch = "armv7-m";
171 };
172 armv7l = {
173 bits = 32;
174 significantByte = littleEndian;
175 family = "arm";
176 version = "7";
177 arch = "armv7";
178 };
179 armv8a = {
180 bits = 32;
181 significantByte = littleEndian;
182 family = "arm";
183 version = "8";
184 arch = "armv8-a";
185 };
186 armv8r = {
187 bits = 32;
188 significantByte = littleEndian;
189 family = "arm";
190 version = "8";
191 arch = "armv8-a";
192 };
193 armv8m = {
194 bits = 32;
195 significantByte = littleEndian;
196 family = "arm";
197 version = "8";
198 arch = "armv8-m";
199 };
200 aarch64 = {
201 bits = 64;
202 significantByte = littleEndian;
203 family = "arm";
204 version = "8";
205 arch = "armv8-a";
206 };
207 aarch64_be = {
208 bits = 64;
209 significantByte = bigEndian;
210 family = "arm";
211 version = "8";
212 arch = "armv8-a";
213 };
214
215 i386 = {
216 bits = 32;
217 significantByte = littleEndian;
218 family = "x86";
219 arch = "i386";
220 };
221 i486 = {
222 bits = 32;
223 significantByte = littleEndian;
224 family = "x86";
225 arch = "i486";
226 };
227 i586 = {
228 bits = 32;
229 significantByte = littleEndian;
230 family = "x86";
231 arch = "i586";
232 };
233 i686 = {
234 bits = 32;
235 significantByte = littleEndian;
236 family = "x86";
237 arch = "i686";
238 };
239 x86_64 = {
240 bits = 64;
241 significantByte = littleEndian;
242 family = "x86";
243 arch = "x86-64";
244 };
245
246 microblaze = {
247 bits = 32;
248 significantByte = bigEndian;
249 family = "microblaze";
250 };
251 microblazeel = {
252 bits = 32;
253 significantByte = littleEndian;
254 family = "microblaze";
255 };
256
257 mips = {
258 bits = 32;
259 significantByte = bigEndian;
260 family = "mips";
261 };
262 mipsel = {
263 bits = 32;
264 significantByte = littleEndian;
265 family = "mips";
266 };
267 mips64 = {
268 bits = 64;
269 significantByte = bigEndian;
270 family = "mips";
271 };
272 mips64el = {
273 bits = 64;
274 significantByte = littleEndian;
275 family = "mips";
276 };
277
278 mmix = {
279 bits = 64;
280 significantByte = bigEndian;
281 family = "mmix";
282 };
283
284 m68k = {
285 bits = 32;
286 significantByte = bigEndian;
287 family = "m68k";
288 };
289
290 powerpc = {
291 bits = 32;
292 significantByte = bigEndian;
293 family = "power";
294 };
295 powerpc64 = {
296 bits = 64;
297 significantByte = bigEndian;
298 family = "power";
299 };
300 powerpc64le = {
301 bits = 64;
302 significantByte = littleEndian;
303 family = "power";
304 };
305 powerpcle = {
306 bits = 32;
307 significantByte = littleEndian;
308 family = "power";
309 };
310
311 riscv32 = {
312 bits = 32;
313 significantByte = littleEndian;
314 family = "riscv";
315 };
316 riscv64 = {
317 bits = 64;
318 significantByte = littleEndian;
319 family = "riscv";
320 };
321
322 s390 = {
323 bits = 32;
324 significantByte = bigEndian;
325 family = "s390";
326 };
327 s390x = {
328 bits = 64;
329 significantByte = bigEndian;
330 family = "s390";
331 };
332
333 sparc = {
334 bits = 32;
335 significantByte = bigEndian;
336 family = "sparc";
337 };
338 sparc64 = {
339 bits = 64;
340 significantByte = bigEndian;
341 family = "sparc";
342 };
343
344 wasm32 = {
345 bits = 32;
346 significantByte = littleEndian;
347 family = "wasm";
348 };
349 wasm64 = {
350 bits = 64;
351 significantByte = littleEndian;
352 family = "wasm";
353 };
354
355 alpha = {
356 bits = 64;
357 significantByte = littleEndian;
358 family = "alpha";
359 };
360
361 rx = {
362 bits = 32;
363 significantByte = littleEndian;
364 family = "rx";
365 };
366 msp430 = {
367 bits = 16;
368 significantByte = littleEndian;
369 family = "msp430";
370 };
371 avr = {
372 bits = 8;
373 family = "avr";
374 };
375
376 vc4 = {
377 bits = 32;
378 significantByte = littleEndian;
379 family = "vc4";
380 };
381
382 or1k = {
383 bits = 32;
384 significantByte = bigEndian;
385 family = "or1k";
386 };
387
388 loongarch64 = {
389 bits = 64;
390 significantByte = littleEndian;
391 family = "loongarch";
392 };
393
394 javascript = {
395 bits = 32;
396 significantByte = littleEndian;
397 family = "javascript";
398 };
399 }
400 // {
401 # aliases
402 # Apple architecture name, as used by `darwinArch`; required by
403 # LLVM ≥ 20.
404 arm64 = cpuTypes.aarch64;
405 };
406
407 # GNU build systems assume that older NetBSD architectures are using a.out.
408 gnuNetBSDDefaultExecFormat =
409 cpu:
410 if
411 (cpu.family == "arm" && cpu.bits == 32)
412 || (cpu.family == "sparc" && cpu.bits == 32)
413 || (cpu.family == "m68k" && cpu.bits == 32)
414 || (cpu.family == "x86" && cpu.bits == 32)
415 then
416 execFormats.aout
417 else
418 execFormats.elf;
419
420 # Determine when two CPUs are compatible with each other. That is,
421 # can code built for system B run on system A? For that to happen,
422 # the programs that system B accepts must be a subset of the
423 # programs that system A accepts.
424 #
425 # We have the following properties of the compatibility relation,
426 # which must be preserved when adding compatibility information for
427 # additional CPUs.
428 # - (reflexivity)
429 # Every CPU is compatible with itself.
430 # - (transitivity)
431 # If A is compatible with B and B is compatible with C then A is compatible with C.
432 #
433 # Note: Since 22.11 the archs of a mode switching CPU are no longer considered
434 # pairwise compatible. Mode switching implies that binaries built for A
435 # and B respectively can't be executed at the same time.
436 isCompatible =
437 with cpuTypes;
438 a: b:
439 any id [
440 # x86
441 (b == i386 && isCompatible a i486)
442 (b == i486 && isCompatible a i586)
443 (b == i586 && isCompatible a i686)
444
445 # XXX: Not true in some cases. Like in WSL mode.
446 (b == i686 && isCompatible a x86_64)
447
448 # ARMv4
449 (b == arm && isCompatible a armv5tel)
450
451 # ARMv5
452 (b == armv5tel && isCompatible a armv6l)
453
454 # ARMv6
455 (b == armv6l && isCompatible a armv6m)
456 (b == armv6m && isCompatible a armv7l)
457
458 # ARMv7
459 (b == armv7l && isCompatible a armv7a)
460 (b == armv7l && isCompatible a armv7r)
461 (b == armv7l && isCompatible a armv7m)
462
463 # ARMv8
464 (b == aarch64 && a == armv8a)
465 (b == armv8a && isCompatible a aarch64)
466 (b == armv8r && isCompatible a armv8a)
467 (b == armv8m && isCompatible a armv8a)
468
469 # PowerPC
470 (b == powerpc && isCompatible a powerpc64)
471 (b == powerpcle && isCompatible a powerpc64le)
472
473 # MIPS
474 (b == mips && isCompatible a mips64)
475 (b == mipsel && isCompatible a mips64el)
476
477 # RISCV
478 (b == riscv32 && isCompatible a riscv64)
479
480 # SPARC
481 (b == sparc && isCompatible a sparc64)
482
483 # WASM
484 (b == wasm32 && isCompatible a wasm64)
485
486 # identity
487 (b == a)
488 ];
489
490 ################################################################################
491
492 types.openVendor = mkOptionType {
493 name = "vendor";
494 description = "vendor for the platform";
495 merge = mergeOneOption;
496 };
497
498 types.vendor = enum (attrValues vendors);
499
500 vendors = setTypes types.openVendor {
501 apple = { };
502 pc = { };
503 knuth = { };
504
505 # Actually matters, unlocking some MinGW-w64-specific options in GCC. See
506 # bottom of https://sourceforge.net/p/mingw-w64/wiki2/Unicode%20apps/
507 w64 = { };
508
509 none = { };
510 unknown = { };
511 };
512
513 ################################################################################
514
515 types.openExecFormat = mkOptionType {
516 name = "exec-format";
517 description = "executable container used by the kernel";
518 merge = mergeOneOption;
519 };
520
521 types.execFormat = enum (attrValues execFormats);
522
523 execFormats = setTypes types.openExecFormat {
524 aout = { }; # a.out
525 elf = { };
526 macho = { };
527 pe = { };
528 wasm = { };
529
530 unknown = { };
531 };
532
533 ################################################################################
534
535 types.openKernelFamily = mkOptionType {
536 name = "exec-format";
537 description = "executable container used by the kernel";
538 merge = mergeOneOption;
539 };
540
541 types.kernelFamily = enum (attrValues kernelFamilies);
542
543 kernelFamilies = setTypes types.openKernelFamily {
544 bsd = { };
545 darwin = { };
546 };
547
548 ################################################################################
549
550 types.openKernel = mkOptionType {
551 name = "kernel";
552 description = "kernel name and information";
553 merge = mergeOneOption;
554 check =
555 x: types.execFormat.check x.execFormat && all types.kernelFamily.check (attrValues x.families);
556 };
557
558 types.kernel = enum (attrValues kernels);
559
560 kernels =
561 let
562 inherit (execFormats)
563 elf
564 pe
565 wasm
566 unknown
567 macho
568 ;
569 inherit (kernelFamilies) bsd darwin;
570 in
571 setTypes types.openKernel {
572 # TODO(@Ericson2314): Don't want to mass-rebuild yet to keeping 'darwin' as
573 # the normalized name for macOS.
574 macos = {
575 execFormat = macho;
576 families = { inherit darwin; };
577 name = "darwin";
578 };
579 ios = {
580 execFormat = macho;
581 families = { inherit darwin; };
582 };
583 freebsd = {
584 execFormat = elf;
585 families = { inherit bsd; };
586 name = "freebsd";
587 };
588 linux = {
589 execFormat = elf;
590 families = { };
591 };
592 netbsd = {
593 execFormat = elf;
594 families = { inherit bsd; };
595 };
596 none = {
597 execFormat = unknown;
598 families = { };
599 };
600 openbsd = {
601 execFormat = elf;
602 families = { inherit bsd; };
603 };
604 solaris = {
605 execFormat = elf;
606 families = { };
607 };
608 wasi = {
609 execFormat = wasm;
610 families = { };
611 };
612 redox = {
613 execFormat = elf;
614 families = { };
615 };
616 windows = {
617 execFormat = pe;
618 families = { };
619 };
620 ghcjs = {
621 execFormat = unknown;
622 families = { };
623 };
624 genode = {
625 execFormat = elf;
626 families = { };
627 };
628 mmixware = {
629 execFormat = unknown;
630 families = { };
631 };
632 }
633 // {
634 # aliases
635 # 'darwin' is the kernel for all of them. We choose macOS by default.
636 darwin = kernels.macos;
637 watchos = kernels.ios;
638 tvos = kernels.ios;
639 win32 = kernels.windows;
640 };
641
642 ################################################################################
643
644 types.openAbi = mkOptionType {
645 name = "abi";
646 description = "binary interface for compiled code and syscalls";
647 merge = mergeOneOption;
648 };
649
650 types.abi = enum (attrValues abis);
651
652 abis = setTypes types.openAbi {
653 cygnus = { };
654 msvc = { };
655
656 # Note: eabi is specific to ARM and PowerPC.
657 # On PowerPC, this corresponds to PPCEABI.
658 # On ARM, this corresponds to ARMEABI.
659 eabi = {
660 float = "soft";
661 };
662 eabihf = {
663 float = "hard";
664 };
665
666 # Other architectures should use ELF in embedded situations.
667 elf = { };
668
669 androideabi = { };
670 android = {
671 assertions = [
672 {
673 assertion = platform: !platform.isAarch32;
674 message = ''
675 The "android" ABI is not for 32-bit ARM. Use "androideabi" instead.
676 '';
677 }
678 ];
679 };
680
681 gnueabi = {
682 float = "soft";
683 };
684 gnueabihf = {
685 float = "hard";
686 };
687 gnu = {
688 assertions = [
689 {
690 assertion = platform: !platform.isAarch32;
691 message = ''
692 The "gnu" ABI is ambiguous on 32-bit ARM. Use "gnueabi" or "gnueabihf" instead.
693 '';
694 }
695 {
696 assertion = platform: !(platform.isPower64 && platform.isBigEndian);
697 message = ''
698 The "gnu" ABI is ambiguous on big-endian 64-bit PowerPC. Use "gnuabielfv2" or "gnuabielfv1" instead.
699 '';
700 }
701 ];
702 };
703 gnuabi64 = {
704 abi = "64";
705 };
706 muslabi64 = {
707 abi = "64";
708 };
709
710 # NOTE: abi=n32 requires a 64-bit MIPS chip! That is not a typo.
711 # It is basically the 64-bit abi with 32-bit pointers. Details:
712 # https://www.linux-mips.org/pub/linux/mips/doc/ABI/MIPS-N32-ABI-Handbook.pdf
713 gnuabin32 = {
714 abi = "n32";
715 };
716 muslabin32 = {
717 abi = "n32";
718 };
719
720 gnuabielfv2 = {
721 abi = "elfv2";
722 };
723 gnuabielfv1 = {
724 abi = "elfv1";
725 };
726
727 musleabi = {
728 float = "soft";
729 };
730 musleabihf = {
731 float = "hard";
732 };
733 musl = { };
734
735 uclibceabi = {
736 float = "soft";
737 };
738 uclibceabihf = {
739 float = "hard";
740 };
741 uclibc = { };
742
743 unknown = { };
744 };
745
746 ################################################################################
747
748 types.parsedPlatform = mkOptionType {
749 name = "system";
750 description = "fully parsed representation of llvm- or nix-style platform tuple";
751 merge = mergeOneOption;
752 check =
753 {
754 cpu,
755 vendor,
756 kernel,
757 abi,
758 }:
759 types.cpuType.check cpu
760 && types.vendor.check vendor
761 && types.kernel.check kernel
762 && types.abi.check abi;
763 };
764
765 isSystem = isType "system";
766
767 mkSystem =
768 components:
769 assert types.parsedPlatform.check components;
770 setType "system" components;
771
772 mkSkeletonFromList =
773 l:
774 {
775 "1" =
776 if elemAt l 0 == "avr" then
777 {
778 cpu = elemAt l 0;
779 kernel = "none";
780 abi = "unknown";
781 }
782 else
783 throw "system string '${lib.concatStringsSep "-" l}' with 1 component is ambiguous";
784 "2" = # We only do 2-part hacks for things Nix already supports
785 if elemAt l 1 == "cygwin" then
786 {
787 cpu = elemAt l 0;
788 kernel = "windows";
789 abi = "cygnus";
790 }
791 # MSVC ought to be the default ABI so this case isn't needed. But then it
792 # becomes difficult to handle the gnu* variants for Aarch32 correctly for
793 # minGW. So it's easier to make gnu* the default for the MinGW, but
794 # hack-in MSVC for the non-MinGW case right here.
795 else if elemAt l 1 == "windows" then
796 {
797 cpu = elemAt l 0;
798 kernel = "windows";
799 abi = "msvc";
800 }
801 else if (elemAt l 1) == "elf" then
802 {
803 cpu = elemAt l 0;
804 vendor = "unknown";
805 kernel = "none";
806 abi = elemAt l 1;
807 }
808 else
809 {
810 cpu = elemAt l 0;
811 kernel = elemAt l 1;
812 };
813 "3" =
814 # cpu-kernel-environment
815 if
816 elemAt l 1 == "linux"
817 || elem (elemAt l 2) [
818 "eabi"
819 "eabihf"
820 "elf"
821 "gnu"
822 ]
823 then
824 {
825 cpu = elemAt l 0;
826 kernel = elemAt l 1;
827 abi = elemAt l 2;
828 vendor = "unknown";
829 }
830 # cpu-vendor-os
831 else if
832 elemAt l 1 == "apple"
833 || elem (elemAt l 2) [
834 "redox"
835 "mmixware"
836 "ghcjs"
837 "mingw32"
838 ]
839 || hasPrefix "freebsd" (elemAt l 2)
840 || hasPrefix "netbsd" (elemAt l 2)
841 || hasPrefix "openbsd" (elemAt l 2)
842 || hasPrefix "genode" (elemAt l 2)
843 || hasPrefix "wasm32" (elemAt l 0)
844 then
845 {
846 cpu = elemAt l 0;
847 vendor = elemAt l 1;
848 kernel =
849 if elemAt l 2 == "mingw32" then
850 "windows" # autotools breaks on -gnu for window
851 else
852 elemAt l 2;
853 }
854 else
855 throw "system string '${lib.concatStringsSep "-" l}' with 3 components is ambiguous";
856 "4" = {
857 cpu = elemAt l 0;
858 vendor = elemAt l 1;
859 kernel = elemAt l 2;
860 abi = elemAt l 3;
861 };
862 }
863 .${toString (length l)}
864 or (throw "system string '${lib.concatStringsSep "-" l}' has invalid number of hyphen-separated components");
865
866 # This should revert the job done by config.guess from the gcc compiler.
867 mkSystemFromSkeleton =
868 {
869 cpu,
870 # Optional, but fallback too complex for here.
871 # Inferred below instead.
872 vendor ?
873 assert false;
874 null,
875 kernel,
876 # Also inferred below
877 abi ?
878 assert false;
879 null,
880 }@args:
881 let
882 getCpu = name: cpuTypes.${name} or (throw "Unknown CPU type: ${name}");
883 getVendor = name: vendors.${name} or (throw "Unknown vendor: ${name}");
884 getKernel = name: kernels.${name} or (throw "Unknown kernel: ${name}");
885 getAbi = name: abis.${name} or (throw "Unknown ABI: ${name}");
886
887 parsed = {
888 cpu = getCpu args.cpu;
889 vendor =
890 if args ? vendor then
891 getVendor args.vendor
892 else if isDarwin parsed then
893 vendors.apple
894 else if isWindows parsed then
895 vendors.pc
896 else
897 vendors.unknown;
898 kernel =
899 if hasPrefix "darwin" args.kernel then
900 getKernel "darwin"
901 else if hasPrefix "netbsd" args.kernel then
902 getKernel "netbsd"
903 else
904 getKernel (removeAbiSuffix args.kernel);
905 abi =
906 if args ? abi then
907 getAbi args.abi
908 else if isLinux parsed || isWindows parsed then
909 if isAarch32 parsed then
910 if versionAtLeast (parsed.cpu.version or "0") "6" then abis.gnueabihf else abis.gnueabi
911 # Default ppc64 BE to ELFv2
912 else if isPower64 parsed && isBigEndian parsed then
913 abis.gnuabielfv2
914 else
915 abis.gnu
916 else
917 abis.unknown;
918 };
919
920 in
921 mkSystem parsed;
922
923 mkSystemFromString = s: mkSystemFromSkeleton (mkSkeletonFromList (splitString "-" s));
924
925 kernelName = kernel: kernel.name + toString (kernel.version or "");
926
927 darwinArch = cpu: if cpu.name == "aarch64" then "arm64" else cpu.name;
928
929 doubleFromSystem =
930 {
931 cpu,
932 kernel,
933 abi,
934 ...
935 }:
936 if abi == abis.cygnus then
937 "${cpu.name}-cygwin"
938 else if kernel.families ? darwin then
939 "${cpu.name}-darwin"
940 else
941 "${cpu.name}-${kernelName kernel}";
942
943 tripleFromSystem =
944 {
945 cpu,
946 vendor,
947 kernel,
948 abi,
949 ...
950 }@sys:
951 assert isSystem sys;
952 let
953 optExecFormat = optionalString (
954 kernel.name == "netbsd" && gnuNetBSDDefaultExecFormat cpu != kernel.execFormat
955 ) kernel.execFormat.name;
956 optAbi = optionalString (abi != abis.unknown) "-${abi.name}";
957 cpuName = if kernel.families ? darwin then darwinArch cpu else cpu.name;
958 in
959 "${cpuName}-${vendor.name}-${kernelName kernel}${optExecFormat}${optAbi}";
960
961 ################################################################################
962
963}