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