817c933878
Generated from https://github.com/NixOS/nix/pull/2761: ``` ns calls ns/call - /home/grahamc/projects/github.com/NixOS/nixpkgs/pkgs/stdenv/generic/check-meta.nix:22:5 591200 15026 39.3451 + /home/grahamc/projects/github.com/NixOS/nixpkgs/pkgs/stdenv/generic/check-meta.nix:22:5 8744 308 28.3896 ``` more, generated by: ``` $ NIX_SHOW_STATS=1 NIX_COUNT_CALLS=1 nix-instantiate ./pkgs/top-level/release.nix -A unstable > before 2>&1 $ jq -r '.functions | map((.name + ":" + .file + ":" + (.line|tostring) + ":" + (.column|tostring) + " " + (.count|tostring))) | .[]' before | sort > before.list ``` applying this patch, then: ``` $ NIX_SHOW_STATS=1 NIX_COUNT_CALLS=1 nix-instantiate ./pkgs/top-level/release.nix -A unstable > after 2>&1 $ jq -r '.functions | map((.name + ":" + .file + ":" + (.line|tostring) + ":" + (.column|tostring) + " " + (.count|tostring))) | .[]' after | sort > after.list ``` and then diffing before.list and after.list to get: ``` calls - :/home/grahamc/projects/github.com/NixOS/nixpkgs/pkgs/stdenv/generic/check-meta.nix:4:1 7513 + :/home/grahamc/projects/github.com/NixOS/nixpkgs/pkgs/stdenv/generic/check-meta.nix:4:1 154 - mutuallyExclusive:/home/grahamc/projects/github.com/NixOS/nixpkgs/lib/lists.nix:658:23 7513 + mutuallyExclusive:/home/grahamc/projects/github.com/NixOS/nixpkgs/lib/lists.nix:658:23 154 - mutuallyExclusive:/home/grahamc/projects/github.com/NixOS/nixpkgs/lib/lists.nix:658:26 7513 + mutuallyExclusive:/home/grahamc/projects/github.com/NixOS/nixpkgs/lib/lists.nix:658:26 154 - onlyLicenses:/home/grahamc/projects/github.com/NixOS/nixpkgs/pkgs/stdenv/generic/check-meta.nix:21:18 15026 + onlyLicenses:/home/grahamc/projects/github.com/NixOS/nixpkgs/pkgs/stdenv/generic/check-meta.nix:21:18 308 ``` The following information is from `NIX_SHOW_STATS=1 GC_INITIAL_HEAP_SIZE=4g nix-env -f ./outpaths.nix -qaP --no-name --out-path --arg checkMeta true`: | stat | before | after | Δ | Δ% | |:---------------------------|---------------:|---------------:|:----------------|--------:| | **cpuTime** | 179.915 | 145.543 | 🡖 34.372 | -19.10% | | **envs-bytes** | 3,900,878,824 | 3,599,483,208 | 🡖 301,395,616 | -7.73% | | **envs-elements** | 214,426,071 | 185,881,709 | 🡖 28,544,362 | -13.31% | | **envs-number** | 136,591,891 | 132,026,846 | 🡖 4,565,045 | -3.34% | | **gc-heapSize** | 11,400,048,640 | 12,314,890,240 | 🡕 914,841,600 | 8.02% | | **gc-totalBytes** | 25,976,902,560 | 24,510,740,176 | 🡖 1,466,162,384 | -5.64% | | **list-bytes** | 1,665,290,080 | 1,665,290,080 | 0 | | | **list-concats** | 7,264,417 | 7,264,417 | 0 | | | **list-elements** | 208,161,260 | 208,161,260 | 0 | | | **nrAvoided** | 191,359,386 | 179,693,661 | 🡖 11,665,725 | -6.10% | | **nrFunctionCalls** | 119,665,062 | 116,348,547 | 🡖 3,316,515 | -2.77% | | **nrLookups** | 80,996,257 | 76,069,825 | 🡖 4,926,432 | -6.08% | | **nrOpUpdateValuesCopied** | 213,930,649 | 213,930,649 | 0 | | | **nrOpUpdates** | 12,025,937 | 12,025,937 | 0 | | | **nrPrimOpCalls** | 88,105,604 | 86,451,598 | 🡖 1,654,006 | -1.88% | | **nrThunks** | 196,842,044 | 175,126,701 | 🡖 21,715,343 | -11.03% | | **sets-bytes** | 7,678,425,776 | 7,285,767,928 | 🡖 392,657,848 | -5.11% | | **sets-elements** | 310,241,340 | 294,373,227 | 🡖 15,868,113 | -5.11% | | **sets-number** | 29,079,202 | 27,601,310 | 🡖 1,477,892 | -5.08% | | **sizes-Attr** | 24 | 24 | 0 | | | **sizes-Bindings** | 8 | 8 | 0 | | | **sizes-Env** | 16 | 16 | 0 | | | **sizes-Value** | 24 | 24 | 0 | | | **symbols-bytes** | 16,474,666 | 16,474,676 | 🡕 10 | 0.00% | | **symbols-number** | 376,426 | 376,427 | 🡕 1 | 0.00% | | **values-bytes** | 6,856,506,288 | 6,316,585,560 | 🡖 539,920,728 | -7.87% | | **values-number** | 285,687,762 | 263,191,065 | 🡖 22,496,697 | -7.87% | The following information is from `NIX_SHOW_STATS=1 GC_INITIAL_HEAP_SIZE=4g nix-instantiate ./nixos/release-combined.nix -A tested`: | stat | before | after | Δ | Δ% | |:---------------------------|---------------:|---------------:|:----------------|-------:| | **cpuTime** | 256.071 | 237.531 | 🡖 18.54 | -7.24% | | **envs-bytes** | 7,111,004,192 | 7,041,478,520 | 🡖 69,525,672 | -0.98% | | **envs-elements** | 346,236,940 | 339,588,487 | 🡖 6,648,453 | -1.92% | | **envs-number** | 271,319,292 | 270,298,164 | 🡖 1,021,128 | -0.38% | | **gc-heapSize** | 8,995,291,136 | 10,110,009,344 | 🡕 1,114,718,208 | 12.39% | | **gc-totalBytes** | 37,172,737,408 | 36,878,391,888 | 🡖 294,345,520 | -0.79% | | **list-bytes** | 1,886,162,656 | 1,886,163,472 | 🡕 816 | 0.00% | | **list-concats** | 6,898,114 | 6,898,114 | 0 | | | **list-elements** | 235,770,332 | 235,770,434 | 🡕 102 | 0.00% | | **nrAvoided** | 328,829,821 | 326,618,157 | 🡖 2,211,664 | -0.67% | | **nrFunctionCalls** | 240,850,845 | 239,998,495 | 🡖 852,350 | -0.35% | | **nrLookups** | 144,849,632 | 142,126,339 | 🡖 2,723,293 | -1.88% | | **nrOpUpdateValuesCopied** | 251,032,504 | 251,032,504 | 0 | | | **nrOpUpdates** | 17,903,110 | 17,903,110 | 0 | | | **nrPrimOpCalls** | 140,674,913 | 139,485,975 | 🡖 1,188,938 | -0.85% | | **nrThunks** | 294,643,131 | 288,678,022 | 🡖 5,965,109 | -2.02% | | **sets-bytes** | 9,464,322,192 | 9,456,172,048 | 🡖 8,150,144 | -0.09% | | **sets-elements** | 377,474,889 | 377,134,877 | 🡖 340,012 | -0.09% | | **sets-number** | 50,615,607 | 50,616,875 | 🡕 1,268 | 0.00% | | **sizes-Attr** | 24 | 24 | 0 | | | **sizes-Bindings** | 8 | 8 | 0 | | | **sizes-Env** | 16 | 16 | 0 | | | **sizes-Value** | 24 | 24 | 0 | | | **symbols-bytes** | 3,147,102 | 3,147,064 | 🡖 38 | -0.00% | | **symbols-number** | 82,819 | 82,819 | 0 | | | **values-bytes** | 11,147,448,768 | 10,996,111,512 | 🡖 151,337,256 | -1.36% | | **values-number** | 464,477,032 | 458,171,313 | 🡖 6,305,719 | -1.36% |
257 lines
9.7 KiB
Nix
257 lines
9.7 KiB
Nix
# Checks derivation meta and attrs for problems (like brokenness,
|
||
# licenses, etc).
|
||
|
||
{ lib, config, hostPlatform }:
|
||
|
||
let
|
||
# 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;
|
||
|
||
# See discussion at https://github.com/NixOS/nixpkgs/pull/25304#issuecomment-298385426
|
||
# for why this defaults to false, but I (@copumpkin) want to default it to true soon.
|
||
shouldCheckMeta = config.checkMeta or false;
|
||
|
||
allowUnfree = config.allowUnfree or false
|
||
|| builtins.getEnv "NIXPKGS_ALLOW_UNFREE" == "1";
|
||
|
||
whitelist = config.whitelistedLicenses or [];
|
||
blacklist = config.blacklistedLicenses or [];
|
||
|
||
onlyLicenses = list:
|
||
lib.lists.all (license:
|
||
let l = lib.licenses.${license.shortName or "BROKEN"} or false; in
|
||
if license == l then true else
|
||
throw ''‘${showLicense license}’ is not an attribute of lib.licenses''
|
||
) list;
|
||
|
||
areLicenseListsValid =
|
||
if lib.mutuallyExclusive whitelist blacklist then
|
||
assert onlyLicenses whitelist; assert onlyLicenses blacklist; true
|
||
else
|
||
throw "whitelistedLicenses and blacklistedLicenses are not mutually exclusive.";
|
||
|
||
hasLicense = attrs:
|
||
attrs ? meta.license;
|
||
|
||
hasWhitelistedLicense = assert areLicenseListsValid; attrs:
|
||
hasLicense attrs && builtins.elem attrs.meta.license whitelist;
|
||
|
||
hasBlacklistedLicense = assert areLicenseListsValid; attrs:
|
||
hasLicense attrs && builtins.elem attrs.meta.license blacklist;
|
||
|
||
allowBroken = config.allowBroken or false
|
||
|| builtins.getEnv "NIXPKGS_ALLOW_BROKEN" == "1";
|
||
|
||
allowUnsupportedSystem = config.allowUnsupportedSystem or false
|
||
|| builtins.getEnv "NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM" == "1";
|
||
|
||
isUnfree = licenses: lib.lists.any (l: !l.free or true) licenses;
|
||
|
||
# Alow granular checks to allow only some unfree packages
|
||
# Example:
|
||
# {pkgs, ...}:
|
||
# {
|
||
# allowUnfree = false;
|
||
# allowUnfreePredicate = (x: pkgs.lib.hasPrefix "flashplayer-" x.name);
|
||
# }
|
||
allowUnfreePredicate = config.allowUnfreePredicate or (x: false);
|
||
|
||
# Check whether unfree packages are allowed and if not, whether the
|
||
# package has an unfree license and is not explicitely allowed by the
|
||
# `allowUnfreePredicate` function.
|
||
hasDeniedUnfreeLicense = attrs:
|
||
!allowUnfree &&
|
||
hasLicense attrs &&
|
||
isUnfree (lib.lists.toList attrs.meta.license) &&
|
||
!allowUnfreePredicate attrs;
|
||
|
||
allowInsecureDefaultPredicate = x: builtins.elem x.name (config.permittedInsecurePackages or []);
|
||
allowInsecurePredicate = x: (config.allowInsecurePredicate or allowInsecureDefaultPredicate) x;
|
||
|
||
hasAllowedInsecure = attrs:
|
||
(attrs.meta.knownVulnerabilities or []) == [] ||
|
||
allowInsecurePredicate attrs ||
|
||
builtins.getEnv "NIXPKGS_ALLOW_INSECURE" == "1";
|
||
|
||
showLicense = license: license.shortName or "unknown";
|
||
|
||
pos_str = meta: meta.position or "«unknown-file»";
|
||
|
||
remediation = {
|
||
unfree = remediate_whitelist "Unfree";
|
||
broken = remediate_whitelist "Broken";
|
||
unsupported = remediate_whitelist "UnsupportedSystem";
|
||
blacklisted = x: "";
|
||
insecure = remediate_insecure;
|
||
broken-outputs = remediateOutputsToInstall;
|
||
unknown-meta = x: "";
|
||
};
|
||
remediate_whitelist = allow_attr: attrs:
|
||
''
|
||
a) For `nixos-rebuild` you can set
|
||
{ nixpkgs.config.allow${allow_attr} = true; }
|
||
in configuration.nix to override this.
|
||
|
||
b) 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:
|
||
'' + (lib.concatStrings (map (issue: " - ${issue}\n") attrs.meta.knownVulnerabilities)) + ''
|
||
|
||
You can install it anyway by whitelisting this package, using the
|
||
following methods:
|
||
|
||
a) for `nixos-rebuild` you can add ‘${attrs.name or "«name-missing»"}’ to
|
||
`nixpkgs.config.permittedInsecurePackages` in the configuration.nix,
|
||
like so:
|
||
|
||
{
|
||
nixpkgs.config.permittedInsecurePackages = [
|
||
"${attrs.name or "«name-missing»"}"
|
||
];
|
||
}
|
||
|
||
b) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add
|
||
‘${attrs.name or "«name-missing»"}’ to `permittedInsecurePackages` in
|
||
~/.config/nixpkgs/config.nix, like so:
|
||
|
||
{
|
||
permittedInsecurePackages = [
|
||
"${attrs.name or "«name-missing»"}"
|
||
];
|
||
}
|
||
|
||
'';
|
||
|
||
remediateOutputsToInstall = attrs: let
|
||
expectedOutputs = attrs.meta.outputsToInstall or [];
|
||
actualOutputs = attrs.outputs or [ "out" ];
|
||
missingOutputs = builtins.filter (output: ! builtins.elem output actualOutputs) expectedOutputs;
|
||
in ''
|
||
The package ${attrs.name} has set meta.outputsToInstall to: ${builtins.concatStringsSep ", " expectedOutputs}
|
||
|
||
however ${attrs.name} only has the outputs: ${builtins.concatStringsSep ", " actualOutputs}
|
||
|
||
and is missing the following ouputs:
|
||
|
||
${lib.concatStrings (builtins.map (output: " - ${output}\n") missingOutputs)}
|
||
'';
|
||
|
||
handleEvalIssue = { meta, attrs }: { reason , errormsg ? "" }:
|
||
let
|
||
msg = if inHydra
|
||
then "Failed to evaluate ${attrs.name or "«name-missing»"}: «${reason}»: ${errormsg}"
|
||
else ''
|
||
Package ‘${attrs.name or "«name-missing»"}’ in ${pos_str meta} ${errormsg}, refusing to evaluate.
|
||
|
||
'' + (builtins.getAttr reason remediation) attrs;
|
||
|
||
handler = if config ? "handleEvalIssue"
|
||
then config.handleEvalIssue reason
|
||
else throw;
|
||
in handler msg;
|
||
|
||
|
||
metaTypes = with lib.types; rec {
|
||
# These keys are documented
|
||
description = str;
|
||
longDescription = str;
|
||
branch = str;
|
||
homepage = either (listOf str) str;
|
||
downloadPage = str;
|
||
license = either (listOf lib.types.attrs) (either lib.types.attrs str);
|
||
maintainers = listOf (attrsOf str);
|
||
priority = int;
|
||
platforms = listOf (either str lib.systems.parsedPlatform.types.system);
|
||
hydraPlatforms = listOf str;
|
||
broken = bool;
|
||
# TODO: refactor once something like Profpatsch's types-simple will land
|
||
# This is currently dead code due to https://github.com/NixOS/nix/issues/2532
|
||
tests = attrsOf (mkOptionType {
|
||
name = "test";
|
||
check = x: x == {} || ( # Accept {} for tests that are unsupported
|
||
isDerivation x &&
|
||
x ? meta.timeout
|
||
);
|
||
merge = lib.options.mergeOneOption;
|
||
});
|
||
timeout = int;
|
||
|
||
# Weirder stuff that doesn't appear in the documentation?
|
||
knownVulnerabilities = listOf str;
|
||
name = str;
|
||
version = str;
|
||
tag = str;
|
||
updateWalker = bool;
|
||
executables = listOf str;
|
||
outputsToInstall = listOf str;
|
||
position = str;
|
||
available = bool;
|
||
repositories = attrsOf str;
|
||
isBuildPythonPackage = platforms;
|
||
schedulingPriority = int;
|
||
downloadURLRegexp = str;
|
||
isFcitxEngine = bool;
|
||
isIbusEngine = bool;
|
||
isGutenprint = bool;
|
||
badPlatforms = platforms;
|
||
};
|
||
|
||
checkMetaAttr = k: v:
|
||
if metaTypes?${k} then
|
||
if metaTypes.${k}.check v then null else "key '${k}' has a value ${toString v} of an invalid type ${builtins.typeOf v}; expected ${metaTypes.${k}.description}"
|
||
else "key '${k}' is unrecognized; expected one of: \n\t [${lib.concatMapStringsSep ", " (x: "'${x}'") (lib.attrNames metaTypes)}]";
|
||
checkMeta = meta: if shouldCheckMeta then lib.remove null (lib.mapAttrsToList checkMetaAttr meta) else [];
|
||
|
||
checkPlatform = attrs: let
|
||
anyMatch = lib.any (lib.meta.platformMatch hostPlatform);
|
||
in anyMatch (attrs.meta.platforms or lib.platforms.all) &&
|
||
! anyMatch (attrs.meta.badPlatforms or []);
|
||
|
||
checkOutputsToInstall = attrs: let
|
||
expectedOutputs = attrs.meta.outputsToInstall or [];
|
||
actualOutputs = attrs.outputs or [ "out" ];
|
||
missingOutputs = builtins.filter (output: ! builtins.elem output actualOutputs) expectedOutputs;
|
||
in if shouldCheckMeta
|
||
then builtins.length missingOutputs > 0
|
||
else false;
|
||
|
||
# Check if a derivation is valid, that is whether it passes checks for
|
||
# e.g brokenness or license.
|
||
#
|
||
# Return { valid: Bool } and additionally
|
||
# { reason: String; errormsg: String } if it is not valid, where
|
||
# reason is one of "unfree", "blacklisted" or "broken".
|
||
checkValidity = attrs:
|
||
if hasDeniedUnfreeLicense attrs && !(hasWhitelistedLicense attrs) then
|
||
{ valid = false; reason = "unfree"; errormsg = "has an unfree license (‘${showLicense attrs.meta.license}’)"; }
|
||
else if hasBlacklistedLicense attrs then
|
||
{ valid = false; reason = "blacklisted"; errormsg = "has a blacklisted license (‘${showLicense attrs.meta.license}’)"; }
|
||
else if !allowBroken && attrs.meta.broken or false then
|
||
{ valid = false; reason = "broken"; errormsg = "is marked as broken"; }
|
||
else if !allowUnsupportedSystem && !(checkPlatform attrs) then
|
||
{ valid = false; reason = "unsupported"; errormsg = "is not supported on ‘${hostPlatform.config}’"; }
|
||
else if !(hasAllowedInsecure attrs) then
|
||
{ valid = false; reason = "insecure"; errormsg = "is marked as insecure"; }
|
||
else if checkOutputsToInstall attrs then
|
||
{ valid = false; reason = "broken-outputs"; errormsg = "has invalid meta.outputsToInstall"; }
|
||
else let res = checkMeta (attrs.meta or {}); in if res != [] then
|
||
{ valid = false; reason = "unknown-meta"; errormsg = "has an invalid meta attrset:${lib.concatMapStrings (x: "\n\t - " + x) res}"; }
|
||
else { valid = true; };
|
||
|
||
assertValidity = { meta, attrs }: let
|
||
validity = checkValidity attrs;
|
||
in validity // {
|
||
# Throw an error if trying to evaluate an non-valid derivation
|
||
handled = if !validity.valid
|
||
then handleEvalIssue { inherit meta attrs; } (removeAttrs validity ["valid"])
|
||
else true;
|
||
};
|
||
|
||
in assertValidity
|