push sheeet
Some checks failed
Periodic Merges (6h) / master → staging-nixos (push) Failing after 12m50s
Periodic Merges (6h) / master → staging-next (push) Failing after 12m54s
Periodic Merges (24h) / merge-base(master,staging) → haskell-updates (push) Failing after 11m54s
Periodic Merges (6h) / staging-next → staging (push) Failing after 12m13s
Periodic Merges (24h) / staging-next-25.05 → staging-25.05 (push) Failing after 13m24s
Periodic Merges (24h) / release-25.05 → staging-next-25.05 (push) Failing after 14m28s

This commit is contained in:
Dark Steveneq
2025-10-09 14:15:47 +02:00
commit 646b892680
49168 changed files with 5897842 additions and 0 deletions

View File

@@ -0,0 +1,626 @@
{ lib }:
rec {
# gcc.arch to its features (as in /proc/cpuinfo)
features = {
# x86_64 Generic
# Spec: https://gitlab.com/x86-psABIs/x86-64-ABI/
default = [ ];
x86-64 = [ ];
x86-64-v2 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
];
x86-64-v3 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"avx"
"avx2"
"fma"
];
x86-64-v4 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"avx"
"avx2"
"avx512"
"fma"
];
# x86_64 Intel
nehalem = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
];
westmere = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
];
silvermont = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
];
sandybridge = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"avx"
];
ivybridge = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"avx"
];
haswell = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"avx"
"avx2"
"fma"
];
broadwell = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"avx"
"avx2"
"fma"
];
skylake = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"fma"
];
skylake-avx512 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
cannonlake = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
icelake-client = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
icelake-server = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
cascadelake = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
cooperlake = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
tigerlake = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
alderlake = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"fma"
];
sapphirerapids = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
emeraldrapids = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
sierraforest = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"fma"
];
# x86_64 AMD
btver1 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
];
btver2 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
];
bdver1 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"fma"
"fma4"
];
bdver2 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"fma"
"fma4"
];
bdver3 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"fma"
"fma4"
];
bdver4 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"avx2"
"fma"
"fma4"
];
znver1 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"avx2"
"fma"
];
znver2 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"avx2"
"fma"
];
znver3 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"avx2"
"fma"
];
znver4 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
znver5 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
# LoongArch64
# https://github.com/loongson/la-toolchain-conventions
loongarch64 = [
"fpu64"
];
la464 = [
"fpu64"
"lsx"
"lasx"
];
la664 = [
"fpu64"
"lsx"
"lasx"
"div32"
"frecipe"
"lam-bh"
"lamcas"
"ld-seq-sa"
];
"la64v1.0" = [
"fpu64"
"lsx"
];
"la64v1.1" = [
"fpu64"
"lsx"
"div32"
"frecipe"
"lam-bh"
"lamcas"
"ld-seq-sa"
];
# other
armv5te = [ ];
armv6 = [ ];
armv7-a = [ ];
armv8-a = [ ];
mips32 = [ ];
loongson2f = [ ];
};
# a superior CPU has all the features of an inferior and is able to build and test code for it
inferiors =
let
withInferiors = archs: lib.unique (archs ++ lib.flatten (lib.attrVals archs inferiors));
in
{
# x86_64 Generic
default = [ ];
x86-64 = [ ];
x86-64-v2 = [ "x86-64" ];
x86-64-v3 = [ "x86-64-v2" ] ++ inferiors.x86-64-v2;
x86-64-v4 = [ "x86-64-v3" ] ++ inferiors.x86-64-v3;
# x86_64 Intel
# https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html
nehalem = [ "x86-64-v2" ] ++ inferiors.x86-64-v2;
westmere = [ "nehalem" ] ++ inferiors.nehalem;
sandybridge = [ "westmere" ] ++ inferiors.westmere;
ivybridge = [ "sandybridge" ] ++ inferiors.sandybridge;
haswell = lib.unique (
[
"ivybridge"
"x86-64-v3"
]
++ inferiors.ivybridge
++ inferiors.x86-64-v3
);
broadwell = [ "haswell" ] ++ inferiors.haswell;
skylake = [ "broadwell" ] ++ inferiors.broadwell;
skylake-avx512 = lib.unique (
[
"skylake"
"x86-64-v4"
]
++ inferiors.skylake
++ inferiors.x86-64-v4
);
cannonlake = [ "skylake-avx512" ] ++ inferiors.skylake-avx512;
icelake-client = [ "cannonlake" ] ++ inferiors.cannonlake;
icelake-server = [ "icelake-client" ] ++ inferiors.icelake-client;
cascadelake = [ "cannonlake" ] ++ inferiors.cannonlake;
cooperlake = [ "cascadelake" ] ++ inferiors.cascadelake;
tigerlake = [ "icelake-server" ] ++ inferiors.icelake-server;
sapphirerapids = [ "tigerlake" ] ++ inferiors.tigerlake;
emeraldrapids = [ "sapphirerapids" ] ++ inferiors.sapphirerapids;
alderlake = [ "skylake" ] ++ inferiors.skylake;
sierraforest = [ "alderlake" ] ++ inferiors.alderlake;
# x86_64 AMD
# TODO: fill in specific CPU architecture inferiors
btver1 = [ "x86-64" ];
btver2 = [ "x86-64-v2" ] ++ inferiors.x86-64-v2;
bdver1 = [ "x86-64-v2" ] ++ inferiors.x86-64-v2;
bdver2 = [ "x86-64-v2" ] ++ inferiors.x86-64-v2;
bdver3 = [ "x86-64-v2" ] ++ inferiors.x86-64-v2;
bdver4 = [ "x86-64-v3" ] ++ inferiors.x86-64-v3;
# Regarding `skylake` as inferior of `znver1`, there are reports of
# successful usage by Gentoo users and Phoronix benchmarking of different
# `-march` targets.
#
# The GCC documentation on extensions used and wikichip documentation
# regarding supperted extensions on znver1 and skylake was used to create
# this partial order.
#
# Note:
#
# - The successors of `skylake` (`cannonlake`, `icelake`, etc) use `avx512`
# which no current AMD Zen michroarch support.
# - `znver1` uses `ABM`, `CLZERO`, `CX16`, `MWAITX`, and `SSE4A` which no
# current Intel microarch support.
#
# https://www.phoronix.com/scan.php?page=article&item=amd-znver3-gcc11&num=1
# https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html
# https://en.wikichip.org/wiki/amd/microarchitectures/zen
# https://en.wikichip.org/wiki/intel/microarchitectures/skylake
znver1 = [ "skylake" ] ++ inferiors.skylake; # Includes haswell and x86-64-v3
znver2 = [ "znver1" ] ++ inferiors.znver1;
znver3 = [ "znver2" ] ++ inferiors.znver2;
znver4 = lib.unique (
[
"znver3"
"x86-64-v4"
]
++ inferiors.znver3
++ inferiors.x86-64-v4
);
znver5 = [ "znver4" ] ++ inferiors.znver4;
# ARM64 (AArch64)
armv8-a = [ ];
"armv8.1-a" = [ "armv8-a" ];
"armv8.2-a" = [ "armv8.1-a" ] ++ inferiors."armv8.1-a";
"armv8.3-a" = [ "armv8.2-a" ] ++ inferiors."armv8.2-a";
"armv8.4-a" = [ "armv8.3-a" ] ++ inferiors."armv8.3-a";
"armv8.5-a" = [ "armv8.4-a" ] ++ inferiors."armv8.4-a";
"armv8.6-a" = [ "armv8.5-a" ] ++ inferiors."armv8.5-a";
"armv8.7-a" = [ "armv8.6-a" ] ++ inferiors."armv8.6-a";
"armv8.8-a" = [ "armv8.7-a" ] ++ inferiors."armv8.7-a";
"armv8.9-a" = [ "armv8.8-a" ] ++ inferiors."armv8.8-a";
armv9-a = [ "armv8.5-a" ] ++ inferiors."armv8.5-a";
"armv9.1-a" = [
"armv9-a"
"armv8.6-a"
]
++ inferiors."armv8.6-a";
"armv9.2-a" = lib.unique (
[
"armv9.1-a"
"armv8.7-a"
]
++ inferiors."armv9.1-a"
++ inferiors."armv8.7-a"
);
"armv9.3-a" = lib.unique (
[
"armv9.2-a"
"armv8.8-a"
]
++ inferiors."armv9.2-a"
++ inferiors."armv8.8-a"
);
"armv9.4-a" = [ "armv9.3-a" ] ++ inferiors."armv9.3-a";
# ARM
cortex-a53 = [ "armv8-a" ];
cortex-a72 = [ "armv8-a" ];
cortex-a55 = [
"armv8.2-a"
"cortex-a53"
"cortex-a72"
]
++ inferiors."armv8.2-a";
cortex-a76 = [
"armv8.2-a"
"cortex-a53"
"cortex-a72"
]
++ inferiors."armv8.2-a";
# Ampere
ampere1 = withInferiors [
"armv8.6-a"
"cortex-a55"
"cortex-a76"
];
ampere1a = [ "ampere1" ] ++ inferiors.ampere1;
ampere1b = [ "ampere1a" ] ++ inferiors.ampere1a;
# LoongArch64
loongarch64 = [ ];
"la64v1.0" = [ "loongarch64" ];
la464 = [ "la64v1.0" ] ++ inferiors."la64v1.0";
"la64v1.1" = [ "la64v1.0" ] ++ inferiors."la64v1.0";
la664 = withInferiors [
"la464"
"la64v1.1"
];
# other
armv5te = [ ];
armv6 = [ ];
armv7-a = [ ];
mips32 = [ ];
loongson2f = [ ];
};
/**
Check whether one GCC architecture has the the other inferior architecture.
# Inputs
`arch1`
: GCC architecture in string
`arch2`
: GCC architecture in string
# Type
```
hasInferior :: string -> string -> bool
```
# Examples
::: {.example}
## `lib.systems.architectures.hasInferior` usage example
```nix
hasInferior "x86-64-v3" "x86-64"
=> true
hasInferior "x86-64" "x86-64-v3"
=> false
hasInferior "x86-64" "x86-64"
=> false
```
*/
hasInferior = arch1: arch2: inferiors ? ${arch1} && lib.elem arch2 inferiors.${arch1};
/**
Check whether one GCC architecture can execute the other.
# Inputs
`arch1`
: GCC architecture in string
`arch2`
: GCC architecture in string
# Type
```
canExecute :: string -> string -> bool
```
# Examples
::: {.example}
## `lib.systems.architectures.canExecute` usage example
```nix
canExecute "x86-64" "x86-64-v3"
=> false
canExecute "x86-64-v3" "x86-64"
=> true
canExecute "x86-64" "x86-64"
=> true
```
*/
canExecute = arch1: arch2: arch1 == arch2 || hasInferior arch1 arch2;
predicates =
let
featureSupport = feature: x: builtins.elem feature features.${x} or [ ];
in
{
sse3Support = featureSupport "sse3";
ssse3Support = featureSupport "ssse3";
sse4_1Support = featureSupport "sse4_1";
sse4_2Support = featureSupport "sse4_2";
sse4_aSupport = featureSupport "sse4a";
avxSupport = featureSupport "avx";
avx2Support = featureSupport "avx2";
avx512Support = featureSupport "avx512";
aesSupport = featureSupport "aes";
fmaSupport = featureSupport "fma";
fma4Support = featureSupport "fma4";
lsxSupport = featureSupport "lsx";
lasxSupport = featureSupport "lasx";
};
}

