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