diff options
Diffstat (limited to 'systems')
-rw-r--r-- | systems/architectures.nix | 114 | ||||
-rw-r--r-- | systems/default.nix | 228 | ||||
-rw-r--r-- | systems/doubles.nix | 119 | ||||
-rw-r--r-- | systems/examples.nix | 335 | ||||
-rw-r--r-- | systems/flake-systems.nix | 29 | ||||
-rw-r--r-- | systems/inspect.nix | 117 | ||||
-rw-r--r-- | systems/parse.nix | 503 | ||||
-rw-r--r-- | systems/platforms.nix | 565 |
8 files changed, 2010 insertions, 0 deletions
diff --git a/systems/architectures.nix b/systems/architectures.nix new file mode 100644 index 000000000..57b9184ca --- /dev/null +++ b/systems/architectures.nix @@ -0,0 +1,114 @@ +{ lib }: + +rec { + # gcc.arch to its features (as in /proc/cpuinfo) + features = { + default = [ ]; + # x86_64 Intel + westmere = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" ]; + sandybridge = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" ]; + ivybridge = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" ]; + haswell = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "fma" ]; + broadwell = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "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" ]; + # 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" ]; + # 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 = { + # x86_64 Intel + # https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html + default = [ ]; + westmere = [ ]; + sandybridge = [ "westmere" ] ++ inferiors.westmere; + ivybridge = [ "sandybridge" ] ++ inferiors.sandybridge; + haswell = [ "ivybridge" ] ++ inferiors.ivybridge; + broadwell = [ "haswell" ] ++ inferiors.haswell; + skylake = [ "broadwell" ] ++ inferiors.broadwell; + skylake-avx512 = [ "skylake" ] ++ inferiors.skylake; + cannonlake = [ "skylake-avx512" ] ++ inferiors.skylake-avx512; + icelake-client = [ "cannonlake" ] ++ inferiors.cannonlake; + icelake-server = [ "icelake-client" ] ++ inferiors.icelake-client; + cascadelake = [ "skylake-avx512" ] ++ inferiors.cannonlake; + cooperlake = [ "cascadelake" ] ++ inferiors.cascadelake; + tigerlake = [ "icelake-server" ] ++ inferiors.icelake-server; + + # x86_64 AMD + # TODO: fill this (need testing) + btver1 = [ ]; + btver2 = [ ]; + bdver1 = [ ]; + bdver2 = [ ]; + bdver3 = [ ]; + bdver4 = [ ]; + # 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; + znver2 = [ "znver1" ] ++ inferiors.znver1; + znver3 = [ "znver2" ] ++ inferiors.znver2; + + # other + armv5te = [ ]; + armv6 = [ ]; + armv7-a = [ ]; + armv8-a = [ ]; + mips32 = [ ]; + loongson2f = [ ]; + }; + + 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"; + }; +} diff --git a/systems/default.nix b/systems/default.nix new file mode 100644 index 000000000..f4784c61c --- /dev/null +++ b/systems/default.nix @@ -0,0 +1,228 @@ +{ lib }: + let inherit (lib.attrsets) mapAttrs; in + +rec { + 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; }; + + /* 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 { }; + + # 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 = args': let + args = if lib.isString args' then { system = args'; } + else args'; + final = { + # Prefer to parse `config` as it is strictly more informative. + parsed = parse.mkSystemFromString (if args ? config then args.config else args.system); + # Either of these can be losslessly-extracted from `parsed` iff parsing succeeds. + system = parse.doubleFromSystem final.parsed; + 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; + isCompatible = _: throw "2022-05-23: isCompatible has been removed in favor of canExecute, refer to the 22.11 changelog for details"; + # Derived meta-data + libc = + /**/ if final.isDarwin then "libSystem" + else if final.isMinGW then "msvcrt" + else if final.isWasi then "wasilibc" + 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.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"; + extensions = rec { + sharedLibrary = + /**/ if final.isDarwin then ".dylib" + else if final.isWindows then ".dll" + else ".so"; + staticLibrary = + /**/ if final.isWindows then ".lib" + else ".a"; + library = + /**/ if final.isStatic then staticLibrary + else sharedLibrary; + executable = + /**/ if final.isWindows then ".exe" + else ""; + }; + # Misc boolean options + useAndroidPrebuilt = false; + useiOSPrebuilt = false; + + # Output from uname + uname = { + # uname -s + system = { + linux = "Linux"; + windows = "Windows"; + 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${lib.optionalString final.isLittleEndian "le"}" + else if final.isPower + then "ppc${lib.optionalString final.isLittleEndian "le"}" + else if final.isMips64 + then "mips64" # endianness is *not* included on mips64 + else final.parsed.cpu.name; + + # uname -r + release = null; + }; + isStatic = final.isWasm || final.isRedox; + + # Just a guess, based on `system` + inherit + ({ + linux-kernel = args.linux-kernel or {}; + gcc = args.gcc or {}; + rustc = args.rustc or {}; + } // platforms.select final) + linux-kernel gcc rustc; + + 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; + + qemuArch = + if final.isAarch32 then "arm" + 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.isMips64 then "mips64${lib.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 = { + armv7a = "armv7"; + aarch64 = "arm64"; + }.${final.parsed.cpu.name} or final.parsed.cpu.name; + + 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 (if final.isAarch64 then "11.0" else "10.12"); + darwinMinVersion = final.darwinSdkVersion; + darwinMinVersionVariable = + if final.isMacOS then "MACOSX_DEPLOYMENT_TARGET" + else if final.isiOS then "IPHONEOS_DEPLOYMENT_TARGET" + else null; + } // ( + let + selectEmulator = pkgs: + let + qemu-user = pkgs.qemu.override { + smartcardSupport = false; + spiceSupport = false; + openGLSupport = false; + virglSupport = false; + vncSupport = false; + gtkSupport = false; + sdlSupport = false; + pulseSupport = false; + smbdSupport = false; + seccompSupport = false; + enableDocs = false; + hostCpuTargets = [ "${final.qemuArch}-linux-user" ]; + }; + wine = (pkgs.winePackagesFor "wine${toString final.parsed.cpu.bits}").minimal; + in + if final.parsed.kernel.name == pkgs.stdenv.hostPlatform.parsed.kernel.name && + pkgs.stdenv.hostPlatform.canExecute final + then "${pkgs.runtimeShell} -c '\"$@\"' --" + else if final.isWindows + then "${wine}/bin/wine${lib.optionalString (final.parsed.cpu.bits == 64) "64"}" + else if final.isLinux && pkgs.stdenv.hostPlatform.isLinux && final.qemuArch != null + then "${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; + + 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; + in assert final.useAndroidPrebuilt -> final.isAndroid; + assert lib.foldl + (pass: { assertion, message }: + if assertion final + then pass + else throw message) + true + (final.parsed.abi.assertions or []); + final; +} diff --git a/systems/doubles.nix b/systems/doubles.nix new file mode 100644 index 000000000..66bf3d872 --- /dev/null +++ b/systems/doubles.nix @@ -0,0 +1,119 @@ +{ 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" "i686-darwin" "aarch64-darwin" "armv7a-darwin" + + # FreeBSD + "i686-freebsd13" "x86_64-freebsd13" + + # 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" "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" "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 + "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; + 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; + + mesaPlatforms = ["i686-linux" "x86_64-linux" "x86_64-darwin" "armv5tel-linux" "armv6l-linux" "armv7l-linux" "armv7a-linux" "aarch64-linux" "powerpc64-linux" "powerpc64le-linux" "aarch64-darwin" "riscv64-linux"]; +} diff --git a/systems/examples.nix b/systems/examples.nix new file mode 100644 index 000000000..45b95c150 --- /dev/null +++ b/systems/examples.nix @@ -0,0 +1,335 @@ +# 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 = { + config = "powerpc64-unknown-linux-gnuabielfv2"; + }; + ppc64-musl = { + config = "powerpc64-unknown-linux-musl"; + gcc = { abi = "elfv2"; }; + }; + + sheevaplug = { + config = "armv5tel-unknown-linux-gnueabi"; + } // platforms.sheevaplug; + + raspberryPi = { + config = "armv6l-unknown-linux-gnueabihf"; + } // platforms.raspberrypi; + + 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"; + rustc.config = "armv7-linux-androideabi"; + sdkVer = "28"; + ndkVer = "24"; + useAndroidPrebuilt = true; + } // platforms.armv7a-android; + + aarch64-android-prebuilt = { + config = "aarch64-unknown-linux-android"; + rustc.config = "aarch64-linux-android"; + sdkVer = "28"; + ndkVer = "24"; + useAndroidPrebuilt = true; + }; + + aarch64-android = { + config = "aarch64-unknown-linux-android"; + sdkVer = "30"; + ndkVer = "24"; + 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"; }; + 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-embedded = { + config = "riscv64-none-elf"; + libc = "newlib"; + }; + + riscv32-embedded = { + config = "riscv32-none-elf"; + libc = "newlib"; + }; + + loongarch64-linux = { + config = "loongarch64-unknown-linux-gnu"; + }; + + 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"; + }; + 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"; + }; + + 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"; + }; + + # + # Redox + # + + x86_64-unknown-redox = { + config = "x86_64-unknown-redox"; + libc = "relibc"; + }; + + # + # Darwin + # + + iphone64 = { + config = "aarch64-apple-ios"; + # config = "aarch64-apple-darwin14"; + sdkVer = "14.3"; + xcodeVer = "12.3"; + xcodePlatform = "iPhoneOS"; + useiOSPrebuilt = true; + }; + + iphone32 = { + config = "armv7a-apple-ios"; + # config = "arm-apple-darwin10"; + sdkVer = "14.3"; + xcodeVer = "12.3"; + xcodePlatform = "iPhoneOS"; + useiOSPrebuilt = true; + }; + + iphone64-simulator = { + config = "x86_64-apple-ios"; + # config = "x86_64-apple-darwin14"; + sdkVer = "14.3"; + xcodeVer = "12.3"; + xcodePlatform = "iPhoneSimulator"; + darwinPlatform = "ios-simulator"; + useiOSPrebuilt = true; + }; + + iphone32-simulator = { + config = "i686-apple-ios"; + # config = "i386-apple-darwin11"; + sdkVer = "14.3"; + xcodeVer = "12.3"; + xcodePlatform = "iPhoneSimulator"; + darwinPlatform = "ios-simulator"; + useiOSPrebuilt = true; + }; + + aarch64-darwin = { + config = "aarch64-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 + }; + + # BSDs + + x86_64-freebsd = { + config = "x86_64-unknown-freebsd13"; + 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; + }; + + # + # WASM + # + + wasi32 = { + config = "wasm32-unknown-wasi"; + 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"; + }; +} diff --git a/systems/flake-systems.nix b/systems/flake-systems.nix new file mode 100644 index 000000000..b1988c6a4 --- /dev/null +++ b/systems/flake-systems.nix @@ -0,0 +1,29 @@ +# 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" + + # Other platforms with sufficient support in stdenv which is not formally + # mandated by their platform tier. + "aarch64-darwin" + "armv5tel-linux" + "powerpc64le-linux" + "riscv64-linux" + + # "x86_64-freebsd" is excluded because it is mostly broken +] diff --git a/systems/inspect.nix b/systems/inspect.nix new file mode 100644 index 000000000..89e9f4231 --- /dev/null +++ b/systems/inspect.nix @@ -0,0 +1,117 @@ +{ lib }: +with import ./parse.nix { inherit lib; }; +with lib.attrsets; +with lib.lists; + +let abis_ = abis; in +let abis = lib.mapAttrs (_: abi: builtins.removeAttrs abi [ "assertions" ]) 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; }; }; + # 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; }; }) + (lib.filter (cpu: lib.hasPrefix "armv7" cpu.arch or "") + (lib.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"; }; }; + 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 = 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.windows; abi = abis.cygnus; }; + isMinGW = { kernel = kernels.windows; abi = abis.gnu; }; + 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 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"; }; } + ]; + }; + + matchAnyAttrs = patterns: + if builtins.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; }; + }; +} diff --git a/systems/parse.nix b/systems/parse.nix new file mode 100644 index 000000000..6eb4f27cc --- /dev/null +++ b/systems/parse.nix @@ -0,0 +1,503 @@ +# 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 }: +with lib.lists; +with lib.types; +with lib.attrsets; +with lib.strings; +with (import ./inspect.nix { inherit lib; }).predicates; + +let + inherit (lib.options) mergeOneOption; + + setTypes = type: + mapAttrs (name: value: + assert type.check value; + setType type.name ({ inherit name; } // value)); + +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 = with significantBytes; 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"; }; + }; + + # 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 = a: b: with cpuTypes; lib.any lib.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 = {}; + # 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 = with execFormats; with kernelFamilies; 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; }; }; + # A tricky thing about FreeBSD is that there is no stable ABI across + # versions. That means that putting in the version as part of the + # config string is paramount. + freebsd12 = { execFormat = elf; families = { inherit bsd; }; name = "freebsd"; version = 12; }; + freebsd13 = { execFormat = elf; families = { inherit bsd; }; name = "freebsd"; version = 13; }; + 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 = { }; }; + 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 { + cygnus = {}; + 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: with platform; !(isPower64 && 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 "Target specification with 1 components is ambiguous"; + "2" = # We only do 2-part hacks for things Nix already supports + if elemAt l 1 == "cygwin" + then { cpu = elemAt l 0; kernel = "windows"; abi = "cygnus"; } + # 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) [ "wasi" "redox" "mmixware" "ghcjs" "mingw32" ] || + hasPrefix "freebsd" (elemAt l 2) || + hasPrefix "netbsd" (elemAt l 2) || + hasPrefix "genode" (elemAt l 2) + 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; + } + else throw "Target specification 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 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 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 args.kernel; + abi = + /**/ if args ? abi then getAbi args.abi + else if isLinux parsed || isWindows parsed then + if isAarch32 parsed then + if lib.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 (lib.splitString "-" s)); + + kernelName = kernel: + kernel.name + toString (kernel.version or ""); + + doubleFromSystem = { cpu, kernel, abi, ... }: + /**/ if abi == abis.cygnus then "${cpu.name}-cygwin" + else if kernel.families ? darwin then "${cpu.name}-darwin" + else "${cpu.name}-${kernelName kernel}"; + + tripleFromSystem = { cpu, vendor, kernel, abi, ... } @ sys: assert isSystem sys; let + optExecFormat = + lib.optionalString (kernel.name == "netbsd" && + gnuNetBSDDefaultExecFormat cpu != kernel.execFormat) + kernel.execFormat.name; + optAbi = lib.optionalString (abi != abis.unknown) "-${abi.name}"; + in "${cpu.name}-${vendor.name}-${kernelName kernel}${optExecFormat}${optAbi}"; + + ################################################################################ + +} diff --git a/systems/platforms.nix b/systems/platforms.nix new file mode 100644 index 000000000..d574943e4 --- /dev/null +++ b/systems/platforms.nix @@ -0,0 +1,565 @@ +# 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 = { + arch = "armv6"; + fpu = "vfp"; + }; + }; + + # Legacy attribute, for compatibility with existing configs only. + raspberrypi2 = armv7l-hf-multiplatform; + + 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; + baseConfig = "defconfig"; + DTB = true; + extraConfig = '' + SERIAL_OF_PLATFORM y + ''; + }; + }; + + # 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.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 { }; +} |