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