638
lib/systems/default.nix Normal file
View File

@@ -0,0 +1,638 @@
{ lib }:
let
inherit (lib)
any
filterAttrs
foldl
hasInfix
isAttrs
isFunction
isList
mapAttrs
optional
optionalAttrs
optionalString
removeSuffix
replaceString
toUpper
;
inherit (lib.strings) toJSON;
doubles = import ./doubles.nix { inherit lib; };
parse = import ./parse.nix { inherit lib; };
inspect = import ./inspect.nix { inherit lib; };
platforms = import ./platforms.nix { inherit lib; };
examples = import ./examples.nix { inherit lib; };
architectures = import ./architectures.nix { inherit lib; };
/**
Elaborated systems contain functions, which means that they don't satisfy
`==` for a lack of reflexivity.
They might *appear* to satisfy `==` reflexivity when the same exact value is
compared to itself, because object identity is used as an "optimization";
compare the value with a reconstruction of itself, e.g. with `f == a: f a`,
or perhaps calling `elaborate` twice, and one will see reflexivity fail as described.
Hence a custom equality test.
Note that this does not canonicalize the systems, so you'll want to make sure
both arguments have been `elaborate`-d.
*/
equals =
let
removeFunctions = a: filterAttrs (_: v: !isFunction v) a;
in
a: b: removeFunctions a == removeFunctions b;
/**
List of all Nix system doubles the nixpkgs flake will expose the package set
for. All systems listed here must be supported by nixpkgs as `localSystem`.
:::{.warning}
This attribute is considered experimental and is subject to change.
:::
*/
flakeExposed = import ./flake-systems.nix { };
# Turn localSystem or crossSystem, which could be system-string or attrset, into
# attrset.
systemToAttrs =
systemOrArgs: if isAttrs systemOrArgs then systemOrArgs else { system = systemOrArgs; };
# Elaborate a `localSystem` or `crossSystem` so that it contains everything
# necessary.
#
# `parsed` is inferred from args, both because there are two options with one
# clearly preferred, and to prevent cycles. A simpler fixed point where the RHS
# always just used `final.*` would fail on both counts.
elaborate =
systemOrArgs:
let
allArgs = systemToAttrs systemOrArgs;
# Those two will always be derived from "config", if given, so they should NOT
# be overridden further down with "// args".
args = removeAttrs allArgs [
"parsed"
"system"
];
# TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL.
rust = args.rust or args.rustc or { };
final = {
# Prefer to parse `config` as it is strictly more informative.
parsed = parse.mkSystemFromString (args.config or allArgs.system);
# This can be losslessly-extracted from `parsed` iff parsing succeeds.
system = parse.doubleFromSystem final.parsed;
# TODO: This currently can't be losslessly-extracted from `parsed`, for example
# because of -mingw32.
config = parse.tripleFromSystem final.parsed;
# Determine whether we can execute binaries built for the provided platform.
canExecute =
platform:
final.isAndroid == platform.isAndroid
&& parse.isCompatible final.parsed.cpu platform.parsed.cpu
&& final.parsed.kernel == platform.parsed.kernel
&& (
# Only perform this check when cpus have the same type;
# assume compatible cpu have all the instructions included
final.parsed.cpu == platform.parsed.cpu
->
# if both have gcc.arch defined, check whether final can execute the given platform
(
(final ? gcc.arch && platform ? gcc.arch)
-> architectures.canExecute final.gcc.arch platform.gcc.arch
)
# if platform has gcc.arch defined but final doesn't, don't assume it can be executed
|| (platform ? gcc.arch -> !(final ? gcc.arch))
);
isCompatible =
_:
throw "2022-05-23: isCompatible has been removed in favor of canExecute, refer to the 22.11 changelog for details";
# Derived meta-data
useLLVM = final.isFreeBSD || final.isOpenBSD;
libc =
if final.isDarwin then
"libSystem"
else if final.isMsvc then
"ucrt"
else if final.isMinGW then
"msvcrt"
else if final.isCygwin then
"cygwin"
else if final.isWasi then
"wasilibc"
else if final.isWasm && !final.isWasi then
null
else if final.isRedox then
"relibc"
else if final.isMusl then
"musl"
else if final.isUClibc then
"uclibc"
else if final.isAndroid then
"bionic"
else if
final.isLinux # default
then
"glibc"
else if final.isFreeBSD then
"fblibc"
else if final.isOpenBSD then
"oblibc"
else if final.isNetBSD then
"nblibc"
else if final.isAvr then
"avrlibc"
else if final.isGhcjs then
null
else if final.isNone then
"newlib"
# TODO(@Ericson2314) think more about other operating systems
else
"native/impure";
# Choose what linker we wish to use by default. Someday we might also
# choose the C compiler, runtime library, C++ standard library, etc. in
# this way, nice and orthogonally, and deprecate `useLLVM`. But due to
# the monolithic GCC build we cannot actually make those choices
# independently, so we are just doing `linker` and keeping `useLLVM` for
# now.
linker =
if final.useLLVM or false then
"lld"
else if final.isDarwin then
"cctools"
# "bfd" and "gold" both come from GNU binutils. The existence of Gold
# is why we use the more obscure "bfd" and not "binutils" for this
# choice.
else
"bfd";
# The standard lib directory name that non-nixpkgs binaries distributed
# for this platform normally assume.
libDir =
if final.isLinux then
if final.isx86_64 || final.isMips64 || final.isPower64 then "lib64" else "lib"
else
null;
extensions =
optionalAttrs final.hasSharedLibraries {
sharedLibrary =
if final.isDarwin then
".dylib"
else if (final.isWindows || final.isCygwin) then
".dll"
else
".so";
}
// {
staticLibrary = if final.isWindows then ".lib" else ".a";
library = if final.isStatic then final.extensions.staticLibrary else final.extensions.sharedLibrary;
executable = if (final.isWindows || final.isCygwin) then ".exe" else "";
};
# Misc boolean options
useAndroidPrebuilt = false;
useiOSPrebuilt = false;
# Output from uname
uname = {
# uname -s
system =
{
linux = "Linux";
windows = "Windows";
cygwin = "CYGWIN_NT";
darwin = "Darwin";
netbsd = "NetBSD";
freebsd = "FreeBSD";
openbsd = "OpenBSD";
wasi = "Wasi";
redox = "Redox";
genode = "Genode";
}
.${final.parsed.kernel.name} or null;
# uname -m
processor =
if final.isPower64 then
"ppc64${optionalString final.isLittleEndian "le"}"
else if final.isPower then
"ppc${optionalString final.isLittleEndian "le"}"
else if final.isMips64 then
"mips64" # endianness is *not* included on mips64
else if final.isDarwin then
final.darwinArch
else
final.parsed.cpu.name;
# uname -r
release = null;
};
# It is important that hasSharedLibraries==false when the platform has no
# dynamic library loader. Various tools (including the gcc build system)
# have knowledge of which platforms are incapable of dynamic linking, and
# will still build on/for those platforms with --enable-shared, but simply
# omit any `.so` build products such as libgcc_s.so. When that happens,
# it causes hard-to-troubleshoot build failures.
hasSharedLibraries =
with final;
(
isAndroid
|| isGnu
|| isMusl # Linux (allows multiple libcs)
|| isDarwin
|| isSunOS
|| isOpenBSD
|| isFreeBSD
|| isNetBSD # BSDs
|| isCygwin
|| isMinGW
|| isWindows # Windows
|| isWasm # WASM
)
&& !isStatic;
# The difference between `isStatic` and `hasSharedLibraries` is mainly the
# addition of the `staticMarker` (see make-derivation.nix). Some
# platforms, like embedded machines without a libc (e.g. arm-none-eabi)
# don't support dynamic linking, but don't get the `staticMarker`.
# `pkgsStatic` sets `isStatic=true`, so `pkgsStatic.hostPlatform` always
# has the `staticMarker`.
isStatic = final.isWasi || final.isRedox;
# Just a guess, based on `system`
inherit
(
{
linux-kernel = args.linux-kernel or { };
gcc = args.gcc or { };
}
// platforms.select final
)
linux-kernel
gcc
;
# TODO: remove after 23.05 is EOL, with an error pointing to the rust.* attrs.
rustc = args.rustc or { };
linuxArch =
if final.isAarch32 then
"arm"
else if final.isAarch64 then
"arm64"
else if final.isx86_32 then
"i386"
else if final.isx86_64 then
"x86_64"
# linux kernel does not distinguish microblaze/microblazeel
else if final.isMicroBlaze then
"microblaze"
else if final.isMips32 then
"mips"
else if final.isMips64 then
"mips" # linux kernel does not distinguish mips32/mips64
else if final.isPower then
"powerpc"
else if final.isRiscV then
"riscv"
else if final.isS390 then
"s390"
else if final.isLoongArch64 then
"loongarch"
else
final.parsed.cpu.name;
# https://source.denx.de/u-boot/u-boot/-/blob/9bfb567e5f1bfe7de8eb41f8c6d00f49d2b9a426/common/image.c#L81-106
ubootArch =
if final.isx86_32 then
"x86" # not i386
else if final.isMips64 then
"mips64" # uboot *does* distinguish between mips32/mips64
else
final.linuxArch; # other cases appear to agree with linuxArch
qemuArch =
if final.isAarch32 then
"arm"
else if final.isAarch64 then
"aarch64"
else if final.isS390 && !final.isS390x then
null
else if final.isx86_64 then
"x86_64"
else if final.isx86 then
"i386"
else if final.isMips64n32 then
"mipsn32${optionalString final.isLittleEndian "el"}"
else if final.isMips64 then
"mips64${optionalString final.isLittleEndian "el"}"
else
final.uname.processor;
# Name used by UEFI for architectures.
efiArch =
if final.isx86_32 then
"ia32"
else if final.isx86_64 then
"x64"
else if final.isAarch32 then
"arm"
else if final.isAarch64 then
"aa64"
else
final.parsed.cpu.name;
darwinArch = parse.darwinArch final.parsed.cpu;
darwinPlatform =
if final.isMacOS then
"macos"
else if final.isiOS then
"ios"
else
null;
# The canonical name for this attribute is darwinSdkVersion, but some
# platforms define the old name "sdkVer".
darwinSdkVersion = final.sdkVer or "11.3";
darwinMinVersion = final.darwinSdkVersion;
darwinMinVersionVariable =
if final.isMacOS then
"MACOSX_DEPLOYMENT_TARGET"
else if final.isiOS then
"IPHONEOS_DEPLOYMENT_TARGET"
else
null;
# Handle Android SDK and NDK versions.
androidSdkVersion = args.androidSdkVersion or null;
androidNdkVersion = args.androidNdkVersion or null;
}
// (
let
selectEmulator =
pkgs:
let
wine = (pkgs.winePackagesFor "wine${toString final.parsed.cpu.bits}").minimal;
in
# Note: we guarantee that the return value is either `null` or a path
# to an emulator program. That is, if an emulator requires additional
# arguments, a wrapper should be used.
if pkgs.stdenv.hostPlatform.canExecute final then
lib.getExe (pkgs.writeShellScriptBin "exec" ''exec "$@"'')
else if final.isWindows then
"${wine}/bin/wine${optionalString (final.parsed.cpu.bits == 64) "64"}"
else if final.isLinux && pkgs.stdenv.hostPlatform.isLinux && final.qemuArch != null then
"${pkgs.qemu-user}/bin/qemu-${final.qemuArch}"
else if final.isWasi then
"${pkgs.wasmtime}/bin/wasmtime"
else if final.isMmix then
"${pkgs.mmixware}/bin/mmix"
else
null;
in
{
emulatorAvailable = pkgs: (selectEmulator pkgs) != null;
# whether final.emulator pkgs.pkgsStatic works
staticEmulatorAvailable =
pkgs: final.emulatorAvailable pkgs && (final.isLinux || final.isWasi || final.isMmix);
emulator =
pkgs:
if (final.emulatorAvailable pkgs) then
selectEmulator pkgs
else
throw "Don't know how to run ${final.config} executables.";
}
)
// mapAttrs (n: v: v final.parsed) inspect.predicates
// mapAttrs (n: v: v final.gcc.arch or "default") architectures.predicates
// args
// {
rust = rust // {
# Once args.rustc.platform.target-family is deprecated and
# removed, there will no longer be any need to modify any
# values from args.rust.platform, so we can drop all the
# "args ? rust" etc. checks, and merge args.rust.platform in
# /after/.
platform = rust.platform or { } // {
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch
arch =
if rust ? platform then
rust.platform.arch
else if final.isAarch32 then
"arm"
else if final.isMips64 then
"mips64" # never add "el" suffix
else if final.isPower64 then
"powerpc64" # never add "le" suffix
else
final.parsed.cpu.name;
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_os
os =
if rust ? platform then
rust.platform.os or "none"
else if final.isDarwin then
"macos"
else if final.isWasm && !final.isWasi then
"unknown" # Needed for {wasm32,wasm64}-unknown-unknown.
else
final.parsed.kernel.name;
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_family
target-family =
if args ? rust.platform.target-family then
args.rust.platform.target-family
else if args ? rustc.platform.target-family then
(
# Since https://github.com/rust-lang/rust/pull/84072
# `target-family` is a list instead of single value.
let
f = args.rustc.platform.target-family;
in
if isList f then f else [ f ]
)
else
optional final.isUnix "unix" ++ optional final.isWindows "windows" ++ optional final.isWasm "wasm";
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_vendor
vendor =
let
inherit (final.parsed) vendor;
in
rust.platform.vendor or {
"w64" = "pc";
}
.${vendor.name} or vendor.name;
};
# The name of the rust target, even if it is custom. Adjustments are
# because rust has slightly different naming conventions than we do.
rustcTarget =
let
inherit (final.parsed) cpu kernel abi;
cpu_ =
rust.platform.arch or {
"armv7a" = "armv7";
"armv7l" = "armv7";
"armv6l" = "arm";
"armv5tel" = "armv5te";
"riscv32" = "riscv32gc";
"riscv64" = "riscv64gc";
}
.${cpu.name} or cpu.name;
vendor_ = final.rust.platform.vendor;
in
# TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL.
args.rust.rustcTarget or args.rustc.config or (
# Rust uses `wasm32-wasip?` rather than `wasm32-unknown-wasi`.
# We cannot know which subversion does the user want, and
# currently use WASI 0.1 as default for compatibility. Custom
# users can set `rust.rustcTarget` to override it.
if final.isWasi then
"${cpu_}-wasip1"
else
"${cpu_}-${vendor_}-${kernel.name}${optionalString (abi.name != "unknown") "-${abi.name}"}"
);
# The name of the rust target if it is standard, or the json file
# containing the custom target spec.
rustcTargetSpec =
rust.rustcTargetSpec or (
if rust ? platform then
builtins.toFile (final.rust.rustcTarget + ".json") (toJSON rust.platform)
else
final.rust.rustcTarget
);
# The name of the rust target if it is standard, or the
# basename of the file containing the custom target spec,
# without the .json extension.
#
# This is the name used by Cargo for target subdirectories.
cargoShortTarget = removeSuffix ".json" (baseNameOf "${final.rust.rustcTargetSpec}");
# When used as part of an environment variable name, triples are
# uppercased and have all hyphens replaced by underscores:
#
# https://github.com/rust-lang/cargo/pull/9169
# https://github.com/rust-lang/cargo/issues/8285#issuecomment-634202431
cargoEnvVarTarget = replaceString "-" "_" (toUpper final.rust.cargoShortTarget);
# True if the target is no_std
# https://github.com/rust-lang/rust/blob/2e44c17c12cec45b6a682b1e53a04ac5b5fcc9d2/src/bootstrap/config.rs#L415-L421
isNoStdTarget = any (t: hasInfix t final.rust.rustcTarget) [
"-none"
"nvptx"
"switch"
"-uefi"
];
};
}
// {
go = {
# See https://pkg.go.dev/internal/platform for a list of known platforms
GOARCH =
{
"aarch64" = "arm64";
"arm" = "arm";
"armv5tel" = "arm";
"armv6l" = "arm";
"armv7l" = "arm";
"i686" = "386";
"loongarch64" = "loong64";
"mips" = "mips";
"mips64el" = "mips64le";
"mipsel" = "mipsle";
"powerpc64" = "ppc64";
"powerpc64le" = "ppc64le";
"riscv64" = "riscv64";
"s390x" = "s390x";
"x86_64" = "amd64";
"wasm32" = "wasm";
}
.${final.parsed.cpu.name} or null;
GOOS = if final.isWasi then "wasip1" else final.parsed.kernel.name;
# See https://go.dev/wiki/GoArm
GOARM = toString (lib.intersectLists [ (final.parsed.cpu.version or "") ] [ "5" "6" "7" ]);
};
node = {
# See these locations for a list of known architectures/platforms:
# - https://nodejs.org/api/os.html#osarch
# - https://nodejs.org/api/os.html#osplatform
arch =
if final.isAarch then
"arm" + lib.optionalString final.is64bit "64"
else if final.isMips32 then
"mips" + lib.optionalString final.isLittleEndian "el"
else if final.isMips64 && final.isLittleEndian then
"mips64el"
else if final.isPower then
"ppc" + lib.optionalString final.is64bit "64"
else if final.isx86_64 then
"x64"
else if final.isx86_32 then
"ia32"
else if final.isS390x then
"s390x"
else if final.isRiscV64 then
"riscv64"
else if final.isLoongArch64 then
"loong64"
else
null;
platform =
if final.isAndroid then
"android"
else if final.isDarwin then
"darwin"
else if final.isFreeBSD then
"freebsd"
else if final.isLinux then
"linux"
else if final.isOpenBSD then
"openbsd"
else if final.isSunOS then
"sunos"
else if (final.isWindows || final.isCygwin) then
"win32"
else
null;
};
};
in
assert final.useAndroidPrebuilt -> final.isAndroid;
assert foldl (pass: { assertion, message }: if assertion final then pass else throw message) true (
final.parsed.abi.assertions or [ ]
);
final;
in
# Everything in this attrset is the public interface of the file.
{
inherit
architectures
doubles
elaborate
equals
examples
flakeExposed
inspect
parse
platforms
systemToAttrs
;
}

