Loading lib/customisation.nix +134 −151 Original line number Diff line number Diff line Loading @@ -6,6 +6,8 @@ let unsafeGetAttrPos ; inherit (lib) all attrValues functionArgs isFunction mirrorFunctionArgs Loading @@ -19,8 +21,6 @@ let sortOn take length filterAttrs flip head pipe isDerivation Loading Loading @@ -98,19 +98,18 @@ rec { */ overrideDerivation = drv: f: let newDrv = derivation (drv.drvAttrs // (f drv)); in flip (extendDerivation (seq drv.drvPath true)) newDrv ( (extendDerivation (seq drv.drvPath true)) ( { meta = drv.meta or { }; passthru = if drv ? passthru then drv.passthru else { }; passthru = drv.passthru or { }; } // (drv.passthru or { }) // optionalAttrs (drv ? __spliced) { __spliced = { } // (mapAttrs (_: sDrv: overrideDerivation sDrv f) drv.__spliced); // { ${if drv ? __spliced then "__spliced" else null} = mapAttrs ( _: sDrv: overrideDerivation sDrv f ) drv.__spliced; } ); ) (derivation (drv.drvAttrs // (f drv))); /** `makeOverridable` takes a function from attribute set to attribute set and Loading Loading @@ -155,32 +154,12 @@ rec { let # Creates a functor with the same arguments as f mirrorArgs = mirrorFunctionArgs f; # Recover overrider and additional attributes for f # When f is a callable attribute set, # it may contain its own `f.override` and additional attributes. # This helper function recovers those attributes and decorate the overrider. recoverMetadata = if isAttrs f then fDecorated: # Preserve additional attributes for f f // fDecorated # Decorate f.override if presented // lib.optionalAttrs (f ? override) { override = fdrv: makeOverridable (f.override fdrv); } else id; decorate = f': recoverMetadata (mirrorArgs f'); in decorate ( f' = origArgs: let result = f origArgs; # Changes the original arguments with (potentially a function that returns) a set of new attributes overrideWith = newArgs: origArgs // (if isFunction newArgs then newArgs origArgs else newArgs); # Re-call the function but with different arguments overrideArgs = mirrorArgs ( /** Loading @@ -190,16 +169,15 @@ rec { This function was provided by `lib.makeOverridable`. */ newArgs: makeOverridable f (overrideWith newArgs) newArgs: makeOverridable f (origArgs // (if isFunction newArgs then newArgs origArgs else newArgs)) ); # Change the result of the function call by applying g to it overrideResult = g: makeOverridable (mirrorArgs (args: g (f args))) origArgs; in if isAttrs result then result // { override = overrideArgs; overrideDerivation = fdrv: overrideResult (x: overrideDerivation x fdrv); overrideDerivation = fdrv: makeOverridable (mirrorArgs (args: overrideDerivation (f args) fdrv)) origArgs; ${if result ? overrideAttrs then "overrideAttrs" else null} = /** Override the attributes that were passed to `mkDerivation` in order to generate this derivation. Loading @@ -211,7 +189,7 @@ rec { */ # NOTE: part of the above documentation had to be duplicated in `mkDerivation`'s `overrideAttrs`. # design/tech debt issue: https://github.com/NixOS/nixpkgs/issues/273815 fdrv: overrideResult (x: x.overrideAttrs fdrv); fdrv: makeOverridable (mirrorArgs (args: (f args).overrideAttrs fdrv)) origArgs; } else if isFunction result then # Transform the result into a functor while propagating its arguments Loading @@ -220,8 +198,23 @@ rec { override = overrideArgs; } else result ); result; in # Recover overrider and additional attributes for f # When f is a callable attribute set, # it may contain its own `f.override` and additional attributes. # This recovers those attributes and decorates the overrider. if isAttrs f then # Preserve additional attributes for f f // (mirrorArgs f') # Decorate f.override if presented // { ${if f ? override then "override" else null} = fdrv: makeOverridable (f.override fdrv); } else mirrorArgs f'; /** Call the package function in the file `fn` with the required Loading Loading @@ -272,25 +265,16 @@ rec { ``` */ callPackageWith = autoArgs: fn: args: let f = if isFunction fn then fn else import fn; fargs = functionArgs f; # All arguments that will be passed to the function # This includes automatic ones and ones passed explicitly allArgs = intersectAttrs fargs autoArgs // args; # a list of argument names that the function requires, but # wouldn't be passed to it missingArgs = # Filter out arguments that have a default value ( filterAttrs (name: value: !value) # Filter out arguments that would be passed (removeAttrs fargs (attrNames allArgs)) makeErrorMessage = autoArgs: fn: args: fargs: unpassedArgs: let # The first missing arg arg = head ( # Filter out the default args. We did a similar computation in the # happy path, but we're okay recomputing it in an error case filter (name: !fargs.${name}) (attrNames unpassedArgs) ); # Get a list of suggested argument names for a given missing one getSuggestions = arg: Loading @@ -316,27 +300,34 @@ rec { else ", did you mean ${concatStringsSep ", " (lib.init suggestions)} or ${lib.last suggestions}?"; errorForArg = arg: let loc = unsafeGetAttrPos arg fargs; loc' = if loc != null then loc.file + ":" + toString loc.line else "<unknown location>"; in "Function called without required argument \"${arg}\" at " + "${loc'}${prettySuggestions (getSuggestions arg)}"; "lib.customisation.callPackageWith: Function called without required argument \"${arg}\" at ${loc'}${prettySuggestions (getSuggestions arg)}"; in autoArgs: fn: args: let f = if isFunction fn then fn else import fn; fargs = functionArgs f; # Only show the error for the first missing argument error = errorForArg (head (attrNames missingArgs)); # All arguments that will be passed to the function # This includes automatic ones and ones passed explicitly allArgs = intersectAttrs fargs autoArgs // args; # arguments that weren't passed automatically to the function unpassedArgs = removeAttrs fargs (attrNames allArgs); in if missingArgs == { } then # if nonempty, check if the function has defaults for those other args if unpassedArgs == { } || all (value: value) (attrValues unpassedArgs) then makeOverridable f allArgs else # Only show the error for the first missing argument # 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, # which is especially relevant with allowAliases = false else abort "lib.customisation.callPackageWith: ${error}"; abort (makeErrorMessage autoArgs fn args fargs unpassedArgs); /** Like `callPackage`, but for a function that returns an attribute Loading Loading @@ -409,16 +400,12 @@ rec { extendDerivation = condition: passthru: drv: let outputs = drv.outputs or [ "out" ]; commonAttrs = drv // (listToAttrs outputsList) // { all = map (x: x.value) outputsList; } // passthru; outputToAttrListElement = outputName: { outputsList = map (outputName: { name = outputName; value = commonAttrs // { value = commonAttrs // { inherit (drv.${outputName}) type outputName; outputSpecified = true; drvPath = Loading @@ -427,18 +414,14 @@ rec { outPath = assert condition; drv.${outputName}.outPath; } // # TODO: give the derivation control over the outputs. # `overrideAttrs` may not be the only attribute that needs # updating when switching outputs. optionalAttrs (passthru ? overrideAttrs) { # TODO: also add overrideAttrs when overrideAttrs is not custom, e.g. when not splicing. overrideAttrs = f: (passthru.overrideAttrs f).${outputName}; ${if passthru ? overrideAttrs then "overrideAttrs" else null} = f: (passthru.overrideAttrs f).${outputName}; }; }; outputsList = map outputToAttrListElement outputs; }) (drv.outputs or [ "out" ]); in commonAttrs // { Loading Loading
lib/customisation.nix +134 −151 Original line number Diff line number Diff line Loading @@ -6,6 +6,8 @@ let unsafeGetAttrPos ; inherit (lib) all attrValues functionArgs isFunction mirrorFunctionArgs Loading @@ -19,8 +21,6 @@ let sortOn take length filterAttrs flip head pipe isDerivation Loading Loading @@ -98,19 +98,18 @@ rec { */ overrideDerivation = drv: f: let newDrv = derivation (drv.drvAttrs // (f drv)); in flip (extendDerivation (seq drv.drvPath true)) newDrv ( (extendDerivation (seq drv.drvPath true)) ( { meta = drv.meta or { }; passthru = if drv ? passthru then drv.passthru else { }; passthru = drv.passthru or { }; } // (drv.passthru or { }) // optionalAttrs (drv ? __spliced) { __spliced = { } // (mapAttrs (_: sDrv: overrideDerivation sDrv f) drv.__spliced); // { ${if drv ? __spliced then "__spliced" else null} = mapAttrs ( _: sDrv: overrideDerivation sDrv f ) drv.__spliced; } ); ) (derivation (drv.drvAttrs // (f drv))); /** `makeOverridable` takes a function from attribute set to attribute set and Loading Loading @@ -155,32 +154,12 @@ rec { let # Creates a functor with the same arguments as f mirrorArgs = mirrorFunctionArgs f; # Recover overrider and additional attributes for f # When f is a callable attribute set, # it may contain its own `f.override` and additional attributes. # This helper function recovers those attributes and decorate the overrider. recoverMetadata = if isAttrs f then fDecorated: # Preserve additional attributes for f f // fDecorated # Decorate f.override if presented // lib.optionalAttrs (f ? override) { override = fdrv: makeOverridable (f.override fdrv); } else id; decorate = f': recoverMetadata (mirrorArgs f'); in decorate ( f' = origArgs: let result = f origArgs; # Changes the original arguments with (potentially a function that returns) a set of new attributes overrideWith = newArgs: origArgs // (if isFunction newArgs then newArgs origArgs else newArgs); # Re-call the function but with different arguments overrideArgs = mirrorArgs ( /** Loading @@ -190,16 +169,15 @@ rec { This function was provided by `lib.makeOverridable`. */ newArgs: makeOverridable f (overrideWith newArgs) newArgs: makeOverridable f (origArgs // (if isFunction newArgs then newArgs origArgs else newArgs)) ); # Change the result of the function call by applying g to it overrideResult = g: makeOverridable (mirrorArgs (args: g (f args))) origArgs; in if isAttrs result then result // { override = overrideArgs; overrideDerivation = fdrv: overrideResult (x: overrideDerivation x fdrv); overrideDerivation = fdrv: makeOverridable (mirrorArgs (args: overrideDerivation (f args) fdrv)) origArgs; ${if result ? overrideAttrs then "overrideAttrs" else null} = /** Override the attributes that were passed to `mkDerivation` in order to generate this derivation. Loading @@ -211,7 +189,7 @@ rec { */ # NOTE: part of the above documentation had to be duplicated in `mkDerivation`'s `overrideAttrs`. # design/tech debt issue: https://github.com/NixOS/nixpkgs/issues/273815 fdrv: overrideResult (x: x.overrideAttrs fdrv); fdrv: makeOverridable (mirrorArgs (args: (f args).overrideAttrs fdrv)) origArgs; } else if isFunction result then # Transform the result into a functor while propagating its arguments Loading @@ -220,8 +198,23 @@ rec { override = overrideArgs; } else result ); result; in # Recover overrider and additional attributes for f # When f is a callable attribute set, # it may contain its own `f.override` and additional attributes. # This recovers those attributes and decorates the overrider. if isAttrs f then # Preserve additional attributes for f f // (mirrorArgs f') # Decorate f.override if presented // { ${if f ? override then "override" else null} = fdrv: makeOverridable (f.override fdrv); } else mirrorArgs f'; /** Call the package function in the file `fn` with the required Loading Loading @@ -272,25 +265,16 @@ rec { ``` */ callPackageWith = autoArgs: fn: args: let f = if isFunction fn then fn else import fn; fargs = functionArgs f; # All arguments that will be passed to the function # This includes automatic ones and ones passed explicitly allArgs = intersectAttrs fargs autoArgs // args; # a list of argument names that the function requires, but # wouldn't be passed to it missingArgs = # Filter out arguments that have a default value ( filterAttrs (name: value: !value) # Filter out arguments that would be passed (removeAttrs fargs (attrNames allArgs)) makeErrorMessage = autoArgs: fn: args: fargs: unpassedArgs: let # The first missing arg arg = head ( # Filter out the default args. We did a similar computation in the # happy path, but we're okay recomputing it in an error case filter (name: !fargs.${name}) (attrNames unpassedArgs) ); # Get a list of suggested argument names for a given missing one getSuggestions = arg: Loading @@ -316,27 +300,34 @@ rec { else ", did you mean ${concatStringsSep ", " (lib.init suggestions)} or ${lib.last suggestions}?"; errorForArg = arg: let loc = unsafeGetAttrPos arg fargs; loc' = if loc != null then loc.file + ":" + toString loc.line else "<unknown location>"; in "Function called without required argument \"${arg}\" at " + "${loc'}${prettySuggestions (getSuggestions arg)}"; "lib.customisation.callPackageWith: Function called without required argument \"${arg}\" at ${loc'}${prettySuggestions (getSuggestions arg)}"; in autoArgs: fn: args: let f = if isFunction fn then fn else import fn; fargs = functionArgs f; # Only show the error for the first missing argument error = errorForArg (head (attrNames missingArgs)); # All arguments that will be passed to the function # This includes automatic ones and ones passed explicitly allArgs = intersectAttrs fargs autoArgs // args; # arguments that weren't passed automatically to the function unpassedArgs = removeAttrs fargs (attrNames allArgs); in if missingArgs == { } then # if nonempty, check if the function has defaults for those other args if unpassedArgs == { } || all (value: value) (attrValues unpassedArgs) then makeOverridable f allArgs else # Only show the error for the first missing argument # 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, # which is especially relevant with allowAliases = false else abort "lib.customisation.callPackageWith: ${error}"; abort (makeErrorMessage autoArgs fn args fargs unpassedArgs); /** Like `callPackage`, but for a function that returns an attribute Loading Loading @@ -409,16 +400,12 @@ rec { extendDerivation = condition: passthru: drv: let outputs = drv.outputs or [ "out" ]; commonAttrs = drv // (listToAttrs outputsList) // { all = map (x: x.value) outputsList; } // passthru; outputToAttrListElement = outputName: { outputsList = map (outputName: { name = outputName; value = commonAttrs // { value = commonAttrs // { inherit (drv.${outputName}) type outputName; outputSpecified = true; drvPath = Loading @@ -427,18 +414,14 @@ rec { outPath = assert condition; drv.${outputName}.outPath; } // # TODO: give the derivation control over the outputs. # `overrideAttrs` may not be the only attribute that needs # updating when switching outputs. optionalAttrs (passthru ? overrideAttrs) { # TODO: also add overrideAttrs when overrideAttrs is not custom, e.g. when not splicing. overrideAttrs = f: (passthru.overrideAttrs f).${outputName}; ${if passthru ? overrideAttrs then "overrideAttrs" else null} = f: (passthru.overrideAttrs f).${outputName}; }; }; outputsList = map outputToAttrListElement outputs; }) (drv.outputs or [ "out" ]); in commonAttrs // { Loading