diff --git a/pkgs/stdenv/generic/check-meta.nix b/pkgs/stdenv/generic/check-meta.nix index 70c009c3e83ae..691ed7a9a6268 100644 --- a/pkgs/stdenv/generic/check-meta.nix +++ b/pkgs/stdenv/generic/check-meta.nix @@ -11,8 +11,6 @@ let all attrValues concatMapStrings - concatStrings - filter findFirst getName length @@ -46,7 +44,15 @@ let getEnv ; - inherit (import ./problems.nix { inherit lib; }) + remediationLib = import ./remediation.nix { inherit lib; }; + inherit (remediationLib) + remediateOutputsToInstall + remediate_insecure + remediate_allowlist + remediate_predicate + ; + + inherit (import ./problems.nix { inherit lib remediationLib; }) problemsType genCheckProblems ; @@ -97,9 +103,6 @@ let hasBlocklistedLicense = hasListedLicense blocklist; - allowUnsupportedSystem = - config.allowUnsupportedSystem || getEnv "NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM" == "1"; - isUnfree = licenses: if isAttrs licenses && licenses ? "licenseType" then @@ -119,20 +122,6 @@ let isMarkedBroken = attrs: attrs.meta.broken or false; - # Logical inversion of meta.availableOn for hostPlatform - hasUnsupportedPlatform = - hostPlatform: - let - inherit (hostPlatform) system; - # in almost all cases, meta.platforms is a simple list of strings, and we - # can just check if it contains the current system. we only run the more - # intensive platformMatch if necessary - anyHostPlatform = list: elem system list || any (platformMatch hostPlatform) list; - in - pkg: - pkg ? meta.platforms && !(anyHostPlatform pkg.meta.platforms) - || pkg ? meta.badPlatforms && anyHostPlatform pkg.meta.badPlatforms; - isMarkedInsecure = attrs: (attrs.meta.knownVulnerabilities or [ ]) != [ ]; # Allow granular checks to allow only some unfree packages @@ -195,109 +184,6 @@ let showSourceType = showLicenseOrSourceType; pos_str = meta: meta.position or "«unknown-file»"; - - remediation_env_var = - allow_attr: - { - Unfree = "NIXPKGS_ALLOW_UNFREE"; - UnsupportedSystem = "NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM"; - NonSource = "NIXPKGS_ALLOW_NONSOURCE"; - } - .${allow_attr}; - remediation_phrase = - allow_attr: - { - Unfree = "unfree packages"; - UnsupportedSystem = "packages that are unsupported for this system"; - NonSource = "packages not built from source"; - } - .${allow_attr}; - remediate_predicate = predicateConfigAttr: attrs: '' - - Alternatively you can configure a predicate to allow specific packages: - { nixpkgs.config.${predicateConfigAttr} = pkg: builtins.elem (lib.getName pkg) [ - "${getName attrs}" - ]; - } - ''; - - # flakeNote will be printed in the remediation messages below. - flakeNote = " - Note: When using `nix shell`, `nix build`, `nix develop`, etc with a flake, - then pass `--impure` in order to allow use of environment variables. - "; - - remediate_allowlist = allow_attr: rebuild_amendment: '' - a) To temporarily allow ${remediation_phrase allow_attr}, you can use an environment variable - for a single invocation of the nix tools. - - $ export ${remediation_env_var allow_attr}=1 - ${flakeNote} - b) For `nixos-rebuild` you can set - { nixpkgs.config.allow${allow_attr} = true; } - in configuration.nix to override this. - ${rebuild_amendment} - c) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add - { allow${allow_attr} = true; } - to ~/.config/nixpkgs/config.nix. - ''; - - remediate_insecure = - attrs: - '' - - Known issues: - '' - + (concatStrings (map (issue: " - ${issue}\n") attrs.meta.knownVulnerabilities)) - + '' - - You can install it anyway by allowing this package, using the - following methods: - - a) To temporarily allow all insecure packages, you can use an environment - variable for a single invocation of the nix tools: - - $ export NIXPKGS_ALLOW_INSECURE=1 - ${flakeNote} - b) for `nixos-rebuild` you can add ‘${getNameWithVersion attrs}’ to - `nixpkgs.config.permittedInsecurePackages` in the configuration.nix, - like so: - - { - nixpkgs.config.permittedInsecurePackages = [ - "${getNameWithVersion attrs}" - ]; - } - - c) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add - ‘${getNameWithVersion attrs}’ to `permittedInsecurePackages` in - ~/.config/nixpkgs/config.nix, like so: - - { - permittedInsecurePackages = [ - "${getNameWithVersion attrs}" - ]; - } - - ''; - - remediateOutputsToInstall = - attrs: - let - expectedOutputs = attrs.meta.outputsToInstall or [ ]; - actualOutputs = attrs.outputs or [ "out" ]; - missingOutputs = filter (output: !elem output actualOutputs) expectedOutputs; - in - '' - The package ${getNameWithVersion attrs} has set meta.outputsToInstall to: ${builtins.concatStringsSep ", " expectedOutputs} - - however ${getNameWithVersion attrs} only has the outputs: ${builtins.concatStringsSep ", " actualOutputs} - - and is missing the following outputs: - - ${concatStrings (map (output: " - ${output}\n") missingOutputs)} - ''; - metaType = let types = import ./meta-types.nix { inherit lib; }; @@ -417,9 +303,6 @@ let # Along with a boolean flag for each reason checkValidity = hostPlatform: - let - hasUnsupportedPlatform' = hasUnsupportedPlatform hostPlatform; - in attrs: if !attrs ? meta then null @@ -462,23 +345,6 @@ let msg = "contains elements not built from source (‘${showSourceType attrs.meta.sourceProvenance}’)"; remediation = remediate_allowlist "NonSource" (remediate_predicate "allowNonSourcePredicate" attrs); } - else if hasUnsupportedPlatform' attrs && !allowUnsupportedSystem then - let - toPretty' = toPretty { - allowPrettyValues = true; - indent = " "; - }; - in - { - reason = "unsupported"; - msg = '' - is not available on the requested hostPlatform: - hostPlatform.system = "${hostPlatform.system}" - package.meta.platforms = ${toPretty' (attrs.meta.platforms or [ ])} - package.meta.badPlatforms = ${toPretty' (attrs.meta.badPlatforms or [ ])} - ''; - remediation = remediate_allowlist "UnsupportedSystem" ""; - } else if hasDisallowedInsecure attrs then { reason = "insecure"; @@ -526,9 +392,6 @@ let # validity = checkMeta.assertValidity hostPlatform { inherit meta attrs; }; commonMeta = hostPlatform: - let - hasUnsupportedPlatform' = hasUnsupportedPlatform hostPlatform; - in { validity, attrs, @@ -667,7 +530,7 @@ let # Expose the result of the checks for everyone to see. unfree = hasUnfreeLicense attrs; broken = isMarkedBroken attrs; - unsupported = hasUnsupportedPlatform' attrs; + unsupported = warn "Usage of deprectaed `unsupported` marker? FIXME: this should be gathered from the problems emitted for this specific derivation" false; insecure = isMarkedInsecure attrs; available = @@ -720,7 +583,7 @@ let { meta, attrs }: let invalid = checkValidity' attrs; - problems = checkProblems attrs; + problems = checkProblems hostPlatform attrs; in if isNull invalid then if isNull problems then diff --git a/pkgs/stdenv/generic/problems.nix b/pkgs/stdenv/generic/problems.nix index 926075bd82298..56917aeccc7a8 100644 --- a/pkgs/stdenv/generic/problems.nix +++ b/pkgs/stdenv/generic/problems.nix @@ -11,7 +11,10 @@ nix-build -A tests.problems */ -{ lib }: +{ + lib, + remediationLib ? (import ./remediation.nix { inherit lib; }), +}: rec { @@ -19,6 +22,14 @@ rec { escapeNixIdentifier ; + inherit (lib.meta) + platformMatch + ; + + inherit (builtins) + getEnv + ; + inherit (lib) any listToAttrs @@ -50,6 +61,10 @@ rec { genAttrs ; + inherit (lib.generators) + toPretty + ; + handlers = rec { # Ordered from less to more levels = [ @@ -73,7 +88,7 @@ rec { # 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; + known = (builtins.attrNames automaticProblems) ++ manual; # Problem kinds that are currently allowed to be specified in `meta.problems` manual = [ "removal" @@ -90,53 +105,98 @@ rec { 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. - config: attrs: + automaticProblems = { + maintainerless = ( + config: attrs: _: + # 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. + if # 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."; - } - { - 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 = - if config ? allowBrokenPredicate then - lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2605) - "config.allowBrokenPredicate is deprecated, use config.problems.handlers.myPackage.broken = \"warn\" for individual packages instead." - config.allowBrokenPredicate - else - x: false; - in - attrs: attrs.meta.broken or false && !allowBroken && !allowBrokenPredicate attrs; - value.message = "This package is broken."; - } - ]; + && (attrs ? meta.description) + then + { + message = "This package has no declared maintainer, i.e. an empty `meta.maintainers` and `meta.teams` attribute."; + } + else + false + ); + broken = ( + config: + let + # TODO: Consider deprecating this or making it generic for all problems + allowBroken = config.allowBroken || builtins.getEnv "NIXPKGS_ALLOW_BROKEN" == "1"; + allowBrokenPredicate = + if config ? allowBrokenPredicate then + lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2605) + "config.allowBrokenPredicate is deprecated, use config.problems.handlers.myPackage.broken = \"warn\" for individual packages instead." + config.allowBrokenPredicate + else + x: false; + in + attrs: _: + if (attrs.meta.broken or false && !allowBroken && !allowBrokenPredicate attrs) then + { + message = "This package is broken."; + } + else + false + ); + unsupported = ( + config: attrs: hostPlatform: + let + isHostPlatformInList = + default: list: + if list == null then + default + else + (elem hostPlatform.system list) || any (platformMatch hostPlatform) list; + + inPlatforms = isHostPlatformInList true (attrs.meta.platforms or null); + inBadPlatforms = isHostPlatformInList false (attrs.meta.badPlatforms or null); + allowUnsupportedSystem = + config.allowUnsupportedSystem || getEnv "NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM" == "1"; + in + if (!inPlatforms || inBadPlatforms) && !allowUnsupportedSystem then + { + message = + let + toPretty' = toPretty { + allowPrettyValues = true; + indent = " "; + }; + in + # FIXME: Remediation is inlined into the message so it is + # local within the error output, this should be properly + # structured (as in as remediation attribute) and then the + # `processProblems` function should "print" it properly group + # it together with the respective error(s). + '' + This package is not available on the requested hostPlatform: + hostPlatform.system = "${hostPlatform.system}" + package.meta.platforms = ${toPretty' (attrs.meta.platforms or [ ])} + package.meta.badPlatforms = ${toPretty' (attrs.meta.badPlatforms or [ ])} + + ${remediationLib.remediate_allowlist "UnsupportedSystem" ""} + ''; + } + else + false + ); + }; genAutomaticProblems = - config: attrs: - listToAttrs ( - map (problem: lib.nameValuePair problem.kindName problem.value) ( - filter (problem: problem.condition config attrs) automaticProblems - ) + config: attrs: hostPlatform: + filterAttrs (_: v: v != false) ( + mapAttrs (kindName: problemFn: problemFn config attrs hostPlatform) automaticProblems ); # A module system type for Nixpkgs config @@ -440,11 +500,9 @@ rec { handlerForProblem ; # Makes sure that automatic problems can cache with just config applied - automaticProblemsConfigCache = map ( - problem: problem // { condition = problem.condition config; } - ) automaticProblems; + automaticProblemsConfigCache = mapAttrs (_: problem: problem config) automaticProblems; in - attrs: + hostPlatform: attrs: let pname = getName attrs; manualProblems = attrs.meta.problems or { }; @@ -452,10 +510,15 @@ rec { 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" - ) automaticProblemsConfigCache + all (v: v) ( + mapAttrsToList ( + kindName: problemFn: + let + v = problemFn attrs hostPlatform; + in + v != false -> handlerForProblem pname kindName kindName == "ignore" + ) automaticProblemsConfigCache + ) && ( # No manual problems manualProblems == { } @@ -469,7 +532,7 @@ rec { else # Slow path, only here we actually figure out which problems we need to handle let - problems = attrs.meta.problems or { } // genAutomaticProblems config attrs; + problems = attrs.meta.problems or { } // genAutomaticProblems config attrs hostPlatform; problemsToHandle = filter (v: v.handler != "ignore") ( mapAttrsToList (name: problem: rec { inherit name; @@ -501,7 +564,9 @@ rec { 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 + remediation = + (if x ? remediation then x.remediation + "\n" else "") + + "See https://nixos.org/manual/nixpkgs/unstable#sec-problems"; }) warnProblems; error = @@ -519,6 +584,10 @@ rec { ${concatMapStringsSep "\n" (x: "- ${fullMessage x}") errorProblems} ''; ## TODO: Add mention of problem.matchers, or maybe better link to docs of that + ## TODO: Add support for a `remediation` attribute in each + ## of the errors. The output should be logically grouped + ## and the additional remediation information should be + ## local to the error message. remediation = '' See also https://nixos.org/manual/nixpkgs/unstable#sec-problems To allow evaluation regardless, use: diff --git a/pkgs/stdenv/generic/remediation.nix b/pkgs/stdenv/generic/remediation.nix new file mode 100644 index 0000000000000..9aa76cd5ebcbc --- /dev/null +++ b/pkgs/stdenv/generic/remediation.nix @@ -0,0 +1,114 @@ +{ lib }: +let + + inherit (lib) + elem + getName + concatStrings + getNameWithVersion + filter + ; + + remediation_env_var = + allow_attr: + { + Unfree = "NIXPKGS_ALLOW_UNFREE"; + UnsupportedSystem = "NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM"; + NonSource = "NIXPKGS_ALLOW_NONSOURCE"; + } + .${allow_attr}; + remediation_phrase = + allow_attr: + { + Unfree = "unfree packages"; + UnsupportedSystem = "packages that are unsupported for this system"; + NonSource = "packages not built from source"; + } + .${allow_attr}; + + flakeNote = " + Note: When using `nix shell`, `nix build`, `nix develop`, etc with a flake, + then pass `--impure` in order to allow use of environment variables. + "; +in +{ + remediate_predicate = predicateConfigAttr: attrs: '' + + Alternatively you can configure a predicate to allow specific packages: + { nixpkgs.config.${predicateConfigAttr} = pkg: builtins.elem (lib.getName pkg) [ + "${getName attrs}" + ]; + } + ''; + + remediate_allowlist = allow_attr: rebuild_amendment: '' + a) To temporarily allow ${remediation_phrase allow_attr}, you can use an environment variable + for a single invocation of the nix tools. + + $ export ${remediation_env_var allow_attr}=1 + ${flakeNote} + b) For `nixos-rebuild` you can set + { nixpkgs.config.allow${allow_attr} = true; } + in configuration.nix to override this. + ${rebuild_amendment} + c) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add + { allow${allow_attr} = true; } + to ~/.config/nixpkgs/config.nix. + ''; + + remediate_insecure = + attrs: + '' + + Known issues: + '' + + (concatStrings (map (issue: " - ${issue}\n") attrs.meta.knownVulnerabilities)) + + '' + + You can install it anyway by allowing this package, using the + following methods: + + a) To temporarily allow all insecure packages, you can use an environment + variable for a single invocation of the nix tools: + + $ export NIXPKGS_ALLOW_INSECURE=1 + ${flakeNote} + b) for `nixos-rebuild` you can add ‘${getNameWithVersion attrs}’ to + `nixpkgs.config.permittedInsecurePackages` in the configuration.nix, + like so: + + { + nixpkgs.config.permittedInsecurePackages = [ + "${getNameWithVersion attrs}" + ]; + } + + c) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add + ‘${getNameWithVersion attrs}’ to `permittedInsecurePackages` in + ~/.config/nixpkgs/config.nix, like so: + + { + permittedInsecurePackages = [ + "${getNameWithVersion attrs}" + ]; + } + + ''; + + remediateOutputsToInstall = + attrs: + let + expectedOutputs = attrs.meta.outputsToInstall or [ ]; + actualOutputs = attrs.outputs or [ "out" ]; + missingOutputs = filter (output: !elem output actualOutputs) expectedOutputs; + in + '' + The package ${getNameWithVersion attrs} has set meta.outputsToInstall to: ${builtins.concatStringsSep ", " expectedOutputs} + + however ${getNameWithVersion attrs} only has the outputs: ${builtins.concatStringsSep ", " actualOutputs} + + and is missing the following outputs: + + ${concatStrings (map (output: " - ${output}\n") missingOutputs)} + ''; +} diff --git a/pkgs/test/problems/cases/unsupported/default.nix b/pkgs/test/problems/cases/unsupported/default.nix new file mode 100644 index 0000000000000..40c62417fff88 --- /dev/null +++ b/pkgs/test/problems/cases/unsupported/default.nix @@ -0,0 +1,14 @@ +{ nixpkgs }: +let + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ ]; + config = { }; + }; +in +pkgs.stdenvNoCC.mkDerivation { + pname = "a"; + version = "0"; + meta.platforms = [ "aarch64-darwin" ]; + meta.badPlatforms = [ "x86_64-linux" ]; +} diff --git a/pkgs/test/problems/cases/unsupported/expected-stderr b/pkgs/test/problems/cases/unsupported/expected-stderr new file mode 100644 index 0000000000000..68ad4d1d67ed9 --- /dev/null +++ b/pkgs/test/problems/cases/unsupported/expected-stderr @@ -0,0 +1,41 @@ +(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 problems: +- unsupported: This package is not available on the requested hostPlatform: + hostPlatform.system = "x86_64-linux" + package.meta.platforms = [ + "aarch64-darwin" + ] + package.meta.badPlatforms = [ + "x86_64-linux" + ] + +a) To temporarily allow packages that are unsupported for this system, you can use an environment variable + for a single invocation of the nix tools. + + $ export NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM=1 + + Note: When using `nix shell`, `nix build`, `nix develop`, etc with a flake, + then pass `--impure` in order to allow use of environment variables. + +b) For `nixos-rebuild` you can set + { nixpkgs.config.allowUnsupportedSystem = true; } +in configuration.nix to override this. + +c) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add + { allowUnsupportedSystem = true; } +to ~/.config/nixpkgs/config.nix. + + + +See also https://nixos.org/manual/nixpkgs/unstable#sec-problems +To allow evaluation regardless, use: +- Nixpkgs import: import nixpkgs { config = ; } +- NixOS: nixpkgs.config = ; +- nix-* commands: Put below code in ~/.config/nixpkgs/config.nix + + { + problems.handlers = { + a.unsupported = "warn"; # or "ignore" + }; + } diff --git a/pkgs/top-level/config.nix b/pkgs/top-level/config.nix index 526dd41827026..cd204c06c3d44 100644 --- a/pkgs/top-level/config.nix +++ b/pkgs/top-level/config.nix @@ -526,6 +526,11 @@ in kind = "maintainerless"; handler = "warn"; }) + + { + kind = "unsupported"; + handler = "error"; + } ]; };