194
lib/systems/doubles.nix Normal file
View File

@@ -0,0 +1,194 @@
{ lib }:
let
inherit (lib) lists;
inherit (lib.systems) parse;
inherit (lib.systems.inspect) predicates;
inherit (lib.attrsets) matchAttrs;
all = [
# Cygwin
"i686-cygwin"
"x86_64-cygwin"
# Darwin
"x86_64-darwin"
"aarch64-darwin"
# FreeBSD
"i686-freebsd"
"x86_64-freebsd"
"aarch64-freebsd"
# Genode
"aarch64-genode"
"i686-genode"
"x86_64-genode"
# illumos
"x86_64-solaris"
# JS
"javascript-ghcjs"
# Linux
"aarch64-linux"
"armv5tel-linux"
"armv6l-linux"
"armv7a-linux"
"armv7l-linux"
"i686-linux"
"loongarch64-linux"
"m68k-linux"
"microblaze-linux"
"microblazeel-linux"
"mips-linux"
"mips64-linux"
"mips64el-linux"
"mipsel-linux"
"powerpc-linux"
"powerpc64-linux"
"powerpc64le-linux"
"riscv32-linux"
"riscv64-linux"
"s390-linux"
"s390x-linux"
"x86_64-linux"
# MMIXware
"mmix-mmixware"
# NetBSD
"aarch64-netbsd"
"armv6l-netbsd"
"armv7a-netbsd"
"armv7l-netbsd"
"i686-netbsd"
"m68k-netbsd"
"mipsel-netbsd"
"powerpc-netbsd"
"riscv32-netbsd"
"riscv64-netbsd"
"x86_64-netbsd"
# none
"aarch64_be-none"
"aarch64-none"
"arm-none"
"armv6l-none"
"avr-none"
"i686-none"
"microblaze-none"
"microblazeel-none"
"mips-none"
"mips64-none"
"msp430-none"
"or1k-none"
"m68k-none"
"powerpc-none"
"powerpcle-none"
"riscv32-none"
"riscv64-none"
"rx-none"
"s390-none"
"s390x-none"
"vc4-none"
"x86_64-none"
# OpenBSD
"i686-openbsd"
"x86_64-openbsd"
# Redox
"x86_64-redox"
# WASI
"wasm64-wasi"
"wasm32-wasi"
# Windows
"aarch64-windows"
"x86_64-windows"
"i686-windows"
];
allParsed = map parse.mkSystemFromString all;
filterDoubles = f: map parse.doubleFromSystem (lists.filter f allParsed);
in
{
inherit all;
none = [ ];
arm = filterDoubles predicates.isAarch32;
armv7 = filterDoubles predicates.isArmv7;
aarch = filterDoubles predicates.isAarch;
aarch64 = filterDoubles predicates.isAarch64;
x86 = filterDoubles predicates.isx86;
i686 = filterDoubles predicates.isi686;
x86_64 = filterDoubles predicates.isx86_64;
microblaze = filterDoubles predicates.isMicroBlaze;
mips = filterDoubles predicates.isMips;
mmix = filterDoubles predicates.isMmix;
power = filterDoubles predicates.isPower;
riscv = filterDoubles predicates.isRiscV;
riscv32 = filterDoubles predicates.isRiscV32;
riscv64 = filterDoubles predicates.isRiscV64;
rx = filterDoubles predicates.isRx;
vc4 = filterDoubles predicates.isVc4;
or1k = filterDoubles predicates.isOr1k;
m68k = filterDoubles predicates.isM68k;
s390 = filterDoubles predicates.isS390;
s390x = filterDoubles predicates.isS390x;
loongarch64 = filterDoubles predicates.isLoongArch64;
js = filterDoubles predicates.isJavaScript;
bigEndian = filterDoubles predicates.isBigEndian;
littleEndian = filterDoubles predicates.isLittleEndian;
cygwin = filterDoubles predicates.isCygwin;
darwin = filterDoubles predicates.isDarwin;
freebsd = filterDoubles predicates.isFreeBSD;
# Should be better, but MinGW is unclear.
gnu =
filterDoubles (matchAttrs {
kernel = parse.kernels.linux;
abi = parse.abis.gnu;
})
++ filterDoubles (matchAttrs {
kernel = parse.kernels.linux;
abi = parse.abis.gnueabi;
})
++ filterDoubles (matchAttrs {
kernel = parse.kernels.linux;
abi = parse.abis.gnueabihf;
})
++ filterDoubles (matchAttrs {
kernel = parse.kernels.linux;
abi = parse.abis.gnuabin32;
})
++ filterDoubles (matchAttrs {
kernel = parse.kernels.linux;
abi = parse.abis.gnuabi64;
})
++ filterDoubles (matchAttrs {
kernel = parse.kernels.linux;
abi = parse.abis.gnuabielfv1;
})
++ filterDoubles (matchAttrs {
kernel = parse.kernels.linux;
abi = parse.abis.gnuabielfv2;
});
illumos = filterDoubles predicates.isSunOS;
linux = filterDoubles predicates.isLinux;
netbsd = filterDoubles predicates.isNetBSD;
openbsd = filterDoubles predicates.isOpenBSD;
unix = filterDoubles predicates.isUnix;
wasi = filterDoubles predicates.isWasi;
redox = filterDoubles predicates.isRedox;
windows = filterDoubles predicates.isWindows;
genode = filterDoubles predicates.isGenode;
embedded = filterDoubles predicates.isNone;
}

