Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 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
37 changes: 35 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 @@ -291,6 +291,15 @@ rec {
functor = (defaultFunctor name) // { wrapped = elemType; };
};

# Same as attrsOf, but lazy towards attribute values (at the cost of not supporting mkIf and etc)
lazyAttrsOf = elemType: attrsOf elemType // {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if it's worth introducing this confusing and slightly module breaking type (because it makes mkIf, etc. not work anymore), just to add support for using the slightly deprecated packageOverrides as overlays. If people want overlays they can use overlays directly, if the package set they're using doesn't support this then that should be fixed there imo.

merge = loc: defs: zipAttrsWith (name: defs:
(mergeDefinitions (loc ++ [name]) elemType defs).mergedValue
)
# Push down position info.
(map (def: mapAttrs (n: v: { inherit (def) file; value = v; }) def.value) defs);
};

# List or attribute set of ...
loaOf = elemType:
let
Expand Down Expand Up @@ -356,6 +365,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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try to get #44601 moving and merged so it can be used here

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this the complete opposite of opaque? Having access to all assignments is as transparent as can be, nothing gets hidden, maybe it should be called passthru instead?

# 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 +401,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
24 changes: 24 additions & 0 deletions nixos/doc/manual/release-notes/rl-1909.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,30 @@
</itemizedlist>
</section>

<section xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
version="5.0"
xml:id="sec-release-19.09-incompatibilities">
<title>Backward Incompatibilities</title>

<para>
When upgrading from a previous release, please be aware of the following
incompatible changes:
</para>

<itemizedlist>
<listitem>
<para>
The internal top-level NixOS module option <option>_file</option> was
renamed to <option>file</option>. Using the wrong name will cause a
evaluation error, but the indicated location of the error might be
incorrect since that option is one of the sources for those locations.
</para>
</listitem>
</itemizedlist>
</section>

<section xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And with such a type, all the values you can set won't be visible in the manual. I really think the whole config type checking part should be cleanly separated as a module not tied to pkgs, then it can be used as type = types.submodule (import ./path/to/config-module.nix) here, and similarly in pkgs.

description = ''
The configuration of the Nix Packages collection. (For
details, see the Nixpkgs documentation.) It allows you to set
Expand Down
Loading