nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix
at r-updates 654 lines 24 kB view raw
1{ lib }: 2 3let 4 inherit (lib) 5 any 6 filterAttrs 7 foldl 8 hasInfix 9 isAttrs 10 isFunction 11 isList 12 mapAttrs 13 optional 14 optionalAttrs 15 optionalString 16 removeSuffix 17 replaceString 18 toUpper 19 ; 20 21 inherit (lib.strings) toJSON; 22 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; }; 29 30 /** 31 Elaborated systems contain functions, which means that they don't satisfy 32 `==` for a lack of reflexivity. 33 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. 38 39 Hence a custom equality test. 40 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 46 removeFunctions = a: filterAttrs (_: v: !isFunction v) a; 47 in 48 a: b: removeFunctions a == removeFunctions b; 49 50 /** 51 List of all Nix system doubles the nixpkgs flake will expose the package set 52 for. All systems listed here must be supported by nixpkgs as `localSystem`. 53 54 :::{.warning} 55 This attribute is considered experimental and is subject to change. 56 ::: 57 */ 58 flakeExposed = import ./flake-systems.nix { }; 59 60 # Turn localSystem or crossSystem, which could be system-string or attrset, into 61 # attrset. 62 systemToAttrs = 63 systemOrArgs: if isAttrs systemOrArgs then systemOrArgs else { system = systemOrArgs; }; 64 65 # Elaborate a `localSystem` or `crossSystem` so that it contains everything 66 # necessary. 67 # 68 # `parsed` is inferred from args, both because there are two options with one 69 # clearly preferred, and to prevent cycles. A simpler fixed point where the RHS 70 # always just used `final.*` would fail on both counts. 71 elaborate = 72 systemOrArgs: 73 let 74 allArgs = systemToAttrs systemOrArgs; 75 76 # Those two will always be derived from "config", if given, so they should NOT 77 # be overridden further down with "// args". 78 args = removeAttrs allArgs [ 79 "parsed" 80 "system" 81 ]; 82 83 # TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL. 84 rust = args.rust or args.rustc or { }; 85 86 final = { 87 # Prefer to parse `config` as it is strictly more informative. 88 parsed = parse.mkSystemFromString (args.config or allArgs.system); 89 # This can be losslessly-extracted from `parsed` iff parsing succeeds. 90 system = parse.doubleFromSystem final.parsed; 91 # TODO: This currently can't be losslessly-extracted from `parsed`, for example 92 # because of -mingw32. 93 config = parse.tripleFromSystem final.parsed; 94 # Determine whether we can execute binaries built for the provided platform. 95 canExecute = 96 platform: 97 final.isAndroid == platform.isAndroid 98 && parse.isCompatible final.parsed.cpu platform.parsed.cpu 99 && final.parsed.kernel == platform.parsed.kernel 100 && ( 101 # Only perform this check when cpus have the same type; 102 # assume compatible cpu have all the instructions included 103 final.parsed.cpu == platform.parsed.cpu 104 -> 105 # if platform has gcc.arch, final must also have and can execute the gcc.arch of platform 106 ( 107 platform ? gcc.arch -> final ? gcc.arch && architectures.canExecute final.gcc.arch platform.gcc.arch 108 ) 109 ); 110 111 isCompatible = 112 _: 113 throw "2022-05-23: isCompatible has been removed in favor of canExecute, refer to the 22.11 changelog for details"; 114 # Derived meta-data 115 useLLVM = final.isFreeBSD || final.isOpenBSD; 116 117 libc = 118 if final.isDarwin then 119 "libSystem" 120 else if final.isMsvc then 121 "ucrt" 122 else if final.isMinGW then 123 "msvcrt" 124 else if final.isCygwin then 125 "cygwin" 126 else if final.isWasi then 127 "wasilibc" 128 else if final.isWasm && !final.isWasi then 129 null 130 else if final.isRedox then 131 "relibc" 132 else if final.isMusl then 133 "musl" 134 else if final.isUClibc then 135 "uclibc" 136 else if final.isAndroid then 137 "bionic" 138 else if 139 final.isLinux # default 140 then 141 "glibc" 142 else if final.isFreeBSD then 143 "fblibc" 144 else if final.isOpenBSD then 145 "oblibc" 146 else if final.isNetBSD then 147 "nblibc" 148 else if final.isAvr then 149 "avrlibc" 150 else if final.isGhcjs then 151 null 152 else if final.isNone then 153 "newlib" 154 # TODO(@Ericson2314) think more about other operating systems 155 else 156 "native/impure"; 157 # Choose what linker we wish to use by default. Someday we might also 158 # choose the C compiler, runtime library, C++ standard library, etc. in 159 # this way, nice and orthogonally, and deprecate `useLLVM`. But due to 160 # the monolithic GCC build we cannot actually make those choices 161 # independently, so we are just doing `linker` and keeping `useLLVM` for 162 # now. 163 linker = 164 if final.useLLVM or false then 165 "lld" 166 else if final.isDarwin then 167 "cctools" 168 # "bfd" and "gold" both come from GNU binutils. The existence of Gold 169 # is why we use the more obscure "bfd" and not "binutils" for this 170 # choice. 171 else 172 "bfd"; 173 # The standard lib directory name that non-nixpkgs binaries distributed 174 # for this platform normally assume. 175 libDir = 176 if final.isLinux then 177 if final.isx86_64 || final.isMips64 || final.isPower64 then "lib64" else "lib" 178 else 179 null; 180 extensions = 181 optionalAttrs final.hasSharedLibraries { 182 sharedLibrary = 183 if final.isDarwin then 184 ".dylib" 185 else if (final.isWindows || final.isCygwin) then 186 ".dll" 187 else 188 ".so"; 189 } 190 // { 191 staticLibrary = if final.isWindows then ".lib" else ".a"; 192 library = if final.isStatic then final.extensions.staticLibrary else final.extensions.sharedLibrary; 193 executable = if (final.isWindows || final.isCygwin) then ".exe" else ""; 194 }; 195 # Misc boolean options 196 useAndroidPrebuilt = false; 197 useiOSPrebuilt = false; 198 199 # Output from uname 200 uname = { 201 # uname -s 202 system = 203 { 204 linux = "Linux"; 205 windows = "Windows"; 206 cygwin = "CYGWIN_NT"; 207 darwin = "Darwin"; 208 netbsd = "NetBSD"; 209 freebsd = "FreeBSD"; 210 openbsd = "OpenBSD"; 211 wasi = "Wasi"; 212 redox = "Redox"; 213 genode = "Genode"; 214 } 215 .${final.parsed.kernel.name} or null; 216 217 # uname -m 218 processor = 219 if final.isPower64 then 220 "ppc64${optionalString final.isLittleEndian "le"}" 221 else if final.isPower then 222 "ppc${optionalString final.isLittleEndian "le"}" 223 else if final.isMips64 then 224 "mips64" # endianness is *not* included on mips64 225 else if final.isDarwin then 226 final.darwinArch 227 else 228 final.parsed.cpu.name; 229 230 # uname -r 231 release = null; 232 }; 233 234 # It is important that hasSharedLibraries==false when the platform has no 235 # dynamic library loader. Various tools (including the gcc build system) 236 # have knowledge of which platforms are incapable of dynamic linking, and 237 # will still build on/for those platforms with --enable-shared, but simply 238 # omit any `.so` build products such as libgcc_s.so. When that happens, 239 # it causes hard-to-troubleshoot build failures. 240 hasSharedLibraries = 241 with final; 242 ( 243 isAndroid 244 || isGnu 245 || isMusl # Linux (allows multiple libcs) 246 || isDarwin 247 || isSunOS 248 || isOpenBSD 249 || isFreeBSD 250 || isNetBSD # BSDs 251 || isCygwin 252 || isMinGW 253 || isWindows # Windows 254 || isWasm # WASM 255 ) 256 && !isStatic; 257 258 # The difference between `isStatic` and `hasSharedLibraries` is mainly the 259 # addition of the `staticMarker` (see make-derivation.nix). Some 260 # platforms, like embedded machines without a libc (e.g. arm-none-eabi) 261 # don't support dynamic linking, but don't get the `staticMarker`. 262 # `pkgsStatic` sets `isStatic=true`, so `pkgsStatic.hostPlatform` always 263 # has the `staticMarker`. 264 isStatic = final.isWasi || final.isRedox; 265 266 # Just a guess, based on `system` 267 inherit 268 ( 269 { 270 linux-kernel = args.linux-kernel or { }; 271 gcc = args.gcc or { }; 272 } 273 // platforms.select final 274 ) 275 linux-kernel 276 gcc 277 ; 278 279 # TODO: remove after 23.05 is EOL, with an error pointing to the rust.* attrs. 280 rustc = args.rustc or { }; 281 282 linuxArch = 283 if final.isAarch32 then 284 "arm" 285 else if final.isAarch64 then 286 "arm64" 287 else if final.isx86_32 then 288 "i386" 289 else if final.isx86_64 then 290 "x86_64" 291 # linux kernel does not distinguish microblaze/microblazeel 292 else if final.isMicroBlaze then 293 "microblaze" 294 else if final.isMips32 then 295 "mips" 296 else if final.isMips64 then 297 "mips" # linux kernel does not distinguish mips32/mips64 298 else if final.isPower then 299 "powerpc" 300 else if final.isRiscV then 301 "riscv" 302 else if final.isS390 then 303 "s390" 304 else if final.isLoongArch64 then 305 "loongarch" 306 else 307 final.parsed.cpu.name; 308 309 # https://source.denx.de/u-boot/u-boot/-/blob/9bfb567e5f1bfe7de8eb41f8c6d00f49d2b9a426/common/image.c#L81-106 310 ubootArch = 311 if final.isx86_32 then 312 "x86" # not i386 313 else if final.isMips64 then 314 "mips64" # uboot *does* distinguish between mips32/mips64 315 else 316 final.linuxArch; # other cases appear to agree with linuxArch 317 318 qemuArch = 319 if final.isAarch32 then 320 "arm" 321 else if final.isAarch64 then 322 "aarch64" 323 else if final.isS390 && !final.isS390x then 324 null 325 else if final.isx86_64 then 326 "x86_64" 327 else if final.isx86 then 328 "i386" 329 else if final.isMips64n32 then 330 "mipsn32${optionalString final.isLittleEndian "el"}" 331 else if final.isMips64 then 332 "mips64${optionalString final.isLittleEndian "el"}" 333 else 334 final.uname.processor; 335 336 # Name used by UEFI for architectures. 337 efiArch = 338 if final.isx86_32 then 339 "ia32" 340 else if final.isx86_64 then 341 "x64" 342 else if final.isAarch32 then 343 "arm" 344 else if final.isAarch64 then 345 "aa64" 346 else 347 final.parsed.cpu.name; 348 349 darwinArch = parse.darwinArch final.parsed.cpu; 350 351 darwinPlatform = 352 if final.isMacOS then 353 "macos" 354 else if final.isiOS then 355 "ios" 356 else 357 null; 358 # The canonical name for this attribute is darwinSdkVersion, but some 359 # platforms define the old name "sdkVer". 360 darwinSdkVersion = final.sdkVer or "14.4"; 361 darwinMinVersion = "14.0"; 362 darwinMinVersionVariable = 363 if final.isMacOS then 364 "MACOSX_DEPLOYMENT_TARGET" 365 else if final.isiOS then 366 "IPHONEOS_DEPLOYMENT_TARGET" 367 else 368 null; 369 370 # Handle Android SDK and NDK versions. 371 androidSdkVersion = args.androidSdkVersion or null; 372 androidNdkVersion = args.androidNdkVersion or null; 373 } 374 // ( 375 let 376 selectEmulator = 377 pkgs: 378 let 379 wine = (pkgs.winePackagesFor "wine${toString final.parsed.cpu.bits}").minimal; 380 in 381 # Note: we guarantee that the return value is either `null` or a path 382 # to an emulator program. That is, if an emulator requires additional 383 # arguments, a wrapper should be used. 384 if pkgs.stdenv.hostPlatform.canExecute final then 385 lib.getExe (pkgs.writeShellScriptBin "exec" ''exec "$@"'') 386 else if final.isWindows then 387 "${wine}/bin/wine" 388 else if final.isLinux && pkgs.stdenv.hostPlatform.isLinux && final.qemuArch != null then 389 "${pkgs.qemu-user}/bin/qemu-${final.qemuArch}" 390 else if final.isWasi then 391 "${pkgs.wasmtime}/bin/wasmtime" 392 else if final.isGhcjs then 393 "${pkgs.nodejs-slim}/bin/node" 394 else if final.isMmix then 395 "${pkgs.mmixware}/bin/mmix" 396 else 397 null; 398 in 399 { 400 emulatorAvailable = pkgs: (selectEmulator pkgs) != null; 401 402 # whether final.emulator pkgs.pkgsStatic works 403 staticEmulatorAvailable = 404 pkgs: final.emulatorAvailable pkgs && (final.isLinux || final.isWasi || final.isMmix); 405 406 emulator = 407 pkgs: 408 if (final.emulatorAvailable pkgs) then 409 selectEmulator pkgs 410 else 411 throw "Don't know how to run ${final.config} executables."; 412 413 } 414 ) 415 // mapAttrs (n: v: v final.parsed) inspect.predicates 416 // mapAttrs (n: v: v final.gcc.arch or "default") architectures.predicates 417 // args 418 // { 419 rust = rust // { 420 platform = 421 rust.platform or ( 422 if lib.hasSuffix ".json" (rust.rustcTargetSpec or "") then 423 lib.importJSON rust.rustcTargetSpec 424 else 425 { } 426 ) 427 428 # Once args.rustc.platform.target-family is deprecated and 429 # removed, there will no longer be any need to modify any 430 # values from args.rust.platform, so we can drop all the 431 # "args ? rust" etc. checks, and merge args.rust.platform in 432 # /after/. 433 // { 434 # https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch 435 arch = 436 if rust ? platform then 437 rust.platform.arch 438 else if final.isAarch32 then 439 "arm" 440 else if final.isMips64 then 441 "mips64" # never add "el" suffix 442 else if final.isPower64 then 443 "powerpc64" # never add "le" suffix 444 else 445 final.parsed.cpu.name; 446 447 # https://doc.rust-lang.org/reference/conditional-compilation.html#target_os 448 os = 449 if rust ? platform then 450 rust.platform.os or "none" 451 else if final.isDarwin then 452 "macos" 453 else if final.isWasm && !final.isWasi then 454 "unknown" # Needed for {wasm32,wasm64}-unknown-unknown. 455 else 456 final.parsed.kernel.name; 457 458 # https://doc.rust-lang.org/reference/conditional-compilation.html#target_family 459 target-family = 460 if args ? rust.platform.target-family then 461 args.rust.platform.target-family 462 else if args ? rustc.platform.target-family then 463 ( 464 # Since https://github.com/rust-lang/rust/pull/84072 465 # `target-family` is a list instead of single value. 466 let 467 f = args.rustc.platform.target-family; 468 in 469 if isList f then f else [ f ] 470 ) 471 else 472 optional final.isUnix "unix" ++ optional final.isWindows "windows" ++ optional final.isWasm "wasm"; 473 474 # https://doc.rust-lang.org/reference/conditional-compilation.html#target_vendor 475 vendor = 476 let 477 inherit (final.parsed) vendor; 478 in 479 rust.platform.vendor or { 480 "w64" = "pc"; 481 } 482 .${vendor.name} or vendor.name; 483 }; 484 485 # The name of the rust target if it is standard, or the json file 486 # containing the custom target spec. Adjustments are because rust has 487 # slightly different naming conventions than we do. 488 rustcTargetSpec = 489 let 490 inherit (final.parsed) cpu kernel abi; 491 cpu_ = 492 rust.platform.arch or { 493 "armv7a" = "armv7"; 494 "armv7l" = "armv7"; 495 "armv6l" = "arm"; 496 "armv5tel" = "armv5te"; 497 "riscv32" = "riscv32gc"; 498 "riscv64" = "riscv64gc"; 499 } 500 .${cpu.name} or cpu.name; 501 vendor_ = final.rust.platform.vendor; 502 abi_ = 503 # We're very explicit about the POWER ELF ABI w/ glibc in our parsing, while Rust is not. 504 # TODO: Somehow ensure that Rust actually *uses* the correct ABI, and not just a libc-based default. 505 if (lib.strings.hasPrefix "powerpc" cpu.name) && (lib.strings.hasPrefix "gnuabielfv" abi.name) then 506 "gnu" 507 else 508 abi.name; 509 510 inferred = 511 if final.isWasi then 512 # Rust uses `wasm32-wasip?` rather than `wasm32-unknown-wasi`. 513 # We cannot know which subversion does the user want, and 514 # currently use WASI 0.1 as default for compatibility. Custom 515 # users can set `rust.rustcTargetSpec` to override it. 516 "${cpu_}-wasip1" 517 else 518 "${cpu_}-${vendor_}-${kernel.name}${optionalString (abi.name != "unknown") "-${abi_}"}"; 519 in 520 # TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL. 521 args.rust.rustcTargetSpec or args.rustc.config or ( 522 if rust ? platform then 523 # TODO: This breaks cc-rs and thus std support, so maybe remove support? 524 builtins.toFile (rust.rustcTarget or inferred + ".json") (toJSON rust.platform) 525 else 526 args.rust.rustcTarget or inferred 527 ); 528 529 # Do not use rustcTarget. Use rustcTargetSpec or cargoShortTarget. 530 # TODO: Remove all in-tree usages, and deprecate 531 rustcTarget = rust.rustcTarget or final.rust.cargoShortTarget; 532 533 # The name of the rust target if it is standard, or the 534 # basename of the file containing the custom target spec, 535 # without the .json extension. 536 # 537 # This is the name used by Cargo for target subdirectories. 538 cargoShortTarget = removeSuffix ".json" (baseNameOf "${final.rust.rustcTargetSpec}"); 539 540 # When used as part of an environment variable name, triples are 541 # uppercased and have all hyphens replaced by underscores: 542 # 543 # https://github.com/rust-lang/cargo/pull/9169 544 # https://github.com/rust-lang/cargo/issues/8285#issuecomment-634202431 545 cargoEnvVarTarget = replaceString "-" "_" (toUpper final.rust.cargoShortTarget); 546 547 # True if the target is no_std 548 # https://github.com/rust-lang/rust/blob/2e44c17c12cec45b6a682b1e53a04ac5b5fcc9d2/src/bootstrap/config.rs#L415-L421 549 isNoStdTarget = any (t: hasInfix t final.rust.rustcTarget) [ 550 "-none" 551 "nvptx" 552 "switch" 553 "-uefi" 554 ]; 555 }; 556 } 557 // { 558 go = { 559 # See https://pkg.go.dev/internal/platform for a list of known platforms 560 GOARCH = 561 { 562 "aarch64" = "arm64"; 563 "arm" = "arm"; 564 "armv5tel" = "arm"; 565 "armv6l" = "arm"; 566 "armv7l" = "arm"; 567 "i686" = "386"; 568 "loongarch64" = "loong64"; 569 "mips" = "mips"; 570 "mips64el" = "mips64le"; 571 "mipsel" = "mipsle"; 572 "powerpc64" = "ppc64"; 573 "powerpc64le" = "ppc64le"; 574 "riscv64" = "riscv64"; 575 "s390x" = "s390x"; 576 "x86_64" = "amd64"; 577 "wasm32" = "wasm"; 578 } 579 .${final.parsed.cpu.name} or null; 580 GOOS = if final.isWasi then "wasip1" else final.parsed.kernel.name; 581 582 # See https://go.dev/wiki/GoArm 583 GOARM = toString (lib.intersectLists [ (final.parsed.cpu.version or "") ] [ "5" "6" "7" ]); 584 }; 585 586 node = { 587 # See these locations for a list of known architectures/platforms: 588 # - https://nodejs.org/api/os.html#osarch 589 # - https://nodejs.org/api/os.html#osplatform 590 arch = 591 if final.isAarch then 592 "arm" + lib.optionalString final.is64bit "64" 593 else if final.isMips32 then 594 "mips" + lib.optionalString final.isLittleEndian "el" 595 else if final.isMips64 && final.isLittleEndian then 596 "mips64el" 597 else if final.isPower then 598 "ppc" + lib.optionalString final.is64bit "64" 599 else if final.isx86_64 then 600 "x64" 601 else if final.isx86_32 then 602 "ia32" 603 else if final.isS390x then 604 "s390x" 605 else if final.isRiscV64 then 606 "riscv64" 607 else if final.isLoongArch64 then 608 "loong64" 609 else 610 null; 611 612 platform = 613 if final.isAndroid then 614 "android" 615 else if final.isDarwin then 616 "darwin" 617 else if final.isFreeBSD then 618 "freebsd" 619 else if final.isLinux then 620 "linux" 621 else if final.isOpenBSD then 622 "openbsd" 623 else if final.isSunOS then 624 "sunos" 625 else if (final.isWindows || final.isCygwin) then 626 "win32" 627 else 628 null; 629 }; 630 }; 631 in 632 assert final.useAndroidPrebuilt -> final.isAndroid; 633 assert foldl (pass: { assertion, message }: if assertion final then pass else throw message) true ( 634 final.parsed.abi.assertions or [ ] 635 ); 636 final; 637 638in 639 640# Everything in this attrset is the public interface of the file. 641{ 642 inherit 643 architectures 644 doubles 645 elaborate 646 equals 647 examples 648 flakeExposed 649 inspect 650 parse 651 platforms 652 systemToAttrs 653 ; 654}