444
lib/systems/examples.nix Normal file
View File

@@ -0,0 +1,444 @@
# These can be passed to nixpkgs as either the `localSystem` or
# `crossSystem`. They are put here for user convenience, but also used by cross
# tests and linux cross stdenv building, so handle with care!
{ lib }:
let
platforms = import ./platforms.nix { inherit lib; };
riscv = bits: {
config = "riscv${bits}-unknown-linux-gnu";
};
in
rec {
#
# Linux
#
powernv = {
config = "powerpc64le-unknown-linux-gnu";
};
musl-power = {
config = "powerpc64le-unknown-linux-musl";
};
ppc64-elfv1 = {
config = "powerpc64-unknown-linux-gnuabielfv1";
rust.rustcTarget = "powerpc64-unknown-linux-gnu";
};
ppc64-elfv2 = {
config = "powerpc64-unknown-linux-gnuabielfv2";
};
ppc64 = ppc64-elfv2;
ppc64-musl = {
config = "powerpc64-unknown-linux-musl";
gcc = {
abi = "elfv2";
};
};
ppc32 = {
config = "powerpc-unknown-linux-gnu";
rust.rustcTarget = "powerpc-unknown-linux-gnu";
};
sheevaplug = {
config = "armv5tel-unknown-linux-gnueabi";
}
// platforms.sheevaplug;
raspberryPi = {
config = "armv6l-unknown-linux-gnueabihf";
}
// platforms.raspberrypi;
bluefield2 = {
config = "aarch64-unknown-linux-gnu";
}
// platforms.bluefield2;
remarkable1 = {
config = "armv7l-unknown-linux-gnueabihf";
}
// platforms.zero-gravitas;
remarkable2 = {
config = "armv7l-unknown-linux-gnueabihf";
}
// platforms.zero-sugar;
armv7l-hf-multiplatform = {
config = "armv7l-unknown-linux-gnueabihf";
};
aarch64-multiplatform = {
config = "aarch64-unknown-linux-gnu";
};
armv7a-android-prebuilt = {
config = "armv7a-unknown-linux-androideabi";
rust.rustcTarget = "armv7-linux-androideabi";
androidSdkVersion = "35";
androidNdkVersion = "27";
useAndroidPrebuilt = true;
}
// platforms.armv7a-android;
aarch64-android-prebuilt = {
config = "aarch64-unknown-linux-android";
rust.rustcTarget = "aarch64-linux-android";
androidSdkVersion = "35";
androidNdkVersion = "27";
useAndroidPrebuilt = true;
};
aarch64-android = {
config = "aarch64-unknown-linux-android";
androidSdkVersion = "35";
androidNdkVersion = "27";
libc = "bionic";
useAndroidPrebuilt = false;
useLLVM = true;
};
pogoplug4 = {
config = "armv5tel-unknown-linux-gnueabi";
}
// platforms.pogoplug4;
ben-nanonote = {
config = "mipsel-unknown-linux-uclibc";
}
// platforms.ben_nanonote;
fuloongminipc = {
config = "mipsel-unknown-linux-gnu";
}
// platforms.fuloong2f_n32;
# can execute on 32bit chip
mips-linux-gnu = {
config = "mips-unknown-linux-gnu";
}
// platforms.gcc_mips32r2_o32;
mipsel-linux-gnu = {
config = "mipsel-unknown-linux-gnu";
}
// platforms.gcc_mips32r2_o32;
# require 64bit chip (for more registers, 64-bit floating point, 64-bit "long long") but use 32bit pointers
mips64-linux-gnuabin32 = {
config = "mips64-unknown-linux-gnuabin32";
}
// platforms.gcc_mips64r2_n32;
mips64el-linux-gnuabin32 = {
config = "mips64el-unknown-linux-gnuabin32";
}
// platforms.gcc_mips64r2_n32;
# 64bit pointers
mips64-linux-gnuabi64 = {
config = "mips64-unknown-linux-gnuabi64";
}
// platforms.gcc_mips64r2_64;
mips64el-linux-gnuabi64 = {
config = "mips64el-unknown-linux-gnuabi64";
}
// platforms.gcc_mips64r2_64;
muslpi = raspberryPi // {
config = "armv6l-unknown-linux-musleabihf";
};
aarch64-multiplatform-musl = {
config = "aarch64-unknown-linux-musl";
};
gnu64 = {
config = "x86_64-unknown-linux-gnu";
};
gnu64_simplekernel = gnu64 // platforms.pc_simplekernel; # see test/cross/default.nix
gnu32 = {
config = "i686-unknown-linux-gnu";
};
musl64 = {
config = "x86_64-unknown-linux-musl";
};
musl32 = {
config = "i686-unknown-linux-musl";
};
riscv64 = riscv "64";
riscv32 = riscv "32";
riscv64-musl = {
config = "riscv64-unknown-linux-musl";
};
riscv64-embedded = {
config = "riscv64-none-elf";
libc = "newlib";
};
riscv32-embedded = {
config = "riscv32-none-elf";
libc = "newlib";
};
mips64-embedded = {
config = "mips64-none-elf";
libc = "newlib";
};
mips-embedded = {
config = "mips-none-elf";
libc = "newlib";
};
# https://github.com/loongson/la-softdev-convention/blob/master/la-softdev-convention.adoc#10-operating-system-package-build-requirements
loongarch64-linux = lib.recursiveUpdate platforms.loongarch64-multiplatform {
config = "loongarch64-unknown-linux-gnu";
};
loongarch64-linux-embedded = lib.recursiveUpdate platforms.loongarch64-multiplatform {
config = "loongarch64-unknown-linux-gnu";
gcc = {
arch = "loongarch64";
strict-align = true;
};
};
mmix = {
config = "mmix-unknown-mmixware";
libc = "newlib";
};
rx-embedded = {
config = "rx-none-elf";
libc = "newlib";
};
msp430 = {
config = "msp430-elf";
libc = "newlib";
};
avr = {
config = "avr";
};
vc4 = {
config = "vc4-elf";
libc = "newlib";
};
or1k = {
config = "or1k-elf";
libc = "newlib";
};
m68k = {
config = "m68k-unknown-linux-gnu";
};
s390 = {
config = "s390-unknown-linux-gnu";
};
s390x = {
config = "s390x-unknown-linux-gnu";
};
arm-embedded = {
config = "arm-none-eabi";
libc = "newlib";
};
arm-embedded-nano = {
config = "arm-none-eabi";
libc = "newlib-nano";
};
armhf-embedded = {
config = "arm-none-eabihf";
libc = "newlib";
# GCC8+ does not build without this
# (https://www.mail-archive.com/gcc-bugs@gcc.gnu.org/msg552339.html):
gcc = {
arch = "armv5t";
fpu = "vfp";
};
};
aarch64-embedded = {
config = "aarch64-none-elf";
libc = "newlib";
rust.rustcTarget = "aarch64-unknown-none";
};
aarch64be-embedded = {
config = "aarch64_be-none-elf";
libc = "newlib";
};
ppc-embedded = {
config = "powerpc-none-eabi";
libc = "newlib";
};
ppcle-embedded = {
config = "powerpcle-none-eabi";
libc = "newlib";
};
i686-embedded = {
config = "i686-elf";
libc = "newlib";
};
x86_64-embedded = {
config = "x86_64-elf";
libc = "newlib";
};
microblaze-embedded = {
config = "microblazeel-none-elf";
libc = "newlib";
};
#
# Redox
#
x86_64-unknown-redox = {
config = "x86_64-unknown-redox";
libc = "relibc";
};
#
# Darwin
#
iphone64 = {
config = "arm64-apple-ios";
# config = "aarch64-apple-darwin14";
darwinSdkVersion = "14.3";
xcodeVer = "12.3";
xcodePlatform = "iPhoneOS";
useiOSPrebuilt = true;
};
iphone64-simulator = {
config = "x86_64-apple-ios";
# config = "x86_64-apple-darwin14";
darwinSdkVersion = "14.3";
xcodeVer = "12.3";
xcodePlatform = "iPhoneSimulator";
darwinPlatform = "ios-simulator";
useiOSPrebuilt = true;
};
aarch64-darwin = {
config = "arm64-apple-darwin";
xcodePlatform = "MacOSX";
platform = { };
};
x86_64-darwin = {
config = "x86_64-apple-darwin";
xcodePlatform = "MacOSX";
platform = { };
};
#
# Windows
#
# 32 bit mingw-w64
mingw32 = {
config = "i686-w64-mingw32";
libc = "msvcrt"; # This distinguishes the mingw (non posix) toolchain
};
# 64 bit mingw-w64
mingwW64 = {
# That's the triplet they use in the mingw-w64 docs.
config = "x86_64-w64-mingw32";
libc = "msvcrt"; # This distinguishes the mingw (non posix) toolchain
};
ucrt64 = {
config = "x86_64-w64-mingw32";
libc = "ucrt"; # This distinguishes the mingw (non posix) toolchain
};
# LLVM-based mingw-w64 for ARM
ucrtAarch64 = {
config = "aarch64-w64-mingw32";
libc = "ucrt";
rust.rustcTarget = "aarch64-pc-windows-gnullvm";
useLLVM = true;
};
# Target the MSVC ABI
x86_64-windows = {
config = "x86_64-pc-windows-msvc";
useLLVM = true;
};
aarch64-windows = {
config = "aarch64-pc-windows-msvc";
useLLVM = true;
};
x86_64-cygwin = {
config = "x86_64-pc-cygwin";
};
# BSDs
aarch64-freebsd = {
config = "aarch64-unknown-freebsd";
useLLVM = true;
};
x86_64-freebsd = {
config = "x86_64-unknown-freebsd";
useLLVM = true;
};
x86_64-netbsd = {
config = "x86_64-unknown-netbsd";
};
# this is broken and never worked fully
x86_64-netbsd-llvm = {
config = "x86_64-unknown-netbsd";
useLLVM = true;
};
x86_64-openbsd = {
config = "x86_64-unknown-openbsd";
useLLVM = true;
};
#
# WASM
#
wasi32 = {
config = "wasm32-unknown-wasi";
useLLVM = true;
};
wasm32-unknown-none = {
config = "wasm32-unknown-none";
rust.rustcTarget = "wasm32-unknown-unknown";
useLLVM = true;
};
# Ghcjs
ghcjs = {
# This triple is special to GHC/Cabal/GHCJS and not recognized by autotools
# See: https://gitlab.haskell.org/ghc/ghc/-/commit/6636b670233522f01d002c9b97827d00289dbf5c
# https://github.com/ghcjs/ghcjs/issues/53
config = "javascript-unknown-ghcjs";
};
}

