diff --git a/lib/customisation.nix b/lib/customisation.nix index 30747f7fde7e4..302cc48b88827 100644 --- a/lib/customisation.nix +++ b/lib/customisation.nix @@ -261,9 +261,14 @@ rec { f = if isFunction fn then fn else import fn; fargs = functionArgs f; + argSelectors = f.argSelectors or { }; + selectedArgs = mapAttrs (_: select: select autoArgs) argSelectors; + + customArgs = selectedArgs // args; + # All arguments that will be passed to the function # This includes automatic ones and ones passed explicitly - allArgs = intersectAttrs fargs autoArgs // args; + allArgs = intersectAttrs fargs autoArgs // customArgs; # a list of argument names that the function requires, but # wouldn't be passed to it @@ -278,7 +283,7 @@ rec { # Get a list of suggested argument names for a given missing one getSuggestions = arg: - pipe (autoArgs // args) [ + pipe (autoArgs // customArgs) [ attrNames # Only use ones that are at most 2 edits away. While mork would work, # levenshteinAtMost is only fast for 2 or less. @@ -312,9 +317,20 @@ rec { # Only show the error for the first missing argument error = errorForArg (head (attrNames missingArgs)); + result = makeOverridable f allArgs; in if missingArgs == { } then - makeOverridable f allArgs + if lib.isAttrs result then + result + // { + override = + result.override + // optionalAttrs (args == { }) { + inherit argSelectors; + }; + } + else + result # This needs to be an abort so it can't be caught with `builtins.tryEval`, # which is used by nix-env and ofborg to filter out packages that don't evaluate. # This way we're forced to fix such errors in Nixpkgs, @@ -353,9 +369,25 @@ rec { f = if isFunction fn then fn else import fn; auto = intersectAttrs (functionArgs f) autoArgs; mirrorArgs = mirrorFunctionArgs f; - origArgs = auto // args; + + argSelectors = f.argSelectors or { }; + selectedArgs = mapAttrs (_: select: select autoArgs) argSelectors; + + customArgs = selectedArgs // args; + origArgs = auto // customArgs; pkgs = f origArgs; - mkAttrOverridable = name: _: makeOverridable (mirrorArgs (newArgs: (f newArgs).${name})) origArgs; + mkAttrOverridable = + name: _: + let + result = makeOverridable (mirrorArgs (newArgs: (f newArgs).${name})) origArgs; + in + if isAttrs result then + result + // { + override = result.override // optionalAttrs (args == { }) { inherit argSelectors; }; + } + else + result; in if isDerivation pkgs then throw ( @@ -366,6 +398,19 @@ rec { else mapAttrs mkAttrOverridable pkgs; + makeCallPackageWithArgSelectors = + callPackage: fn: extraArgSelectors: + let + f = if isFunction fn then fn else import fn; + f' = if isAttrs f then f else mirrorFunctionArgs f f; + in + callPackage ( + f' + // { + argSelectors = f.argSelectors or { } // extraArgSelectors; + } + ) { }; + /** Add attributes to each output of a derivation without changing the derivation itself and check a given condition when evaluating. diff --git a/lib/default.nix b/lib/default.nix index e10332ca58dd4..57e690c62a931 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -391,6 +391,7 @@ let makeOverridable callPackageWith callPackagesWith + makeCallPackageWithArgSelectors extendDerivation hydraJob makeScope diff --git a/pkgs/top-level/by-name-overlay.nix b/pkgs/top-level/by-name-overlay.nix index e6fe5b7215560..df2f7bfaffeba 100644 --- a/pkgs/top-level/by-name-overlay.nix +++ b/pkgs/top-level/by-name-overlay.nix @@ -10,13 +10,20 @@ let lib = import ../../lib; inherit (builtins) + pathExists readDir ; inherit (lib.attrsets) + getAttrFromPath mapAttrs mapAttrsToList mergeAttrsList + optionalAttrs + ; + + inherit (lib.customisation) + makeCallPackageWithArgSelectors ; # Package files for a single shard @@ -49,6 +56,31 @@ self: super: # and whether it's defined by this file here or `all-packages.nix`. # TODO: This can be removed once `pkgs/by-name` can handle custom `callPackage` arguments without `all-packages.nix` (or any other way of achieving the same result). # Because at that point the code in ./stage.nix can be changed to not allow definitions in `all-packages.nix` to override ones from `pkgs/by-name` anymore and throw an error if that happens instead. - _internalCallByNamePackageFile = file: self.callPackage file { }; + _internalCallByNamePackageFile = + file: + let + fArg = import file; + isSibling = fArg.type or null == "sibling"; + fArg' = + if isSibling then + ( + if fArg ? package then + self.${fArg.package} + else if fArg ? packagePath then + getAttrFromPath fArg.packagePath self + else + throw "${baseNameOf (dirOf file)}: expect `package' or `packagePath' in sibling metadata." + ).override + else + fArg; + transform = if isSibling && (fArg ? transform) then fArg.transform else { package, ... }: package; + package = makeCallPackageWithArgSelectors self.callPackage fArg' ( + let + argSelectorsFile = "${toString (dirOf file)}/arg-selectors.nix"; + in + optionalAttrs (pathExists argSelectorsFile) (import argSelectorsFile) + ); + in + transform { inherit lib package; }; } // mapAttrs (name: self._internalCallByNamePackageFile) packageFiles diff --git a/pkgs/top-level/splice.nix b/pkgs/top-level/splice.nix index 66721e3bee565..0ba3adda9572e 100644 --- a/pkgs/top-level/splice.nix +++ b/pkgs/top-level/splice.nix @@ -138,6 +138,8 @@ in callPackages = lib.callPackagesWith pkgsForCall; + callPackageWithArgSelectors = lib.makeCallPackageWithArgSelectors pkgs.callPackage; + newScope = extra: lib.callPackageWith (pkgsForCall // extra); pkgs = if actuallySplice then splicedPackages // { recurseForDerivations = false; } else pkgs;