opencomposite: add meta.platforms (#357198)
[NixPkgs.git] / lib / systems / parse.nix
bloba2ee288f2c1f8f87498b1a7418820244f4020879
1 # Define the list of system with their properties.
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.
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.
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.
17 { lib }:
19 let
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     ;
36   inherit (lib.strings) match;
38   inherit (lib.systems.inspect.predicates)
39     isAarch32
40     isBigEndian
41     isDarwin
42     isLinux
43     isPower64
44     isWindows
45     ;
47   inherit (lib.types)
48     enum
49     float
50     isType
51     mkOptionType
52     number
53     setType
54     string
55     types
56     ;
58   setTypes = type:
59     mapAttrs (name: value:
60       assert type.check value;
61       setType type.name ({ inherit name; } // value));
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;
74 rec {
76   ################################################################################
78   types.openSignificantByte = mkOptionType {
79     name = "significant-byte";
80     description = "Endianness";
81     merge = mergeOneOption;
82   };
84   types.significantByte = enum (attrValues significantBytes);
86   significantBytes = setTypes types.openSignificantByte {
87     bigEndian = {};
88     littleEndian = {};
89   };
91   ################################################################################
93   # Reasonable power of 2
94   types.bitWidth = enum [ 8 16 32 64 128 ];
96   ################################################################################
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   };
108   types.cpuType = enum (attrValues cpuTypes);
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"; };
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"; };
131     microblaze   = { bits = 32; significantByte = bigEndian;    family = "microblaze"; };
132     microblazeel = { bits = 32; significantByte = littleEndian; family = "microblaze"; };
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"; };
139     mmix     = { bits = 64; significantByte = bigEndian;    family = "mmix"; };
141     m68k     = { bits = 32; significantByte = bigEndian; family = "m68k"; };
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"; };
148     riscv32  = { bits = 32; significantByte = littleEndian; family = "riscv"; };
149     riscv64  = { bits = 64; significantByte = littleEndian; family = "riscv"; };
151     s390     = { bits = 32; significantByte = bigEndian; family = "s390"; };
152     s390x    = { bits = 64; significantByte = bigEndian; family = "s390"; };
154     sparc    = { bits = 32; significantByte = bigEndian;    family = "sparc"; };
155     sparc64  = { bits = 64; significantByte = bigEndian;    family = "sparc"; };
157     wasm32   = { bits = 32; significantByte = littleEndian; family = "wasm"; };
158     wasm64   = { bits = 64; significantByte = littleEndian; family = "wasm"; };
160     alpha    = { bits = 64; significantByte = littleEndian; family = "alpha"; };
162     rx       = { bits = 32; significantByte = littleEndian; family = "rx"; };
163     msp430   = { bits = 16; significantByte = littleEndian; family = "msp430"; };
164     avr      = { bits = 8; family = "avr"; };
166     vc4      = { bits = 32; significantByte = littleEndian; family = "vc4"; };
168     or1k     = { bits = 32; significantByte = bigEndian; family = "or1k"; };
170     loongarch64 = { bits = 64; significantByte = littleEndian; family = "loongarch"; };
172     javascript = { bits = 32; significantByte = littleEndian; family = "javascript"; };
173   };
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;
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)
206     # XXX: Not true in some cases. Like in WSL mode.
207     (b == i686 && isCompatible a x86_64)
209     # ARMv4
210     (b == arm && isCompatible a armv5tel)
212     # ARMv5
213     (b == armv5tel && isCompatible a armv6l)
215     # ARMv6
216     (b == armv6l && isCompatible a armv6m)
217     (b == armv6m && isCompatible a armv7l)
219     # ARMv7
220     (b == armv7l && isCompatible a armv7a)
221     (b == armv7l && isCompatible a armv7r)
222     (b == armv7l && isCompatible a armv7m)
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)
230     # PowerPC
231     (b == powerpc && isCompatible a powerpc64)
232     (b == powerpcle && isCompatible a powerpc64le)
234     # MIPS
235     (b == mips && isCompatible a mips64)
236     (b == mipsel && isCompatible a mips64el)
238     # RISCV
239     (b == riscv32 && isCompatible a riscv64)
241     # SPARC
242     (b == sparc && isCompatible a sparc64)
244     # WASM
245     (b == wasm32 && isCompatible a wasm64)
247     # identity
248     (b == a)
249   ];
251   ################################################################################
253   types.openVendor = mkOptionType {
254     name = "vendor";
255     description = "vendor for the platform";
256     merge = mergeOneOption;
257   };
259   types.vendor = enum (attrValues vendors);
261   vendors = setTypes types.openVendor {
262     apple = {};
263     pc = {};
264     knuth = {};
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 = {};
270     none = {};
271     unknown = {};
272   };
274   ################################################################################
276   types.openExecFormat = mkOptionType {
277     name = "exec-format";
278     description = "executable container used by the kernel";
279     merge = mergeOneOption;
280   };
282   types.execFormat = enum (attrValues execFormats);
284   execFormats = setTypes types.openExecFormat {
285     aout = {}; # a.out
286     elf = {};
287     macho = {};
288     pe = {};
289     wasm = {};
291     unknown = {};
292   };
294   ################################################################################
296   types.openKernelFamily = mkOptionType {
297     name = "exec-format";
298     description = "executable container used by the kernel";
299     merge = mergeOneOption;
300   };
302   types.kernelFamily = enum (attrValues kernelFamilies);
304   kernelFamilies = setTypes types.openKernelFamily {
305     bsd = {};
306     darwin = {};
307   };
309   ################################################################################
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   };
319   types.kernel = enum (attrValues kernels);
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   };
349   ################################################################################
351   types.openAbi = mkOptionType {
352     name = "abi";
353     description = "binary interface for compiled code and syscalls";
354     merge = mergeOneOption;
355   };
357   types.abi = enum (attrValues abis);
359   abis = setTypes types.openAbi {
360     cygnus       = {};
361     msvc         = {};
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"; };
369     # Other architectures should use ELF in embedded situations.
370     elf          = {};
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     };
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"; };
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"; };
408     gnuabielfv2  = { abi = "elfv2"; };
409     gnuabielfv1  = { abi = "elfv1"; };
411     musleabi     = { float = "soft"; };
412     musleabihf   = { float = "hard"; };
413     musl         = {};
415     uclibceabi   = { float = "soft"; };
416     uclibceabihf = { float = "hard"; };
417     uclibc       = {};
419     unknown = {};
420   };
422   ################################################################################
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   };
435   isSystem = isType "system";
437   mkSystem = components:
438     assert types.parsedPlatform.check components;
439     setType "system" components;
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) [ "redox" "mmixware" "ghcjs" "mingw32" ] ||
470               hasPrefix "freebsd" (elemAt l 2) ||
471               hasPrefix "netbsd" (elemAt l 2) ||
472               hasPrefix "openbsd" (elemAt l 2) ||
473               hasPrefix "genode" (elemAt l 2) ||
474               hasPrefix "wasm32" (elemAt l 0)
475       then {
476         cpu    = elemAt l 0;
477         vendor = elemAt l 1;
478         kernel = if elemAt l 2 == "mingw32"
479                  then "windows"  # autotools breaks on -gnu for window
480                  else elemAt l 2;
481       }
482       else throw "Target specification with 3 components is ambiguous";
483     "4" =    { cpu = elemAt l 0; vendor = elemAt l 1; kernel = elemAt l 2; abi = elemAt l 3; };
484   }.${toString (length l)}
485     or (throw "system string has invalid number of hyphen-separated components");
487   # This should revert the job done by config.guess from the gcc compiler.
488   mkSystemFromSkeleton = { cpu
489                          , # Optional, but fallback too complex for here.
490                            # Inferred below instead.
491                            vendor ? assert false; null
492                          , kernel
493                          , # Also inferred below
494                            abi    ? assert false; null
495                          } @ args: let
496     getCpu    = name: cpuTypes.${name} or (throw "Unknown CPU type: ${name}");
497     getVendor = name:  vendors.${name} or (throw "Unknown vendor: ${name}");
498     getKernel = name:  kernels.${name} or (throw "Unknown kernel: ${name}");
499     getAbi    = name:     abis.${name} or (throw "Unknown ABI: ${name}");
501     parsed = {
502       cpu = getCpu args.cpu;
503       vendor =
504         /**/ if args ? vendor    then getVendor args.vendor
505         else if isDarwin  parsed then vendors.apple
506         else if isWindows parsed then vendors.pc
507         else                     vendors.unknown;
508       kernel = if hasPrefix "darwin" args.kernel      then getKernel "darwin"
509                else if hasPrefix "netbsd" args.kernel then getKernel "netbsd"
510                else                                   getKernel (removeAbiSuffix args.kernel);
511       abi =
512         /**/ if args ? abi       then getAbi args.abi
513         else if isLinux parsed || isWindows parsed then
514           if isAarch32 parsed then
515             if versionAtLeast (parsed.cpu.version or "0") "6"
516             then abis.gnueabihf
517             else abis.gnueabi
518           # Default ppc64 BE to ELFv2
519           else if isPower64 parsed && isBigEndian parsed then abis.gnuabielfv2
520           else abis.gnu
521         else                     abis.unknown;
522     };
524   in mkSystem parsed;
526   mkSystemFromString = s: mkSystemFromSkeleton (mkSkeletonFromList (splitString "-" s));
528   kernelName = kernel:
529     kernel.name + toString (kernel.version or "");
531   doubleFromSystem = { cpu, kernel, abi, ... }:
532     /**/ if abi == abis.cygnus       then "${cpu.name}-cygwin"
533     else if kernel.families ? darwin then "${cpu.name}-darwin"
534     else "${cpu.name}-${kernelName kernel}";
536   tripleFromSystem = { cpu, vendor, kernel, abi, ... } @ sys: assert isSystem sys; let
537     optExecFormat =
538       optionalString (kernel.name == "netbsd" &&
539                           gnuNetBSDDefaultExecFormat cpu != kernel.execFormat)
540         kernel.execFormat.name;
541     optAbi = optionalString (abi != abis.unknown) "-${abi.name}";
542   in "${cpu.name}-${vendor.name}-${kernelName kernel}${optExecFormat}${optAbi}";
544   ################################################################################