View File

@@ -0,0 +1,28 @@
# See [RFC 46] for mandated platform support and ../../pkgs/stdenv for
# implemented platform support. This list is mainly descriptive, i.e. all
# system doubles for platforms where nixpkgs can do native compilation
# reasonably well are included.
#
# [RFC 46]: https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md
{ }:
[
# Tier 1
"x86_64-linux"
# Tier 2
"aarch64-linux"
"x86_64-darwin"
# Tier 3
"armv6l-linux"
"armv7l-linux"
"i686-linux"
# "mipsel-linux" is excluded because it is not bootstrapped
# Other platforms with sufficient support in stdenv which is not formally
# mandated by their platform tier.
"aarch64-darwin"
# "armv5tel-linux" is excluded because it is not bootstrapped
"powerpc64le-linux"
"riscv64-linux"
"x86_64-freebsd"
]

489
lib/systems/inspect.nix Normal file
View File

@@ -0,0 +1,489 @@
{ lib }:
let
inherit (lib)
any
attrValues
concatMap
filter
hasPrefix
isList
mapAttrs
matchAttrs
recursiveUpdateUntil
toList
;
inherit (lib.strings) toJSON;
inherit (lib.systems.parse)
kernels
kernelFamilies
significantBytes
cpuTypes
execFormats
;
abis = mapAttrs (_: abi: removeAttrs abi [ "assertions" ]) lib.systems.parse.abis;
in
rec {
# these patterns are to be matched against {host,build,target}Platform.parsed
patterns = rec {
# The patterns below are lists in sum-of-products form.
#
# Each attribute is list of product conditions; non-list values are treated
# as a singleton list. If *any* product condition in the list matches then
# the predicate matches. Each product condition is tested by
# `lib.attrsets.matchAttrs`, which requires a match on *all* attributes of
# the product.
isi686 = {
cpu = cpuTypes.i686;
};
isx86_32 = {
cpu = {
family = "x86";
bits = 32;
};
};
isx86_64 = {
cpu = {
family = "x86";
bits = 64;
};
};
isPower = {
cpu = {
family = "power";
};
};
isPower64 = {
cpu = {
family = "power";
bits = 64;
};
};
isAbiElfv1 = {
abi = {
abi = "elfv1";
};
};
# This ABI is the default in NixOS PowerPC64 BE, but not on mainline GCC,
# so it sometimes causes issues in certain packages that makes the wrong
# assumption on the used ABI.
isAbiElfv2 = [
{
abi = {
abi = "elfv2";
};
}
{
abi = {
name = "musl";
};
cpu = {
family = "power";
bits = 64;
};
}
];
isx86 = {
cpu = {
family = "x86";
};
};
isAarch32 = {
cpu = {
family = "arm";
bits = 32;
};
};
isArmv7 = map (
{ arch, ... }:
{
cpu = { inherit arch; };
}
) (filter (cpu: hasPrefix "armv7" cpu.arch or "") (attrValues cpuTypes));
isAarch64 = {
cpu = {
family = "arm";
bits = 64;
};
};
isAarch = {
cpu = {
family = "arm";
};
};
isMicroBlaze = {
cpu = {
family = "microblaze";
};
};
isMips = {
cpu = {
family = "mips";
};
};
isMips32 = {
cpu = {
family = "mips";
bits = 32;
};
};
isMips64 = {
cpu = {
family = "mips";
bits = 64;
};
};
isMips64n32 = {
cpu = {
family = "mips";
bits = 64;
};
abi = {
abi = "n32";
};
};
isMips64n64 = {
cpu = {
family = "mips";
bits = 64;
};
abi = {
abi = "64";
};
};
isMmix = {
cpu = {
family = "mmix";
};
};
isRiscV = {
cpu = {
family = "riscv";
};
};
isRiscV32 = {
cpu = {
family = "riscv";
bits = 32;
};
};
isRiscV64 = {
cpu = {
family = "riscv";
bits = 64;
};
};
isRx = {
cpu = {
family = "rx";
};
};
isSparc = {
cpu = {
family = "sparc";
};
};
isSparc64 = {
cpu = {
family = "sparc";
bits = 64;
};
};
isWasm = {
cpu = {
family = "wasm";
};
};
isMsp430 = {
cpu = {
family = "msp430";
};
};
isVc4 = {
cpu = {
family = "vc4";
};
};
isAvr = {
cpu = {
family = "avr";
};
};
isAlpha = {
cpu = {
family = "alpha";
};
};
isOr1k = {
cpu = {
family = "or1k";
};
};
isM68k = {
cpu = {
family = "m68k";
};
};
isS390 = {
cpu = {
family = "s390";
};
};
isS390x = {
cpu = {
family = "s390";
bits = 64;
};
};
isLoongArch64 = {
cpu = {
family = "loongarch";
bits = 64;
};
};
isJavaScript = {
cpu = cpuTypes.javascript;
};
is32bit = {
cpu = {
bits = 32;
};
};
is64bit = {
cpu = {
bits = 64;
};
};
isILP32 = [
{
cpu = {
family = "wasm";
bits = 32;
};
}
]
++
map
(a: {
abi = {
abi = a;
};
})
[
"n32"
"ilp32"
"x32"
];
isBigEndian = {
cpu = {
significantByte = significantBytes.bigEndian;
};
};
isLittleEndian = {
cpu = {
significantByte = significantBytes.littleEndian;
};
};
isBSD = {
kernel = {
families = { inherit (kernelFamilies) bsd; };
};
};
isDarwin = {
kernel = {
families = { inherit (kernelFamilies) darwin; };
};
};
isUnix = [
isBSD
isDarwin
isLinux
isSunOS
isCygwin
isRedox
];
isMacOS = {
kernel = kernels.macos;
};
isiOS = {
kernel = kernels.ios;
};
isLinux = {
kernel = kernels.linux;
};
isSunOS = {
kernel = kernels.solaris;
};
isFreeBSD = {
kernel = {
name = "freebsd";
};
};
isNetBSD = {
kernel = kernels.netbsd;
};
isOpenBSD = {
kernel = kernels.openbsd;
};
isWindows = {
kernel = kernels.windows;
};
isCygwin = {
kernel = kernels.cygwin;
};
isMinGW = {
kernel = kernels.windows;
abi = abis.gnu;
};
isMsvc = {
kernel = kernels.windows;
abi = abis.msvc;
};
isWasi = {
kernel = kernels.wasi;
};
isRedox = {
kernel = kernels.redox;
};
isGhcjs = {
kernel = kernels.ghcjs;
};
isGenode = {
kernel = kernels.genode;
};
isNone = {
kernel = kernels.none;
};
isAndroid = [
{ abi = abis.android; }
{ abi = abis.androideabi; }
];
isGnu =
with abis;
map (a: { abi = a; }) [
gnuabi64
gnuabin32
gnu
gnueabi
gnueabihf
gnuabielfv1
gnuabielfv2
];
isMusl =
with abis;
map (a: { abi = a; }) [
musl
musleabi
musleabihf
muslabin32
muslabi64
];
isUClibc =
with abis;
map (a: { abi = a; }) [
uclibc
uclibceabi
uclibceabihf
];
isEfi = [
{
cpu = {
family = "arm";
version = "6";
};
}
{
cpu = {
family = "arm";
version = "7";
};
}
{
cpu = {
family = "arm";
version = "8";
};
}
{
cpu = {
family = "riscv";
};
}
{
cpu = {
family = "x86";
};
}
{
cpu = {
family = "loongarch";
};
}
];
isElf = {
kernel.execFormat = execFormats.elf;
};
isMacho = {
kernel.execFormat = execFormats.macho;
};
};
# given two patterns, return a pattern which is their logical AND.
# Since a pattern is a list-of-disjuncts, this needs to
patternLogicalAnd =
pat1_: pat2_:
let
# patterns can be either a list or a (bare) singleton; turn
# them into singletons for uniform handling
pat1 = toList pat1_;
pat2 = toList pat2_;
in
concatMap (
attr1:
map (
attr2:
recursiveUpdateUntil (
path: subattr1: subattr2:
if (builtins.intersectAttrs subattr1 subattr2) == { } || subattr1 == subattr2 then
true
else
throw ''
pattern conflict at path ${toString path}:
${toJSON subattr1}
${toJSON subattr2}
''
) attr1 attr2
) pat2
) pat1;
matchAnyAttrs =
patterns:
if isList patterns then
attrs: any (pattern: matchAttrs pattern attrs) patterns
else
matchAttrs patterns;
predicates = mapAttrs (_: matchAnyAttrs) patterns;
# these patterns are to be matched against the entire
# {host,build,target}Platform structure; they include a `parsed={}` marker so
# that `lib.meta.availableOn` can distinguish them from the patterns which
# apply only to the `parsed` field.
platformPatterns = mapAttrs (_: p: { parsed = { }; } // p) {
isStatic = {
isStatic = true;
};
};
}

969
lib/systems/parse.nix Normal file
View File

