From bf8d30275a97c85bfbd86063f342599345a9b02e Mon Sep 17 00:00:00 2001 From: Matthew Bauer Date: Wed, 11 Jul 2018 14:40:08 -0400 Subject: [PATCH] top-level: add config handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pkgs/top-level/config.nix provides some defaults for the config values. This allows global defaults to be provided for config options & prevents individual packages for having to interpret the defaults. Hopefully this will make way for better documentation of configuration options. Eventually I want to move this into a proper module system, see (#39580). Other squashed commits: - check-meta: use default values from pkgs/top-level/config.nix We no longer have to check that these exist - their defaults are configured there. - config: propagate original config Lots of times we will have custom config options. We want these to still be accessible in Nixpkgs. - config: add packageOverrides One config option I originally missed. - config: fix licenses loop blacklistedLicenses & whitelistedLicense cannot depend on each other in their apply function. Instead just use the original config’. - treewide: remove ‘or’ default for configs these are now provided by config.nix - config: make types enforced config.shouldCheckMeta -> config.checkMeta This was the original name. --- nixos/modules/hardware/all-firmware.nix | 2 +- .../git-and-tools/default.nix | 2 +- pkgs/desktops/gnome-3/default.nix | 2 +- pkgs/desktops/xfce/default.nix | 2 +- .../python/mk-python-derivation.nix | 2 +- pkgs/misc/vim-plugins/default.nix | 2 +- pkgs/stdenv/generic/check-meta.nix | 190 ++++++++---------- pkgs/stdenv/generic/make-derivation.nix | 6 +- pkgs/top-level/config.nix | 173 ++++++++++++++++ pkgs/top-level/default.nix | 9 +- pkgs/top-level/perl-packages.nix | 2 +- pkgs/top-level/stage.nix | 5 +- 12 files changed, 269 insertions(+), 128 deletions(-) create mode 100644 pkgs/top-level/config.nix diff --git a/nixos/modules/hardware/all-firmware.nix b/nixos/modules/hardware/all-firmware.nix index e978ec6b40ada..9ff8ee91ff25b 100644 --- a/nixos/modules/hardware/all-firmware.nix +++ b/nixos/modules/hardware/all-firmware.nix @@ -45,7 +45,7 @@ in { }) (mkIf cfg.enableAllFirmware { assertions = [{ - assertion = !cfg.enableAllFirmware || (config.nixpkgs.config.allowUnfree or false); + assertion = !cfg.enableAllFirmware || config.nixpkgs.config.allowUnfree; message = '' the list of hardware.enableAllFirmware contains non-redistributable licensed firmware files. This requires nixpkgs.config.allowUnfree to be true. diff --git a/pkgs/applications/version-management/git-and-tools/default.nix b/pkgs/applications/version-management/git-and-tools/default.nix index ba5486dc0158f..9db395746fdbd 100644 --- a/pkgs/applications/version-management/git-and-tools/default.nix +++ b/pkgs/applications/version-management/git-and-tools/default.nix @@ -130,7 +130,7 @@ let transcrypt = callPackage ./transcrypt { }; -} // lib.optionalAttrs (config.allowAliases or true) (with self; { +} // lib.optionalAttrs config.allowAliases (with self; { # aliases gitAnnex = git-annex; svn_all_fast_export = svn-all-fast-export; diff --git a/pkgs/desktops/gnome-3/default.nix b/pkgs/desktops/gnome-3/default.nix index be962b3e289e3..c5c24fa816c44 100644 --- a/pkgs/desktops/gnome-3/default.nix +++ b/pkgs/desktops/gnome-3/default.nix @@ -391,7 +391,7 @@ lib.makeScope pkgs.newScope (self: with self; { gnome-packagekit = callPackage ./misc/gnome-packagekit { }; -} // lib.optionalAttrs (config.allowAliases or true) { +} // lib.optionalAttrs config.allowAliases { #### Legacy aliases evolution_data_server = evolution-data-server; # added 2018-02-25 diff --git a/pkgs/desktops/xfce/default.nix b/pkgs/desktops/xfce/default.nix index abf03d4277be1..cb74c72756739 100644 --- a/pkgs/desktops/xfce/default.nix +++ b/pkgs/desktops/xfce/default.nix @@ -159,7 +159,7 @@ lib.makeScope pkgs.newScope (self: with self; { xfce4_power_manager_gtk3 = xfce4-power-manager.override { withGtk3 = true; }; -} // lib.optionalAttrs (config.allowAliases or true) { +} // lib.optionalAttrs config.allowAliases { #### ALIASES - added 2018-01 terminal = xfce4-terminal; diff --git a/pkgs/development/interpreters/python/mk-python-derivation.nix b/pkgs/development/interpreters/python/mk-python-derivation.nix index 63ffdbb8c0ac4..b02563ac71ecd 100644 --- a/pkgs/development/interpreters/python/mk-python-derivation.nix +++ b/pkgs/development/interpreters/python/mk-python-derivation.nix @@ -54,7 +54,7 @@ , passthru ? {} -, doCheck ? config.doCheckByDefault or false +, doCheck ? config.doCheckByDefault , ... } @ attrs: diff --git a/pkgs/misc/vim-plugins/default.nix b/pkgs/misc/vim-plugins/default.nix index 0085e9280a922..65d63afbbd0d1 100644 --- a/pkgs/misc/vim-plugins/default.nix +++ b/pkgs/misc/vim-plugins/default.nix @@ -3213,7 +3213,7 @@ let }; -} // lib.optionalAttrs (config.allowAliases or true) (with self; { +} // lib.optionalAttrs config.allowAliases (with self; { # aliasess airline = vim-airline; diff --git a/pkgs/stdenv/generic/check-meta.nix b/pkgs/stdenv/generic/check-meta.nix index a5c8ca705231b..0ebe65f7b1094 100644 --- a/pkgs/stdenv/generic/check-meta.nix +++ b/pkgs/stdenv/generic/check-meta.nix @@ -4,139 +4,93 @@ { lib, config, hostPlatform, meta }: let - # 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"; + hasWhitelistedLicense = attrs: + hasLicense attrs && + builtins.elem attrs.meta.license config.whitelistedLicenses; - allowUnsupportedSystem = config.allowUnsupportedSystem or false - || builtins.getEnv "NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM" == "1"; + hasBlacklistedLicense = attrs: + hasLicense attrs && + builtins.elem attrs.meta.license config.blacklistedLicenses; isUnfree = licenses: lib.lists.any (l: !l.free or true || l == "unfree" || l == "unfree-redistributable") 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; + !config.allowUnfreePredicate attrs; - allowInsecureDefaultPredicate = x: builtins.elem x.name (config.permittedInsecurePackages or []); - allowInsecurePredicate = x: (config.allowInsecurePredicate or allowInsecureDefaultPredicate) x; + pos_str = meta.position or "«unknown-file»"; - hasAllowedInsecure = attrs: - (attrs.meta.knownVulnerabilities or []) == [] || - allowInsecurePredicate attrs || - builtins.getEnv "NIXPKGS_ALLOW_INSECURE" == "1"; + remediation = (let + whitelist = str: attrs: '' - showLicense = license: license.shortName or "unknown"; + a) For `nixos-rebuild` you can set: - pos_str = meta.position or "«unknown-file»"; + { nixpkgs.config.${str}; } - remediation = { - unfree = remediate_whitelist "Unfree"; - broken = remediate_whitelist "Broken"; - unsupported = remediate_whitelist "UnsupportedSystem"; - blacklisted = x: ""; - insecure = remediate_insecure; - 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. - ''; + in configuration.nix to override this. + + b) For `nix-env`, `nix-build`, `nix-shell` or any other Nix + command you can add: + + { ${str}; } + + to ~/.config/nixpkgs/config.nix. + ''; - remediate_insecure = attrs: - '' + 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: + '' + (lib.concatStrings (map (issue: " - ${issue}\n") + attrs.meta.knownVulnerabilities)) + '' - a) for `nixos-rebuild` you can add ‘${attrs.name or "«name-missing»"}’ to - `nixpkgs.config.permittedInsecurePackages` in the configuration.nix, - like so: + You can install it anyway by whitelisting this package, using + the following methods: + + a) for `nixos-rebuild` you can add ‘${attrs.name}’ to + `nixpkgs.config.permittedInsecurePackages` in the + configuration.nix, like so: { nixpkgs.config.permittedInsecurePackages = [ - "${attrs.name or "«name-missing»"}" + "${attrs.name}" ]; } - 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: + b) For `nix-env`, `nix-build`, `nix-shell` or any other Nix + command you can add ‘${attrs.name}’ to + `permittedInsecurePackages` in + ~/.config/nixpkgs/config.nix, like so: { permittedInsecurePackages = [ - "${attrs.name or "«name-missing»"}" + "${attrs.name}" ]; } + ''; - ''; - - handleEvalIssue = attrs: { reason , errormsg ? "" }: - let - msg = '' - Package ‘${attrs.name or "«name-missing»"}’ in ${pos_str} ${errormsg}, refusing to evaluate. + in { + unfree = whitelist "allowUnfree = true"; + broken = whitelist "allowBroken = true"; + unsupported = whitelist "allowUnsupportedSystem = true"; + inherit insecure; + }); - '' + (builtins.getAttr reason remediation) attrs; + handleEvalIssue = attrs: { reason , errormsg ? "" }: config.handleEvalIssue '' - handler = if config ? "handleEvalIssue" - then config.handleEvalIssue reason - else throw; - in handler msg; +Package ‘${attrs.name or "«name-missing»"}’ in ${pos_str} ${errormsg}, +refusing to evaluate. +${(remediation.${reason} or (_: "")) attrs} + ''; metaTypes = with lib.types; rec { # These keys are documented @@ -176,9 +130,12 @@ let 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}" + 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 []; + checkMeta = meta: if config.checkMeta + then lib.remove null (lib.mapAttrsToList checkMetaAttr meta) + else []; checkPlatform = attrs: let anyMatch = lib.any (lib.meta.platformMatch hostPlatform); @@ -192,19 +149,34 @@ let # { 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 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; }; + if hasDeniedUnfreeLicense attrs && !(hasWhitelistedLicense attrs) then { + valid = false; + reason = "unfree"; + errormsg = "has an unfree license (‘${attrs.meta.license.shortName or "unknown"}’)"; + } else if hasBlacklistedLicense attrs then { + valid = false; + reason = "blacklisted"; + errormsg = "has a blacklisted license (‘${attrs.meta.license.shortName or "unknown"}’)"; + } else if !config.allowBroken && attrs.meta.broken or false then { + valid = false; + reason = "broken"; + errormsg = "is marked as broken"; + } else if !config.allowUnsupportedSystem && !(checkPlatform attrs) then { + valid = false; + reason = "unsupported"; + errormsg = "is not supported on ‘${hostPlatform.config}’"; + } else if !(((attrs.meta.knownVulnerabilities or []) == []) || + (config.allowInsecurePredicate attrs)) then { + valid = false; + reason = "insecure"; + errormsg = "is marked as insecure"; + } else let res = checkMeta (attrs.meta or {}); in if res != [] then { + valid = false; + errormsg = '' + has an invalid meta attrset: + ${lib.concatMapStrings (x: "\n\t - " + x) res} + ''; + } else { valid = true; }; assertValidity = attrs: let validity = checkValidity attrs; diff --git a/pkgs/stdenv/generic/make-derivation.nix b/pkgs/stdenv/generic/make-derivation.nix index 778f107f71e14..6550ddc9b23ee 100644 --- a/pkgs/stdenv/generic/make-derivation.nix +++ b/pkgs/stdenv/generic/make-derivation.nix @@ -51,11 +51,11 @@ rec { # TODO(@Ericson2314): Make unconditional / resolve #33599 # Check phase - , doCheck ? config.doCheckByDefault or false + , doCheck ? config.doCheckByDefault # TODO(@Ericson2314): Make unconditional / resolve #33599 # InstallCheck phase - , doInstallCheck ? config.doCheckByDefault or false + , doInstallCheck ? config.doCheckByDefault , # TODO(@Ericson2314): Make always true and remove strictDeps ? stdenv.hostPlatform != stdenv.buildPlatform @@ -272,7 +272,7 @@ rec { # Expose the result of the checks for everyone to see. } // { available = validity.valid - && (if config.checkMetaRecursively or false + && (if config.checkMetaRecursively then lib.all (d: d.meta.available or true) references else true); }; diff --git a/pkgs/top-level/config.nix b/pkgs/top-level/config.nix new file mode 100644 index 0000000000000..3a0062b36c602 --- /dev/null +++ b/pkgs/top-level/config.nix @@ -0,0 +1,173 @@ +{ configExpr, lib, pkgs }: + +with lib; + +let + # Allow both: + # { /* the config */ } and + # { pkgs, ... } : { /* the config */ } + config' = (if lib.isFunction configExpr + then configExpr { inherit pkgs; } + else configExpr); + + onlyLicenses = list: + lib.lists.all (license: + let l = lib.licenses.${license.shortName or "unknown"} or false; in + if license == l then true else + throw "${license.shortName or "unknown"} is not an attribute of lib.licenses" + ) list; + + defaults = { + allowUnfree = mkOption { + default = (builtins.getEnv "NIXPKGS_ALLOW_UNFREE") == "1"; + type = types.bool; + description = '' + Allow unfree software to be evaluated. + ''; + }; + + # 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. + checkMeta = mkOption { + default = false; + type = types.bool; + description = '' + Should we check meta in derivations ahead of time. + ''; + }; + + whitelistedLicenses = mkOption { + default = []; + type = types.listOf types.attrs; + description = '' + List of licenses to allow to evaluate. + ''; + apply = x: + assert lib.mutuallyExclusive x (config'.blacklistedLicenses or []); + assert onlyLicenses x; x; + }; + + blacklistedLicenses = mkOption { + default = []; + type = types.listOf types.attrs; + description = '' + List of license to prevent from evaluating. + ''; + apply = x: + assert lib.mutuallyExclusive x (config'.whitelistedLicenses or []); + assert onlyLicenses x; x; + }; + + allowBroken = mkOption { + default = (builtins.getEnv "NIXPKGS_ALLOW_BROKEN") == "1"; + type = types.bool; + description = '' + Should we allow broken packages to be evaluated. + ''; + }; + + allowUnsupportedSystem = mkOption { + default = (builtins.getEnv "NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM") == "1"; + type = types.bool; + description = '' + Should we allow packages to evaluate on unsupported systems. + ''; + }; + + # Alow granular checks to allow only some unfree packages + # Example: + # {pkgs, ...}: + # { + # allowUnfree = false; + # allowUnfreePredicate = (x: pkgs.lib.hasPrefix "flashplayer-" x.name); + # } + allowUnfreePredicate = mkOption { + default = _: config.allowUnfree; + # type = types.function; + description = '' + A function taking an unfree package that returns true if that + package is okay to use. + ''; + }; + + allowInsecurePredicate = mkOption { + default = x: elem x.name config.permittedInsecurePackages; + # type = types.function; + description = '' + Function taking an insecure package that returns true if that + package is okay to use. + ''; + }; + + permittedInsecurePackages = mkOption { + default = []; + type = types.listOf types.string; + description = '' + A list of insecure package names to allow to evaluate. + ''; + }; + + allowAliases = mkOption { + default = true; + type = types.bool; + description = '' + Whether to include aliases in Nixpkgs. These aliases are + usually included for compatibility purposes. To prevent + removed names from breaking user's configs, we provide aliases + by default. However, their usage is still strongly discourages + and may lead to evaluation issues for users with this set to + false. + ''; + }; + + handleEvalIssue = mkOption { + default = throw; + # type = types.function; + description = '' + A function to run to handle an evaluation issue. The default + is throw but set it to something else to ignore evaluation + issues. + ''; + }; + + packageOverrides = mkOption { + default = _: {}; + # type = types.function; + description = '' + A function returning an attribute set to override the + top-level of Nixpkgs. The only argument is the previous + package set before the override. + ''; + }; + + checkMetaRecursively = mkOption { + default = false; + type = types.bool; + description = '' + Whether to recursively check meta attributes. + ''; + }; + + doCheckByDefault = mkOption { + default = false; + type = types.bool; + description = '' + Whether to run tests by default in the default Nix builder. + ''; + }; + }; + + checkType = name: option: value: + if hasAttr "type" option && isType option.type value then throw '' + value, ${toString value}, for ${name} is not of correct type + ${option.type.name}. + '' else value; + + config = config' // (lib.mapAttrs (name: option: + (checkType name option + ((option.apply or id) (config'.${name} or option.default)))) + defaults); + +in config diff --git a/pkgs/top-level/default.nix b/pkgs/top-level/default.nix index da7fc1bed34c5..8c5dbba545aa6 100644 --- a/pkgs/top-level/default.nix +++ b/pkgs/top-level/default.nix @@ -45,13 +45,8 @@ let # Rename the function arguments in let lib = import ../../lib; - # Allow both: - # { /* the config */ } and - # { pkgs, ... } : { /* the config */ } - config = - if lib.isFunction configExpr - then configExpr { inherit pkgs; } - else configExpr; + # Build a config attribute set with builtin defaults. + config = import ./config.nix { inherit pkgs lib configExpr; }; # From a minimum of `system` or `config` (actually a target triple, *not* # nixpkgs configuration), infer the other one and platform as needed. diff --git a/pkgs/top-level/perl-packages.nix b/pkgs/top-level/perl-packages.nix index b98b5b508cf04..4b9fe5d396ef7 100644 --- a/pkgs/top-level/perl-packages.nix +++ b/pkgs/top-level/perl-packages.nix @@ -17909,7 +17909,7 @@ let self = _self // overrides; _self = with self; { }; }; -} // stdenv.lib.optionalAttrs (config.allowAliases or true) { +} // stdenv.lib.optionalAttrs config.allowAliases { autodie = null; # part of Perl AutoLoader = null; # part of Perl 5.22 constant = null; # part of Perl 5.22 diff --git a/pkgs/top-level/stage.nix b/pkgs/top-level/stage.nix index 5b802aea07586..e24f5cb3c22e5 100644 --- a/pkgs/top-level/stage.nix +++ b/pkgs/top-level/stage.nix @@ -97,7 +97,8 @@ let res self; in res; - aliases = self: super: lib.optionalAttrs (config.allowAliases or true) (import ./aliases.nix lib self super); + aliases = self: super: + lib.optionalAttrs config.allowAliases (import ./aliases.nix lib self super); # stdenvOverrides is used to avoid having multiple of versions # of certain dependencies that were used in bootstrapping the @@ -114,7 +115,7 @@ let # ... pkgs.foo ..."). configOverrides = self: super: lib.optionalAttrs allowCustomOverrides - ((config.packageOverrides or (super: {})) super); + (config.packageOverrides super); # Convenience attributes for instantitating package sets. Each of # these will instantiate a new version of allPackages. Currently the