{ lib }: let inherit (lib) all any attrNames attrValues concatMap filter flip hasPrefix isAttrs isList mapAttrs recursiveUpdateUntil toList ; inherit (lib.strings) toJSON; inherit (lib.systems.parse) kernels kernelFamilies significantBytes cpuTypes execFormats ; hasArmv7Prefix = hasPrefix "armv7"; # Based on lib.attrsets.matchAttrs, but with: # - the initial isAttrs assertion removed, since this function is only ever # called with attrsets # - isAttrs only performed on one side when recursing, since our input data # will always share a structure matchAttrsUnchecked = pattern: attrs: all ( # Compare equality between `pattern` & `attrs`. attr: # Missing attr, not equal. attrs ? ${attr} && ( let lhs = pattern.${attr}; rhs = attrs.${attr}; in # Simple equality check is primarily for non-attrsets, but we run it # on attrsets too, since it may let us avoid recursing lhs == rhs || isAttrs lhs && matchAttrsUnchecked lhs rhs ) ) (attrNames pattern); removeAssertions = flip removeAttrs [ "assertions" ]; abis = mapAttrs ( _: abi: if abi ? assertions then removeAssertions abi else abi ) lib.systems.parse.abis; in rec { # these patterns are to be matched against {host,build,target}Platform.parsed # # Note: All toplevel attributes within a pattern are expected to be attrsets. # matchAttrsUnchecked should be changed if a pattern is ever added that # doesn't follow this axiom 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 # `matchAttrsUnchecked`, 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: cpu ? arch && hasArmv7Prefix cpu.arch) (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"; }; }; isArc = { cpu = { family = "arc"; }; }; isSh4 = { cpu = { family = "sh"; }; }; 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.wasip1; } ]; isWasiP1 = { kernel = kernels.wasip1; }; 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 ]; isPicolibc = { abi = abis.picolibc; }; 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"; }; } ]; isUefi = [ { kernel = kernels.uefi; } ]; isElf = { kernel.execFormat = execFormats.elf; }; isMacho = { kernel.execFormat = execFormats.macho; }; isPE = { kernel.execFormat = execFormats.pe; }; isEabi = { abi.eabi = true; }; }; # 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; matchAnyPattern = let # same as matchAttrsUnchecked definition at the top of the file, but: # - pattern names are cached and reused for multiple attrset calls # - avoid running isAttrs since all patterns are nested attrsets matchPattern = pattern: let names = attrNames pattern; in attrs: all ( attr: attrs ? ${attr} && ( let lhs = pattern.${attr}; rhs = attrs.${attr}; in lhs == rhs || matchAttrsUnchecked lhs rhs ) ) names; in pattern: if isList pattern then let cachedPatterns = map matchPattern pattern; in attrs: any (pattern: pattern attrs) cachedPatterns else matchPattern pattern; predicates = mapAttrs (_: matchAnyPattern) 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; }; }; }