at 24.11-pre 17 kB view raw
1{ lib }: 2 3let 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 ; 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 removeFunctions = a: filterAttrs (_: v: !isFunction v) a; 46 in a: b: removeFunctions a == removeFunctions b; 47 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`. 51 52 :::{.warning} 53 This attribute is considered experimental and is subject to change. 54 ::: 55 */ 56 flakeExposed = import ./flake-systems.nix { }; 57 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'; 67 68 # TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL. 69 rust = args.rust or args.rustc or {}; 70 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; 85 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.isRedox then "relibc" 91 else if final.isMusl then "musl" 92 else if final.isUClibc then "uclibc" 93 else if final.isAndroid then "bionic" 94 else if final.isLinux /* default */ then "glibc" 95 else if final.isFreeBSD then "fblibc" 96 else if final.isNetBSD then "nblibc" 97 else if final.isAvr then "avrlibc" 98 else if final.isGhcjs then null 99 else if final.isNone then "newlib" 100 # TODO(@Ericson2314) think more about other operating systems 101 else "native/impure"; 102 # Choose what linker we wish to use by default. Someday we might also 103 # choose the C compiler, runtime library, C++ standard library, etc. in 104 # this way, nice and orthogonally, and deprecate `useLLVM`. But due to 105 # the monolithic GCC build we cannot actually make those choices 106 # independently, so we are just doing `linker` and keeping `useLLVM` for 107 # now. 108 linker = 109 /**/ if final.useLLVM or false then "lld" 110 else if final.isDarwin then "cctools" 111 # "bfd" and "gold" both come from GNU binutils. The existence of Gold 112 # is why we use the more obscure "bfd" and not "binutils" for this 113 # choice. 114 else "bfd"; 115 # The standard lib directory name that non-nixpkgs binaries distributed 116 # for this platform normally assume. 117 libDir = if final.isLinux then 118 if final.isx86_64 || final.isMips64 || final.isPower64 119 then "lib64" 120 else "lib" 121 else null; 122 extensions = optionalAttrs final.hasSharedLibraries { 123 sharedLibrary = 124 if final.isDarwin then ".dylib" 125 else if final.isWindows then ".dll" 126 else ".so"; 127 } // { 128 staticLibrary = 129 /**/ if final.isWindows then ".lib" 130 else ".a"; 131 library = 132 /**/ if final.isStatic then final.extensions.staticLibrary 133 else final.extensions.sharedLibrary; 134 executable = 135 /**/ if final.isWindows then ".exe" 136 else ""; 137 }; 138 # Misc boolean options 139 useAndroidPrebuilt = false; 140 useiOSPrebuilt = false; 141 142 # Output from uname 143 uname = { 144 # uname -s 145 system = { 146 linux = "Linux"; 147 windows = "Windows"; 148 darwin = "Darwin"; 149 netbsd = "NetBSD"; 150 freebsd = "FreeBSD"; 151 openbsd = "OpenBSD"; 152 wasi = "Wasi"; 153 redox = "Redox"; 154 genode = "Genode"; 155 }.${final.parsed.kernel.name} or null; 156 157 # uname -m 158 processor = 159 if final.isPower64 160 then "ppc64${optionalString final.isLittleEndian "le"}" 161 else if final.isPower 162 then "ppc${optionalString final.isLittleEndian "le"}" 163 else if final.isMips64 164 then "mips64" # endianness is *not* included on mips64 165 else final.parsed.cpu.name; 166 167 # uname -r 168 release = null; 169 }; 170 171 # It is important that hasSharedLibraries==false when the platform has no 172 # dynamic library loader. Various tools (including the gcc build system) 173 # have knowledge of which platforms are incapable of dynamic linking, and 174 # will still build on/for those platforms with --enable-shared, but simply 175 # omit any `.so` build products such as libgcc_s.so. When that happens, 176 # it causes hard-to-troubleshoot build failures. 177 hasSharedLibraries = with final; 178 (isAndroid || isGnu || isMusl # Linux (allows multiple libcs) 179 || isDarwin || isSunOS || isOpenBSD || isFreeBSD || isNetBSD # BSDs 180 || isCygwin || isMinGW # Windows 181 ) && !isStatic; 182 183 # The difference between `isStatic` and `hasSharedLibraries` is mainly the 184 # addition of the `staticMarker` (see make-derivation.nix). Some 185 # platforms, like embedded machines without a libc (e.g. arm-none-eabi) 186 # don't support dynamic linking, but don't get the `staticMarker`. 187 # `pkgsStatic` sets `isStatic=true`, so `pkgsStatic.hostPlatform` always 188 # has the `staticMarker`. 189 isStatic = final.isWasm || final.isRedox; 190 191 # Just a guess, based on `system` 192 inherit 193 ({ 194 linux-kernel = args.linux-kernel or {}; 195 gcc = args.gcc or {}; 196 } // platforms.select final) 197 linux-kernel gcc; 198 199 # TODO: remove after 23.05 is EOL, with an error pointing to the rust.* attrs. 200 rustc = args.rustc or {}; 201 202 linuxArch = 203 if final.isAarch32 then "arm" 204 else if final.isAarch64 then "arm64" 205 else if final.isx86_32 then "i386" 206 else if final.isx86_64 then "x86_64" 207 # linux kernel does not distinguish microblaze/microblazeel 208 else if final.isMicroBlaze then "microblaze" 209 else if final.isMips32 then "mips" 210 else if final.isMips64 then "mips" # linux kernel does not distinguish mips32/mips64 211 else if final.isPower then "powerpc" 212 else if final.isRiscV then "riscv" 213 else if final.isS390 then "s390" 214 else if final.isLoongArch64 then "loongarch" 215 else final.parsed.cpu.name; 216 217 # https://source.denx.de/u-boot/u-boot/-/blob/9bfb567e5f1bfe7de8eb41f8c6d00f49d2b9a426/common/image.c#L81-106 218 ubootArch = 219 if final.isx86_32 then "x86" # not i386 220 else if final.isMips64 then "mips64" # uboot *does* distinguish between mips32/mips64 221 else final.linuxArch; # other cases appear to agree with linuxArch 222 223 qemuArch = 224 if final.isAarch32 then "arm" 225 else if final.isS390 && !final.isS390x then null 226 else if final.isx86_64 then "x86_64" 227 else if final.isx86 then "i386" 228 else if final.isMips64n32 then "mipsn32${optionalString final.isLittleEndian "el"}" 229 else if final.isMips64 then "mips64${optionalString final.isLittleEndian "el"}" 230 else final.uname.processor; 231 232 # Name used by UEFI for architectures. 233 efiArch = 234 if final.isx86_32 then "ia32" 235 else if final.isx86_64 then "x64" 236 else if final.isAarch32 then "arm" 237 else if final.isAarch64 then "aa64" 238 else final.parsed.cpu.name; 239 240 darwinArch = { 241 armv7a = "armv7"; 242 aarch64 = "arm64"; 243 }.${final.parsed.cpu.name} or final.parsed.cpu.name; 244 245 darwinPlatform = 246 if final.isMacOS then "macos" 247 else if final.isiOS then "ios" 248 else null; 249 # The canonical name for this attribute is darwinSdkVersion, but some 250 # platforms define the old name "sdkVer". 251 darwinSdkVersion = final.sdkVer or (if final.isAarch64 then "11.0" else "10.12"); 252 darwinMinVersion = final.darwinSdkVersion; 253 darwinMinVersionVariable = 254 if final.isMacOS then "MACOSX_DEPLOYMENT_TARGET" 255 else if final.isiOS then "IPHONEOS_DEPLOYMENT_TARGET" 256 else null; 257 } // ( 258 let 259 selectEmulator = pkgs: 260 let 261 qemu-user = pkgs.qemu.override { 262 smartcardSupport = false; 263 spiceSupport = false; 264 openGLSupport = false; 265 virglSupport = false; 266 vncSupport = false; 267 gtkSupport = false; 268 sdlSupport = false; 269 alsaSupport = false; 270 pulseSupport = false; 271 pipewireSupport = false; 272 jackSupport = false; 273 smbdSupport = false; 274 seccompSupport = false; 275 tpmSupport = false; 276 capstoneSupport = false; 277 enableDocs = false; 278 hostCpuTargets = [ "${final.qemuArch}-linux-user" ]; 279 }; 280 wine = (pkgs.winePackagesFor "wine${toString final.parsed.cpu.bits}").minimal; 281 in 282 if pkgs.stdenv.hostPlatform.canExecute final 283 then "${pkgs.runtimeShell} -c '\"$@\"' --" 284 else if final.isWindows 285 then "${wine}/bin/wine${optionalString (final.parsed.cpu.bits == 64) "64"}" 286 else if final.isLinux && pkgs.stdenv.hostPlatform.isLinux && final.qemuArch != null 287 then "${qemu-user}/bin/qemu-${final.qemuArch}" 288 else if final.isWasi 289 then "${pkgs.wasmtime}/bin/wasmtime" 290 else if final.isMmix 291 then "${pkgs.mmixware}/bin/mmix" 292 else null; 293 in { 294 emulatorAvailable = pkgs: (selectEmulator pkgs) != null; 295 296 emulator = pkgs: 297 if (final.emulatorAvailable pkgs) 298 then selectEmulator pkgs 299 else throw "Don't know how to run ${final.config} executables."; 300 301 }) // mapAttrs (n: v: v final.parsed) inspect.predicates 302 // mapAttrs (n: v: v final.gcc.arch or "default") architectures.predicates 303 // args // { 304 rust = rust // { 305 # Once args.rustc.platform.target-family is deprecated and 306 # removed, there will no longer be any need to modify any 307 # values from args.rust.platform, so we can drop all the 308 # "args ? rust" etc. checks, and merge args.rust.platform in 309 # /after/. 310 platform = rust.platform or {} // { 311 # https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch 312 arch = 313 /**/ if rust ? platform then rust.platform.arch 314 else if final.isAarch32 then "arm" 315 else if final.isMips64 then "mips64" # never add "el" suffix 316 else if final.isPower64 then "powerpc64" # never add "le" suffix 317 else final.parsed.cpu.name; 318 319 # https://doc.rust-lang.org/reference/conditional-compilation.html#target_os 320 os = 321 /**/ if rust ? platform then rust.platform.os or "none" 322 else if final.isDarwin then "macos" 323 else final.parsed.kernel.name; 324 325 # https://doc.rust-lang.org/reference/conditional-compilation.html#target_family 326 target-family = 327 /**/ if args ? rust.platform.target-family then args.rust.platform.target-family 328 else if args ? rustc.platform.target-family 329 then 330 ( 331 # Since https://github.com/rust-lang/rust/pull/84072 332 # `target-family` is a list instead of single value. 333 let 334 f = args.rustc.platform.target-family; 335 in 336 if isList f then f else [ f ] 337 ) 338 else optional final.isUnix "unix" 339 ++ optional final.isWindows "windows"; 340 341 # https://doc.rust-lang.org/reference/conditional-compilation.html#target_vendor 342 vendor = let 343 inherit (final.parsed) vendor; 344 in rust.platform.vendor or { 345 "w64" = "pc"; 346 }.${vendor.name} or vendor.name; 347 }; 348 349 # The name of the rust target, even if it is custom. Adjustments are 350 # because rust has slightly different naming conventions than we do. 351 rustcTarget = let 352 inherit (final.parsed) cpu kernel abi; 353 cpu_ = rust.platform.arch or { 354 "armv7a" = "armv7"; 355 "armv7l" = "armv7"; 356 "armv6l" = "arm"; 357 "armv5tel" = "armv5te"; 358 "riscv64" = "riscv64gc"; 359 }.${cpu.name} or cpu.name; 360 vendor_ = final.rust.platform.vendor; 361 # TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL. 362 in args.rust.rustcTarget or args.rustc.config 363 or "${cpu_}-${vendor_}-${kernel.name}${optionalString (abi.name != "unknown") "-${abi.name}"}"; 364 365 # The name of the rust target if it is standard, or the json file 366 # containing the custom target spec. 367 rustcTargetSpec = rust.rustcTargetSpec or ( 368 /**/ if rust ? platform 369 then builtins.toFile (final.rust.rustcTarget + ".json") (toJSON rust.platform) 370 else final.rust.rustcTarget); 371 372 # The name of the rust target if it is standard, or the 373 # basename of the file containing the custom target spec, 374 # without the .json extension. 375 # 376 # This is the name used by Cargo for target subdirectories. 377 cargoShortTarget = 378 removeSuffix ".json" (baseNameOf "${final.rust.rustcTargetSpec}"); 379 380 # When used as part of an environment variable name, triples are 381 # uppercased and have all hyphens replaced by underscores: 382 # 383 # https://github.com/rust-lang/cargo/pull/9169 384 # https://github.com/rust-lang/cargo/issues/8285#issuecomment-634202431 385 cargoEnvVarTarget = 386 replaceStrings ["-"] ["_"] 387 (toUpper final.rust.cargoShortTarget); 388 389 # True if the target is no_std 390 # https://github.com/rust-lang/rust/blob/2e44c17c12cec45b6a682b1e53a04ac5b5fcc9d2/src/bootstrap/config.rs#L415-L421 391 isNoStdTarget = 392 any (t: hasInfix t final.rust.rustcTarget) ["-none" "nvptx" "switch" "-uefi"]; 393 }; 394 }; 395 in assert final.useAndroidPrebuilt -> final.isAndroid; 396 assert foldl 397 (pass: { assertion, message }: 398 if assertion final 399 then pass 400 else throw message) 401 true 402 (final.parsed.abi.assertions or []); 403 final; 404 405in 406 407# Everything in this attrset is the public interface of the file. 408{ 409 inherit 410 architectures 411 doubles 412 elaborate 413 equals 414 examples 415 flakeExposed 416 inspect 417 parse 418 platforms 419 ; 420}