Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typed nixpkgs.config with Gentoo-like use-flags #56227

Closed
wants to merge 12 commits into from
Closed
30 changes: 15 additions & 15 deletions lib/modules.nix
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ rec {
# attribute. These options are fragile, as they are used by the
# module system to change the interpretation of modules.
internalModule = rec {
_file = ./modules.nix;
file = ./modules.nix;

key = _file;
key = file;

options = {
_module.args = mkOption {
Expand Down Expand Up @@ -116,29 +116,29 @@ rec {
/* Massage a module into canonical form, that is, a set consisting
of ‘options’, ‘config’ and ‘imports’ attributes. */
unifyModuleSyntax = file: key: m:
let metaSet = if m ? meta
then { meta = m.meta; }
else {};
let addMetaSet = arg: if m ? meta
then mkMerge [ arg { meta = m.meta; } ]
else arg;
in
if m ? config || m ? options then
let badAttrs = removeAttrs m ["_file" "key" "disabledModules" "imports" "options" "config" "meta"]; in
let badAttrs = removeAttrs m ["file" "key" "disabledModules" "imports" "options" "config" "meta"]; in
if badAttrs != {} then
throw "Module `${key}' has an unsupported attribute `${head (attrNames badAttrs)}'. This is caused by assignments to the top-level attributes `config' or `options'."
else
{ file = m._file or file;
{ file = m.file or file;
key = toString m.key or key;
disabledModules = m.disabledModules or [];
imports = m.imports or [];
options = m.options or {};
config = mkMerge [ (m.config or {}) metaSet ];
config = addMetaSet (m.config or {});
}
else
{ file = m._file or file;
{ file = m.file or file;
key = toString m.key or key;
disabledModules = m.disabledModules or [];
imports = m.require or [] ++ m.imports or [];
options = {};
config = mkMerge [ (removeAttrs m ["_file" "key" "disabledModules" "require" "imports"]) metaSet ];
config = addMetaSet (removeAttrs m ["file" "key" "disabledModules" "require" "imports"]);
};

applyIfFunction = key: f: args@{ config, options, lib, ... }: if isFunction f then
Expand Down Expand Up @@ -176,7 +176,7 @@ rec {
of the submodule. (see applyIfFunction) */
unpackSubmodule = unpack: m: args:
if isType "submodule" m then
{ _file = m.file; } // (unpack m.submodule args)
{ inherit (m) file; } // (unpack m.submodule args)
else unpack m args;

packSubmodule = file: m:
Expand Down Expand Up @@ -394,12 +394,12 @@ rec {
to refer to the full configuration without creating an infinite
recursion.
*/
pushDownProperties = cfg:
if cfg._type or "" == "merge" then
pushDownProperties = cfg: let type = cfg._type or ""; in
if type == "merge" then
concatMap pushDownProperties cfg.contents
else if cfg._type or "" == "if" then
else if type == "if" then
map (mapAttrs (n: v: mkIf cfg.condition v)) (pushDownProperties cfg.content)
else if cfg._type or "" == "override" then
else if type == "override" then
map (mapAttrs (n: v: mkOverride cfg.priority v)) (pushDownProperties cfg.content)
else # FIXME: handle mkOrder?
[ cfg ];
Expand Down
28 changes: 26 additions & 2 deletions lib/types.nix
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ rec {


# When adding new types don't forget to document them in
# nixos/doc/manual/development/option-types.xml!
# ../nixos/doc/manual/development/option-types.xml!
types = rec {
unspecified = mkOptionType {
name = "unspecified";
Expand Down Expand Up @@ -356,6 +356,30 @@ rec {
functor = (defaultFunctor name) // { wrapped = elemType; };
};

# A function to something mergeable (i.e., to a monoid). You
# should use something else. This type is a last resort and should
# only be used when there is absolutely nothing else you can do.
# Most uses of this type imply bad design.
functionTo = elemType: mkOptionType {
name = "function that evaluates to a(n) ${elemType.name}";
check = isFunction;
merge = loc: defs:
fnArgs: elemType.merge loc (map (fn: { inherit (fn) file; value = fn.value fnArgs; }) defs);
getSubOptions = elemType.getSubOptions;
getSubModules = elemType.getSubModules;
substSubModules = m: functionTo (elemType.substSubModules m);
};

# An opaque value. The result of evaluation of an option of this
# type is a list of { file, value } pairs describing all the
# values assigned to this option (with corresponding files where
# these values were applied).
opaque = mkOptionType {
name = "opaque";
description = "opaque value";
merge = loc: args: args;
};

# A submodule (like typed attribute set). See NixOS manual.
submodule = opts:
let
Expand All @@ -368,7 +392,7 @@ rec {
merge = loc: defs:
let
coerce = def: if isFunction def then def else { config = def; };
modules = opts' ++ map (def: { _file = def.file; imports = [(coerce def.value)]; }) defs;
modules = opts' ++ map (def: { inherit (def) file; imports = [(coerce def.value)]; }) defs;
in (evalModules {
inherit modules;
args.name = last loc;
Expand Down
14 changes: 14 additions & 0 deletions nixos/doc/manual/development/option-types.xml
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,20 @@
</variablelist>
</section>

<section xml:id='section-option-types-opaque>
<title>Opaque</title>

<para>
<literal>opaque</literal> is a type that simply collects assignments to itself into
a list of <literal>{ file, value }</literal> pairs.
</para>

<para>
This is useful when you want to merge these values outside of
<literal>evalModules</literal> done by NixOS.
</para>
</section>

<section xml:id='section-option-types-submodule'>
<title>Submodule</title>

Expand Down
4 changes: 2 additions & 2 deletions nixos/lib/eval-config.nix
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ in

let
pkgsModule = rec {
_file = ./eval-config.nix;
key = _file;
file = ./eval-config.nix;
key = file;
config = {
# Explicit `nixpkgs.system` or `nixpkgs.localSystem` should override
# this. Since the latter defaults to the former, the former should
Expand Down
41 changes: 3 additions & 38 deletions nixos/modules/misc/nixpkgs.nix
Original file line number Diff line number Diff line change
Expand Up @@ -6,42 +6,6 @@ let
cfg = config.nixpkgs;
opt = options.nixpkgs;

isConfig = x:
builtins.isAttrs x || lib.isFunction x;

optCall = f: x:
if lib.isFunction f
then f x
else f;

mergeConfig = lhs_: rhs_:
let
lhs = optCall lhs_ { inherit pkgs; };
rhs = optCall rhs_ { inherit pkgs; };
in
lhs // rhs //
optionalAttrs (lhs ? packageOverrides) {
packageOverrides = pkgs:
optCall lhs.packageOverrides pkgs //
optCall (attrByPath ["packageOverrides"] ({}) rhs) pkgs;
} //
optionalAttrs (lhs ? perlPackageOverrides) {
perlPackageOverrides = pkgs:
optCall lhs.perlPackageOverrides pkgs //
optCall (attrByPath ["perlPackageOverrides"] ({}) rhs) pkgs;
};

configType = mkOptionType {
name = "nixpkgs-config";
description = "nixpkgs config";
check = x:
let traceXIfNot = c:
if c x then true
else lib.traceSeqN 1 x false;
in traceXIfNot isConfig;
merge = args: fold (def: mergeConfig def.value) {};
};

overlayType = mkOptionType {
name = "nixpkgs-overlay";
description = "nixpkgs overlay";
Expand All @@ -56,7 +20,8 @@ let
};

defaultPkgs = import ../../.. {
inherit (cfg) config overlays localSystem crossSystem;
configs = cfg.config;
inherit (cfg) overlays localSystem crossSystem;
};

finalPkgs = if opt.pkgs.isDefined then cfg.pkgs.appendOverlays cfg.overlays else defaultPkgs;
Expand Down Expand Up @@ -113,7 +78,7 @@ in
''
{ allowBroken = true; allowUnfree = true; }
'';
type = configType;
type = types.opaque;
description = ''
The configuration of the Nix Packages collection. (For
details, see the Nixpkgs documentation.) It allows you to set
Expand Down
Loading