diff --git a/lib/customisation.nix b/lib/customisation.nix index 16f248cd1b207..0b8a56e271538 100644 --- a/lib/customisation.nix +++ b/lib/customisation.nix @@ -3,6 +3,7 @@ let inherit (builtins) intersectAttrs + unsafeGetAttrPos ; inherit (lib) functionArgs @@ -33,6 +34,7 @@ let extends toFunction id + defaultTo ; inherit (lib.strings) levenshtein levenshteinAtMost; @@ -303,7 +305,7 @@ rec { errorForArg = arg: let - loc = builtins.unsafeGetAttrPos arg fargs; + loc = unsafeGetAttrPos arg fargs; in "Function called without required argument \"${arg}\" at " + "${loc.file}:${toString loc.line}${prettySuggestions (getSuggestions arg)}"; @@ -863,4 +865,94 @@ rec { transformDrv ; }; + + /** + Get informative message string about attribute missing under `finalAttrs.passthru` + due to accidental overriting `passthru` when calling `.overrideAttrs`. + + # Inputs + + `getFinalPassthruMissingMessage`-specific configurations + : `attrName`: the missing attribute name. + : `finalAttrs`: the final state of the [fixed-point arguments](#chap-build-helpers-finalAttrs), to get possible possition of incorrect overriding. + : `prefix`: Additional prefix to the message for the name of the package and/or the build helper. + + # Type + + ``` + getFinalPassthruMissingMessage :: + { + attrName :: String, + finalAttrs :: null or { passthru :: AttrSet, ... }, + prefix :: String, + } + -> String + ``` + */ + getFinalPassthruMissingMessage = + { + attrName, + finalAttrs ? null, + prefix ? "", + }: + let + pos = unsafeGetAttrPos "passthru" finalAttrs; + in + "${prefix}passthru.${attrName} missing after overrideAttrs overriding." + + optionalString ( + finalAttrs != null && pos != null + ) " Last overridden at ${pos.file}:${toString pos.line}"; + + /** + Get attribute from finalAttrs.passthru, falling back to a custom default and an optional message. + + `getFinalPassthruWith` roughly resembles + + ```nix + finalAttrs.passthru.${attrName} or (handler "message" default) + ``` + + Where [`getFinalPassthruMissingMessage`](#function-library-lib.customisation.getFinalPassthruMissingMessage) provides the message. + + # Inputs + + `getFinalPassthruWith`-specific configurations + : `handler`: Function to take the message and the default value in case the attribute is missing. Typical values include `throw`, `lib.warn` and `null`, where `null` means silently using the default value. + : `prefix`: Additional prefix to the message for the name of the package and/or the build helper. + + `finalAttrs` + : The final state of the [fixed-point arguments](#chap-build-helpers-finalAttrs). + + `attrName` + : The attribute name to get from `finalAttrs.passthru`. + + # Type + + ``` + getFinalPassthruWith :: + { + handler :: String -> a -> a, + prefix :: String, + } + -> { passthru :: AttrSet, ... } + -> String + -> a + ``` + */ + getFinalPassthruWith = + { + handler ? throw, + prefix ? "", + }: + finalAttrs: attrName: + if finalAttrs.passthru ? ${attrName} then + default: finalAttrs.passthru.${attrName} + else + (defaultTo (m: d: d) handler) (getFinalPassthruMissingMessage { + inherit + prefix + finalAttrs + attrName + ; + }); } diff --git a/lib/default.nix b/lib/default.nix index 3eb2ecc308a4a..5e1079f1c4ead 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -401,6 +401,8 @@ let makeScopeWithSplicing makeScopeWithSplicing' extendMkDerivation + getFinalPassthruMissingMessage + getFinalPassthruWith ; inherit (self.derivations) lazyDerivation optionalDrvAttr warnOnInstantiate; inherit (self.generators) mkLuaInline; diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix index a9c78defdffb3..990cfeb873eb6 100644 --- a/lib/tests/misc.nix +++ b/lib/tests/misc.nix @@ -61,6 +61,7 @@ let genList getExe getExe' + getFinalPassthruWith getLicenseFromSpdxIdOr groupBy groupBy' @@ -118,6 +119,7 @@ let uniqueStrings updateManyAttrsByPath versions + warn xor ; @@ -239,6 +241,49 @@ runTests { expected = functionArgs f; }; + testGetFinalPassthruFallback = + let + finalAttrs = { + passthru = { }; + }; + in + { + expr = { + nullWithDefault = getFinalPassthruWith { handler = null; } finalAttrs "foo" "myDefaultValue"; + throwWithDefault = + (builtins.tryEval (getFinalPassthruWith { handler = throw; } finalAttrs "foo" "myDefaultValue")) + .success; + throwWithoutDefault = + (builtins.tryEval (getFinalPassthruWith { handler = throw; } finalAttrs "foo")).success; + }; + expected = { + nullWithDefault = "myDefaultValue"; + throwWithDefault = false; + throwWithoutDefault = false; + }; + }; + + testGetFinalPassthruSuccess = + let + finalAttrs = { + passthru = { + foo = "bar"; + }; + }; + in + { + expr = { + nullWithDefault = getFinalPassthruWith { handler = null; } finalAttrs "foo" "myDefaultValue"; + warnWithDefault = getFinalPassthruWith { handler = warn; } finalAttrs "foo" "myDefaultValue"; + throwWithDefault = getFinalPassthruWith { handler = throw; } finalAttrs "foo" "myDefaultValue"; + }; + expected = { + nullWithDefault = "bar"; + warnWithDefault = "bar"; + throwWithDefault = "bar"; + }; + }; + # TRIVIAL testId = { diff --git a/pkgs/build-support/go/module.nix b/pkgs/build-support/go/module.nix index c9ed1cf0e36ed..cfb133fdf7deb 100644 --- a/pkgs/build-support/go/module.nix +++ b/pkgs/build-support/go/module.nix @@ -69,6 +69,12 @@ lib.extendMkDerivation { ... }@args: + let + getFinalPassthruOr = lib.getFinalPassthruWith { + prefix = "buildGoModule: ${finalAttrs.name or finalAttrs.pname}: "; + handler = lib.warn; + } finalAttrs; + in { inherit modRoot @@ -205,15 +211,7 @@ lib.extendMkDerivation { outputHashAlgo = if finalAttrs.vendorHash == "" then "sha256" else null; # in case an overlay clears passthru by accident, don't fail evaluation }).overrideAttrs - ( - let - pos = builtins.unsafeGetAttrPos "passthru" finalAttrs; - posString = - if pos == null then "unknown" else "${pos.file}:${toString pos.line}:${toString pos.column}"; - in - finalAttrs.passthru.overrideModAttrs - or (lib.warn "buildGoModule: ${finalAttrs.name or finalAttrs.pname}: passthru.overrideModAttrs missing after overrideAttrs. Last overridden at ${posString}." overrideModAttrs) - ); + (getFinalPassthruOr "overrideModAttrs" overrideModAttrs); nativeBuildInputs = [ go ] ++ nativeBuildInputs;