stdenv.mkDerivation: Initial RFC 127 implementation

See https://github.com/NixOS/rfcs/blob/master/rfcs/0127-issues-warnings.md

Co-Authored-By: piegames <git@piegames.de>
Co-Authored-By: AkechiShiro <14914796+AkechiShiro@users.noreply.github.com>
This commit is contained in:
Silvan Mosberger 2026-01-09 20:18:11 +01:00
parent 8fe8f0eb36
commit 27dd434480
57 changed files with 1500 additions and 76 deletions

View file

@ -58,8 +58,10 @@
/pkgs/top-level/by-name-overlay.nix @infinisil @philiptaron
/pkgs/stdenv @philiptaron @NixOS/stdenv
/pkgs/stdenv/generic @Ericson2314 @NixOS/stdenv
/pkgs/stdenv/generic/check-meta.nix @Ericson2314 @adisbladis @NixOS/stdenv
/pkgs/stdenv/generic/meta-types.nix @adisbladis @NixOS/stdenv
/pkgs/stdenv/generic/problems.nix @infinisil
/pkgs/test/problems @infinisil
/pkgs/stdenv/generic/check-meta.nix @infinisil @Ericson2314 @adisbladis @NixOS/stdenv
/pkgs/stdenv/generic/meta-types.nix @infinisil @adisbladis @NixOS/stdenv
/pkgs/stdenv/cross @Ericson2314 @NixOS/stdenv
/pkgs/build-support @philiptaron
/pkgs/build-support/cc-wrapper @Ericson2314

View file

@ -629,6 +629,9 @@
"chap-stdenv": [
"index.html#chap-stdenv"
],
"sec-problems": [
"index.html#sec-problems"
],
"sec-using-llvm": [
"index.html#sec-using-llvm"
],

View file

@ -11,6 +11,8 @@ By default, Nix will prevent installation if any of the following criteria are t
- The package has known security vulnerabilities but has not or can not be updated for some reason, and a list of issues has been entered into the package's `meta.knownVulnerabilities`.
- There are problems for packages which must be acknowledged, e.g. deprecation notices.
Each of these criteria can be altered in the Nixpkgs configuration.
:::{.note}
@ -166,6 +168,57 @@ There are several ways to tweak how Nix handles a package which has been marked
Note that `permittedInsecurePackages` is only checked if `allowInsecurePredicate` is not specified.
## Packages with problems {#sec-problems}
A package may have several problems associated with it.
These can be either manually declared in `meta.problems`, or automatically generated from its other `meta` attributes.
Each problem has a name, a "kind", a message, and optionally a list of URLs.
Not all kinds can be manually specified in `meta.problems`, and some kinds can exist only up to once per package.
Currently, the following problem kinds are known (with more reserved to be added in the future):
- "removal": The package is planned to be removed some time in the future. Unique.
- "deprecated": The package relies on software which has reached its end of life.
- "maintainerless": Automatically generated for packages with `meta.maintainers == []`. Unique, not manually specifiable.
Each problem has a handler that deals with it, which can be one of "error", "warn" or "ignore".
"error" will disallow evaluating a package, while "warn" will simply print a message to the log.
The handler for problems can be specified using `config.problems.handlers.${packageName}.${problemName} = "${handler}";`.
There is also the possibility to specify some generic matchers, which can set a handler for more than a specific problem of a specific package.
This works through the `config.problems.matchers` option:
```nix
{
problems.matchers = [
# Fail to build any packages which are about to be removed anyway
{
kind = "removal";
handler = "error";
}
# Get warnings when using packages with no declared maintainers
{
kind = "maintainerless";
handler = "warn";
}
# You deeply care about this package and want to absolutely know when it has any problems
{
package = "hello";
handler = "error";
}
];
}
```
Matchers can match one or more of package name, problem name or problem kind.
If multiple conditions are present, all must be met to match.
If multiple matchers match a problem, then the highest severity handler will be chosen.
The current default value contains `{ kind = "removal"; handler = "warn"; }`, meaning that people will be notified about package removals in advance.
Package names for both `problems.handlers` and `problems.matchers` are taken from `lib.getName`, which looks at the `pname` first and falls back to extracting the "pname" part from the `name` attribute.
## Modify packages via `packageOverrides` {#sec-modify-via-packageOverrides}
You can define a function called `packageOverrides` in your local `~/.config/nixpkgs/config.nix` to override Nix packages. It must be a function that takes pkgs as an argument and returns a modified set of packages.

View file

