stdenv: Use meta.problems for meta.broken underneath

And add tests for it
This commit is contained in:
Silvan Mosberger
2026-02-26 16:49:05 +01:00
parent c7cbcd9117
commit 2e97caa6d3
20 changed files with 201 additions and 30 deletions

View File

@@ -153,7 +153,7 @@ The list of Nix platform types for which the [Hydra](https://github.com/nixos/hy
### `broken` {#var-meta-broken}
If set to `true`, the package is marked as "broken", meaning that it wont show up in [search.nixos.org](https://search.nixos.org/packages), and cannot be built or installed unless the environment variable [`NIXPKGS_ALLOW_BROKEN`](#opt-allowBroken) is set.
If set to `true`, the package is marked as "broken", meaning that it wont show up in [search.nixos.org](https://search.nixos.org/packages), and cannot be built or installed unless [explicitly allowed](#sec-allow-broken).
Such unconditionally-broken packages should be removed from Nixpkgs eventually unless they are fixed.
The value of this attribute can depend on a package's arguments, including `stdenv`.
@@ -181,6 +181,15 @@ This means that `broken` can be used to express constraints, for example:
This makes `broken` strictly more powerful than `meta.badPlatforms`.
However `meta.availableOn` currently examines only `meta.platforms` and `meta.badPlatforms`, so `meta.broken` does not influence the default values for optional dependencies.
Underneath, `meta.broken = true;` is the same as
```nix
{
meta.problems.broken.message = "This package is broken.";
}
```
By specifying this manually, the error message can be customised.
## `knownVulnerabilities` {#var-meta-knownVulnerabilities}
A list of known vulnerabilities affecting the package, usually identified by CVE identifiers.

View File

@@ -179,6 +179,7 @@ Currently, the following problem kinds are known (with more reserved to be added
- "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.
- "broken": Automatically generated for packages with `meta.broken = true`.
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.

View File

@@ -99,8 +99,6 @@ let
hasBlocklistedLicense = hasListedLicense blocklist;
allowBroken = config.allowBroken || getEnv "NIXPKGS_ALLOW_BROKEN" == "1";
allowUnsupportedSystem =
config.allowUnsupportedSystem || getEnv "NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM" == "1";
@@ -121,18 +119,6 @@ let
isMarkedBroken = attrs: attrs.meta.broken or false;
# Allow granular checks to allow only some broken packages
# Example:
# { pkgs, ... }:
# {
# allowBroken = false;
# allowBrokenPredicate = pkg: builtins.elem (pkgs.lib.getName pkg) [ "hello" ];
# }
allowBrokenPredicate = config.allowBrokenPredicate or (x: false);
hasDeniedBroken =
attrs: (attrs.meta.broken or false) && !allowBroken && !allowBrokenPredicate attrs;
hasUnsupportedPlatform = pkg: !(availableOn hostPlatform pkg);
isMarkedInsecure = attrs: (attrs.meta.knownVulnerabilities or [ ]) != [ ];
@@ -203,7 +189,6 @@ let
allow_attr:
{
Unfree = "NIXPKGS_ALLOW_UNFREE";
Broken = "NIXPKGS_ALLOW_BROKEN";
UnsupportedSystem = "NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM";
NonSource = "NIXPKGS_ALLOW_NONSOURCE";
}
@@ -212,7 +197,6 @@ let
allow_attr:
{
Unfree = "unfree packages";
Broken = "broken packages";
UnsupportedSystem = "packages that are unsupported for this system";
NonSource = "packages not built from source";
}
@@ -357,6 +341,7 @@ let
pkgConfigModules = listOf str;
inherit platforms;
hydraPlatforms = listOf str;
# Automatically turns into meta.problems.broken, see ./problems.nix
broken = bool;
unfree = bool;
unsupported = bool;
@@ -453,12 +438,6 @@ let
msg = "contains elements not built from source (${showSourceType attrs.meta.sourceProvenance})";
remediation = remediate_allowlist "NonSource" (remediate_predicate "allowNonSourcePredicate" attrs);
}
else if hasDeniedBroken attrs then
{
reason = "broken";
msg = "is marked as broken";
remediation = remediate_allowlist "Broken" "";
}
else if hasUnsupportedPlatform attrs && !allowUnsupportedSystem then
let
toPretty' = toPretty {

View File

@@ -78,6 +78,7 @@ rec {
manual = [
"removal"
"deprecated"
"broken"
];
# Problem kinds that are currently only allowed to be specified once
unique = [
@@ -101,7 +102,7 @@ rec {
# 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:
config: attrs:
# Order of checks optimised for short-circuiting the common case of having maintainers
(attrs.meta.maintainers or [ ] == [ ])
&& (attrs.meta.teams or [ ] == [ ])
@@ -109,13 +110,26 @@ rec {
&& (attrs ? meta.description);
value.message = "This package has no declared maintainer, i.e. an empty `meta.maintainers` and `meta.teams` attribute.";
}
{
kindName = "broken";
condition =
config:
let
# TODO: Consider deprecating this or making it generic for all problems
allowBroken = config.allowBroken || builtins.getEnv "NIXPKGS_ALLOW_BROKEN" == "1";
allowBrokenPredicate = config.allowBrokenPredicate or (x: false);
in
attrs: attrs.meta.broken or false && !allowBroken && !allowBrokenPredicate attrs;
value.message = "This package is broken.";
}
];
genAutomaticProblems =
attrs:
config: attrs:
listToAttrs (
map (problem: lib.nameValuePair problem.kindName problem.value) (
filter (problem: problem.condition attrs) automaticProblems
filter (problem: problem.condition config attrs) automaticProblems
)
);
@@ -419,6 +433,10 @@ rec {
inherit (genHandlerSwitch config)
handlerForProblem
;
# Makes sure that automatic problems can cache with just config applied
automaticProblemsConfigCache = map (
problem: problem // { condition = problem.condition config; }
) automaticProblems;
in
attrs:
let
@@ -431,7 +449,7 @@ rec {
all (
problem:
problem.condition attrs -> handlerForProblem pname problem.kindName problem.kindName == "ignore"
) automaticProblems
) automaticProblemsConfigCache
&& (
# No manual problems
manualProblems == { }
@@ -445,7 +463,7 @@ rec {
else
# Slow path, only here we actually figure out which problems we need to handle
let
problems = attrs.meta.problems or { } // genAutomaticProblems attrs;
problems = attrs.meta.problems or { } // genAutomaticProblems config attrs;
problemsToHandle = filter (v: v.handler != "ignore") (
mapAttrsToList (name: problem: rec {
inherit name;

View File

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

View File

@@ -0,0 +1,3 @@
{
NIXPKGS_ALLOW_BROKEN = 1;
}

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
lib = pkgs.lib;
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = {
allowBrokenPredicate = attrs: lib.getName attrs == "a";
};
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.maintainers = [ "hello" ];
meta.description = "Some package";
meta.broken = true;
}

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
lib = pkgs.lib;
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = {
allowBrokenPredicate = attrs: lib.getName attrs == "b";
};
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.maintainers = [ "hello" ];
meta.description = "Some package";
meta.broken = true;
}

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:
- broken: This package is broken.
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.broken = "warn"; # or "ignore"
};
}

View File

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

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,14 @@
{ nixpkgs }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ ];
config = { };
};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "a";
version = "0";
meta.description = "Some package";
meta.problems.broken.message = "This package is broken because horse.";
}

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:12 because it has problems:
- broken: This package is broken because horse.
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.broken = "warn"; # or "ignore"
};
}

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.description = "Some package";
meta.broken = true;
}

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:12 because it has problems:
- broken: This package is broken.
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.broken = "warn"; # or "ignore"
};
}

View File

@@ -1,4 +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`
- a.meta.problems.invalid: Problem kind invalid, inferred from the problem name, is invalid; expected enum<removal,deprecated,broken>. You can specify an explicit problem kind with `a.meta.problems.invalid.kind`

View File

@@ -20,7 +20,17 @@ lib.mapAttrs (
export NIX_STATE_DIR=$(mktemp -d)
mkdir $out
command=(
command=()
${lib.optionalString (builtins.pathExists (./cases + "/${name}/env.nix")) ''
command+=(
env
${lib.concatMapAttrsStringSep "\n" (name: value: "${name}=${toString value}") (
import (./cases + "/${name}/env.nix")
)}
)
''}
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"

View File

@@ -498,6 +498,10 @@ in
# 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 = [
{
kind = "broken";
handler = "error";
}
# Be loud and clear about package removals
{
kind = "removal";