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 }:
18with lib.lists;
19with lib.types;
20with lib.attrsets;
21with lib.strings;
22with (import ./inspect.nix { inherit lib; }).predicates;
23
24let
25 inherit (lib.options) mergeOneOption;
26
27 setTypes = type:
28 mapAttrs (name: value:
29 assert type.check value;
30 setType type.name ({ inherit name; } // value));
31
32in
33
34rec {
35
36 ################################################################################
37
38 types.openSignificantByte = mkOptionType {
39 name = "significant-byte";
40 description = "Endianness";
41 merge = mergeOneOption;
42 };
43
44 types.significantByte = enum (attrValues significantBytes);
45
46 significantBytes = setTypes types.openSignificantByte {
47 bigEndian = {};
48 littleEndian = {};
49 };
50
51 ################################################################################
52
53 # Reasonable power of 2
54 types.bitWidth = enum [ 8 16 32 64 128 ];
55
56 ################################################################################
57
58 types.openCpuType = mkOptionType {
59 name = "cpu-type";
60 description = "instruction set architecture name and information";
61 merge = mergeOneOption;
62 check = x: types.bitWidth.check x.bits
63 && (if 8 < x.bits
64 then types.significantByte.check x.significantByte
65 else !(x ? significantByte));
66 };
67
68 types.cpuType = enum (attrValues cpuTypes);
69
70 cpuTypes = with significantBytes; setTypes types.openCpuType {
71 arm = { bits = 32; significantByte = littleEndian; family = "arm"; };
72 armv5tel = { bits = 32; significantByte = littleEndian; family = "arm"; version = "5"; };
73 armv6m = { bits = 32; significantByte = littleEndian; family = "arm"; version = "6"; };
74 armv6l = { bits = 32; significantByte = littleEndian; family = "arm"; version = "6"; };
75 armv7a = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; };
76 armv7r = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; };
77 armv7m = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; };
78 armv7l = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; };
79 armv8a = { bits = 32; significantByte = littleEndian; family = "arm"; version = "8"; };
80 armv8r = { bits = 32; significantByte = littleEndian; family = "arm"; version = "8"; };
81 armv8m = { bits = 32; significantByte = littleEndian; family = "arm"; version = "8"; };
82 aarch64 = { bits = 64; significantByte = littleEndian; family = "arm"; version = "8"; };
83
84 i686 = { bits = 32; significantByte = littleEndian; family = "x86"; };
85 x86_64 = { bits = 64; significantByte = littleEndian; family = "x86"; };
86
87 mips = { bits = 32; significantByte = bigEndian; family = "mips"; };
88 mipsel = { bits = 32; significantByte = littleEndian; family = "mips"; };
89 mips64 = { bits = 64; significantByte = bigEndian; family = "mips"; };
90 mips64el = { bits = 64; significantByte = littleEndian; family = "mips"; };
91
92 powerpc = { bits = 32; significantByte = bigEndian; family = "power"; };
93 powerpc64 = { bits = 64; significantByte = bigEndian; family = "power"; };
94 powerpc64le = { bits = 64; significantByte = littleEndian; family = "power"; };
95
96 riscv32 = { bits = 32; significantByte = littleEndian; family = "riscv"; };
97 riscv64 = { bits = 64; significantByte = littleEndian; family = "riscv"; };
98
99 sparc = { bits = 32; significantByte = bigEndian; family = "sparc"; };
100 sparc64 = { bits = 64; significantByte = bigEndian; family = "sparc"; };
101
102 wasm32 = { bits = 32; significantByte = littleEndian; family = "wasm"; };
103 wasm64 = { bits = 64; significantByte = littleEndian; family = "wasm"; };
104 };
105
106 ################################################################################
107
108 types.openVendor = mkOptionType {
109 name = "vendor";
110 description = "vendor for the platform";
111 merge = mergeOneOption;
112 };
113
114 types.vendor = enum (attrValues vendors);
115
116 vendors = setTypes types.openVendor {
117 apple = {};
118 pc = {};
119
120 unknown = {};
121 };
122
123 ################################################################################
124
125 types.openExecFormat = mkOptionType {
126 name = "exec-format";
127 description = "executable container used by the kernel";
128 merge = mergeOneOption;
129 };
130
131 types.execFormat = enum (attrValues execFormats);
132
133 execFormats = setTypes types.openExecFormat {
134 aout = {}; # a.out
135 elf = {};
136 macho = {};
137 pe = {};
138
139 unknown = {};
140 };
141
142 ################################################################################
143
144 types.openKernelFamily = mkOptionType {
145 name = "exec-format";
146 description = "executable container used by the kernel";
147 merge = mergeOneOption;
148 };
149
150 types.kernelFamily = enum (attrValues kernelFamilies);
151
152 kernelFamilies = setTypes types.openKernelFamily {
153 bsd = {};
154 darwin = {};
155 };
156
157 ################################################################################
158
159 types.openKernel = mkOptionType {
160 name = "kernel";
161 description = "kernel name and information";
162 merge = mergeOneOption;
163 check = x: types.execFormat.check x.execFormat
164 && all types.kernelFamily.check (attrValues x.families);
165 };
166
167 types.kernel = enum (attrValues kernels);
168
169 kernels = with execFormats; with kernelFamilies; setTypes types.openKernel {
170 # TODO(@Ericson2314): Don't want to mass-rebuild yet to keeping 'darwin' as
171 # the nnormalized name for macOS.
172 macos = { execFormat = macho; families = { inherit darwin; }; name = "darwin"; };
173 ios = { execFormat = macho; families = { inherit darwin; }; };
174 freebsd = { execFormat = elf; families = { inherit bsd; }; };
175 linux = { execFormat = elf; families = { }; };
176 netbsd = { execFormat = elf; families = { inherit bsd; }; };
177 none = { execFormat = unknown; families = { }; };
178 openbsd = { execFormat = elf; families = { inherit bsd; }; };
179 solaris = { execFormat = elf; families = { }; };
180 windows = { execFormat = pe; families = { }; };
181 } // { # aliases
182 # 'darwin' is the kernel for all of them. We choose macOS by default.
183 darwin = kernels.macos;
184 watchos = kernels.ios;
185 tvos = kernels.ios;
186 win32 = kernels.windows;
187 };
188
189 ################################################################################
190
191 types.openAbi = mkOptionType {
192 name = "abi";
193 description = "binary interface for compiled code and syscalls";
194 merge = mergeOneOption;
195 };
196
197 types.abi = enum (attrValues abis);
198
199 abis = setTypes types.openAbi {
200 cygnus = {};
201 msvc = {};
202 eabi = {};
203
204 androideabi = {};
205 android = {
206 assertions = [
207 { assertion = platform: !platform.isAarch32;
208 message = ''
209 The "android" ABI is not for 32-bit ARM. Use "androideabi" instead.
210 '';
211 }
212 ];
213 };
214
215 gnueabi = { float = "soft"; };
216 gnueabihf = { float = "hard"; };
217 gnu = {
218 assertions = [
219 { assertion = platform: !platform.isAarch32;
220 message = ''
221 The "gnu" ABI is ambiguous on 32-bit ARM. Use "gnueabi" or "gnueabihf" instead.
222 '';
223 }
224 ];
225 };
226
227 musleabi = { float = "soft"; };
228 musleabihf = { float = "hard"; };
229 musl = {};
230
231 uclibceabihf = { float = "soft"; };
232 uclibceabi = { float = "hard"; };
233 uclibc = {};
234
235 unknown = {};
236 };
237
238 ################################################################################
239
240 types.parsedPlatform = mkOptionType {
241 name = "system";
242 description = "fully parsed representation of llvm- or nix-style platform tuple";
243 merge = mergeOneOption;
244 check = { cpu, vendor, kernel, abi }:
245 types.cpuType.check cpu
246 && types.vendor.check vendor
247 && types.kernel.check kernel
248 && types.abi.check abi;
249 };
250
251 isSystem = isType "system";
252
253 mkSystem = components:
254 assert types.parsedPlatform.check components;
255 setType "system" components;
256
257 mkSkeletonFromList = l: {
258 "2" = # We only do 2-part hacks for things Nix already supports
259 if elemAt l 1 == "cygwin"
260 then { cpu = elemAt l 0; kernel = "windows"; abi = "cygnus"; }
261 else { cpu = elemAt l 0; kernel = elemAt l 1; };
262 "3" = # Awkwards hacks, beware!
263 if elemAt l 1 == "apple"
264 then { cpu = elemAt l 0; vendor = "apple"; kernel = elemAt l 2; }
265 else if (elemAt l 1 == "linux") || (elemAt l 2 == "gnu")
266 then { cpu = elemAt l 0; kernel = elemAt l 1; abi = elemAt l 2; }
267 else if (elemAt l 2 == "mingw32") # autotools breaks on -gnu for window
268 then { cpu = elemAt l 0; vendor = elemAt l 1; kernel = "windows"; abi = "gnu"; }
269 else if hasPrefix "netbsd" (elemAt l 2)
270 then { cpu = elemAt l 0; vendor = elemAt l 1; kernel = elemAt l 2; }
271 else throw "Target specification with 3 components is ambiguous";
272 "4" = { cpu = elemAt l 0; vendor = elemAt l 1; kernel = elemAt l 2; abi = elemAt l 3; };
273 }.${toString (length l)}
274 or (throw "system string has invalid number of hyphen-separated components");
275
276 # This should revert the job done by config.guess from the gcc compiler.
277 mkSystemFromSkeleton = { cpu
278 , # Optional, but fallback too complex for here.
279 # Inferred below instead.
280 vendor ? assert false; null
281 , kernel
282 , # Also inferred below
283 abi ? assert false; null
284 } @ args: let
285 getCpu = name: cpuTypes.${name} or (throw "Unknown CPU type: ${name}");
286 getVendor = name: vendors.${name} or (throw "Unknown vendor: ${name}");
287 getKernel = name: kernels.${name} or (throw "Unknown kernel: ${name}");
288 getAbi = name: abis.${name} or (throw "Unknown ABI: ${name}");
289
290 parsed = rec {
291 cpu = getCpu args.cpu;
292 vendor =
293 /**/ if args ? vendor then getVendor args.vendor
294 else if isDarwin parsed then vendors.apple
295 else if isWindows parsed then vendors.pc
296 else vendors.unknown;
297 kernel = if hasPrefix "darwin" args.kernel then getKernel "darwin"
298 else if hasPrefix "netbsd" args.kernel then getKernel "netbsd"
299 else getKernel args.kernel;
300 abi =
301 /**/ if args ? abi then getAbi args.abi
302 else if isLinux parsed then
303 if isAarch32 parsed then
304 if lib.versionAtLeast (parsed.cpu.version or "0") "6"
305 then abis.gnueabihf
306 else abis.gnueabi
307 else abis.gnu
308 else if isWindows parsed then abis.gnu
309 else abis.unknown;
310 };
311
312 in mkSystem parsed;
313
314 mkSystemFromString = s: mkSystemFromSkeleton (mkSkeletonFromList (lib.splitString "-" s));
315
316 doubleFromSystem = { cpu, vendor, kernel, abi, ... }:
317 /**/ if abi == abis.cygnus then "${cpu.name}-cygwin"
318 else if kernel.families ? darwin then "${cpu.name}-darwin"
319 else "${cpu.name}-${kernel.name}";
320
321 tripleFromSystem = { cpu, vendor, kernel, abi, ... } @ sys: assert isSystem sys; let
322 optAbi = lib.optionalString (abi != abis.unknown) "-${abi.name}";
323 in "${cpu.name}-${vendor.name}-${kernel.name}${optAbi}";
324
325 ################################################################################
326
327}