stdenv: separate all meta-checking code (~200 lines)

Only cosmetic changes are done otherwise.
Real refactoring is left for later.

There's a small slow-down on my machine:
$ time nix-env -qa -P >/dev/null
gets from ~2.8 to ~3.5 seconds (negligible change in RAM).
That's most likely caused by sharing less computation between different
mkDerivation calls, and I plan to improve that soon.

authored by Vladimír Čunát and committed by John Ericson e8e57452 dfc004e6

+212 -196
+197
pkgs/stdenv/generic/check-meta.nix
···
··· 1 + # Extend a derivation with checks for brokenness, license, etc. Throw a 2 + # descriptive error when the check fails; return `derivationArg` otherwise. 3 + # Note: no dependencies are checked in this step. 4 + 5 + { lib, config, system, meta, derivationArg, mkDerivationArg }: 6 + 7 + let 8 + attrs = mkDerivationArg; # TODO: probably get rid of passing this one 9 + 10 + # See discussion at https://github.com/NixOS/nixpkgs/pull/25304#issuecomment-298385426 11 + # for why this defaults to false, but I (@copumpkin) want to default it to true soon. 12 + shouldCheckMeta = config.checkMeta or false; 13 + 14 + allowUnfree = config.allowUnfree or false || builtins.getEnv "NIXPKGS_ALLOW_UNFREE" == "1"; 15 + 16 + whitelist = config.whitelistedLicenses or []; 17 + blacklist = config.blacklistedLicenses or []; 18 + 19 + onlyLicenses = list: 20 + lib.lists.all (license: 21 + let l = lib.licenses.${license.shortName or "BROKEN"} or false; in 22 + if license == l then true else 23 + throw ''‘${showLicense license}’ is not an attribute of lib.licenses'' 24 + ) list; 25 + 26 + areLicenseListsValid = 27 + if lib.mutuallyExclusive whitelist blacklist then 28 + assert onlyLicenses whitelist; assert onlyLicenses blacklist; true 29 + else 30 + throw "whitelistedLicenses and blacklistedLicenses are not mutually exclusive."; 31 + 32 + hasLicense = attrs: 33 + attrs ? meta.license; 34 + 35 + hasWhitelistedLicense = assert areLicenseListsValid; attrs: 36 + hasLicense attrs && builtins.elem attrs.meta.license whitelist; 37 + 38 + hasBlacklistedLicense = assert areLicenseListsValid; attrs: 39 + hasLicense attrs && builtins.elem attrs.meta.license blacklist; 40 + 41 + allowBroken = config.allowBroken or false || builtins.getEnv "NIXPKGS_ALLOW_BROKEN" == "1"; 42 + 43 + isUnfree = licenses: lib.lists.any (l: 44 + !l.free or true || l == "unfree" || l == "unfree-redistributable") licenses; 45 + 46 + # Alow granular checks to allow only some unfree packages 47 + # Example: 48 + # {pkgs, ...}: 49 + # { 50 + # allowUnfree = false; 51 + # allowUnfreePredicate = (x: pkgs.lib.hasPrefix "flashplayer-" x.name); 52 + # } 53 + allowUnfreePredicate = config.allowUnfreePredicate or (x: false); 54 + 55 + # Check whether unfree packages are allowed and if not, whether the 56 + # package has an unfree license and is not explicitely allowed by the 57 + # `allowUNfreePredicate` function. 58 + hasDeniedUnfreeLicense = attrs: 59 + !allowUnfree && 60 + hasLicense attrs && 61 + isUnfree (lib.lists.toList attrs.meta.license) && 62 + !allowUnfreePredicate attrs; 63 + 64 + allowInsecureDefaultPredicate = x: builtins.elem x.name (config.permittedInsecurePackages or []); 65 + allowInsecurePredicate = x: (config.allowUnfreePredicate or allowInsecureDefaultPredicate) x; 66 + 67 + hasAllowedInsecure = attrs: 68 + (attrs.meta.knownVulnerabilities or []) == [] || 69 + allowInsecurePredicate attrs || 70 + builtins.getEnv "NIXPKGS_ALLOW_INSECURE" == "1"; 71 + 72 + showLicense = license: license.shortName or "unknown"; 73 + 74 + pos_str = meta.position or "«unknown-file»"; 75 + 76 + remediation = { 77 + unfree = remediate_whitelist "Unfree"; 78 + broken = remediate_whitelist "Broken"; 79 + blacklisted = x: ""; 80 + insecure = remediate_insecure; 81 + unknown-meta = x: ""; 82 + }; 83 + remediate_whitelist = allow_attr: attrs: 84 + '' 85 + a) For `nixos-rebuild` you can set 86 + { nixpkgs.config.allow${allow_attr} = true; } 87 + in configuration.nix to override this. 88 + 89 + b) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add 90 + { allow${allow_attr} = true; } 91 + to ~/.config/nixpkgs/config.nix. 92 + ''; 93 + 94 + remediate_insecure = attrs: 95 + '' 96 + 97 + Known issues: 98 + 99 + '' + (lib.fold (issue: default: "${default} - ${issue}\n") "" attrs.meta.knownVulnerabilities) + '' 100 + 101 + You can install it anyway by whitelisting this package, using the 102 + following methods: 103 + 104 + a) for `nixos-rebuild` you can add ‘${attrs.name or "«name-missing»"}’ to 105 + `nixpkgs.config.permittedInsecurePackages` in the configuration.nix, 106 + like so: 107 + 108 + { 109 + nixpkgs.config.permittedInsecurePackages = [ 110 + "${attrs.name or "«name-missing»"}" 111 + ]; 112 + } 113 + 114 + b) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add 115 + ‘${attrs.name or "«name-missing»"}’ to `permittedInsecurePackages` in 116 + ~/.config/nixpkgs/config.nix, like so: 117 + 118 + { 119 + permittedInsecurePackages = [ 120 + "${attrs.name or "«name-missing»"}" 121 + ]; 122 + } 123 + 124 + ''; 125 + 126 + throwEvalHelp = { reason , errormsg ? "" }: 127 + throw ('' 128 + Package ‘${attrs.name or "«name-missing»"}’ in ${pos_str} ${errormsg}, refusing to evaluate. 129 + 130 + '' + ((builtins.getAttr reason remediation) attrs)); 131 + 132 + metaTypes = with lib.types; rec { 133 + # These keys are documented 134 + description = str; 135 + longDescription = str; 136 + branch = str; 137 + homepage = str; 138 + downloadPage = str; 139 + license = either (listOf lib.types.attrs) (either lib.types.attrs str); 140 + maintainers = listOf str; 141 + priority = int; 142 + platforms = listOf str; 143 + hydraPlatforms = listOf str; 144 + broken = bool; 145 + 146 + # Weirder stuff that doesn't appear in the documentation? 147 + version = str; 148 + tag = str; 149 + updateWalker = bool; 150 + executables = listOf str; 151 + outputsToInstall = listOf str; 152 + position = str; 153 + repositories = attrsOf str; 154 + isBuildPythonPackage = platforms; 155 + schedulingPriority = str; 156 + downloadURLRegexp = str; 157 + isFcitxEngine = bool; 158 + isIbusEngine = bool; 159 + }; 160 + 161 + checkMetaAttr = k: v: 162 + if metaTypes?${k} then 163 + if metaTypes.${k}.check v then null else "key '${k}' has a value ${v} of an invalid type ${builtins.typeOf v}; expected ${metaTypes.${k}.description}" 164 + else "key '${k}' is unrecognized; expected one of: \n\t [${lib.concatMapStringsSep ", " (x: "'${x}'") (lib.attrNames metaTypes)}]"; 165 + checkMeta = meta: if shouldCheckMeta then lib.remove null (lib.mapAttrsToList checkMetaAttr meta) else []; 166 + 167 + # Check if a derivation is valid, that is whether it passes checks for 168 + # e.g brokenness or license. 169 + # 170 + # Return { valid: Bool } and additionally 171 + # { reason: String; errormsg: String } if it is not valid, where 172 + # reason is one of "unfree", "blacklisted" or "broken". 173 + checkValidity = attrs: 174 + if hasDeniedUnfreeLicense attrs && !(hasWhitelistedLicense attrs) then 175 + { valid = false; reason = "unfree"; errormsg = "has an unfree license (‘${showLicense attrs.meta.license}’)"; } 176 + else if hasBlacklistedLicense attrs then 177 + { valid = false; reason = "blacklisted"; errormsg = "has a blacklisted license (‘${showLicense attrs.meta.license}’)"; } 178 + else if !allowBroken && attrs.meta.broken or false then 179 + { valid = false; reason = "broken"; errormsg = "is marked as broken"; } 180 + else if !allowBroken && attrs.meta.platforms or null != null && !lib.lists.elem system attrs.meta.platforms then 181 + { valid = false; reason = "broken"; errormsg = "is not supported on ‘${system}’"; } 182 + else if !(hasAllowedInsecure attrs) then 183 + { valid = false; reason = "insecure"; errormsg = "is marked as insecure"; } 184 + else let res = checkMeta (attrs.meta or {}); in if res != [] then 185 + { valid = false; reason = "unknown-meta"; errormsg = "has an invalid meta attrset:${lib.concatMapStrings (x: "\n\t - " + x) res}"; } 186 + else { valid = true; }; 187 + 188 + # Throw an error if trying to evaluate an non-valid derivation 189 + validityCondition = 190 + let v = checkValidity attrs; 191 + in if !v.valid 192 + then throwEvalHelp (removeAttrs v ["valid"]) 193 + else true; 194 + 195 + in 196 + assert validityCondition; 197 + derivationArg
+15 -196
pkgs/stdenv/generic/default.nix
··· 27 let 28 inherit (targetPlatform) system; 29 30 - # See discussion at https://github.com/NixOS/nixpkgs/pull/25304#issuecomment-298385426 31 - # for why this defaults to false, but I (@copumpkin) want to default it to true soon. 32 - shouldCheckMeta = config.checkMeta or false; 33 - 34 - allowUnfree = config.allowUnfree or false || builtins.getEnv "NIXPKGS_ALLOW_UNFREE" == "1"; 35 - 36 - whitelist = config.whitelistedLicenses or []; 37 - blacklist = config.blacklistedLicenses or []; 38 - 39 ifDarwin = attrs: if system == "x86_64-darwin" then attrs else {}; 40 41 - onlyLicenses = list: 42 - lib.lists.all (license: 43 - let l = lib.licenses.${license.shortName or "BROKEN"} or false; in 44 - if license == l then true else 45 - throw ''‘${showLicense license}’ is not an attribute of lib.licenses'' 46 - ) list; 47 - 48 - areLicenseListsValid = 49 - if lib.mutuallyExclusive whitelist blacklist then 50 - assert onlyLicenses whitelist; assert onlyLicenses blacklist; true 51 - else 52 - throw "whitelistedLicenses and blacklistedLicenses are not mutually exclusive."; 53 - 54 - hasLicense = attrs: 55 - attrs ? meta.license; 56 - 57 - hasWhitelistedLicense = assert areLicenseListsValid; attrs: 58 - hasLicense attrs && builtins.elem attrs.meta.license whitelist; 59 - 60 - hasBlacklistedLicense = assert areLicenseListsValid; attrs: 61 - hasLicense attrs && builtins.elem attrs.meta.license blacklist; 62 - 63 - allowBroken = config.allowBroken or false || builtins.getEnv "NIXPKGS_ALLOW_BROKEN" == "1"; 64 - 65 - isUnfree = licenses: lib.lists.any (l: 66 - !l.free or true || l == "unfree" || l == "unfree-redistributable") licenses; 67 - 68 - # Alow granular checks to allow only some unfree packages 69 - # Example: 70 - # {pkgs, ...}: 71 - # { 72 - # allowUnfree = false; 73 - # allowUnfreePredicate = (x: pkgs.lib.hasPrefix "flashplayer-" x.name); 74 - # } 75 - allowUnfreePredicate = config.allowUnfreePredicate or (x: false); 76 - 77 - # Check whether unfree packages are allowed and if not, whether the 78 - # package has an unfree license and is not explicitely allowed by the 79 - # `allowUNfreePredicate` function. 80 - hasDeniedUnfreeLicense = attrs: 81 - !allowUnfree && 82 - hasLicense attrs && 83 - isUnfree (lib.lists.toList attrs.meta.license) && 84 - !allowUnfreePredicate attrs; 85 - 86 - allowInsecureDefaultPredicate = x: builtins.elem x.name (config.permittedInsecurePackages or []); 87 - allowInsecurePredicate = x: (config.allowUnfreePredicate or allowInsecureDefaultPredicate) x; 88 - 89 - hasAllowedInsecure = attrs: 90 - (attrs.meta.knownVulnerabilities or []) == [] || 91 - allowInsecurePredicate attrs || 92 - builtins.getEnv "NIXPKGS_ALLOW_INSECURE" == "1"; 93 - 94 - showLicense = license: license.shortName or "unknown"; 95 - 96 defaultNativeBuildInputs = extraBuildInputs ++ 97 [ ../../build-support/setup-hooks/move-docs.sh 98 ../../build-support/setup-hooks/compress-man-pages.sh ··· 151 (map (drv: drv.crossDrv or drv) propagatedBuildInputs) 152 ]; 153 in let 154 - pos_str = if pos != null then "‘" + pos.file + ":" + toString pos.line + "’" else "«unknown-file»"; 155 - 156 - remediation = { 157 - unfree = remediate_whitelist "Unfree"; 158 - broken = remediate_whitelist "Broken"; 159 - blacklisted = x: ""; 160 - insecure = remediate_insecure; 161 - unknown-meta = x: ""; 162 - }; 163 - remediate_whitelist = allow_attr: attrs: 164 - '' 165 - a) For `nixos-rebuild` you can set 166 - { nixpkgs.config.allow${allow_attr} = true; } 167 - in configuration.nix to override this. 168 - 169 - b) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add 170 - { allow${allow_attr} = true; } 171 - to ~/.config/nixpkgs/config.nix. 172 - ''; 173 - 174 - remediate_insecure = attrs: 175 - '' 176 - 177 - Known issues: 178 - 179 - '' + (lib.fold (issue: default: "${default} - ${issue}\n") "" attrs.meta.knownVulnerabilities) + '' 180 - 181 - You can install it anyway by whitelisting this package, using the 182 - following methods: 183 - 184 - a) for `nixos-rebuild` you can add ‘${attrs.name or "«name-missing»"}’ to 185 - `nixpkgs.config.permittedInsecurePackages` in the configuration.nix, 186 - like so: 187 - 188 - { 189 - nixpkgs.config.permittedInsecurePackages = [ 190 - "${attrs.name or "«name-missing»"}" 191 - ]; 192 - } 193 - 194 - b) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add 195 - ‘${attrs.name or "«name-missing»"}’ to `permittedInsecurePackages` in 196 - ~/.config/nixpkgs/config.nix, like so: 197 - 198 - { 199 - permittedInsecurePackages = [ 200 - "${attrs.name or "«name-missing»"}" 201 - ]; 202 - } 203 - 204 - ''; 205 - 206 - 207 - throwEvalHelp = { reason , errormsg ? "" }: 208 - throw ('' 209 - Package ‘${attrs.name or "«name-missing»"}’ in ${pos_str} ${errormsg}, refusing to evaluate. 210 - 211 - '' + ((builtins.getAttr reason remediation) attrs)); 212 - 213 - metaTypes = with lib.types; rec { 214 - # These keys are documented 215 - description = str; 216 - longDescription = str; 217 - branch = str; 218 - homepage = str; 219 - downloadPage = str; 220 - license = either (listOf lib.types.attrs) (either lib.types.attrs str); 221 - maintainers = listOf str; 222 - priority = int; 223 - platforms = listOf str; 224 - hydraPlatforms = listOf str; 225 - broken = bool; 226 - 227 - # Weirder stuff that doesn't appear in the documentation? 228 - version = str; 229 - tag = str; 230 - updateWalker = bool; 231 - executables = listOf str; 232 - outputsToInstall = listOf str; 233 - position = str; 234 - repositories = attrsOf str; 235 - isBuildPythonPackage = platforms; 236 - schedulingPriority = str; 237 - downloadURLRegexp = str; 238 - isFcitxEngine = bool; 239 - isIbusEngine = bool; 240 - }; 241 - 242 - checkMetaAttr = k: v: 243 - if metaTypes?${k} then 244 - if metaTypes.${k}.check v then null else "key '${k}' has a value ${v} of an invalid type ${builtins.typeOf v}; expected ${metaTypes.${k}.description}" 245 - else "key '${k}' is unrecognized; expected one of: \n\t [${lib.concatMapStringsSep ", " (x: "'${x}'") (lib.attrNames metaTypes)}]"; 246 - checkMeta = meta: if shouldCheckMeta then lib.remove null (lib.mapAttrsToList checkMetaAttr meta) else []; 247 - 248 - # Check if a derivation is valid, that is whether it passes checks for 249 - # e.g brokenness or license. 250 - # 251 - # Return { valid: Bool } and additionally 252 - # { reason: String; errormsg: String } if it is not valid, where 253 - # reason is one of "unfree", "blacklisted" or "broken". 254 - checkValidity = attrs: 255 - if hasDeniedUnfreeLicense attrs && !(hasWhitelistedLicense attrs) then 256 - { valid = false; reason = "unfree"; errormsg = "has an unfree license (‘${showLicense attrs.meta.license}’)"; } 257 - else if hasBlacklistedLicense attrs then 258 - { valid = false; reason = "blacklisted"; errormsg = "has a blacklisted license (‘${showLicense attrs.meta.license}’)"; } 259 - else if !allowBroken && attrs.meta.broken or false then 260 - { valid = false; reason = "broken"; errormsg = "is marked as broken"; } 261 - else if !allowBroken && attrs.meta.platforms or null != null && !lib.lists.elem result.system attrs.meta.platforms then 262 - { valid = false; reason = "broken"; errormsg = "is not supported on ‘${result.system}’"; } 263 - else if !(hasAllowedInsecure attrs) then 264 - { valid = false; reason = "insecure"; errormsg = "is marked as insecure"; } 265 - else let res = checkMeta (attrs.meta or {}); in if res != [] then 266 - { valid = false; reason = "unknown-meta"; errormsg = "has an invalid meta attrset:${lib.concatMapStrings (x: "\n\t - " + x) res}"; } 267 - else { valid = true; }; 268 269 outputs' = 270 outputs ++ ··· 280 in [ nativeBuildInputs buildInputs ]; 281 282 propagatedDependencies' = map lib.chooseDevOutputs propagatedDependencies; 283 - 284 - # Throw an error if trying to evaluate an non-valid derivation 285 - validityCondition = 286 - let v = checkValidity attrs; 287 - in if !v.valid 288 - then throwEvalHelp (removeAttrs v ["valid"]) 289 - else true; 290 291 derivationArg = 292 (removeAttrs attrs ··· 360 361 in 362 363 - assert validityCondition; 364 - 365 - lib.addPassthru (derivation derivationArg) ( 366 - { 367 - overrideAttrs = f: mkDerivation (attrs // (f attrs)); 368 - inherit meta passthru; 369 - } // 370 - # Pass through extra attributes that are not inputs, but 371 - # should be made available to Nix expressions using the 372 - # derivation (e.g., in assertions). 373 - passthru); 374 375 # The stdenv that we are producing. 376 result =
··· 27 let 28 inherit (targetPlatform) system; 29 30 ifDarwin = attrs: if system == "x86_64-darwin" then attrs else {}; 31 32 defaultNativeBuildInputs = extraBuildInputs ++ 33 [ ../../build-support/setup-hooks/move-docs.sh 34 ../../build-support/setup-hooks/compress-man-pages.sh ··· 87 (map (drv: drv.crossDrv or drv) propagatedBuildInputs) 88 ]; 89 in let 90 91 outputs' = 92 outputs ++ ··· 102 in [ nativeBuildInputs buildInputs ]; 103 104 propagatedDependencies' = map lib.chooseDevOutputs propagatedDependencies; 105 106 derivationArg = 107 (removeAttrs attrs ··· 175 176 in 177 178 + lib.addPassthru 179 + (derivation (import ./check-meta.nix 180 + { 181 + inherit lib config meta derivationArg; 182 + mkDerivationArg = attrs; 183 + inherit system; # TODO: cross-compilation? 184 + })) 185 + ( { 186 + overrideAttrs = f: mkDerivation (attrs // (f attrs)); 187 + inherit meta passthru; 188 + } // 189 + # Pass through extra attributes that are not inputs, but 190 + # should be made available to Nix expressions using the 191 + # derivation (e.g., in assertions). 192 + passthru); 193 194 # The stdenv that we are producing. 195 result =