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