@@ -0,0 +1,969 @@
# Define the list of system with their properties.
#
# See https://clang.llvm.org/docs/CrossCompilation.html and
# http://llvm.org/docs/doxygen/html/Triple_8cpp_source.html especially
# Triple::normalize. Parsing should essentially act as a more conservative
# version of that last function.
#
# Most of the types below come in "open" and "closed" pairs. The open ones
# specify what information we need to know about systems in general, and the
# closed ones are sub-types representing the whitelist of systems we support in
# practice.
#
# Code in the remainder of nixpkgs shouldn't rely on the closed ones in
# e.g. exhaustive cases. Its more a sanity check to make sure nobody defines
# systems that overlap with existing ones and won't notice something amiss.
#
{ lib }:
let
inherit (lib)
all
any
attrValues
elem
elemAt
hasPrefix
id
length
mapAttrs
mergeOneOption
optionalString
splitString
versionAtLeast
;
inherit (lib.strings) match;
inherit (lib.systems.inspect.predicates)
isAarch32
isBigEndian
isDarwin
isLinux
isPower64
isWindows
isCygwin
;
inherit (lib.types)
enum
float
isType
mkOptionType
number
setType
string
types
;
setTypes =
type:
mapAttrs (
name: value:
assert type.check value;
setType type.name ({ inherit name; } // value)
);
# gnu-config will ignore the portion of a triple matching the
# regex `e?abi.*$` when determining the validity of a triple. In
# other words, `i386-linuxabichickenlips` is a valid triple.
removeAbiSuffix =
x:
let
found = match "(.*)e?abi.*" x;
in
if found == null then x else elemAt found 0;
in
rec {
################################################################################
types.openSignificantByte = mkOptionType {
name = "significant-byte";
description = "Endianness";
merge = mergeOneOption;
};
types.significantByte = enum (attrValues significantBytes);
significantBytes = setTypes types.openSignificantByte {
bigEndian = { };
littleEndian = { };
};
################################################################################
# Reasonable power of 2
types.bitWidth = enum [
8
16
32
64
128
];
################################################################################
types.openCpuType = mkOptionType {
name = "cpu-type";
description = "instruction set architecture name and information";
merge = mergeOneOption;
check =
x:
types.bitWidth.check x.bits
&& (if 8 < x.bits then types.significantByte.check x.significantByte else !(x ? significantByte));
};
types.cpuType = enum (attrValues cpuTypes);
cpuTypes =
let
inherit (significantBytes) bigEndian littleEndian;
in
setTypes types.openCpuType {
arm = {
bits = 32;
significantByte = littleEndian;
family = "arm";
};
armv5tel = {
bits = 32;
significantByte = littleEndian;
family = "arm";
version = "5";
arch = "armv5t";
};
armv6m = {
bits = 32;
significantByte = littleEndian;
family = "arm";
version = "6";
arch = "armv6-m";
};
armv6l = {
bits = 32;
significantByte = littleEndian;
family = "arm";
version = "6";
arch = "armv6";
};
armv7a = {
bits = 32;
significantByte = littleEndian;
family = "arm";
version = "7";
arch = "armv7-a";
};
armv7r = {
bits = 32;
significantByte = littleEndian;
family = "arm";
version = "7";
arch = "armv7-r";
};
armv7m = {
bits = 32;
significantByte = littleEndian;
family = "arm";
version = "7";
arch = "armv7-m";
};
armv7l = {
bits = 32;
significantByte = littleEndian;
family = "arm";
version = "7";
arch = "armv7";
};
armv8a = {
bits = 32;
significantByte = littleEndian;
family = "arm";
version = "8";
arch = "armv8-a";
};
armv8r = {
bits = 32;
significantByte = littleEndian;
family = "arm";
version = "8";
arch = "armv8-a";
};
armv8m = {
bits = 32;
significantByte = littleEndian;
family = "arm";
version = "8";
arch = "armv8-m";
};
aarch64 = {
bits = 64;
significantByte = littleEndian;
family = "arm";
version = "8";
arch = "armv8-a";
};
aarch64_be = {
bits = 64;
significantByte = bigEndian;
family = "arm";
version = "8";
arch = "armv8-a";
};
i386 = {
bits = 32;
significantByte = littleEndian;
family = "x86";
arch = "i386";
};
i486 = {
bits = 32;
significantByte = littleEndian;
family = "x86";
arch = "i486";
};
i586 = {
bits = 32;
significantByte = littleEndian;
family = "x86";
arch = "i586";
};
i686 = {
bits = 32;
significantByte = littleEndian;
family = "x86";
arch = "i686";
};
x86_64 = {
bits = 64;
significantByte = littleEndian;
family = "x86";
arch = "x86-64";
};
microblaze = {
bits = 32;
significantByte = bigEndian;
family = "microblaze";
};
microblazeel = {
bits = 32;
significantByte = littleEndian;
family = "microblaze";
};
mips = {
bits = 32;
significantByte = bigEndian;
family = "mips";
};
mipsel = {
bits = 32;
significantByte = littleEndian;
family = "mips";
};
mips64 = {
bits = 64;
significantByte = bigEndian;
family = "mips";
};
mips64el = {
bits = 64;
significantByte = littleEndian;
family = "mips";
};
mmix = {
bits = 64;
significantByte = bigEndian;
family = "mmix";
};
m68k = {
bits = 32;
significantByte = bigEndian;
family = "m68k";
};
powerpc = {
bits = 32;
significantByte = bigEndian;
family = "power";
};
powerpc64 = {
bits = 64;
significantByte = bigEndian;
family = "power";
};
powerpc64le = {
bits = 64;
significantByte = littleEndian;
family = "power";
};
powerpcle = {
bits = 32;
significantByte = littleEndian;
family = "power";
};
riscv32 = {
bits = 32;
significantByte = littleEndian;
family = "riscv";
};
riscv64 = {
bits = 64;
significantByte = littleEndian;
family = "riscv";
};
s390 = {
bits = 32;
significantByte = bigEndian;
family = "s390";
};
s390x = {
bits = 64;
significantByte = bigEndian;
family = "s390";
};
sparc = {
bits = 32;
significantByte = bigEndian;
family = "sparc";
};
sparc64 = {
bits = 64;
significantByte = bigEndian;
family = "sparc";
};
wasm32 = {
bits = 32;
significantByte = littleEndian;
family = "wasm";
};
wasm64 = {
bits = 64;
significantByte = littleEndian;
family = "wasm";
};
alpha = {
bits = 64;
significantByte = littleEndian;
family = "alpha";
};
rx = {
bits = 32;
significantByte = littleEndian;
family = "rx";
};
msp430 = {
bits = 16;
significantByte = littleEndian;
family = "msp430";
};
avr = {
bits = 8;
family = "avr";
};
vc4 = {
bits = 32;
significantByte = littleEndian;
family = "vc4";
};
or1k = {
bits = 32;
significantByte = bigEndian;
family = "or1k";
};
loongarch64 = {
bits = 64;
significantByte = littleEndian;
family = "loongarch";
};
javascript = {
bits = 32;
significantByte = littleEndian;
family = "javascript";
};
}
// {
# aliases
# Apple architecture name, as used by `darwinArch`; required by
# LLVM ≥ 20.
arm64 = cpuTypes.aarch64;
};
# GNU build systems assume that older NetBSD architectures are using a.out.
gnuNetBSDDefaultExecFormat =
cpu:
if
(cpu.family == "arm" && cpu.bits == 32)
|| (cpu.family == "sparc" && cpu.bits == 32)
|| (cpu.family == "m68k" && cpu.bits == 32)
|| (cpu.family == "x86" && cpu.bits == 32)
then
execFormats.aout
else
execFormats.elf;
# Determine when two CPUs are compatible with each other. That is,
# can code built for system B run on system A? For that to happen,
# the programs that system B accepts must be a subset of the
# programs that system A accepts.
#
# We have the following properties of the compatibility relation,
# which must be preserved when adding compatibility information for
# additional CPUs.
# - (reflexivity)
# Every CPU is compatible with itself.
# - (transitivity)
# If A is compatible with B and B is compatible with C then A is compatible with C.
#
# Note: Since 22.11 the archs of a mode switching CPU are no longer considered
# pairwise compatible. Mode switching implies that binaries built for A
# and B respectively can't be executed at the same time.
isCompatible =
with cpuTypes;
a: b:
any id [
# x86
(b == i386 && isCompatible a i486)
(b == i486 && isCompatible a i586)
(b == i586 && isCompatible a i686)
# XXX: Not true in some cases. Like in WSL mode.
(b == i686 && isCompatible a x86_64)
# ARMv4
(b == arm && isCompatible a armv5tel)
# ARMv5
(b == armv5tel && isCompatible a armv6l)
# ARMv6
(b == armv6l && isCompatible a armv6m)
(b == armv6m && isCompatible a armv7l)
# ARMv7
(b == armv7l && isCompatible a armv7a)
(b == armv7l && isCompatible a armv7r)
(b == armv7l && isCompatible a armv7m)
# ARMv8
(b == aarch64 && a == armv8a)
(b == armv8a && isCompatible a aarch64)
(b == armv8r && isCompatible a armv8a)
(b == armv8m && isCompatible a armv8a)
# PowerPC
(b == powerpc && isCompatible a powerpc64)
(b == powerpcle && isCompatible a powerpc64le)
# MIPS
(b == mips && isCompatible a mips64)
(b == mipsel && isCompatible a mips64el)
# RISCV
(b == riscv32 && isCompatible a riscv64)
# SPARC
(b == sparc && isCompatible a sparc64)
# WASM
(b == wasm32 && isCompatible a wasm64)
# identity
(b == a)
];
################################################################################
types.openVendor = mkOptionType {
name = "vendor";
description = "vendor for the platform";
merge = mergeOneOption;
};
types.vendor = enum (attrValues vendors);
vendors = setTypes types.openVendor {
apple = { };
pc = { };
knuth = { };
# Actually matters, unlocking some MinGW-w64-specific options in GCC. See
# bottom of https://sourceforge.net/p/mingw-w64/wiki2/Unicode%20apps/
w64 = { };
none = { };
unknown = { };
};
################################################################################
types.openExecFormat = mkOptionType {
name = "exec-format";
description = "executable container used by the kernel";
merge = mergeOneOption;
};
types.execFormat = enum (attrValues execFormats);
execFormats = setTypes types.openExecFormat {
aout = { }; # a.out
elf = { };
macho = { };
pe = { };
wasm = { };
unknown = { };
};
################################################################################
types.openKernelFamily = mkOptionType {
name = "exec-format";
description = "executable container used by the kernel";
merge = mergeOneOption;
};
types.kernelFamily = enum (attrValues kernelFamilies);
kernelFamilies = setTypes types.openKernelFamily {
bsd = { };
darwin = { };
};
################################################################################
types.openKernel = mkOptionType {
name = "kernel";
description = "kernel name and information";
merge = mergeOneOption;
check =
x: types.execFormat.check x.execFormat && all types.kernelFamily.check (attrValues x.families);
};
types.kernel = enum (attrValues kernels);
kernels =
let
inherit (execFormats)
elf
pe
wasm
unknown
macho
;
inherit (kernelFamilies) bsd darwin;
in
setTypes types.openKernel {
# TODO(@Ericson2314): Don't want to mass-rebuild yet to keeping 'darwin' as
# the normalized name for macOS.
macos = {
execFormat = macho;
families = { inherit darwin; };
name = "darwin";
};
ios = {
execFormat = macho;
families = { inherit darwin; };
};
freebsd = {
execFormat = elf;
families = { inherit bsd; };
name = "freebsd";
};
linux = {
execFormat = elf;
families = { };
};
netbsd = {
execFormat = elf;
families = { inherit bsd; };
};
none = {
execFormat = unknown;
families = { };
};
openbsd = {
execFormat = elf;
families = { inherit bsd; };
};
solaris = {
execFormat = elf;
families = { };
};
wasi = {
execFormat = wasm;
families = { };
};
redox = {
execFormat = elf;
families = { };
};
windows = {
execFormat = pe;
families = { };
};
cygwin = {
execFormat = pe;
families = { };
};
ghcjs = {
execFormat = unknown;
families = { };
};
genode = {
execFormat = elf;
families = { };
};
mmixware = {
execFormat = unknown;
families = { };
};
}
// {
# aliases
# 'darwin' is the kernel for all of them. We choose macOS by default.
darwin = kernels.macos;
watchos = kernels.ios;
tvos = kernels.ios;
win32 = kernels.windows;
};
################################################################################
types.openAbi = mkOptionType {
name = "abi";
description = "binary interface for compiled code and syscalls";
merge = mergeOneOption;
};
types.abi = enum (attrValues abis);
abis = setTypes types.openAbi {
msvc = { };
# Note: eabi is specific to ARM and PowerPC.
# On PowerPC, this corresponds to PPCEABI.
# On ARM, this corresponds to ARMEABI.
eabi = {
float = "soft";
};
eabihf = {
float = "hard";
};
# Other architectures should use ELF in embedded situations.
elf = { };
androideabi = { };
android = {
assertions = [
{
assertion = platform: !platform.isAarch32;
message = ''
The "android" ABI is not for 32-bit ARM. Use "androideabi" instead.
'';
}
];
};
gnueabi = {
float = "soft";
};
gnueabihf = {
float = "hard";
};
gnu = {
assertions = [
{
assertion = platform: !platform.isAarch32;
message = ''
The "gnu" ABI is ambiguous on 32-bit ARM. Use "gnueabi" or "gnueabihf" instead.
'';
}
{
assertion = platform: !(platform.isPower64 && platform.isBigEndian);
message = ''
The "gnu" ABI is ambiguous on big-endian 64-bit PowerPC. Use "gnuabielfv2" or "gnuabielfv1" instead.
'';
}
];
};
gnuabi64 = {
abi = "64";
};
muslabi64 = {
abi = "64";
};
# NOTE: abi=n32 requires a 64-bit MIPS chip! That is not a typo.
# It is basically the 64-bit abi with 32-bit pointers. Details:
# https://www.linux-mips.org/pub/linux/mips/doc/ABI/MIPS-N32-ABI-Handbook.pdf
gnuabin32 = {
abi = "n32";
};
muslabin32 = {
abi = "n32";
};
gnuabielfv2 = {
abi = "elfv2";
};
gnuabielfv1 = {
abi = "elfv1";
};
musleabi = {
float = "soft";
};
musleabihf = {
float = "hard";
};
musl = { };
uclibceabi = {
float = "soft";
};
uclibceabihf = {
float = "hard";
};
uclibc = { };
unknown = { };
};
################################################################################
types.parsedPlatform = mkOptionType {
name = "system";
description = "fully parsed representation of llvm- or nix-style platform tuple";
merge = mergeOneOption;
check =
{
cpu,
vendor,
kernel,
abi,
}:
types.cpuType.check cpu
&& types.vendor.check vendor
&& types.kernel.check kernel
&& types.abi.check abi;
};
isSystem = isType "system";
mkSystem =
components:
assert types.parsedPlatform.check components;
setType "system" components;
mkSkeletonFromList =
l:
{
"1" =
if elemAt l 0 == "avr" then
{
cpu = elemAt l 0;
kernel = "none";
abi = "unknown";
}
else
throw "system string '${lib.concatStringsSep "-" l}' with 1 component is ambiguous";
"2" = # We only do 2-part hacks for things Nix already supports
if elemAt l 1 == "cygwin" then
mkSkeletonFromList [
(elemAt l 0)
"pc"
"cygwin"
]
# MSVC ought to be the default ABI so this case isn't needed. But then it
# becomes difficult to handle the gnu* variants for Aarch32 correctly for
# minGW. So it's easier to make gnu* the default for the MinGW, but
# hack-in MSVC for the non-MinGW case right here.
else if elemAt l 1 == "windows" then
{
cpu = elemAt l 0;
kernel = "windows";
abi = "msvc";
}
else if (elemAt l 1) == "elf" then
{
cpu = elemAt l 0;
vendor = "unknown";
kernel = "none";
abi = elemAt l 1;
}
else
{
cpu = elemAt l 0;
kernel = elemAt l 1;
};
"3" =
# cpu-kernel-environment
if
elemAt l 1 == "linux"
|| elem (elemAt l 2) [
"eabi"
"eabihf"
"elf"
"gnu"
]
then
{
cpu = elemAt l 0;
kernel = elemAt l 1;
abi = elemAt l 2;
vendor = "unknown";
}
# cpu-vendor-os
else if
elemAt l 1 == "apple"
|| elem (elemAt l 2) [
"redox"
"mmixware"
"ghcjs"
"mingw32"
]
|| hasPrefix "freebsd" (elemAt l 2)
|| hasPrefix "netbsd" (elemAt l 2)
|| hasPrefix "openbsd" (elemAt l 2)
|| hasPrefix "genode" (elemAt l 2)
|| hasPrefix "wasm32" (elemAt l 0)
then
{
cpu = elemAt l 0;
vendor = elemAt l 1;
kernel =
if elemAt l 2 == "mingw32" then
"windows" # autotools breaks on -gnu for window
else
elemAt l 2;
}
# lots of tools expect a triplet for Cygwin, even though the vendor is just "pc"
else if elemAt l 2 == "cygwin" then
{
cpu = elemAt l 0;
vendor = elemAt l 1;
kernel = "cygwin";
}
else
throw "system string '${lib.concatStringsSep "-" l}' with 3 components is ambiguous";
"4" = {
cpu = elemAt l 0;
vendor = elemAt l 1;
kernel = elemAt l 2;
abi = elemAt l 3;
};
}
.${toString (length l)}
or (throw "system string '${lib.concatStringsSep "-" l}' has invalid number of hyphen-separated components");
# This should revert the job done by config.guess from the gcc compiler.
mkSystemFromSkeleton =
{
cpu,
# Optional, but fallback too complex for here.
# Inferred below instead.
vendor ?
assert false;
null,
kernel,
# Also inferred below
abi ?
assert false;
null,
}@args:
let
getCpu = name: cpuTypes.${name} or (throw "Unknown CPU type: ${name}");
getVendor = name: vendors.${name} or (throw "Unknown vendor: ${name}");
getKernel = name: kernels.${name} or (throw "Unknown kernel: ${name}");
getAbi = name: abis.${name} or (throw "Unknown ABI: ${name}");
parsed = {
cpu = getCpu args.cpu;
vendor =
if args ? vendor then
getVendor args.vendor
else if isDarwin parsed then
vendors.apple
else if (isWindows parsed || isCygwin parsed) then
vendors.pc
else
vendors.unknown;
kernel =
if hasPrefix "darwin" args.kernel then
getKernel "darwin"
else if hasPrefix "netbsd" args.kernel then
getKernel "netbsd"
else
getKernel (removeAbiSuffix args.kernel);
abi =
if args ? abi then
getAbi args.abi
else if isLinux parsed || isWindows parsed then
if isAarch32 parsed then
if versionAtLeast (parsed.cpu.version or "0") "6" then abis.gnueabihf else abis.gnueabi
# Default ppc64 BE to ELFv2
else if isPower64 parsed && isBigEndian parsed then
abis.gnuabielfv2
else
abis.gnu
else
abis.unknown;
};
in
mkSystem parsed;
mkSystemFromString = s: mkSystemFromSkeleton (mkSkeletonFromList (splitString "-" s));
kernelName = kernel: kernel.name + toString (kernel.version or "");
darwinArch = cpu: if cpu.name == "aarch64" then "arm64" else cpu.name;
doubleFromSystem =
{
cpu,
kernel,
abi,
...
}:
if kernel.families ? darwin then "${cpu.name}-darwin" else "${cpu.name}-${kernelName kernel}";
tripleFromSystem =
{
cpu,
vendor,
kernel,
abi,
...
}@sys:
assert isSystem sys;
let
optExecFormat = optionalString (
kernel.name == "netbsd" && gnuNetBSDDefaultExecFormat cpu != kernel.execFormat
) kernel.execFormat.name;
optAbi = optionalString (abi != abis.unknown) "-${abi.name}";
cpuName = if kernel.families ? darwin then darwinArch cpu else cpu.name;
in
"${cpuName}-${vendor.name}-${kernelName kernel}${optExecFormat}${optAbi}";
################################################################################
}

