From 0a19371146130c0e2a402fd0c35f8283b0e81910 Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Sun, 26 Jan 2025 13:23:37 +0100 Subject: [PATCH 1/3] nixos/nixpkgs: make config.nixpkgs.{localSystem,crossSystem,buildPlatform,hostPlatform} write only The description for options.nixpkgs.system already hints at this: Neither ${opt.system} nor any other option in nixpkgs.* is meant to be read by modules and configurations. Use pkgs.stdenv.hostPlatform instead. We can support this goal by not elaborating the systems anymore, forcing users to go via pkgs.stdenv. This will prevent problems when making the top-level package sets composable in the next commit. For this to work, you should pass a fully elaborated system to nixpkgs' localSystem or crossSystem options. --- nixos/modules/misc/nixpkgs.nix | 40 +++++++++---------- nixos/modules/misc/nixpkgs/read-only.nix | 25 +++--------- .../virtualisation/nixos-containers.nix | 8 +++- .../appliance-repart-image-verity-store.nix | 4 +- nixos/tests/appliance-repart-image.nix | 4 +- pkgs/top-level/all-packages.nix | 2 +- 6 files changed, 37 insertions(+), 46 deletions(-) diff --git a/nixos/modules/misc/nixpkgs.nix b/nixos/modules/misc/nixpkgs.nix index e1c705eef3b5..caf57d7d6371 100644 --- a/nixos/modules/misc/nixpkgs.nix +++ b/nixos/modules/misc/nixpkgs.nix @@ -73,7 +73,10 @@ let defaultPkgs = if opt.hostPlatform.isDefined then let - isCross = cfg.buildPlatform != cfg.hostPlatform; + isCross = + !(lib.systems.equals (lib.systems.elaborate cfg.buildPlatform) ( + lib.systems.elaborate cfg.hostPlatform + )); systemArgs = if isCross then { @@ -195,13 +198,10 @@ in }; hostPlatform = lib.mkOption { - type = lib.types.either lib.types.str lib.types.attrs; # TODO utilize lib.systems.parsedPlatform + type = lib.types.either lib.types.str lib.types.attrs; example = { system = "aarch64-linux"; }; - # Make sure that the final value has all fields for sake of other modules - # referring to this. TODO make `lib.systems` itself use the module system. - apply = lib.systems.elaborate; defaultText = lib.literalExpression ''(import "''${nixos}/../lib").lib.systems.examples.aarch64-multiplatform''; description = '' Specifies the platform where the NixOS configuration will run. @@ -213,22 +213,13 @@ in }; buildPlatform = lib.mkOption { - type = lib.types.either lib.types.str lib.types.attrs; # TODO utilize lib.systems.parsedPlatform + type = lib.types.either lib.types.str lib.types.attrs; default = cfg.hostPlatform; example = { system = "x86_64-linux"; }; # Make sure that the final value has all fields for sake of other modules # referring to this. - apply = - inputBuildPlatform: - let - elaborated = lib.systems.elaborate inputBuildPlatform; - in - if lib.systems.equals elaborated cfg.hostPlatform then - cfg.hostPlatform # make identical, so that `==` equality works; see https://github.com/NixOS/nixpkgs/issues/278001 - else - elaborated; defaultText = lib.literalExpression ''config.nixpkgs.hostPlatform''; description = '' Specifies the platform on which NixOS should be built. @@ -245,14 +236,11 @@ in }; localSystem = lib.mkOption { - type = lib.types.attrs; # TODO utilize lib.systems.parsedPlatform + type = lib.types.attrs; default = { inherit (cfg) system; }; example = { system = "aarch64-linux"; }; - # Make sure that the final value has all fields for sake of other modules - # referring to this. TODO make `lib.systems` itself use the module system. - apply = lib.systems.elaborate; defaultText = lib.literalExpression ''(import "''${nixos}/../lib").lib.systems.examples.aarch64-multiplatform''; description = '' Systems with a recently generated `hardware-configuration.nix` @@ -280,7 +268,7 @@ in # is a relation between at least 2 systems in the context of a # specific build step, not a single system. crossSystem = lib.mkOption { - type = lib.types.nullOr lib.types.attrs; # TODO utilize lib.systems.parsedPlatform + type = lib.types.nullOr lib.types.attrs; default = null; example = { system = "aarch64-linux"; @@ -416,6 +404,18 @@ in ${lib.concatMapStringsSep "\n" (file: " - ${file}") opt.config.files} ''; } + { + assertion = + (opt.hostPlatform.isDefined -> builtins.isAttrs cfg.buildPlatform -> !(cfg.buildPlatform ? parsed)) + && (opt.hostPlatform.isDefined -> builtins.isAttrs cfg.hostPlatform -> !(cfg.hostPlatform ? parsed)) + && (builtins.isAttrs cfg.localSystem -> !(cfg.localSystem ? parsed)) + && (builtins.isAttrs cfg.crossSystem -> !(cfg.crossSystem ? parsed)); + message = '' + Passing fully elaborated systems to `nixpkgs.localSystem`, `nixpkgs.crossSystem`, `nixpkgs.buildPlatform` + or `nixpkgs.hostPlatform` will break composability of package sets in nixpkgs. For example, pkgs.pkgsStatic + would not work in modules anymore. + ''; + } ]; }; diff --git a/nixos/modules/misc/nixpkgs/read-only.nix b/nixos/modules/misc/nixpkgs/read-only.nix index fa372d13e545..9d0d452ba52d 100644 --- a/nixos/modules/misc/nixpkgs/read-only.nix +++ b/nixos/modules/misc/nixpkgs/read-only.nix @@ -40,20 +40,11 @@ in The Nixpkgs overlays that `pkgs` was initialized with. ''; }; - hostPlatform = mkOption { - internal = true; - readOnly = true; - description = '' - The platform of the machine that is running the NixOS configuration. - ''; - }; - buildPlatform = mkOption { - internal = true; - readOnly = true; - description = '' - The platform of the machine that built the NixOS configuration. - ''; - }; + # buildPlatform and hostPlatform left out on purpose: + # - They are not supposed to be changed with this read-only module. + # - They are not supposed to be read either, according to the description + # of "system" in the traditional nixpkgs module. + # # NOTE: do not add the legacy options such as localSystem here. Let's keep # this module simple and let module authors upgrade their code instead. }; @@ -61,12 +52,8 @@ in config = { _module.args.pkgs = # find mistaken definitions - builtins.seq cfg.config builtins.seq cfg.overlays builtins.seq cfg.hostPlatform builtins.seq - cfg.buildPlatform - cfg.pkgs; + builtins.seq cfg.config builtins.seq cfg.overlays cfg.pkgs; nixpkgs.config = cfg.pkgs.config; nixpkgs.overlays = cfg.pkgs.overlays; - nixpkgs.hostPlatform = cfg.pkgs.stdenv.hostPlatform; - nixpkgs.buildPlatform = cfg.pkgs.stdenv.buildPlatform; }; } diff --git a/nixos/modules/virtualisation/nixos-containers.nix b/nixos/modules/virtualisation/nixos-containers.nix index 5f43e3322340..036f5129b1b3 100644 --- a/nixos/modules/virtualisation/nixos-containers.nix +++ b/nixos/modules/virtualisation/nixos-containers.nix @@ -506,8 +506,12 @@ in config = { nixpkgs = if options.nixpkgs?hostPlatform - then { inherit (host.pkgs.stdenv) hostPlatform; } - else { localSystem = host.pkgs.stdenv.hostPlatform; } + then { + hostPlatform = + if host.options.nixpkgs.hostPlatform.isDefined + then host.config.nixpkgs.hostPlatform + else lib.defaultTo host.config.nixpkgs.localSystem host.config.nixpkgs.crossSystem; + } else { localSystem = lib.defaultTo host.config.nixpkgs.localSystem host.config.nixpkgs.crossSystem; } ; boot.isContainer = true; networking.hostName = mkDefault name; diff --git a/nixos/tests/appliance-repart-image-verity-store.nix b/nixos/tests/appliance-repart-image-verity-store.nix index ef0fda4f780d..4769dd052e87 100644 --- a/nixos/tests/appliance-repart-image-verity-store.nix +++ b/nixos/tests/appliance-repart-image-verity-store.nix @@ -40,7 +40,7 @@ verityStore = { enable = true; # by default the module works with systemd-boot, for simplicity this test directly boots the UKI - ukiPath = "/EFI/BOOT/BOOT${lib.toUpper config.nixpkgs.hostPlatform.efiArch}.EFI"; + ukiPath = "/EFI/BOOT/BOOT${lib.toUpper pkgs.stdenv.hostPlatform.efiArch}.EFI"; }; name = "appliance-verity-store-image"; @@ -51,7 +51,7 @@ repartConfig = { Type = "esp"; Format = "vfat"; - SizeMinBytes = if config.nixpkgs.hostPlatform.isx86_64 then "64M" else "96M"; + SizeMinBytes = if pkgs.stdenv.hostPlatform.isx86_64 then "64M" else "96M"; }; }; ${partitionIds.store-verity}.repartConfig = { diff --git a/nixos/tests/appliance-repart-image.nix b/nixos/tests/appliance-repart-image.nix index 9798ab4e79fa..1ade220a4610 100644 --- a/nixos/tests/appliance-repart-image.nix +++ b/nixos/tests/appliance-repart-image.nix @@ -53,7 +53,7 @@ in "esp" = { contents = let - efiArch = config.nixpkgs.hostPlatform.efiArch; + efiArch = pkgs.stdenv.hostPlatform.efiArch; in { "/EFI/BOOT/BOOT${lib.toUpper efiArch}.EFI".source = @@ -70,7 +70,7 @@ in # aarch64 kernel seems to generally be a little bigger than the # x86_64 kernel. To stay on the safe side, leave some more slack # for every platform other than x86_64. - SizeMinBytes = if config.nixpkgs.hostPlatform.isx86_64 then "64M" else "96M"; + SizeMinBytes = if pkgs.stdenv.hostPlatform.isx86_64 then "64M" else "96M"; }; }; "root" = { diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index c16d3533f94e..7d543a7175c9 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -17732,7 +17732,7 @@ with pkgs; [( { lib, ... }: { config.nixpkgs.pkgs = lib.mkDefault pkgs; - config.nixpkgs.localSystem = lib.mkDefault stdenv.hostPlatform; + config.nixpkgs.localSystem = lib.mkDefault ({ config = lib.systems.parse.tripleFromSystem stdenv.hostPlatform; }); } )] ++ ( if builtins.isList configuration From d2faa1bbca1b1e4962ce7373c5b0879e5b12cef2 Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Sun, 26 Jan 2025 11:04:53 +0100 Subject: [PATCH 2/3] Reapply "pkgs/top-level: make package sets composable" This reverts commit 7c251e2b5fda2b42c3ff2983fdcc9eac82f2ced6. Left out eec21001b0f7961cb84fe40512e8238ec3effb87, which changed nixos/nixpkgs, doing it differently this time. --- pkgs/test/top-level/stage.nix | 128 +++++++++++++++++++++ pkgs/top-level/default.nix | 18 ++- pkgs/top-level/stage.nix | 206 ++++++++++++++-------------------- 3 files changed, 218 insertions(+), 134 deletions(-) create mode 100644 pkgs/test/top-level/stage.nix diff --git a/pkgs/test/top-level/stage.nix b/pkgs/test/top-level/stage.nix new file mode 100644 index 000000000000..449a36a425ab --- /dev/null +++ b/pkgs/test/top-level/stage.nix @@ -0,0 +1,128 @@ +# run like this: +# nix-build pkgs/test/top-level/stage.nix +{ + localSystem ? { + system = builtins.currentSystem; + }, +}: + +with import ../../top-level { inherit localSystem; }; + +let + # To silence platform specific evaluation errors + discardEvaluationErrors = e: (builtins.tryEval e).success -> e; + + # Basic test for idempotency of the package set, i.e: + # Applying the same package set twice should work and + # not change anything. + isIdempotent = set: discardEvaluationErrors (pkgs.${set}.stdenv == pkgs.${set}.${set}.stdenv); + + # Some package sets should be noops in certain circumstances. + # This is very similar to the idempotency test, but not going + # via the super' overlay. + isNoop = + parent: child: + discardEvaluationErrors ( + (lib.getAttrFromPath parent pkgs).stdenv == (lib.getAttrFromPath parent pkgs).${child}.stdenv + ); + + allMuslExamples = builtins.attrNames ( + lib.filterAttrs (_: system: lib.hasSuffix "-musl" system.config) lib.systems.examples + ); + + allLLVMExamples = builtins.attrNames ( + lib.filterAttrs (_: system: system.useLLVM or false) lib.systems.examples + ); + + # A package set should only change specific configuration, but needs + # to keep all other configuration from previous layers in place. + # Each package set has one or more key characteristics for which we + # test here. Those should be kept, even when applying the "set" package + # set. + isComposable = + set: + ( + # Can't compose two different libcs... + builtins.elem set [ "pkgsLLVMLibc" ] + || discardEvaluationErrors ( + pkgsCross.mingwW64.${set}.stdenv.hostPlatform.config == "x86_64-w64-mingw32" + ) + ) + && ( + # Can't compose two different libcs... + builtins.elem set [ "pkgsLLVMLibc" ] + || discardEvaluationErrors (pkgsCross.mingwW64.${set}.stdenv.hostPlatform.libc == "msvcrt") + ) + && discardEvaluationErrors (pkgsCross.ppc64-musl.${set}.stdenv.hostPlatform.gcc.abi == "elfv2") + && discardEvaluationErrors ( + builtins.elem "trivialautovarinit" pkgs.pkgsExtraHardening.${set}.stdenv.cc.defaultHardeningFlags + ) + && discardEvaluationErrors (pkgs.pkgsLLVM.${set}.stdenv.hostPlatform.useLLVM) + && ( + # Can't compose two different libcs... + builtins.elem set [ + "pkgsMusl" + "pkgsStatic" + ] + || discardEvaluationErrors (pkgs.pkgsLLVMLibc.${set}.stdenv.hostPlatform.isLLVMLibc) + ) + && discardEvaluationErrors (pkgs.pkgsArocc.${set}.stdenv.hostPlatform.useArocc) + && discardEvaluationErrors (pkgs.pkgsZig.${set}.stdenv.hostPlatform.useZig) + && discardEvaluationErrors (pkgs.pkgsLinux.${set}.stdenv.buildPlatform.isLinux) + && ( + # Can't compose two different libcs... + builtins.elem set [ "pkgsLLVMLibc" ] + || discardEvaluationErrors (pkgs.pkgsMusl.${set}.stdenv.hostPlatform.isMusl) + ) + && discardEvaluationErrors (pkgs.pkgsStatic.${set}.stdenv.hostPlatform.isStatic) + && discardEvaluationErrors (pkgs.pkgsi686Linux.${set}.stdenv.hostPlatform.isx86_32) + && discardEvaluationErrors (pkgs.pkgsx86_64Darwin.${set}.stdenv.hostPlatform.isx86_64); +in + +# Appends same defaultHardeningFlags again on each .pkgsExtraHardening - thus not idempotent. +# assert isIdempotent "pkgsExtraHardening"; +# TODO: Remove the isDarwin condition, which currently results in infinite recursion. +# Also see https://github.com/NixOS/nixpkgs/pull/330567#discussion_r1894653309 +assert (stdenv.hostPlatform.isDarwin || isIdempotent "pkgsLLVM"); +# TODO: This currently results in infinite recursion, even on Linux +# assert isIdempotent "pkgsLLVMLibc"; +assert isIdempotent "pkgsArocc"; +assert isIdempotent "pkgsZig"; +assert isIdempotent "pkgsLinux"; +assert isIdempotent "pkgsMusl"; +assert isIdempotent "pkgsStatic"; +assert isIdempotent "pkgsi686Linux"; +assert isIdempotent "pkgsx86_64Darwin"; + +assert isNoop [ "pkgsStatic" ] "pkgsMusl"; +assert lib.all (sys: isNoop [ "pkgsCross" sys ] "pkgsMusl") allMuslExamples; +assert lib.all (sys: isNoop [ "pkgsCross" sys ] "pkgsLLVM") allLLVMExamples; + +assert isComposable "pkgsExtraHardening"; +assert isComposable "pkgsLLVM"; +# TODO: Results in infinite recursion +# assert isComposable "pkgsLLVMLibc"; +assert isComposable "pkgsArocc"; +# TODO: unexpected argument 'bintools' - uncomment once https://github.com/NixOS/nixpkgs/pull/331011 is done +# assert isComposable "pkgsZig"; +assert isComposable "pkgsMusl"; +assert isComposable "pkgsStatic"; +assert isComposable "pkgsi686Linux"; + +# Special cases regarding buildPlatform vs hostPlatform +assert discardEvaluationErrors (pkgsCross.gnu64.pkgsMusl.stdenv.hostPlatform.isMusl); +assert discardEvaluationErrors (pkgsCross.gnu64.pkgsi686Linux.stdenv.hostPlatform.isx86_32); +assert discardEvaluationErrors (pkgsCross.mingwW64.pkgsLinux.stdenv.hostPlatform.isLinux); +assert discardEvaluationErrors ( + pkgsCross.aarch64-darwin.pkgsx86_64Darwin.stdenv.hostPlatform.isx86_64 +); + +# pkgsCross should keep upper cross settings +assert discardEvaluationErrors ( + with pkgsStatic.pkgsCross.gnu64.stdenv.hostPlatform; isGnu && isStatic +); +assert discardEvaluationErrors ( + with pkgsLLVM.pkgsCross.musl64.stdenv.hostPlatform; isMusl && useLLVM +); + +emptyFile diff --git a/pkgs/top-level/default.nix b/pkgs/top-level/default.nix index fd443ac773ff..6787793dbdfc 100644 --- a/pkgs/top-level/default.nix +++ b/pkgs/top-level/default.nix @@ -122,22 +122,18 @@ in let config = lib.showWarnings configEval.config.warnings configEval.config; # A few packages make a new package set to draw their dependencies from. - # (Currently to get a cross tool chain, or forced-i686 package.) Rather than - # give `all-packages.nix` all the arguments to this function, even ones that - # don't concern it, we give it this function to "re-call" nixpkgs, inheriting - # whatever arguments it doesn't explicitly provide. This way, - # `all-packages.nix` doesn't know more than it needs too. + # Rather than give `all-packages.nix` all the arguments to this function, + # even ones that don't concern it, we give it this function to "re-call" + # nixpkgs, inheriting whatever arguments it doesn't explicitly provide. This + # way, `all-packages.nix` doesn't know more than it needs to. # # It's OK that `args` doesn't include default arguments from this file: # they'll be deterministically inferred. In fact we must *not* include them, # because it's important that if some parameter which affects the default is # substituted with a different argument, the default is re-inferred. # - # To put this in concrete terms, this function is basically just used today to - # use package for a different platform for the current platform (namely cross - # compiling toolchains and 32-bit packages on x86_64). In both those cases we - # want the provided non-native `localSystem` argument to affect the stdenv - # chosen. + # To put this in concrete terms, we want the provided non-native `localSystem` + # and `crossSystem` arguments to affect the stdenv chosen. # # NB!!! This thing gets its `config` argument from `args`, i.e. it's actually # `config0`. It is important to keep it to `config0` format (as opposed to the @@ -146,7 +142,7 @@ in let # via `evalModules` is not idempotent. In other words, if you add `config` to # `newArgs`, expect strange very hard to debug errors! (Yes, I'm speaking from # experience here.) - nixpkgsFun = newArgs: import ./. (args // newArgs); + nixpkgsFun = f0: import ./. (args // f0 args); # Partially apply some arguments for building bootstraping stage pkgs # sets. Only apply arguments which no stdenv would want to override. diff --git a/pkgs/top-level/stage.nix b/pkgs/top-level/stage.nix index 64bc2308f6da..49034a7384f4 100644 --- a/pkgs/top-level/stage.nix +++ b/pkgs/top-level/stage.nix @@ -180,168 +180,111 @@ let ((config.packageOverrides or (super: {})) super); # Convenience attributes for instantitating package sets. Each of - # these will instantiate a new version of allPackages. Currently the - # following package sets are provided: - # - # - pkgsCross. where system is a member of lib.systems.examples - # - pkgsMusl - # - pkgsi686Linux - otherPackageSets = self: super: { + # these will instantiate a new version of allPackages. + otherPackageSets = let + mkPkgs = name: fn: nixpkgsFun (prevArgs: let nixpkgsArgs = fn prevArgs; in nixpkgsArgs // { + overlays = [ + (self': super': { + "${name}" = super'; + }) + ] ++ nixpkgsArgs.overlays or [] ++ prevArgs.overlays or []; + }); + # This is always cross. + mkCrossPkgs = name: crossAttrs: mkPkgs name (prevArgs: { + crossSystem = + (lib.systems.systemToAttrs (lib.defaultTo prevArgs.localSystem prevArgs.crossSystem or null)) // crossAttrs; + }); + # This is only cross when we are already cross, otherwise local. + # For the case of "native cross", i.e. pkgsCross.gnu64 on a x86_64-linux system, we need to adjust **both** + # localSystem **and** crossSystem, otherwise they're out of sync. + mkHybridPkgs = name: hybridAttrs: mkPkgs name (prevArgs: let + newSystem = (lib.systems.systemToAttrs (lib.defaultTo prevArgs.localSystem prevArgs.crossSystem or null)) // hybridAttrs; + in { crossSystem = newSystem; } + // lib.optionalAttrs (stdenv.hostPlatform == stdenv.buildPlatform) { localSystem = newSystem; } + ); + in self: super: { # This maps each entry in lib.systems.examples to its own package # set. Each of these will contain all packages cross compiled for # that target system. For instance, pkgsCross.raspberryPi.hello, # will refer to the "hello" package built for the ARM6-based # Raspberry Pi. pkgsCross = lib.mapAttrs (n: crossSystem: - nixpkgsFun { inherit crossSystem; }) + nixpkgsFun (prevArgs: { crossSystem = (lib.systems.systemToAttrs (lib.defaultTo { } prevArgs.crossSystem or null)) // crossSystem; })) lib.systems.examples; - pkgsLLVM = nixpkgsFun { - overlays = [ - (self': super': { - pkgsLLVM = super'; - }) - ] ++ overlays; - # Bootstrap a cross stdenv using the LLVM toolchain. - # This is currently not possible when compiling natively, - # so we don't need to check hostPlatform != buildPlatform. - crossSystem = stdenv.hostPlatform // { - useLLVM = true; - linker = "lld"; - }; + # Bootstrap a cross stdenv using the LLVM toolchain. + # This is currently not possible when compiling natively. + pkgsLLVM = mkCrossPkgs "pkgsLLVM" { + useLLVM = true; + linker = "lld"; }; - pkgsLLVMLibc = nixpkgsFun { - overlays = [ (self': super': { - pkgsLLVMLibc = super'; - })] ++ overlays; - # Bootstrap a cross stdenv using LLVM libc. - # This is currently not possible when compiling natively, - # so we don't need to check hostPlatform != buildPlatform. - crossSystem = stdenv.hostPlatform // { - config = lib.systems.parse.tripleFromSystem (makeLLVMParsedPlatform stdenv.hostPlatform.parsed); - libc = "llvm"; - }; + # Bootstrap a cross stdenv using LLVM libc. + # This is currently not possible when compiling natively. + pkgsLLVMLibc = mkCrossPkgs "pkgsLLVMLibc" { + config = lib.systems.parse.tripleFromSystem (makeLLVMParsedPlatform stdenv.hostPlatform.parsed); + libc = "llvm"; }; - pkgsArocc = nixpkgsFun { - overlays = [ - (self': super': { - pkgsArocc = super'; - }) - ] ++ overlays; - # Bootstrap a cross stdenv using the Aro C compiler. - # This is currently not possible when compiling natively, - # so we don't need to check hostPlatform != buildPlatform. - crossSystem = stdenv.hostPlatform // { - useArocc = true; - linker = "lld"; - }; + # Bootstrap a cross stdenv using the Aro C compiler. + # This is currently not possible when compiling natively. + pkgsArocc = mkCrossPkgs "pkgsArocc" { + useArocc = true; + linker = "lld"; }; - pkgsZig = nixpkgsFun { - overlays = [ - (self': super': { - pkgsZig = super'; - }) - ] ++ overlays; - # Bootstrap a cross stdenv using the Zig toolchain. - # This is currently not possible when compiling natively, - # so we don't need to check hostPlatform != buildPlatform. - crossSystem = stdenv.hostPlatform // { - useZig = true; - linker = "lld"; - }; + # Bootstrap a cross stdenv using the Zig toolchain. + # This is currently not possible when compiling natively. + pkgsZig = mkCrossPkgs "pkgsZig" { + useZig = true; + linker = "lld"; }; # All packages built with the Musl libc. This will override the # default GNU libc on Linux systems. Non-Linux systems are not # supported. 32-bit is also not supported. - pkgsMusl = if stdenv.hostPlatform.isLinux && stdenv.buildPlatform.is64bit then nixpkgsFun { - overlays = [ (self': super': { - pkgsMusl = super'; - })] ++ overlays; - ${if stdenv.hostPlatform == stdenv.buildPlatform - then "localSystem" else "crossSystem"} = { - config = lib.systems.parse.tripleFromSystem (makeMuslParsedPlatform stdenv.hostPlatform.parsed); - }; + pkgsMusl = if stdenv.hostPlatform.isLinux && stdenv.buildPlatform.is64bit then mkHybridPkgs "pkgsMusl" { + config = lib.systems.parse.tripleFromSystem (makeMuslParsedPlatform stdenv.hostPlatform.parsed); } else throw "Musl libc only supports 64-bit Linux systems."; # All packages built for i686 Linux. # Used by wine, firefox with debugging version of Flash, ... - pkgsi686Linux = if stdenv.hostPlatform.isLinux && stdenv.hostPlatform.isx86 then nixpkgsFun { - overlays = [ (self': super': { - pkgsi686Linux = super'; - })] ++ overlays; - ${if stdenv.hostPlatform == stdenv.buildPlatform - then "localSystem" else "crossSystem"} = { - config = lib.systems.parse.tripleFromSystem (stdenv.hostPlatform.parsed // { - cpu = lib.systems.parse.cpuTypes.i686; - }); - }; + pkgsi686Linux = if stdenv.hostPlatform.isLinux && stdenv.hostPlatform.isx86 then mkHybridPkgs "pkgsi686Linux" { + config = lib.systems.parse.tripleFromSystem (stdenv.hostPlatform.parsed // { + cpu = lib.systems.parse.cpuTypes.i686; + }); } else throw "i686 Linux package set can only be used with the x86 family."; # x86_64-darwin packages for aarch64-darwin users to use with Rosetta for incompatible packages - pkgsx86_64Darwin = if stdenv.hostPlatform.isDarwin then nixpkgsFun { - overlays = [ (self': super': { - pkgsx86_64Darwin = super'; - })] ++ overlays; - localSystem = { - config = lib.systems.parse.tripleFromSystem (stdenv.hostPlatform.parsed // { - cpu = lib.systems.parse.cpuTypes.x86_64; - }); - }; + pkgsx86_64Darwin = if stdenv.hostPlatform.isDarwin then mkHybridPkgs "pkgsx86_64Darwin" { + config = lib.systems.parse.tripleFromSystem (stdenv.hostPlatform.parsed // { + cpu = lib.systems.parse.cpuTypes.x86_64; + }); } else throw "x86_64 Darwin package set can only be used on Darwin systems."; # If already linux: the same package set unaltered - # Otherwise, return a natively built linux package set for the current cpu architecture string. + # Otherwise, return a linux package set for the current cpu architecture string. # (ABI and other details will be set to the default for the cpu/os pair) pkgsLinux = if stdenv.hostPlatform.isLinux then self - else nixpkgsFun { - localSystem = lib.systems.elaborate "${stdenv.hostPlatform.parsed.cpu.name}-linux"; + else mkHybridPkgs "pkgsLinux" { + config = lib.systems.parse.tripleFromSystem (lib.systems.elaborate "${stdenv.hostPlatform.parsed.cpu.name}-linux").parsed; }; - # Extend the package set with zero or more overlays. This preserves - # preexisting overlays. Prefer to initialize with the right overlays - # in one go when calling Nixpkgs, for performance and simplicity. - appendOverlays = extraOverlays: - if extraOverlays == [] - then self - else nixpkgsFun { overlays = args.overlays ++ extraOverlays; }; - - # NOTE: each call to extend causes a full nixpkgs rebuild, adding ~130MB - # of allocations. DO NOT USE THIS IN NIXPKGS. - # - # Extend the package set with a single overlay. This preserves - # preexisting overlays. Prefer to initialize with the right overlays - # in one go when calling Nixpkgs, for performance and simplicity. - # Prefer appendOverlays if used repeatedly. - extend = f: self.appendOverlays [f]; - # Fully static packages. # Currently uses Musl on Linux (couldn’t get static glibc to work). - pkgsStatic = nixpkgsFun ({ - overlays = [ (self': super': { - pkgsStatic = super'; - })] ++ overlays; - crossSystem = { - isStatic = true; - config = lib.systems.parse.tripleFromSystem ( - if stdenv.hostPlatform.isLinux - then makeMuslParsedPlatform stdenv.hostPlatform.parsed - else stdenv.hostPlatform.parsed - ); - gcc = lib.optionalAttrs (stdenv.hostPlatform.system == "powerpc64-linux") { abi = "elfv2"; } // - stdenv.hostPlatform.gcc or {}; - }; + pkgsStatic = mkCrossPkgs "pkgsStatic" ({ + isStatic = true; + } // lib.optionalAttrs stdenv.hostPlatform.isLinux { + config = lib.systems.parse.tripleFromSystem (makeMuslParsedPlatform stdenv.hostPlatform.parsed); + } // lib.optionalAttrs (stdenv.hostPlatform.system == "powerpc64-linux") { + gcc = { abi = "elfv2"; } // stdenv.hostPlatform.gcc or {}; }); - pkgsExtraHardening = nixpkgsFun { + pkgsExtraHardening = mkPkgs "pkgsExtraHardening" (_: { overlays = [ (self': super': { - pkgsExtraHardening = super'; stdenv = super'.withDefaultHardeningFlags ( super'.stdenv.cc.defaultHardeningFlags ++ [ "shadowstack" @@ -360,8 +303,25 @@ let pcre-cpp = super'.pcre-cpp.override { enableJit = false; }; pcre16 = super'.pcre16.override { enableJit = false; }; }) - ] ++ overlays; - }; + ]; + }); + + # Extend the package set with zero or more overlays. This preserves + # preexisting overlays. Prefer to initialize with the right overlays + # in one go when calling Nixpkgs, for performance and simplicity. + appendOverlays = extraOverlays: + if extraOverlays == [] + then self + else nixpkgsFun (prevArgs: { overlays = prevArgs.overlays ++ extraOverlays; }); + + # NOTE: each call to extend causes a full nixpkgs rebuild, adding ~130MB + # of allocations. DO NOT USE THIS IN NIXPKGS. + # + # Extend the package set with a single overlay. This preserves + # preexisting overlays. Prefer to initialize with the right overlays + # in one go when calling Nixpkgs, for performance and simplicity. + # Prefer appendOverlays if used repeatedly. + extend = f: self.appendOverlays [f]; }; # The complete chain of package set builders, applied from top to bottom. From 434e36ab91be1454bcd4854878d683aaf50633f5 Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Sun, 26 Jan 2025 14:38:49 +0100 Subject: [PATCH 3/3] pkgs/top-level: add assert to prevent passing elaborated systems The commit to make package sets composable depends on the fact that localSystem and crossSystem can be passed forward to lower package sets for composition. Once we pass a fully elaborated system, this will break down - getters like "isStatic", "isMusl" etc. will not change with the package set anymore, but be stuck on the value passed in via those options. This is a limitation of lib.systems.elaborate primarily, because it can't deal with arbitrary overrides properly. freshBootstrapTools on darwin used to do this, although in this case it didn't do any harm: There shouldn't be any package sets composed during bootstrap anyway. Refactor it, to avoid throwing the assert. --- pkgs/top-level/all-packages.nix | 4 ++-- pkgs/top-level/default.nix | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 7d543a7175c9..020b24c73733 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -1669,9 +1669,9 @@ with pkgs; # pkgsCross.aarch64-multiplatform.freshBootstrapTools.build freshBootstrapTools = if stdenv.hostPlatform.isDarwin then callPackage ../stdenv/darwin/make-bootstrap-tools.nix { - localSystem = stdenv.buildPlatform; + localSystem = { config = lib.systems.parse.tripleFromSystem stdenv.buildPlatform; }; crossSystem = - if stdenv.buildPlatform == stdenv.hostPlatform then null else stdenv.hostPlatform; + if stdenv.buildPlatform == stdenv.hostPlatform then null else { config = lib.systems.parse.tripleFromSystem stdenv.hostPlatform; }; } else if stdenv.hostPlatform.isLinux then callPackage ../stdenv/linux/make-bootstrap-tools.nix {} diff --git a/pkgs/top-level/default.nix b/pkgs/top-level/default.nix index 6787793dbdfc..8aa678b43c4d 100644 --- a/pkgs/top-level/default.nix +++ b/pkgs/top-level/default.nix @@ -46,6 +46,11 @@ ... } @ args: +# Passing fully elaborated systems to localSystem or crossSystem will break composability +# of package sets. +assert builtins.isAttrs localSystem -> !(localSystem ? parsed); +assert builtins.isAttrs crossSystem -> !(crossSystem ? parsed); + let # Rename the function arguments config0 = config; crossSystem0 = crossSystem;