@ -10,10 +10,8 @@
let
inherit (lib)
all
attrNames
attrValues
concatMapStrings
concatMapStringsSep
concatStrings
filter
findFirst
@ -26,7 +24,8 @@ let
optionalString
isAttrs
isString
mapAttrs
warn
foldl'
;
inherit (lib.lists)
@ -49,15 +48,17 @@ let
inherit (builtins)
getEnv
trace
;
inherit (import ./problems.nix { inherit lib; })
problemsType
genCheckProblems
;
checkProblems = genCheckProblems config;
# If we're in hydra, we can dispense with the more verbose error
# messages and make problems easier to spot.
inHydra = config.inHydra or false;
# Allow the user to opt-into additional warnings, e.g.
# import <nixpkgs> { config = { showDerivationWarnings = [ "maintainerless" ]; }; }
showWarnings = config.showDerivationWarnings;
getNameWithVersion =
attrs: attrs.name or "${attrs.pname or "«name-missing»"}-${attrs.version or "«version-missing»"}";
@ -118,21 +119,6 @@ let
hasUnfreeLicense = attrs: attrs ? meta.license && isUnfree attrs.meta.license;
hasNoMaintainers =
# To get usable output, we want to avoid flagging "internal" derivations.
# Because we do not have a way to reliably decide between internal or
# external derivation, some heuristics are required to decide.
#
# If `outputHash` is defined, the derivation is a FOD, such as the output of a fetcher.
# If `description` is not defined, the derivation is probably not a package.
# Simply checking whether `meta` is defined is insufficient,
# as some fetchers and trivial builders do define meta.
attrs:
(!attrs ? outputHash)
&& (attrs ? meta.description)
&& (attrs.meta.maintainers or [ ] == [ ])
&& (attrs.meta.teams or [ ] == [ ]);
isMarkedBroken = attrs: attrs.meta.broken or false;
# Allow granular checks to allow only some broken packages
@ -375,6 +361,8 @@ let
unfree = bool;
unsupported = bool;
insecure = bool;
# This is checked in more detail further down
problems = problemsType;
timeout = int;
knownVulnerabilities = listOf str;
badPlatforms = platforms;
@ -497,18 +485,6 @@ let
else
null;
# Please also update the type in /pkgs/top-level/config.nix alongside this.
checkWarnings =
attrs:
if hasNoMaintainers attrs then
{
reason = "maintainerless";
msg = "has no maintainers or teams";
remediation = "";
}
else
null;
# Helper functions and declarations to handle identifiers, extracted to reduce allocations
hasAllCPEParts =
cpeParts:
@ -669,52 +645,64 @@ let
&& ((config.checkMetaRecursively or false) -> all (d: d.meta.available or true) references);
};
validYes = {
valid = "yes";
handled = true;
};
handle =
{
attrs,
meta,
warnings ? [ ],
error ? null,
}:
let
withError =
if isNull error then
true
else
let
msg =
"Refusing to evaluate package '${getNameWithVersion attrs}' in ${pos_str meta} because it ${error.msg}"
+ lib.optionalString (!inHydra && error.remediation != "") "\n${error.remediation}";
in
if config ? handleEvalIssue then config.handleEvalIssue error.reason msg else throw msg;
giveWarning =
acc: warning:
let
msg =
"Package '${getNameWithVersion attrs}' in ${pos_str meta} ${warning.msg}"
+ lib.optionalString (!inHydra && warning.remediation != "") " ${warning.remediation}";
in
warn msg acc;
in
# Give all warnings first, then error if any
builtins.seq (foldl' giveWarning null warnings) withError;
assertValidity =
{ meta, attrs }:
let
invalid = checkValidity attrs;
warning = checkWarnings attrs;
problems = checkProblems attrs;
in
if isNull invalid then
if isNull warning then
validYes
else
let
msg =
if inHydra then
"Warning while evaluating ${getNameWithVersion attrs}: «${warning.reason}»: ${warning.msg}"
else
"Package ${getNameWithVersion attrs} in ${pos_str meta} ${warning.msg}, continuing anyway."
+ (optionalString (warning.remediation != "") "\n${warning.remediation}");
handled = if elem warning.reason showWarnings then trace msg true else true;
in
if isNull problems then
{
valid = "warn";
handled = handled;
valid = "yes";
handled = true;
}
else
{
valid = if isNull problems.error then "warn" else "no";
handled = handle {
inherit attrs meta;
inherit (problems) error warnings;
};
}
else
let
msg =
if inHydra then
"Failed to evaluate ${getNameWithVersion attrs}: «${invalid.reason}»: ${invalid.msg}"
else
''
Package ${getNameWithVersion attrs} in ${pos_str meta} ${invalid.msg}, refusing to evaluate.
''
+ invalid.remediation;
handled = if config ? handleEvalIssue then config.handleEvalIssue invalid.reason msg else throw msg;
in
{
valid = "no";
handled = handled;
handled = handle {
inherit attrs meta;
error = invalid;
};
};
in

View file

@ -5,7 +5,7 @@
# TODO: add a method to the module system types
# see https://github.com/NixOS/nixpkgs/pull/273935#issuecomment-1854173100
let
inherit (builtins)
inherit (lib)
isString
isInt
isAttrs
@ -167,7 +167,7 @@ lib.fix (self: {
concatMap (
k:
if fieldVerifiers ? ${k} then
lib.optionals (fieldVerifiers.${k} v.${k}) (self.errors fields.${k} (ctx + ".${k}") v.${k})
lib.optionals (!fieldVerifiers.${k} v.${k}) (self.errors fields.${k} "${ctx}.${k}" v.${k})
else
[
"${ctx}: key '${k}' is unrecognized; expected one of: \n [${

View file

@ -0,0 +1,515 @@
/*
This file implements everything around meta.problems, including:
- automaticProblems: Which problems get added automatically based on some condition
- configOptions: Module system options for config.problems
- problemsType: The check for meta.problems
- genHandlerSwitch: The logic to determine the handler for a specific problem based on config.problems
- genCheckProblems: The logic to determine which problems need to be handled and how the messages should look like
There are tests to cover pretty much this entire file, so please run them when making changes ;)
nix-build -A tests.problems
*/
{ lib }:
rec {
inherit (lib.strings)
escapeNixIdentifier
;
inherit (lib)
any
listToAttrs
concatStringsSep
optionalString
getName
optionalAttrs
pipe
isString
filterAttrs
mapAttrs
partition
elemAt
max
foldl'
elem
filter
concatMapStringsSep
optional
optionals
concatLists
all
attrNames
attrValues
length
mapAttrsToList
groupBy
subtractLists
genAttrs
;
handlers = rec {
# Ordered from less to more
levels = [
"ignore"
"warn"
"error"
];
lessThan =
a: b:
if a == "error" then
false
else if a == "warn" then
b == "error"
else
b != "ignore";
max = a: b: if lessThan a b then b else a;
};
# TODO: Combine this and automaticProblems into a `{ removal = { manual = true; ... }; ... }` structure for less error-prone changes
kinds = rec {
# Automatic and manual problem kinds
known = map (problem: problem.kindName) automaticProblems ++ manual;
# Problem kinds that are currently allowed to be specified in `meta.problems`
manual = [
"removal"
"deprecated"
];
# Problem kinds that are currently only allowed to be specified once
unique = [
"removal"
];
# Same thing but a set with null values (comes in handy at times)
manual' = genAttrs manual (k: null);
unique' = genAttrs unique (k: null);
};
automaticProblems = [
{
kindName = "maintainerless";
condition =
# To get usable output, we want to avoid flagging "internal" derivations.
# Because we do not have a way to reliably decide between internal or
# external derivation, some heuristics are required to decide.
#
# If `outputHash` is defined, the derivation is a FOD, such as the output of a fetcher.
# If `description` is not defined, the derivation is probably not a package.
# Simply checking whether `meta` is defined is insufficient,
# as some fetchers and trivial builders do define meta.
attrs:
# Order of checks optimised for short-circuiting the common case of having maintainers
(attrs.meta.maintainers or [ ] == [ ])
&& (attrs.meta.teams or [ ] == [ ])
&& (!attrs ? outputHash)
&& (attrs ? meta.description);
value.message = "This package has no declared maintainer, i.e. an empty `meta.maintainers` and `meta.teams` attribute.";
}
];
genAutomaticProblems =
attrs:
listToAttrs (
map (problem: lib.nameValuePair problem.kindName problem.value) (
filter (problem: problem.condition attrs) automaticProblems
)
);
# A module system type for Nixpkgs config
configOptions =
let
types = lib.types;
handlerType = types.enum handlers.levels;
problemKindType = types.enum kinds.known;
in
{
handlers = lib.mkOption {
type = with types; attrsOf (attrsOf handlerType);
default = { };
description = ''
Specify how to handle packages with problems.
Each key has the format `packageName.problemName`, each value is one of "error", "warn" or "ignore".
This option takes precedence over anything in `problems.matchers`.
Package names are taken from `lib.getName`, which looks at the `pname` first and falls back to extracting the "pname" part from the `name` attribute.
See <link xlink:href="https://nixos.org/manual/nixpkgs/stable/#sec-ignore-problems">Installing packages with problems</link> in the NixOS manual.
'';
};
matchers = lib.mkOption {
type = types.listOf (
types.submodule (
{ config, ... }:
{
options = {
package = lib.mkOption {
type = types.nullOr types.str;
description = "Match problems of packages with this name";
default = null;
};
name = lib.mkOption {
type = types.nullOr types.str;
description = "Match problems with this problem name";
default = null;
};
kind = lib.mkOption {
type = types.nullOr problemKindType;
description = "Match problems of this problem kind";
default = null;
};
handler = lib.mkOption {
type = handlerType;
description = "Specify the handler for matched problems";
};
# Temporary hack to get assertions in submodules, see global assertions below
assertions = lib.mkOption {
type = types.listOf types.anything;
default = [ ];
internal = true;
};
};
config = {
assertions =
# Using optional because otherwise message would be evaluated even when assertion is true
(
optional (config.package != null && config.name != null) {
assertion = false;
# TODO: Does it really matter if we let people specify this? Maybe not, so consider removing this assertion
message = ''
There is a problems.matchers with `package = "${config.package}"` and `name = "${config.name}". Use the following instead:
problems.handlers.${escapeNixIdentifier config.package}.${escapeNixIdentifier config.name} = "${config.handler}";
'';
}
);
};
}
)
);
default = [ ];
description = ''
A more powerful and less ergonomic version of `problems.handlers`.
Each value is a matcher, that may match onto certain properties of a problem and specify a handler for them.
If multiple matchers match a problem, the handler with the highest severity (error > warn > ignore) will be used.
Values in `problems.handlers` always take precedence over matchers.
Any matchers must not contain both a `package` and `name` field, for this should be handled by using `problems.handlers` instead.
'';
example = [
{
kind = "maintainerless";
handler = "warn";
}
{
package = "myPackageICareAbout";
handler = "error";
}
];
};
};
# The type for meta.problems
problemsType =
let
types = import ./meta-types.nix { inherit lib; };
inherit (types)
str
listOf
attrsOf
record
enum
;
kindType = enum kinds.manual;
subRecord = record {
kind = kindType;
message = str;
urls = listOf str;
};
simpleType = attrsOf subRecord;
in
{
name = "problems";
verify =
v:
v == { }
||
simpleType.verify v
&& all (problem: problem ? message) (attrValues v)
&& (
let
kindGroups = groupBy (kind: kind) (mapAttrsToList (name: problem: problem.kind or name) v);
in
all (kind: kinds.manual' ? ${kind} && (kinds.unique' ? ${kind} -> length kindGroups.${kind} == 1)) (
attrNames kindGroups
)
);
errors =
ctx: v:
let
kindGroups = groupBy (attrs: attrs.kind) (
mapAttrsToList (name: problem: {
inherit name;
explicit = problem ? kind;
kind = problem.kind or name;
}) v
);
in
if !simpleType.verify v then
types.errors simpleType ctx v
else
concatLists (
mapAttrsToList (name: p: optional (!p ? message) "${ctx}.${name}: `.message` not specified") v
)
++ concatLists (
mapAttrsToList (
kind: kindGroup:
optionals (!kinds.manual' ? ${kind}) (
map (
el:
"${ctx}.${el.name}: Problem kind ${kind}, inferred from the problem name, is invalid; expected ${kindType.name}. You can specify an explicit problem kind with `${ctx}.${el.name}.kind`"
) (filter (el: !el.explicit) kindGroup)
)
++
optional (kinds.unique' ? ${kind} && length kindGroup > 1)
"${ctx}: Problem kind ${kind} should be unique, but is used for these problems: ${
concatMapStringsSep ", " (el: el.name) kindGroup
}"
) kindGroups
);
};
/*
Construct a structure as follows, with the invariant that a more specific path always has a stricter handler, forming a lattice.
E.g. if `packageSpecific.foo.nameFallback.kindFallback == "warn"`, then `packageFallback.nameFallback.kindFallback` must be "ignore".
packageSpecific.<package> = {
nameSpecific.<name> = {
kindSpecific.<kind> = <ignore|warn|error>;
kindFallback = <ignore|warn|error>;
};
nameFallback = {
kindSpecific.<kind> = <ignore|warn|error>;
kindFallback = <ignore|warn|error>;
};
};
packageFallback = {
nameSpecific.<name> = {
kindSpecific.<kind> = <ignore|warn|error>;
kindFallback = <ignore|warn|error>;
};
nameFallback = {
kindSpecific.<kind> = <ignore|warn|error>;
kindFallback = <ignore|warn|error>;
};
};
Returns both the structure itself for inspection and a function that can query it with very few allocations/lookups
This allows collapsing arbitrarily many problem handlers/matchers into a predictable structure that can be queried in a predictable and fast way
*/
genHandlerSwitch =
config:
let
constraints =
# matchers have low priority
map (m: m // { priority = 0; }) config.problems.matchers
# handlers have higher priority
++ concatLists (
mapAttrsToList (
package: forPackage:
mapAttrsToList (name: handler: {
inherit package name handler;
kind = null;
priority = 1;
}) forPackage
) config.problems.handlers
);
getHandler =
list:
(foldl'
(acc: el: {
priority = max acc.priority el.priority;
handler =
if acc.priority == el.priority then
handlers.max acc.handler el.handler
else if acc.priority > el.priority then
acc.handler
else
el.handler;
})
{
priority = 0;
handler = "ignore";
}
list
).handler;
identOrder = [
"kind"
"name"
"package"
];
doLevel =
index:
let
ident = elemAt identOrder index;
nextLevel = if index + 1 == length identOrder then getHandler else doLevel (index + 1);
in
list:
let
# Partition all matchers into ident-specific (.wrong) and -unspecific (.right) ones
parted = partition (m: isNull m.${ident}) list;
# We only use the unspecific ones to compute the fallback
fallback = nextLevel parted.right;
specific = pipe parted.wrong [
(groupBy (m: m.${ident}))
# For ident-specific handlers, the unspecific ones also apply
(mapAttrs (package: handlers: nextLevel (handlers ++ parted.right)))
# Memory optimisation: Don't need a specific handler if it would end up the same as the fallback
(filterAttrs (name: res: res != fallback))
];
in
# Optimisation in case it's always the same handler,
# can propagate up for the entire switch to just be a string
if specific == { } && isString fallback then
fallback
else
{
"${ident}Fallback" = fallback;
"${ident}Specific" = specific;
};
switch = doLevel 0 constraints;
in
{
inherit switch;
handlerForProblem =
if isString switch then
pname: name: kind:
switch
else
pname: name: kind:
let
switch' = switch.kindSpecific.${kind} or switch.kindFallback;
in
if isString switch' then
switch'
else
let
switch'' = switch'.nameSpecific.${name} or switch'.nameFallback;
in
if isString switch'' then
switch''
else
switch''.packageSpecific.${pname} or switch''.packageFallback;
};
genCheckProblems =
config:
let
# This is here so that it gets cached for a (checkProblems config) thunk
inherit (genHandlerSwitch config)
handlerForProblem
;
in
attrs:
let
pname = getName attrs;
manualProblems = attrs.meta.problems or { };
in
if
# Fast path for when there's no problem that needs to be handled
# No automatic problems that needs handling
all (
problem:
problem.condition attrs -> handlerForProblem pname problem.kindName problem.kindName == "ignore"
) automaticProblems
&& (
# No manual problems
manualProblems == { }
# Or all manual problems are ignored
|| all (name: handlerForProblem pname name (manualProblems.${name}.kind or name) == "ignore") (
attrNames manualProblems
)
)
then
null
else
# Slow path, only here we actually figure out which problems we need to handle
let
problems = attrs.meta.problems or { } // genAutomaticProblems attrs;
problemsToHandle = filter (v: v.handler != "ignore") (
mapAttrsToList (name: problem: rec {
inherit name;
# Kind falls back to the name
kind = problem.kind or name;
handler = handlerForProblem pname name kind;
inherit problem;
}) problems
);
in
processProblems pname problemsToHandle;
processProblems =
pname: problemsToHandle:
let
grouped = groupBy (v: v.handler) problemsToHandle;
warnProblems = grouped.warn or [ ];
errorProblems = grouped.error or [ ];
# assert annotatedProblems != [ ];
fullMessage =
v:
"${v.name}${optionalString (v.kind != v.name) " (kind \"${v.kind}\")"}: "
+ "${v.problem.message}${
optionalString (v.problem.urls or [ ] != [ ]) " (${concatStringsSep ", " v.problem.urls})"
}";
warnings = map (x: {
reason = "problem";
msg = "has the following problem: ${fullMessage x}";
remediation = "See https://nixos.org/manual/nixpkgs/unstable#sec-problems"; # TODO: Add remediation, maybe just link to docs to keep it small
}) warnProblems;
error =
if errorProblems == [ ] then
null
else
{
msg = ''
has problems:
${concatMapStringsSep "\n" (x: "- ${fullMessage x}") errorProblems}
'';
## TODO: Add mention of problem.matchers, or maybe better link to docs of that
remediation = ''
See also https://nixos.org/manual/nixpkgs/unstable#sec-problems
To allow evaluation regardless, use:
- Nixpkgs import: import nixpkgs { config = <below code>; }
- NixOS: nixpkgs.config = <below code>;
- nix-* commands: Put below code in ~/.config/nixpkgs/config.nix
{
problems.handlers = {
${concatMapStringsSep "\n " (
problem:
''${escapeNixIdentifier pname}.${escapeNixIdentifier problem.name} = "warn"; # or "ignore"''
) errorProblems}
};
}
'';
};
in
{
inherit error warnings;
};
}

View file

@ -187,6 +187,11 @@ in
texlive = recurseIntoAttrs (callPackage ./texlive { });
# TODO: Temporarily disabled recursion so we can see the performance comparison in the PR,
# which only runs if there's exactly the same packages before and after, and this would add packages
#problems = recurseIntoAttrs (callPackage ./problems { });
problems = callPackage ./problems { };
cuda = callPackage ./cuda { };
trivial-builders = callPackage ../build-support/trivial-builders/test/default.nix { };

View file

@ -0,0 +1,24 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = {
problems.handlers = {
"a"."deprecated" = "error";
"a"."removal" = "error";
"a"."maintainerless" = "error";
};
};
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.description = "Some package";
meta.problems = {
deprecated.message = "Package is deprecated and replaced by b.";
removal.message = "Package will be removed.";
};
meta.maintainers = [ ];
}

View file

@ -0,0 +1,20 @@
(stack trace truncated; use '--show-trace' to show the full, detailed trace)
error: Refusing to evaluate package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:18 because it has problems:
- deprecated: Package is deprecated and replaced by b.
- maintainerless: This package has no declared maintainer, i.e. an empty `meta.maintainers` and `meta.teams` attribute.
- removal: Package will be removed.
See also https://nixos.org/manual/nixpkgs/unstable#sec-problems
To allow evaluation regardless, use:
- Nixpkgs import: import nixpkgs { config = <below code>; }
- NixOS: nixpkgs.config = <below code>;
- nix-* commands: Put below code in ~/.config/nixpkgs/config.nix
{
problems.handlers = {
a.deprecated = "warn"; # or "ignore"
a.maintainerless = "warn"; # or "ignore"
a.removal = "warn"; # or "ignore"
};
}

View file

@ -0,0 +1,22 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = {
problems.handlers = {
"a"."deprecated" = "error";
"a"."removal" = "error";
};
};
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.problems = {
deprecated.message = "Package is deprecated and replaced by b";
removal.message = "Package will be removed";
};
meta.maintainers = [ "hello" ];
}

View file

@ -0,0 +1,18 @@
(stack trace truncated; use '--show-trace' to show the full, detailed trace)
error: Refusing to evaluate package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:16 because it has problems:
- deprecated: Package is deprecated and replaced by b
- removal: Package will be removed
See also https://nixos.org/manual/nixpkgs/unstable#sec-problems
To allow evaluation regardless, use:
- Nixpkgs import: import nixpkgs { config = <below code>; }
- NixOS: nixpkgs.config = <below code>;
- nix-* commands: Put below code in ~/.config/nixpkgs/config.nix
{
problems.handlers = {
a.deprecated = "warn"; # or "ignore"
a.removal = "warn"; # or "ignore"
};
}

View file

@ -0,0 +1,20 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = {
problems.handlers = {
"a"."deprecated" = "error";
};
};
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.problems.deprecated = {
message = "Package is deprecated and replaced by b";
};
meta.maintainers = [ "hello" ];
}

View file

@ -0,0 +1,16 @@
(stack trace truncated; use '--show-trace' to show the full, detailed trace)
error: Refusing to evaluate package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:15 because it has problems:
- deprecated: Package is deprecated and replaced by b
See also https://nixos.org/manual/nixpkgs/unstable#sec-problems
To allow evaluation regardless, use:
- Nixpkgs import: import nixpkgs { config = <below code>; }
- NixOS: nixpkgs.config = <below code>;
- nix-* commands: Put below code in ~/.config/nixpkgs/config.nix
{
problems.handlers = {
a.deprecated = "warn"; # or "ignore"
};
}

View file

@ -0,0 +1,18 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = {
problems.handlers = {
"a"."deprecated" = "ignore";
};
};
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.problems.deprecated.message = "Package is deprecated and is replaced by b.";
meta.maintainers = [ "hello" ];
}

View file

@ -0,0 +1 @@
warning: you did not specify '--add-root'; the result might be removed by the garbage collector

View file

@ -0,0 +1,18 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = {
problems.handlers = {
"a"."deprecated" = "warn";
};
};
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.problems.deprecated.message = "Package a is deprecated and superseeded by b.";
meta.maintainers = [ "hello" ];
}

View file

@ -0,0 +1,2 @@
evaluation warning: Package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:15 has the following problem: deprecated: Package a is deprecated and superseeded by b. See https://nixos.org/manual/nixpkgs/unstable#sec-problems
warning: you did not specify '--add-root'; the result might be removed by the garbage collector

View file

@ -0,0 +1,15 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config.checkMeta = true;
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.problems = {
invalid.message = "No maintainers";
};
}

View file

@ -0,0 +1,4 @@
(stack trace truncated; use '--show-trace' to show the full, detailed trace)
error: Refusing to evaluate package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:11 because it has an invalid meta attrset:
- a.meta.problems.invalid: Problem kind invalid, inferred from the problem name, is invalid; expected enum<removal,deprecated>. You can specify an explicit problem kind with `a.meta.problems.invalid.kind`

View file

@ -0,0 +1,24 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = {
problems.handlers = {
"a"."maintainerless" = "warn";
"a"."deprecated" = "warn";
"a"."removal" = "warn";
};
};
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.description = "Some package";
meta.maintainers = [ ];
meta.problems = {
removal.message = "Package to be removed.";
deprecated.message = "Package will be deprecated.";
};
}

View file

@ -0,0 +1,4 @@
evaluation warning: Package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:18 has the following problem: deprecated: Package will be deprecated. See https://nixos.org/manual/nixpkgs/unstable#sec-problems
evaluation warning: Package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:18 has the following problem: maintainerless: This package has no declared maintainer, i.e. an empty `meta.maintainers` and `meta.teams` attribute. See https://nixos.org/manual/nixpkgs/unstable#sec-problems
evaluation warning: Package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:18 has the following problem: removal: Package to be removed. See https://nixos.org/manual/nixpkgs/unstable#sec-problems
warning: you did not specify '--add-root'; the result might be removed by the garbage collector

View file

@ -0,0 +1,20 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = {
problems.handlers = {
"a"."maintainerless" = "warn";
"a"."removal" = "warn";
};
};
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.description = "Some package";
meta.maintainers = [ ];
meta.problems.removal.message = "Package will be removed.";
}

View file

@ -0,0 +1,3 @@
evaluation warning: Package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:17 has the following problem: maintainerless: This package has no declared maintainer, i.e. an empty `meta.maintainers` and `meta.teams` attribute. See https://nixos.org/manual/nixpkgs/unstable#sec-problems
evaluation warning: Package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:17 has the following problem: removal: Package will be removed. See https://nixos.org/manual/nixpkgs/unstable#sec-problems
warning: you did not specify '--add-root'; the result might be removed by the garbage collector

View file

@ -0,0 +1,18 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = {
problems.handlers = {
"a"."maintainerless" = "error";
};
};
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.description = "Some package";
meta.maintainers = [ ];
}

View file

@ -0,0 +1,16 @@
(stack trace truncated; use '--show-trace' to show the full, detailed trace)
error: Refusing to evaluate package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:16 because it has problems:
- maintainerless: This package has no declared maintainer, i.e. an empty `meta.maintainers` and `meta.teams` attribute.
See also https://nixos.org/manual/nixpkgs/unstable#sec-problems
To allow evaluation regardless, use:
- Nixpkgs import: import nixpkgs { config = <below code>; }
- NixOS: nixpkgs.config = <below code>;
- nix-* commands: Put below code in ~/.config/nixpkgs/config.nix
{
problems.handlers = {
a.maintainerless = "warn"; # or "ignore"
};
}

View file

@ -0,0 +1,17 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = {
problems.handlers = {
"a"."maintainerless" = "ignore";
};
};
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.maintainers = [ ];
}

View file

@ -0,0 +1 @@
warning: you did not specify '--add-root'; the result might be removed by the garbage collector

View file

@ -0,0 +1,18 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = {
problems.handlers = {
"a"."maintainerless" = "warn";
};
};
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.description = "Some package";
meta.maintainers = [ ];
}

View file

@ -0,0 +1,2 @@
evaluation warning: Package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:16 has the following problem: maintainerless: This package has no declared maintainer, i.e. an empty `meta.maintainers` and `meta.teams` attribute. See https://nixos.org/manual/nixpkgs/unstable#sec-problems
warning: you did not specify '--add-root'; the result might be removed by the garbage collector

View file

@ -0,0 +1,22 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = {
checkMeta = true;
problems.matchers = [
{
package = "a";
handler = "error";
}
];
};
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.description = "Some package";
meta.problems.removal = { };
}

View file

@ -0,0 +1,4 @@
(stack trace truncated; use '--show-trace' to show the full, detailed trace)
error: Refusing to evaluate package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:20 because it has an invalid meta attrset:
- a.meta.problems.removal: `.message` not specified

View file

@ -0,0 +1,19 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = {
problems.matchers = [
{
package = "a";
handler = "error";
}
];
};
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
}

View file

@ -0,0 +1 @@
warning: you did not specify '--add-root'; the result might be removed by the garbage collector

View file

@ -0,0 +1,21 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = {
problems.matchers = [
{
package = "a";
handler = "error";
}
];
};
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.description = "Some package";
outputHash = pkgs.lib.fakeHash;
}

View file

@ -0,0 +1 @@
warning: you did not specify '--add-root'; the result might be removed by the garbage collector

View file

@ -0,0 +1,21 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = {
problems.matchers = [
{
package = "a";
handler = "error";
}
];
};
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.teams = [ "hello" ];
meta.description = "Some package";
}

View file

@ -0,0 +1 @@
warning: you did not specify '--add-root'; the result might be removed by the garbage collector

View file

@ -0,0 +1,21 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = {
problems.matchers = [
{
package = "a";
handler = "error";
}
];
};
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.maintainers = [ "hello" ];
meta.description = "Some package";
}

View file

@ -0,0 +1 @@
warning: you did not specify '--add-root'; the result might be removed by the garbage collector

View file

@ -0,0 +1,21 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = {
checkMeta = true;
problems.matchers = [
{
package = "a";
handler = "error";
}
];
};
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.problems.florp = true;
}

View file

@ -0,0 +1,5 @@
(stack trace truncated; use '--show-trace' to show the full, detailed trace)
error: Refusing to evaluate package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:19 because it has an invalid meta attrset:
- a.meta.problems.florp: Invalid value; expected attrs, got
true

View file

@ -0,0 +1,20 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = {
problems.matchers = [
{
package = "a";
name = "b";
handler = "error";
}
];
};
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
}

View file

@ -0,0 +1,5 @@
(stack trace truncated; use '--show-trace' to show the full, detailed trace)
error: Failed assertions:
- There is a problems.matchers with `package = "a"` and `name = "b". Use the following instead:
problems.handlers.a.b = "error";

View file

@ -0,0 +1,28 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = {
problems.matchers = [
{
package = "a";
handler = "error";
}
];
};
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.maintainers = [ "hello" ];
meta.description = "Some package";
meta.problems.removal = {
message = "Removed because of XYZ.";
urls = [
"https://example.com"
"https://anotherexample.com"
];
};
}

View file

@ -0,0 +1,16 @@
(stack trace truncated; use '--show-trace' to show the full, detailed trace)
error: Refusing to evaluate package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:20 because it has problems:
- removal: Removed because of XYZ. (https://example.com, https://anotherexample.com)
See also https://nixos.org/manual/nixpkgs/unstable#sec-problems
To allow evaluation regardless, use:
- Nixpkgs import: import nixpkgs { config = <below code>; }
- NixOS: nixpkgs.config = <below code>;
- nix-* commands: Put below code in ~/.config/nixpkgs/config.nix
{
problems.handlers = {
a.removal = "warn"; # or "ignore"
};
}

View file

@ -0,0 +1,17 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = {
problems.handlers.a.removal = "error";
};
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.problems = {
removal.message = "This package has been abandoned upstream and will be removed.";
};
}

View file

@ -0,0 +1,16 @@
(stack trace truncated; use '--show-trace' to show the full, detailed trace)
error: Refusing to evaluate package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:13 because it has problems:
- removal: This package has been abandoned upstream and will be removed.
See also https://nixos.org/manual/nixpkgs/unstable#sec-problems
To allow evaluation regardless, use:
- Nixpkgs import: import nixpkgs { config = <below code>; }
- NixOS: nixpkgs.config = <below code>;
- nix-* commands: Put below code in ~/.config/nixpkgs/config.nix
{
problems.handlers = {
a.removal = "warn"; # or "ignore"
};
}

View file

@ -0,0 +1,17 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = {
problems.handlers = {
"a"."removal" = "ignore";
};
};
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.problems.removal.message = "To be removed";
}

View file

@ -0,0 +1 @@
warning: you did not specify '--add-root'; the result might be removed by the garbage collector

View file

@ -0,0 +1,22 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config.checkMeta = true;
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.description = "Some package";
meta.problems = {
removal = {
message = "This package has been abandoned upstream and will be removed";
};
removal2 = {
kind = "removal";
message = "This package has been abandoned upstream and will be removed2";
};
};
}

View file

@ -0,0 +1,4 @@
(stack trace truncated; use '--show-trace' to show the full, detailed trace)
error: Refusing to evaluate package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:12 because it has an invalid meta attrset:
- a.meta.problems: Problem kind removal should be unique, but is used for these problems: removal, removal2

View file

@ -0,0 +1,14 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = { };
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.maintainers = [ "hello" ];
meta.problems.removal.message = "Package to be removed.";
}

View file

@ -0,0 +1,2 @@
evaluation warning: Package 'a-0' in /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-default.nix:11 has the following problem: removal: Package to be removed. See https://nixos.org/manual/nixpkgs/unstable#sec-problems
warning: you did not specify '--add-root'; the result might be removed by the garbage collector

View file

@ -0,0 +1,60 @@
{
lib,
nix,
runCommand,
removeReferencesTo,
path,
gitMinimal,
}:
let
nixpkgs = lib.cleanSource path;
unitResult = import ./unit.nix { inherit lib; };
in
assert lib.assertMsg (unitResult == [ ])
"problems/unit.nix failing: ${lib.generators.toPretty { } unitResult}";
lib.mapAttrs (
name: _:
let
nixFile = "${./cases + "/${name}/default.nix"}";
result = runCommand "test-problems-${name}" { nativeBuildInputs = [ removeReferencesTo ]; } ''
export NIX_STATE_DIR=$(mktemp -d)
mkdir $out
command=(
# FIXME: Using this version because it doesn't print a trace by default
# Probably should have some regex-style error matching instead
"${lib.getBin nix}/bin/nix-instantiate"
${nixFile}
# Readonly mode because we don't need to write anything to the store
"--readonly-mode"
"--arg"
"nixpkgs"
"${nixpkgs}"
)
echo "''${command[*]@Q}" > $out/command
echo "Running ''${command[*]@Q}"
set +e
"''${command[@]}" >$out/stdout 2>$out/stderr
set +e
echo "$?" > $out/code
echo "Command exited with code $(<$out/code)"
remove-references-to -t ${nixFile} $out/stderr
if grep '(stack trace truncated.*)' $out/stderr; then
sed -i -n '/(stack trace truncated.*)/,$ p' $out/stderr
fi
sed -i 's/^ //' $out/stderr
'';
checker = runCommand "test-problems-check-${name}" { } ''
if ! PAGER=cat ${lib.getExe gitMinimal} diff --no-index ${
./cases + "/${name}/expected-stderr"
} ${result}/stderr ; then
echo "Output of $(< ${result}/command) does not match what was expected. To adapt:"
echo "cp ${result}/stderr ${lib.path.removePrefix path ./cases + "/${name}/expected-stderr"}"
exit 1
fi
touch $out
'';
in
checker
) (builtins.readDir ./cases)

153
pkgs/test/problems/unit.nix Normal file
View file

@ -0,0 +1,153 @@
{ lib }:
let
p = import ../../stdenv/generic/problems.nix { inherit lib; };
genHandlerTest =
let
slowReference =
config: package: name: kind:
# Try to find an explicit handler
(config.problems.handlers.${package} or { }).${name}
# Fall back, iterating through the matchers
or (lib.pipe config.problems.matchers [
# Find matches
(lib.filter (
matcher:
(matcher.name != null -> name == matcher.name)
&& (matcher.kind != null -> kind == matcher.kind)
&& (matcher.package != null -> package == matcher.package)
))
# Extract handler level
(map (matcher: matcher.handler))
# Take the strongest matched handler level
(lib.foldl' p.handlers.max "ignore")
]);
genValue =
f:
map
(
package:
map
(
name:
map (kind: f package name kind) [
"k1"
"k2"
"k3"
]
)
[
"n1"
"n2"
"n3"
]
)
[
"p1"
"p2"
"p3"
];
in
v: {
expr = genValue (p.genHandlerSwitch { problems = v; }).handlerForProblem;
expected = genValue (slowReference {
problems = v;
});
};
in
lib.runTests {
testHandlersLessThan =
let
levels = p.handlers.levels;
slowReference =
a: b:
lib.lists.findFirstIndex (v: v == a) (abort "Shouldn't happen") levels
< lib.lists.findFirstIndex (v: v == b) (abort "Shouldn't happen") levels;
genValue =
f:
lib.genList (
i: lib.genList (j: f (lib.elemAt levels i) (lib.elemAt levels j)) (lib.length levels)
) (lib.length levels);
in
{
expr = genValue p.handlers.lessThan;
expected = genValue slowReference;
};
testHandlerEmpty = genHandlerTest {
matchers = [ ];
handlers = { };
};
testHandlerNameSpecificHandlers = genHandlerTest {
matchers = [ ];
handlers.p1.n1 = "error";
handlers.p1.n2 = "warn";
handlers.p1.n3 = "ignore";
};
testHandlerPackageSpecificHandlers = genHandlerTest {
matchers = [ ];
handlers.p1.n1 = "error";
handlers.p2.n1 = "warn";
handlers.p3.n1 = "ignore";
};
testHandlersOverrideMatchers = genHandlerTest {
matchers = [
{
package = "p1";
name = "n1";
kind = null;
handler = "error";
}
];
handlers.p1.n1 = "warn";
};
testMatchersDefault = genHandlerTest {
matchers = [
# Everything should warn by default
{
package = null;
name = null;
kind = null;
handler = "warn";
}
];
handlers = { };
};
testMatchersComplicated = genHandlerTest {
matchers = [
{
package = "p1";
name = null;
kind = null;
handler = "warn";
}
{
package = "p1";
name = "n1";
kind = null;
handler = "error";
}
{
package = "p1";
name = null;
kind = "k1";
handler = "error";
}
{
package = "p1";
name = "n2";
kind = "k2";
handler = "error";
}
];
handlers = { };
};
}

View file

@ -45,11 +45,18 @@ let
internal = true;
};
# Should be replaced by importing <nixos/modules/misc/assertions.nix> in the future
# see also https://github.com/NixOS/nixpkgs/pull/207187
warnings = mkOption {
type = types.listOf types.str;
default = [ ];
internal = true;
};
assertions = mkOption {
type = types.listOf types.anything;
default = [ ];
internal = true;
};
# Config options
@ -448,6 +455,8 @@ let
compatibility of configurations with 26.05.
'';
};
problems = (import ../stdenv/generic/problems.nix { inherit lib; }).configOptions;
};
in
@ -470,9 +479,35 @@ in
inherit options;
config = {
warnings = optionals config.warnUndeclaredOptions (
mapAttrsToList (k: v: "undeclared Nixpkgs option set: config.${k}") config._undeclared or { }
);
warnings =
optionals config.warnUndeclaredOptions (
mapAttrsToList (k: v: "undeclared Nixpkgs option set: config.${k}") config._undeclared or { }
)
++ lib.optional (config.showDerivationWarnings != [ ]) ''
`config.showDerivationWarnings = [ "maintainerless" ]` is deprecated, use `config.problems` instead:
config.problems.matchers = [ { kind = "maintainerless"; handler = "warn"; } ];
See this page for more details: https://nixos.org/manual/nixpkgs/unstable#sec-problems
'';
assertions =
# Collect the assertions from the problems.matchers.* submodules, propagate them into here
lib.concatMap (matcher: matcher.assertions) config.problems.matchers;
# Put the default value for matchers in here (as in, not as an *actual* mkDefault default value),
# to force it being merged with any custom values instead of being overridden.
problems.matchers = [
# Be loud and clear about package removals
{
kind = "removal";
handler = "warn";
}
(lib.mkIf (lib.elem "maintainerless" config.showDerivationWarnings) {
kind = "maintainerless";
handler = "warn";
})
];
};
}

View file

@ -126,7 +126,16 @@ let
};
# take all the rest as-is
config = lib.showWarnings configEval.config.warnings configEval.config;
config =
let
failedAssertionsString = lib.concatMapStringsSep "\n" (x: "- ${x.message}") (
lib.filter (x: !x.assertion) configEval.config.assertions
);
in
if failedAssertionsString != "" then
throw "Failed assertions:\n${failedAssertionsString}"
else
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