638
lib/systems/platforms.nix Normal file
View File

@@ -0,0 +1,638 @@
# Note: lib/systems/default.nix takes care of producing valid,
# fully-formed "platform" values (e.g. hostPlatform, buildPlatform,
# targetPlatform, etc) containing at least the minimal set of attrs
# required (see types.parsedPlatform in lib/systems/parse.nix). This
# file takes an already-valid platform and further elaborates it with
# optional fields; currently these are: linux-kernel, gcc, and rustc.
{ lib }:
rec {
pc = {
linux-kernel = {
name = "pc";
baseConfig = "defconfig";
# Build whatever possible as a module, if not stated in the extra config.
autoModules = true;
target = "bzImage";
};
};
pc_simplekernel = lib.recursiveUpdate pc {
linux-kernel.autoModules = false;
};
powernv = {
linux-kernel = {
name = "PowerNV";
baseConfig = "powernv_defconfig";
target = "vmlinux";
autoModules = true;
# avoid driver/FS trouble arising from unusual page size
extraConfig = ''
PPC_64K_PAGES n
PPC_4K_PAGES y
IPV6 y
ATA_BMDMA y
ATA_SFF y
VIRTIO_MENU y
'';
};
};
##
## ARM
##
pogoplug4 = {
linux-kernel = {
name = "pogoplug4";
baseConfig = "multi_v5_defconfig";
autoModules = false;
extraConfig = ''
# Ubi for the mtd
MTD_UBI y
UBIFS_FS y
UBIFS_FS_XATTR y
UBIFS_FS_ADVANCED_COMPR y
UBIFS_FS_LZO y
UBIFS_FS_ZLIB y
UBIFS_FS_DEBUG n
'';
makeFlags = [ "LOADADDR=0x8000" ];
target = "uImage";
# TODO reenable once manual-config's config actually builds a .dtb and this is checked to be working
#DTB = true;
};
gcc = {
arch = "armv5te";
};
};
sheevaplug = {
linux-kernel = {
name = "sheevaplug";
baseConfig = "multi_v5_defconfig";
autoModules = false;
extraConfig = ''
BLK_DEV_RAM y
BLK_DEV_INITRD y
BLK_DEV_CRYPTOLOOP m
BLK_DEV_DM m
DM_CRYPT m
MD y
REISERFS_FS m
BTRFS_FS m
XFS_FS m
JFS_FS m
EXT4_FS m
USB_STORAGE_CYPRESS_ATACB m
# mv cesa requires this sw fallback, for mv-sha1
CRYPTO_SHA1 y
# Fast crypto
CRYPTO_TWOFISH y
CRYPTO_TWOFISH_COMMON y
CRYPTO_BLOWFISH y
CRYPTO_BLOWFISH_COMMON y
IP_PNP y
IP_PNP_DHCP y
NFS_FS y
ROOT_NFS y
TUN m
NFS_V4 y
NFS_V4_1 y
NFS_FSCACHE y
NFSD m
NFSD_V2_ACL y
NFSD_V3 y
NFSD_V3_ACL y
NFSD_V4 y
NETFILTER y
IP_NF_IPTABLES y
IP_NF_FILTER y
IP_NF_MATCH_ADDRTYPE y
IP_NF_TARGET_LOG y
IP_NF_MANGLE y
IPV6 m
VLAN_8021Q m
CIFS y
CIFS_XATTR y
CIFS_POSIX y
CIFS_FSCACHE y
CIFS_ACL y
WATCHDOG y
WATCHDOG_CORE y
ORION_WATCHDOG m
ZRAM m
NETCONSOLE m
# Disable OABI to have seccomp_filter (required for systemd)
# https://github.com/raspberrypi/firmware/issues/651
OABI_COMPAT n
# Fail to build
DRM n
SCSI_ADVANSYS n
USB_ISP1362_HCD n
SND_SOC n
SND_ALI5451 n
FB_SAVAGE n
SCSI_NSP32 n
ATA_SFF n
SUNGEM n
IRDA n
ATM_HE n
SCSI_ACARD n
BLK_DEV_CMD640_ENHANCED n
FUSE_FS m
# systemd uses cgroups
CGROUPS y
# Latencytop
LATENCYTOP y
# Ubi for the mtd
MTD_UBI y
UBIFS_FS y
UBIFS_FS_XATTR y
UBIFS_FS_ADVANCED_COMPR y
UBIFS_FS_LZO y
UBIFS_FS_ZLIB y
UBIFS_FS_DEBUG n
# Kdb, for kernel troubles
KGDB y
KGDB_SERIAL_CONSOLE y
KGDB_KDB y
'';
makeFlags = [ "LOADADDR=0x0200000" ];
target = "uImage";
DTB = true; # Beyond 3.10
};
gcc = {
arch = "armv5te";
};
};
raspberrypi = {
linux-kernel = {
name = "raspberrypi";
baseConfig = "bcm2835_defconfig";
DTB = true;
autoModules = true;
preferBuiltin = true;
extraConfig = ''
# Disable OABI to have seccomp_filter (required for systemd)
# https://github.com/raspberrypi/firmware/issues/651
OABI_COMPAT n
'';
target = "zImage";
};
gcc = {
# https://en.wikipedia.org/wiki/Raspberry_Pi#Specifications
arch = "armv6kz";
fpu = "vfpv2";
};
};
# Legacy attribute, for compatibility with existing configs only.
raspberrypi2 = armv7l-hf-multiplatform;
# Nvidia Bluefield 2 (w. crypto support)
bluefield2 = {
gcc = {
arch = "armv8-a+fp+simd+crc+crypto";
};
};
zero-gravitas = {
linux-kernel = {
name = "zero-gravitas";
baseConfig = "zero-gravitas_defconfig";
# Target verified by checking /boot on reMarkable 1 device
target = "zImage";
autoModules = false;
DTB = true;
};
gcc = {
fpu = "neon";
cpu = "cortex-a9";
};
};
zero-sugar = {
linux-kernel = {
name = "zero-sugar";
baseConfig = "zero-sugar_defconfig";
DTB = true;
autoModules = false;
preferBuiltin = true;
target = "zImage";
};
gcc = {
cpu = "cortex-a7";
fpu = "neon-vfpv4";
float-abi = "hard";
};
};
utilite = {
linux-kernel = {
name = "utilite";
maseConfig = "multi_v7_defconfig";
autoModules = false;
extraConfig = ''
# Ubi for the mtd
MTD_UBI y
UBIFS_FS y
UBIFS_FS_XATTR y
UBIFS_FS_ADVANCED_COMPR y
UBIFS_FS_LZO y
UBIFS_FS_ZLIB y
UBIFS_FS_DEBUG n
'';
makeFlags = [ "LOADADDR=0x10800000" ];
target = "uImage";
DTB = true;
};
gcc = {
cpu = "cortex-a9";
fpu = "neon";
};
};
guruplug = lib.recursiveUpdate sheevaplug {
# Define `CONFIG_MACH_GURUPLUG' (see
# <http://kerneltrap.org/mailarchive/git-commits-head/2010/5/19/33618>)
# and other GuruPlug-specific things. Requires the `guruplug-defconfig'
# patch.
linux-kernel.baseConfig = "guruplug_defconfig";
};
beaglebone = lib.recursiveUpdate armv7l-hf-multiplatform {
linux-kernel = {
name = "beaglebone";
baseConfig = "bb.org_defconfig";
autoModules = false;
extraConfig = ""; # TBD kernel config
target = "zImage";
};
};
# https://developer.android.com/ndk/guides/abis#v7a
armv7a-android = {
linux-kernel.name = "armeabi-v7a";
gcc = {
arch = "armv7-a";
float-abi = "softfp";
fpu = "vfpv3-d16";
};
};
armv7l-hf-multiplatform = {
linux-kernel = {
name = "armv7l-hf-multiplatform";
Major = "2.6"; # Using "2.6" enables 2.6 kernel syscalls in glibc.
baseConfig = "multi_v7_defconfig";
DTB = true;
autoModules = true;
preferBuiltin = true;
target = "zImage";
extraConfig = ''
# Serial port for Raspberry Pi 3. Wasn't included in ARMv7 defconfig
# until 4.17.
SERIAL_8250_BCM2835AUX y
SERIAL_8250_EXTENDED y
SERIAL_8250_SHARE_IRQ y
# Hangs ODROID-XU4
ARM_BIG_LITTLE_CPUIDLE n
# Disable OABI to have seccomp_filter (required for systemd)
# https://github.com/raspberrypi/firmware/issues/651
OABI_COMPAT n
# >=5.12 fails with:
# drivers/net/ethernet/micrel/ks8851_common.o: in function `ks8851_probe_common':
# ks8851_common.c:(.text+0x179c): undefined reference to `__this_module'
# See: https://lore.kernel.org/netdev/20210116164828.40545-1-marex@denx.de/T/
KS8851_MLL y
'';
};
gcc = {
# Some table about fpu flags:
# http://community.arm.com/servlet/JiveServlet/showImage/38-1981-3827/blogentry-103749-004812900+1365712953_thumb.png
# Cortex-A5: -mfpu=neon-fp16
# Cortex-A7 (rpi2): -mfpu=neon-vfpv4
# Cortex-A8 (beaglebone): -mfpu=neon
# Cortex-A9: -mfpu=neon-fp16
# Cortex-A15: -mfpu=neon-vfpv4
# More about FPU:
# https://wiki.debian.org/ArmHardFloatPort/VfpComparison
# vfpv3-d16 is what Debian uses and seems to be the best compromise: NEON is not supported in e.g. Scaleway or Tegra 2,
# and the above page suggests NEON is only an improvement with hand-written assembly.
arch = "armv7-a";
fpu = "vfpv3-d16";
# For Raspberry Pi the 2 the best would be:
# cpu = "cortex-a7";
# fpu = "neon-vfpv4";
};
};
aarch64-multiplatform = {
linux-kernel = {
name = "aarch64-multiplatform";
baseConfig = "defconfig";
DTB = true;
autoModules = true;
preferBuiltin = true;
extraConfig = ''
# Raspberry Pi 3 stuff. Not needed for s >= 4.10.
ARCH_BCM2835 y
BCM2835_MBOX y
BCM2835_WDT y
RASPBERRYPI_FIRMWARE y
RASPBERRYPI_POWER y
SERIAL_8250_BCM2835AUX y
SERIAL_8250_EXTENDED y
SERIAL_8250_SHARE_IRQ y
# Cavium ThunderX stuff.
PCI_HOST_THUNDER_ECAM y
# Nvidia Tegra stuff.
PCI_TEGRA y
# The default (=y) forces us to have the XHCI firmware available in initrd,
# which our initrd builder can't currently do easily.
USB_XHCI_TEGRA m
'';
target = "Image";
};
gcc = {
arch = "armv8-a";
};
};
apple-m1 = {
gcc = {
arch = "armv8.3-a+crypto+sha2+aes+crc+fp16+lse+simd+ras+rdm+rcpc";
cpu = "apple-a13";
};
};
##
## MIPS
##
ben_nanonote = {
linux-kernel = {
name = "ben_nanonote";
};
gcc = {
arch = "mips32";
float = "soft";
};
};
fuloong2f_n32 = {
linux-kernel = {
name = "fuloong2f_n32";
baseConfig = "lemote2f_defconfig";
autoModules = false;
extraConfig = ''
MIGRATION n
COMPACTION n
# nixos mounts some cgroup
CGROUPS y
BLK_DEV_RAM y
BLK_DEV_INITRD y
BLK_DEV_CRYPTOLOOP m
BLK_DEV_DM m
DM_CRYPT m
MD y
REISERFS_FS m
EXT4_FS m
USB_STORAGE_CYPRESS_ATACB m
IP_PNP y
IP_PNP_DHCP y
IP_PNP_BOOTP y
NFS_FS y
ROOT_NFS y
TUN m
NFS_V4 y
NFS_V4_1 y
NFS_FSCACHE y
NFSD m
NFSD_V2_ACL y
NFSD_V3 y
NFSD_V3_ACL y
NFSD_V4 y
# Fail to build
DRM n
SCSI_ADVANSYS n
USB_ISP1362_HCD n
SND_SOC n
SND_ALI5451 n
FB_SAVAGE n
SCSI_NSP32 n
ATA_SFF n
SUNGEM n
IRDA n
ATM_HE n
SCSI_ACARD n
BLK_DEV_CMD640_ENHANCED n
FUSE_FS m
# Needed for udev >= 150
SYSFS_DEPRECATED_V2 n
VGA_CONSOLE n
VT_HW_CONSOLE_BINDING y
SERIAL_8250_CONSOLE y
FRAMEBUFFER_CONSOLE y
EXT2_FS y
EXT3_FS y
REISERFS_FS y
MAGIC_SYSRQ y
# The kernel doesn't boot at all, with FTRACE
FTRACE n
'';
target = "vmlinux";
};
gcc = {
arch = "loongson2f";
float = "hard";
abi = "n32";
};
};
# can execute on 32bit chip
gcc_mips32r2_o32 = {
gcc = {
arch = "mips32r2";
abi = "32";
};
};
gcc_mips32r6_o32 = {
gcc = {
arch = "mips32r6";
abi = "32";
};
};
gcc_mips64r2_n32 = {
gcc = {
arch = "mips64r2";
abi = "n32";
};
};
gcc_mips64r6_n32 = {
gcc = {
arch = "mips64r6";
abi = "n32";
};
};
gcc_mips64r2_64 = {
gcc = {
arch = "mips64r2";
abi = "64";
};
};
gcc_mips64r6_64 = {
gcc = {
arch = "mips64r6";
abi = "64";
};
};
# based on:
# https://www.mail-archive.com/qemu-discuss@nongnu.org/msg05179.html
# https://gmplib.org/~tege/qemu.html#mips64-debian
mips64el-qemu-linux-gnuabi64 = {
linux-kernel = {
name = "mips64el";
baseConfig = "64r2el_defconfig";
target = "vmlinuz";
autoModules = false;
DTB = true;
# for qemu 9p passthrough filesystem
extraConfig = ''
MIPS_MALTA y
PAGE_SIZE_4KB y
CPU_LITTLE_ENDIAN y
CPU_MIPS64_R2 y
64BIT y
CPU_MIPS64_R2 y
NET_9P y
NET_9P_VIRTIO y
9P_FS y
9P_FS_POSIX_ACL y
PCI y
VIRTIO_PCI y
'';
};
};
##
## Other
##
riscv-multiplatform = {
linux-kernel = {
name = "riscv-multiplatform";
target = "Image";
autoModules = true;
preferBuiltin = true;
baseConfig = "defconfig";
DTB = true;
};
};
loongarch64-multiplatform = {
gcc = {
# https://github.com/loongson/la-softdev-convention/blob/master/la-softdev-convention.adoc#10-operating-system-package-build-requirements
arch = "la64v1.0";
strict-align = false;
# Avoid text sections of large apps exceeding default code model
# Will be default behavior in LLVM 21 and hopefully GCC16
# https://github.com/loongson-community/discussions/issues/43
# https://github.com/llvm/llvm-project/pull/132173
cmodel = "medium";
};
linux-kernel = {
name = "loongarch-multiplatform";
target = "vmlinuz.efi";
autoModules = true;
preferBuiltin = true;
baseConfig = "defconfig";
DTB = true;
};
};
# This function takes a minimally-valid "platform" and returns an
# attrset containing zero or more additional attrs which should be
# included in the platform in order to further elaborate it.
select =
platform:
# x86
if platform.isx86 then
pc
# ARM
else if platform.isAarch32 then
let
version = platform.parsed.cpu.version or null;
in
if version == null then
pc
else if lib.versionOlder version "6" then
sheevaplug
else if lib.versionOlder version "7" then
raspberrypi
else
armv7l-hf-multiplatform
else if platform.isAarch64 then
if platform.isDarwin then apple-m1 else aarch64-multiplatform
else if platform.isLoongArch64 then
loongarch64-multiplatform
else if platform.isRiscV then
riscv-multiplatform
else if platform.parsed.cpu == lib.systems.parse.cpuTypes.mipsel then
(import ./examples.nix { inherit lib; }).mipsel-linux-gnu
else if platform.parsed.cpu == lib.systems.parse.cpuTypes.powerpc64le then
powernv
else if platform.isLoongArch64 then
loongarch64-multiplatform
else
{ };
}