nixos/tests/forgejo: fix after git v2.47 bump
[NixPkgs.git] / lib / systems / default.nix
bloba6ceef2cc3a11382d91b79308d232ca55f6a2931
1 { lib }:
3 let
4   inherit (lib)
5     any
6     filterAttrs
7     foldl
8     hasInfix
9     isFunction
10     isList
11     isString
12     mapAttrs
13     optional
14     optionalAttrs
15     optionalString
16     removeSuffix
17     replaceStrings
18     toUpper
19     ;
21   inherit (lib.strings) toJSON;
23   doubles = import ./doubles.nix { inherit lib; };
24   parse = import ./parse.nix { inherit lib; };
25   inspect = import ./inspect.nix { inherit lib; };
26   platforms = import ./platforms.nix { inherit lib; };
27   examples = import ./examples.nix { inherit lib; };
28   architectures = import ./architectures.nix { inherit lib; };
30   /**
31     Elaborated systems contain functions, which means that they don't satisfy
32     `==` for a lack of reflexivity.
34     They might *appear* to satisfy `==` reflexivity when the same exact value is
35     compared to itself, because object identity is used as an "optimization";
36     compare the value with a reconstruction of itself, e.g. with `f == a: f a`,
37     or perhaps calling `elaborate` twice, and one will see reflexivity fail as described.
39     Hence a custom equality test.
41     Note that this does not canonicalize the systems, so you'll want to make sure
42     both arguments have been `elaborate`-d.
43   */
44   equals =
45     let removeFunctions = a: filterAttrs (_: v: !isFunction v) a;
46     in a: b: removeFunctions a == removeFunctions b;
48   /**
49     List of all Nix system doubles the nixpkgs flake will expose the package set
50     for. All systems listed here must be supported by nixpkgs as `localSystem`.
52     :::{.warning}
53     This attribute is considered experimental and is subject to change.
54     :::
55   */
56   flakeExposed = import ./flake-systems.nix { };
58   # Elaborate a `localSystem` or `crossSystem` so that it contains everything
59   # necessary.
60   #
61   # `parsed` is inferred from args, both because there are two options with one
62   # clearly preferred, and to prevent cycles. A simpler fixed point where the RHS
63   # always just used `final.*` would fail on both counts.
64   elaborate = args': let
65     args = if isString args' then { system = args'; }
66            else args';
68     # TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL.
69     rust = args.rust or args.rustc or {};
71     final = {
72       # Prefer to parse `config` as it is strictly more informative.
73       parsed = parse.mkSystemFromString (if args ? config then args.config else args.system);
74       # Either of these can be losslessly-extracted from `parsed` iff parsing succeeds.
75       system = parse.doubleFromSystem final.parsed;
76       config = parse.tripleFromSystem final.parsed;
77       # Determine whether we can execute binaries built for the provided platform.
78       canExecute = platform:
79         final.isAndroid == platform.isAndroid &&
80         parse.isCompatible final.parsed.cpu platform.parsed.cpu
81         && final.parsed.kernel == platform.parsed.kernel;
82       isCompatible = _: throw "2022-05-23: isCompatible has been removed in favor of canExecute, refer to the 22.11 changelog for details";
83       # Derived meta-data
84       useLLVM = final.isFreeBSD || final.isOpenBSD;
86       libc =
87         /**/ if final.isDarwin                then "libSystem"
88         else if final.isMinGW                 then "msvcrt"
89         else if final.isWasi                  then "wasilibc"
90         else if final.isWasm && !final.isWasi then null
91         else if final.isRedox                 then "relibc"
92         else if final.isMusl                  then "musl"
93         else if final.isUClibc                then "uclibc"
94         else if final.isAndroid               then "bionic"
95         else if final.isLinux  /* default */  then "glibc"
96         else if final.isFreeBSD               then "fblibc"
97         else if final.isOpenBSD               then "oblibc"
98         else if final.isNetBSD                then "nblibc"
99         else if final.isAvr                   then "avrlibc"
100         else if final.isGhcjs                 then null
101         else if final.isNone                  then "newlib"
102         # TODO(@Ericson2314) think more about other operating systems
103         else                                     "native/impure";
104       # Choose what linker we wish to use by default. Someday we might also
105       # choose the C compiler, runtime library, C++ standard library, etc. in
106       # this way, nice and orthogonally, and deprecate `useLLVM`. But due to
107       # the monolithic GCC build we cannot actually make those choices
108       # independently, so we are just doing `linker` and keeping `useLLVM` for
109       # now.
110       linker =
111         /**/ if final.useLLVM or false      then "lld"
112         else if final.isDarwin              then "cctools"
113         # "bfd" and "gold" both come from GNU binutils. The existence of Gold
114         # is why we use the more obscure "bfd" and not "binutils" for this
115         # choice.
116         else                                     "bfd";
117       # The standard lib directory name that non-nixpkgs binaries distributed
118       # for this platform normally assume.
119       libDir = if final.isLinux then
120         if final.isx86_64 || final.isMips64 || final.isPower64
121         then "lib64"
122         else "lib"
123       else null;
124       extensions = optionalAttrs final.hasSharedLibraries {
125         sharedLibrary =
126           if      final.isDarwin  then ".dylib"
127           else if final.isWindows then ".dll"
128           else                         ".so";
129       } // {
130         staticLibrary =
131           /**/ if final.isWindows then ".lib"
132           else                         ".a";
133         library =
134           /**/ if final.isStatic then final.extensions.staticLibrary
135           else                        final.extensions.sharedLibrary;
136         executable =
137           /**/ if final.isWindows then ".exe"
138           else                         "";
139       };
140       # Misc boolean options
141       useAndroidPrebuilt = false;
142       useiOSPrebuilt = false;
144       # Output from uname
145       uname = {
146         # uname -s
147         system = {
148           linux = "Linux";
149           windows = "Windows";
150           darwin = "Darwin";
151           netbsd = "NetBSD";
152           freebsd = "FreeBSD";
153           openbsd = "OpenBSD";
154           wasi = "Wasi";
155           redox = "Redox";
156           genode = "Genode";
157         }.${final.parsed.kernel.name} or null;
159          # uname -m
160          processor =
161            if final.isPower64
162            then "ppc64${optionalString final.isLittleEndian "le"}"
163            else if final.isPower
164            then "ppc${optionalString final.isLittleEndian "le"}"
165            else if final.isMips64
166            then "mips64"  # endianness is *not* included on mips64
167            else final.parsed.cpu.name;
169          # uname -r
170          release = null;
171       };
173       # It is important that hasSharedLibraries==false when the platform has no
174       # dynamic library loader.  Various tools (including the gcc build system)
175       # have knowledge of which platforms are incapable of dynamic linking, and
176       # will still build on/for those platforms with --enable-shared, but simply
177       # omit any `.so` build products such as libgcc_s.so.  When that happens,
178       # it causes hard-to-troubleshoot build failures.
179       hasSharedLibraries = with final;
180         (isAndroid || isGnu || isMusl                                  # Linux (allows multiple libcs)
181          || isDarwin || isSunOS || isOpenBSD || isFreeBSD || isNetBSD  # BSDs
182          || isCygwin || isMinGW || isWindows                           # Windows
183          || isWasm                                                     # WASM
184         ) && !isStatic;
186       # The difference between `isStatic` and `hasSharedLibraries` is mainly the
187       # addition of the `staticMarker` (see make-derivation.nix).  Some
188       # platforms, like embedded machines without a libc (e.g. arm-none-eabi)
189       # don't support dynamic linking, but don't get the `staticMarker`.
190       # `pkgsStatic` sets `isStatic=true`, so `pkgsStatic.hostPlatform` always
191       # has the `staticMarker`.
192       isStatic = final.isWasi || final.isRedox;
194       # Just a guess, based on `system`
195       inherit
196         ({
197           linux-kernel = args.linux-kernel or {};
198           gcc = args.gcc or {};
199         } // platforms.select final)
200         linux-kernel gcc;
202       # TODO: remove after 23.05 is EOL, with an error pointing to the rust.* attrs.
203       rustc = args.rustc or {};
205       linuxArch =
206         if final.isAarch32 then "arm"
207         else if final.isAarch64 then "arm64"
208         else if final.isx86_32 then "i386"
209         else if final.isx86_64 then "x86_64"
210         # linux kernel does not distinguish microblaze/microblazeel
211         else if final.isMicroBlaze then "microblaze"
212         else if final.isMips32 then "mips"
213         else if final.isMips64 then "mips"    # linux kernel does not distinguish mips32/mips64
214         else if final.isPower then "powerpc"
215         else if final.isRiscV then "riscv"
216         else if final.isS390 then "s390"
217         else if final.isLoongArch64 then "loongarch"
218         else final.parsed.cpu.name;
220       # https://source.denx.de/u-boot/u-boot/-/blob/9bfb567e5f1bfe7de8eb41f8c6d00f49d2b9a426/common/image.c#L81-106
221       ubootArch =
222         if      final.isx86_32 then "x86"    # not i386
223         else if final.isMips64 then "mips64" # uboot *does* distinguish between mips32/mips64
224         else final.linuxArch;                # other cases appear to agree with linuxArch
226       qemuArch =
227         if final.isAarch32 then "arm"
228         else if final.isS390 && !final.isS390x then null
229         else if final.isx86_64 then "x86_64"
230         else if final.isx86 then "i386"
231         else if final.isMips64n32 then "mipsn32${optionalString final.isLittleEndian "el"}"
232         else if final.isMips64 then "mips64${optionalString final.isLittleEndian "el"}"
233         else final.uname.processor;
235       # Name used by UEFI for architectures.
236       efiArch =
237         if final.isx86_32 then "ia32"
238         else if final.isx86_64 then "x64"
239         else if final.isAarch32 then "arm"
240         else if final.isAarch64 then "aa64"
241         else final.parsed.cpu.name;
243       darwinArch = {
244         armv7a  = "armv7";
245         aarch64 = "arm64";
246       }.${final.parsed.cpu.name} or final.parsed.cpu.name;
248       darwinPlatform =
249         if final.isMacOS then "macos"
250         else if final.isiOS then "ios"
251         else null;
252       # The canonical name for this attribute is darwinSdkVersion, but some
253       # platforms define the old name "sdkVer".
254       darwinSdkVersion = final.sdkVer or (if final.isAarch64 then "11.0" else "10.12");
255       darwinMinVersion = final.darwinSdkVersion;
256       darwinMinVersionVariable =
257         if final.isMacOS then "MACOSX_DEPLOYMENT_TARGET"
258         else if final.isiOS then "IPHONEOS_DEPLOYMENT_TARGET"
259         else null;
261       # Remove before 25.05
262       androidSdkVersion =
263         if (args ? sdkVer && !args ? androidSdkVersion) then
264           throw "For android `sdkVer` has been renamed to `androidSdkVersion`"
265         else if (args ? androidSdkVersion) then
266           args.androidSdkVersion
267         else
268           null;
269       androidNdkVersion =
270         if (args ? ndkVer && !args ? androidNdkVersion) then
271           throw "For android `ndkVer` has been renamed to `androidNdkVersion`"
272         else if (args ? androidSdkVersion) then
273           args.androidNdkVersion
274         else
275           null;
276     } // (
277       let
278         selectEmulator = pkgs:
279           let
280             wine = (pkgs.winePackagesFor "wine${toString final.parsed.cpu.bits}").minimal;
281           in
282           # Note: we guarantee that the return value is either `null` or a path
283           # to an emulator program. That is, if an emulator requires additional
284           # arguments, a wrapper should be used.
285           if pkgs.stdenv.hostPlatform.canExecute final
286           then "${pkgs.execline}/bin/exec"
287           else if final.isWindows
288           then "${wine}/bin/wine${optionalString (final.parsed.cpu.bits == 64) "64"}"
289           else if final.isLinux && pkgs.stdenv.hostPlatform.isLinux && final.qemuArch != null
290           then "${pkgs.qemu-user}/bin/qemu-${final.qemuArch}"
291           else if final.isWasi
292           then "${pkgs.wasmtime}/bin/wasmtime"
293           else if final.isMmix
294           then "${pkgs.mmixware}/bin/mmix"
295           else null;
296       in {
297         emulatorAvailable = pkgs: (selectEmulator pkgs) != null;
299         # whether final.emulator pkgs.pkgsStatic works
300         staticEmulatorAvailable = pkgs: final.emulatorAvailable pkgs
301           && (final.isLinux || final.isWasi || final.isMmix);
303         emulator = pkgs:
304           if (final.emulatorAvailable pkgs)
305           then selectEmulator pkgs
306           else throw "Don't know how to run ${final.config} executables.";
308     }) // mapAttrs (n: v: v final.parsed) inspect.predicates
309       // mapAttrs (n: v: v final.gcc.arch or "default") architectures.predicates
310       // args // {
311         rust = rust // {
312           # Once args.rustc.platform.target-family is deprecated and
313           # removed, there will no longer be any need to modify any
314           # values from args.rust.platform, so we can drop all the
315           # "args ? rust" etc. checks, and merge args.rust.platform in
316           # /after/.
317           platform = rust.platform or {} // {
318             # https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch
319             arch =
320               /**/ if rust ? platform then rust.platform.arch
321               else if final.isAarch32 then "arm"
322               else if final.isMips64  then "mips64"     # never add "el" suffix
323               else if final.isPower64 then "powerpc64"  # never add "le" suffix
324               else final.parsed.cpu.name;
326             # https://doc.rust-lang.org/reference/conditional-compilation.html#target_os
327             os =
328               /**/ if rust ? platform then rust.platform.os or "none"
329               else if final.isDarwin then "macos"
330               else if final.isWasm && !final.isWasi then "unknown" # Needed for {wasm32,wasm64}-unknown-unknown.
331               else final.parsed.kernel.name;
333             # https://doc.rust-lang.org/reference/conditional-compilation.html#target_family
334             target-family =
335               /**/ if args ? rust.platform.target-family then args.rust.platform.target-family
336               else if args ? rustc.platform.target-family
337               then
338                 (
339                   # Since https://github.com/rust-lang/rust/pull/84072
340                   # `target-family` is a list instead of single value.
341                   let
342                     f = args.rustc.platform.target-family;
343                   in
344                     if isList f then f else [ f ]
345                 )
346               else optional final.isUnix "unix"
347                    ++ optional final.isWindows "windows"
348                    ++ optional final.isWasm "wasm";
350             # https://doc.rust-lang.org/reference/conditional-compilation.html#target_vendor
351             vendor = let
352               inherit (final.parsed) vendor;
353             in rust.platform.vendor or {
354               "w64" = "pc";
355             }.${vendor.name} or vendor.name;
356           };
358           # The name of the rust target, even if it is custom. Adjustments are
359           # because rust has slightly different naming conventions than we do.
360           rustcTarget = let
361             inherit (final.parsed) cpu kernel abi;
362             cpu_ = rust.platform.arch or {
363               "armv7a" = "armv7";
364               "armv7l" = "armv7";
365               "armv6l" = "arm";
366               "armv5tel" = "armv5te";
367               "riscv32" = "riscv32gc";
368               "riscv64" = "riscv64gc";
369             }.${cpu.name} or cpu.name;
370             vendor_ = final.rust.platform.vendor;
371           # TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL.
372           in
373             args.rust.rustcTarget or
374             args.rustc.config or (
375               # Rust uses `wasm32-wasip?` rather than `wasm32-unknown-wasi`.
376               # We cannot know which subversion does the user want, and
377               # currently use WASI 0.1 as default for compatibility. Custom
378               # users can set `rust.rustcTarget` to override it.
379               if final.isWasi
380               then "${cpu_}-wasip1"
381               else "${cpu_}-${vendor_}-${kernel.name}${optionalString (abi.name != "unknown") "-${abi.name}"}"
382             );
384           # The name of the rust target if it is standard, or the json file
385           # containing the custom target spec.
386           rustcTargetSpec = rust.rustcTargetSpec or (
387             /**/ if rust ? platform
388             then builtins.toFile (final.rust.rustcTarget + ".json") (toJSON rust.platform)
389             else final.rust.rustcTarget);
391           # The name of the rust target if it is standard, or the
392           # basename of the file containing the custom target spec,
393           # without the .json extension.
394           #
395           # This is the name used by Cargo for target subdirectories.
396           cargoShortTarget =
397             removeSuffix ".json" (baseNameOf "${final.rust.rustcTargetSpec}");
399           # When used as part of an environment variable name, triples are
400           # uppercased and have all hyphens replaced by underscores:
401           #
402           # https://github.com/rust-lang/cargo/pull/9169
403           # https://github.com/rust-lang/cargo/issues/8285#issuecomment-634202431
404           cargoEnvVarTarget =
405             replaceStrings ["-"] ["_"]
406               (toUpper final.rust.cargoShortTarget);
408           # True if the target is no_std
409           # https://github.com/rust-lang/rust/blob/2e44c17c12cec45b6a682b1e53a04ac5b5fcc9d2/src/bootstrap/config.rs#L415-L421
410           isNoStdTarget =
411             any (t: hasInfix t final.rust.rustcTarget) ["-none" "nvptx" "switch" "-uefi"];
412         };
413       };
414   in assert final.useAndroidPrebuilt -> final.isAndroid;
415      assert foldl
416        (pass: { assertion, message }:
417          if assertion final
418          then pass
419          else throw message)
420        true
421        (final.parsed.abi.assertions or []);
422     final;
426 # Everything in this attrset is the public interface of the file.
428   inherit
429     architectures
430     doubles
431     elaborate
432     equals
433     examples
434     flakeExposed
435     inspect
436     parse
437     platforms
438     ;