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 = type:
59 mapAttrs (name: value:
60 assert type.check value;
61 setType type.name ({ inherit name; } // value));
62
63 # gnu-config will ignore the portion of a triple matching the
64 # regex `e?abi.*$` when determining the validity of a triple. In
65 # other words, `i386-linuxabichickenlips` is a valid triple.
66 removeAbiSuffix = x:
67 let found = match "(.*)e?abi.*" x;
68 in if found == null
69 then x
70 else elemAt found 0;
71
72in
73
74rec {
75
76 ################################################################################
77
78 types.openSignificantByte = mkOptionType {
79 name = "significant-byte";
80 description = "Endianness";
81 merge = mergeOneOption;
82 };
83
84 types.significantByte = enum (attrValues significantBytes);
85
86 significantBytes = setTypes types.openSignificantByte {
87 bigEndian = {};
88 littleEndian = {};
89 };
90
91 ################################################################################
92
93 # Reasonable power of 2
94 types.bitWidth = enum [ 8 16 32 64 128 ];
95
96 ################################################################################
97
98 types.openCpuType = mkOptionType {
99 name = "cpu-type";
100 description = "instruction set architecture name and information";
101 merge = mergeOneOption;
102 check = x: types.bitWidth.check x.bits
103 && (if 8 < x.bits
104 then types.significantByte.check x.significantByte
105 else !(x ? significantByte));
106 };
107
108 types.cpuType = enum (attrValues cpuTypes);
109
110 cpuTypes = let inherit (significantBytes) bigEndian littleEndian; in setTypes types.openCpuType {
111 arm = { bits = 32; significantByte = littleEndian; family = "arm"; };
112 armv5tel = { bits = 32; significantByte = littleEndian; family = "arm"; version = "5"; arch = "armv5t"; };
113 armv6m = { bits = 32; significantByte = littleEndian; family = "arm"; version = "6"; arch = "armv6-m"; };
114 armv6l = { bits = 32; significantByte = littleEndian; family = "arm"; version = "6"; arch = "armv6"; };
115 armv7a = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; arch = "armv7-a"; };
116 armv7r = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; arch = "armv7-r"; };
117 armv7m = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; arch = "armv7-m"; };
118 armv7l = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; arch = "armv7"; };
119 armv8a = { bits = 32; significantByte = littleEndian; family = "arm"; version = "8"; arch = "armv8-a"; };
120 armv8r = { bits = 32; significantByte = littleEndian; family = "arm"; version = "8"; arch = "armv8-a"; };
121 armv8m = { bits = 32; significantByte = littleEndian; family = "arm"; version = "8"; arch = "armv8-m"; };
122 aarch64 = { bits = 64; significantByte = littleEndian; family = "arm"; version = "8"; arch = "armv8-a"; };
123 aarch64_be = { bits = 64; significantByte = bigEndian; family = "arm"; version = "8"; arch = "armv8-a"; };
124
125 i386 = { bits = 32; significantByte = littleEndian; family = "x86"; arch = "i386"; };
126 i486 = { bits = 32; significantByte = littleEndian; family = "x86"; arch = "i486"; };
127 i586 = { bits = 32; significantByte = littleEndian; family = "x86"; arch = "i586"; };
128 i686 = { bits = 32; significantByte = littleEndian; family = "x86"; arch = "i686"; };
129 x86_64 = { bits = 64; significantByte = littleEndian; family = "x86"; arch = "x86-64"; };
130
131 microblaze = { bits = 32; significantByte = bigEndian; family = "microblaze"; };
132 microblazeel = { bits = 32; significantByte = littleEndian; family = "microblaze"; };
133
134 mips = { bits = 32; significantByte = bigEndian; family = "mips"; };
135 mipsel = { bits = 32; significantByte = littleEndian; family = "mips"; };
136 mips64 = { bits = 64; significantByte = bigEndian; family = "mips"; };
137 mips64el = { bits = 64; significantByte = littleEndian; family = "mips"; };
138
139 mmix = { bits = 64; significantByte = bigEndian; family = "mmix"; };
140
141 m68k = { bits = 32; significantByte = bigEndian; family = "m68k"; };
142
143 powerpc = { bits = 32; significantByte = bigEndian; family = "power"; };
144 powerpc64 = { bits = 64; significantByte = bigEndian; family = "power"; };
145 powerpc64le = { bits = 64; significantByte = littleEndian; family = "power"; };
146 powerpcle = { bits = 32; significantByte = littleEndian; family = "power"; };
147
148 riscv32 = { bits = 32; significantByte = littleEndian; family = "riscv"; };
149 riscv64 = { bits = 64; significantByte = littleEndian; family = "riscv"; };
150
151 s390 = { bits = 32; significantByte = bigEndian; family = "s390"; };
152 s390x = { bits = 64; significantByte = bigEndian; family = "s390"; };
153
154 sparc = { bits = 32; significantByte = bigEndian; family = "sparc"; };
155 sparc64 = { bits = 64; significantByte = bigEndian; family = "sparc"; };
156
157 wasm32 = { bits = 32; significantByte = littleEndian; family = "wasm"; };
158 wasm64 = { bits = 64; significantByte = littleEndian; family = "wasm"; };
159
160 alpha = { bits = 64; significantByte = littleEndian; family = "alpha"; };
161
162 rx = { bits = 32; significantByte = littleEndian; family = "rx"; };
163 msp430 = { bits = 16; significantByte = littleEndian; family = "msp430"; };
164 avr = { bits = 8; family = "avr"; };
165
166 vc4 = { bits = 32; significantByte = littleEndian; family = "vc4"; };
167
168 or1k = { bits = 32; significantByte = bigEndian; family = "or1k"; };
169
170 loongarch64 = { bits = 64; significantByte = littleEndian; family = "loongarch"; };
171
172 javascript = { bits = 32; significantByte = littleEndian; family = "javascript"; };
173 };
174
175 # GNU build systems assume that older NetBSD architectures are using a.out.
176 gnuNetBSDDefaultExecFormat = cpu:
177 if (cpu.family == "arm" && cpu.bits == 32) ||
178 (cpu.family == "sparc" && cpu.bits == 32) ||
179 (cpu.family == "m68k" && cpu.bits == 32) ||
180 (cpu.family == "x86" && cpu.bits == 32)
181 then execFormats.aout
182 else execFormats.elf;
183
184 # Determine when two CPUs are compatible with each other. That is,
185 # can code built for system B run on system A? For that to happen,
186 # the programs that system B accepts must be a subset of the
187 # programs that system A accepts.
188 #
189 # We have the following properties of the compatibility relation,
190 # which must be preserved when adding compatibility information for
191 # additional CPUs.
192 # - (reflexivity)
193 # Every CPU is compatible with itself.
194 # - (transitivity)
195 # If A is compatible with B and B is compatible with C then A is compatible with C.
196 #
197 # Note: Since 22.11 the archs of a mode switching CPU are no longer considered
198 # pairwise compatible. Mode switching implies that binaries built for A
199 # and B respectively can't be executed at the same time.
200 isCompatible = with cpuTypes; a: b: any id [
201 # x86
202 (b == i386 && isCompatible a i486)
203 (b == i486 && isCompatible a i586)
204 (b == i586 && isCompatible a i686)
205
206 # XXX: Not true in some cases. Like in WSL mode.
207 (b == i686 && isCompatible a x86_64)
208
209 # ARMv4
210 (b == arm && isCompatible a armv5tel)
211
212 # ARMv5
213 (b == armv5tel && isCompatible a armv6l)
214
215 # ARMv6
216 (b == armv6l && isCompatible a armv6m)
217 (b == armv6m && isCompatible a armv7l)
218
219 # ARMv7
220 (b == armv7l && isCompatible a armv7a)
221 (b == armv7l && isCompatible a armv7r)
222 (b == armv7l && isCompatible a armv7m)
223
224 # ARMv8
225 (b == aarch64 && a == armv8a)
226 (b == armv8a && isCompatible a aarch64)
227 (b == armv8r && isCompatible a armv8a)
228 (b == armv8m && isCompatible a armv8a)
229
230 # PowerPC
231 (b == powerpc && isCompatible a powerpc64)
232 (b == powerpcle && isCompatible a powerpc64le)
233
234 # MIPS
235 (b == mips && isCompatible a mips64)
236 (b == mipsel && isCompatible a mips64el)
237
238 # RISCV
239 (b == riscv32 && isCompatible a riscv64)
240
241 # SPARC
242 (b == sparc && isCompatible a sparc64)
243
244 # WASM
245 (b == wasm32 && isCompatible a wasm64)
246
247 # identity
248 (b == a)
249 ];
250
251 ################################################################################
252
253 types.openVendor = mkOptionType {
254 name = "vendor";
255 description = "vendor for the platform";
256 merge = mergeOneOption;
257 };
258
259 types.vendor = enum (attrValues vendors);
260
261 vendors = setTypes types.openVendor {
262 apple = {};
263 pc = {};
264 knuth = {};
265
266 # Actually matters, unlocking some MinGW-w64-specific options in GCC. See
267 # bottom of https://sourceforge.net/p/mingw-w64/wiki2/Unicode%20apps/
268 w64 = {};
269
270 none = {};
271 unknown = {};
272 };
273
274 ################################################################################
275
276 types.openExecFormat = mkOptionType {
277 name = "exec-format";
278 description = "executable container used by the kernel";
279 merge = mergeOneOption;
280 };
281
282 types.execFormat = enum (attrValues execFormats);
283
284 execFormats = setTypes types.openExecFormat {
285 aout = {}; # a.out
286 elf = {};
287 macho = {};
288 pe = {};
289 wasm = {};
290
291 unknown = {};
292 };
293
294 ################################################################################
295
296 types.openKernelFamily = mkOptionType {
297 name = "exec-format";
298 description = "executable container used by the kernel";
299 merge = mergeOneOption;
300 };
301
302 types.kernelFamily = enum (attrValues kernelFamilies);
303
304 kernelFamilies = setTypes types.openKernelFamily {
305 bsd = {};
306 darwin = {};
307 };
308
309 ################################################################################
310
311 types.openKernel = mkOptionType {
312 name = "kernel";
313 description = "kernel name and information";
314 merge = mergeOneOption;
315 check = x: types.execFormat.check x.execFormat
316 && all types.kernelFamily.check (attrValues x.families);
317 };
318
319 types.kernel = enum (attrValues kernels);
320
321 kernels = let
322 inherit (execFormats) elf pe wasm unknown macho;
323 inherit (kernelFamilies) bsd darwin;
324 in setTypes types.openKernel {
325 # TODO(@Ericson2314): Don't want to mass-rebuild yet to keeping 'darwin' as
326 # the normalized name for macOS.
327 macos = { execFormat = macho; families = { inherit darwin; }; name = "darwin"; };
328 ios = { execFormat = macho; families = { inherit darwin; }; };
329 freebsd = { execFormat = elf; families = { inherit bsd; }; name = "freebsd"; };
330 linux = { execFormat = elf; families = { }; };
331 netbsd = { execFormat = elf; families = { inherit bsd; }; };
332 none = { execFormat = unknown; families = { }; };
333 openbsd = { execFormat = elf; families = { inherit bsd; }; };
334 solaris = { execFormat = elf; families = { }; };
335 wasi = { execFormat = wasm; families = { }; };
336 redox = { execFormat = elf; families = { }; };
337 windows = { execFormat = pe; families = { }; };
338 ghcjs = { execFormat = unknown; families = { }; };
339 genode = { execFormat = elf; families = { }; };
340 mmixware = { execFormat = unknown; families = { }; };
341 } // { # aliases
342 # 'darwin' is the kernel for all of them. We choose macOS by default.
343 darwin = kernels.macos;
344 watchos = kernels.ios;
345 tvos = kernels.ios;
346 win32 = kernels.windows;
347 };
348
349 ################################################################################
350
351 types.openAbi = mkOptionType {
352 name = "abi";
353 description = "binary interface for compiled code and syscalls";
354 merge = mergeOneOption;
355 };
356
357 types.abi = enum (attrValues abis);
358
359 abis = setTypes types.openAbi {
360 cygnus = {};
361 msvc = {};
362
363 # Note: eabi is specific to ARM and PowerPC.
364 # On PowerPC, this corresponds to PPCEABI.
365 # On ARM, this corresponds to ARMEABI.
366 eabi = { float = "soft"; };
367 eabihf = { float = "hard"; };
368
369 # Other architectures should use ELF in embedded situations.
370 elf = {};
371
372 androideabi = {};
373 android = {
374 assertions = [
375 { assertion = platform: !platform.isAarch32;
376 message = ''
377 The "android" ABI is not for 32-bit ARM. Use "androideabi" instead.
378 '';
379 }
380 ];
381 };
382
383 gnueabi = { float = "soft"; };
384 gnueabihf = { float = "hard"; };
385 gnu = {
386 assertions = [
387 { assertion = platform: !platform.isAarch32;
388 message = ''
389 The "gnu" ABI is ambiguous on 32-bit ARM. Use "gnueabi" or "gnueabihf" instead.
390 '';
391 }
392 { assertion = platform: !(platform.isPower64 && platform.isBigEndian);
393 message = ''
394 The "gnu" ABI is ambiguous on big-endian 64-bit PowerPC. Use "gnuabielfv2" or "gnuabielfv1" instead.
395 '';
396 }
397 ];
398 };
399 gnuabi64 = { abi = "64"; };
400 muslabi64 = { abi = "64"; };
401
402 # NOTE: abi=n32 requires a 64-bit MIPS chip! That is not a typo.
403 # It is basically the 64-bit abi with 32-bit pointers. Details:
404 # https://www.linux-mips.org/pub/linux/mips/doc/ABI/MIPS-N32-ABI-Handbook.pdf
405 gnuabin32 = { abi = "n32"; };
406 muslabin32 = { abi = "n32"; };
407
408 gnuabielfv2 = { abi = "elfv2"; };
409 gnuabielfv1 = { abi = "elfv1"; };
410
411 musleabi = { float = "soft"; };
412 musleabihf = { float = "hard"; };
413 musl = {};
414
415 uclibceabi = { float = "soft"; };
416 uclibceabihf = { float = "hard"; };
417 uclibc = {};
418
419 unknown = {};
420 };
421
422 ################################################################################
423
424 types.parsedPlatform = mkOptionType {
425 name = "system";
426 description = "fully parsed representation of llvm- or nix-style platform tuple";
427 merge = mergeOneOption;
428 check = { cpu, vendor, kernel, abi }:
429 types.cpuType.check cpu
430 && types.vendor.check vendor
431 && types.kernel.check kernel
432 && types.abi.check abi;
433 };
434
435 isSystem = isType "system";
436
437 mkSystem = components:
438 assert types.parsedPlatform.check components;
439 setType "system" components;
440
441 mkSkeletonFromList = l: {
442 "1" = if elemAt l 0 == "avr"
443 then { cpu = elemAt l 0; kernel = "none"; abi = "unknown"; }
444 else throw "Target specification with 1 components is ambiguous";
445 "2" = # We only do 2-part hacks for things Nix already supports
446 if elemAt l 1 == "cygwin"
447 then { cpu = elemAt l 0; kernel = "windows"; abi = "cygnus"; }
448 # MSVC ought to be the default ABI so this case isn't needed. But then it
449 # becomes difficult to handle the gnu* variants for Aarch32 correctly for
450 # minGW. So it's easier to make gnu* the default for the MinGW, but
451 # hack-in MSVC for the non-MinGW case right here.
452 else if elemAt l 1 == "windows"
453 then { cpu = elemAt l 0; kernel = "windows"; abi = "msvc"; }
454 else if (elemAt l 1) == "elf"
455 then { cpu = elemAt l 0; vendor = "unknown"; kernel = "none"; abi = elemAt l 1; }
456 else { cpu = elemAt l 0; kernel = elemAt l 1; };
457 "3" =
458 # cpu-kernel-environment
459 if elemAt l 1 == "linux" ||
460 elem (elemAt l 2) ["eabi" "eabihf" "elf" "gnu"]
461 then {
462 cpu = elemAt l 0;
463 kernel = elemAt l 1;
464 abi = elemAt l 2;
465 vendor = "unknown";
466 }
467 # cpu-vendor-os
468 else if elemAt l 1 == "apple" ||
469 elem (elemAt l 2) [ "wasi" "redox" "mmixware" "ghcjs" "mingw32" ] ||
470 hasPrefix "freebsd" (elemAt l 2) ||
471 hasPrefix "netbsd" (elemAt l 2) ||
472 hasPrefix "genode" (elemAt l 2)
473 then {
474 cpu = elemAt l 0;
475 vendor = elemAt l 1;
476 kernel = if elemAt l 2 == "mingw32"
477 then "windows" # autotools breaks on -gnu for window
478 else elemAt l 2;
479 }
480 else throw "Target specification with 3 components is ambiguous";
481 "4" = { cpu = elemAt l 0; vendor = elemAt l 1; kernel = elemAt l 2; abi = elemAt l 3; };
482 }.${toString (length l)}
483 or (throw "system string has invalid number of hyphen-separated components");
484
485 # This should revert the job done by config.guess from the gcc compiler.
486 mkSystemFromSkeleton = { cpu
487 , # Optional, but fallback too complex for here.
488 # Inferred below instead.
489 vendor ? assert false; null
490 , kernel
491 , # Also inferred below
492 abi ? assert false; null
493 } @ args: let
494 getCpu = name: cpuTypes.${name} or (throw "Unknown CPU type: ${name}");
495 getVendor = name: vendors.${name} or (throw "Unknown vendor: ${name}");
496 getKernel = name: kernels.${name} or (throw "Unknown kernel: ${name}");
497 getAbi = name: abis.${name} or (throw "Unknown ABI: ${name}");
498
499 parsed = {
500 cpu = getCpu args.cpu;
501 vendor =
502 /**/ if args ? vendor then getVendor args.vendor
503 else if isDarwin parsed then vendors.apple
504 else if isWindows parsed then vendors.pc
505 else vendors.unknown;
506 kernel = if hasPrefix "darwin" args.kernel then getKernel "darwin"
507 else if hasPrefix "netbsd" args.kernel then getKernel "netbsd"
508 else getKernel (removeAbiSuffix args.kernel);
509 abi =
510 /**/ if args ? abi then getAbi args.abi
511 else if isLinux parsed || isWindows parsed then
512 if isAarch32 parsed then
513 if versionAtLeast (parsed.cpu.version or "0") "6"
514 then abis.gnueabihf
515 else abis.gnueabi
516 # Default ppc64 BE to ELFv2
517 else if isPower64 parsed && isBigEndian parsed then abis.gnuabielfv2
518 else abis.gnu
519 else abis.unknown;
520 };
521
522 in mkSystem parsed;
523
524 mkSystemFromString = s: mkSystemFromSkeleton (mkSkeletonFromList (splitString "-" s));
525
526 kernelName = kernel:
527 kernel.name + toString (kernel.version or "");
528
529 doubleFromSystem = { cpu, kernel, abi, ... }:
530 /**/ if abi == abis.cygnus then "${cpu.name}-cygwin"
531 else if kernel.families ? darwin then "${cpu.name}-darwin"
532 else "${cpu.name}-${kernelName kernel}";
533
534 tripleFromSystem = { cpu, vendor, kernel, abi, ... } @ sys: assert isSystem sys; let
535 optExecFormat =
536 optionalString (kernel.name == "netbsd" &&
537 gnuNetBSDDefaultExecFormat cpu != kernel.execFormat)
538 kernel.execFormat.name;
539 optAbi = optionalString (abi != abis.unknown) "-${abi.name}";
540 in "${cpu.name}-${vendor.name}-${kernelName kernel}${optExecFormat}${optAbi}";
541
542 ################################################################################
543
544}