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

nixos: Add system.build.{toplevel,installBootLoader}, improve error message #156503

Merged
merged 8 commits into from
Jan 25, 2022
5 changes: 3 additions & 2 deletions lib/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,9 @@ let
mkRenamedOptionModule mkMergedOptionModule mkChangedOptionModule
mkAliasOptionModule mkDerivedConfig doRename;
inherit (self.options) isOption mkEnableOption mkSinkUndeclaredOptions
mergeDefaultOption mergeOneOption mergeEqualOption getValues
getFiles optionAttrSetToDocList optionAttrSetToDocList'
mergeDefaultOption mergeOneOption mergeEqualOption mergeUniqueOption
getValues getFiles
optionAttrSetToDocList optionAttrSetToDocList'
scrubOptionValue literalExpression literalExample literalDocBook
showOption showFiles unknownModule mkOption;
inherit (self.types) isType setType defaultTypeMerge defaultFunctor
Expand Down
12 changes: 7 additions & 5 deletions lib/options.nix
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,13 @@ rec {
else if all isInt list && all (x: x == head list) list then head list
else throw "Cannot merge definitions of `${showOption loc}'. Definition values:${showDefs defs}";

mergeOneOption = loc: defs:
if defs == [] then abort "This case should never happen."
else if length defs != 1 then
throw "The unique option `${showOption loc}' is defined multiple times. Definition values:${showDefs defs}"
else (head defs).value;
mergeOneOption = mergeUniqueOption { message = ""; };

mergeUniqueOption = { message }: loc: defs:
if length defs == 1
then (head defs).value
else assert length defs > 1;
throw "The option `${showOption loc}' is defined multiple times.\n${message}\nDefinition values:${showDefs defs}";

/* "Merge" option definitions by checking that they all have the same value. */
mergeEqualOption = loc: defs:
Expand Down
15 changes: 14 additions & 1 deletion lib/types.nix
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ let
last
length
tail
unique
;
inherit (lib.attrsets)
attrNames
Expand All @@ -48,6 +47,7 @@ let
mergeDefaultOption
mergeEqualOption
mergeOneOption
mergeUniqueOption
showFiles
showOption
;
Expand Down Expand Up @@ -470,6 +470,18 @@ rec {
nestedTypes.elemType = elemType;
};

unique = { message }: type: mkOptionType rec {
name = "unique";
inherit (type) description check;
merge = mergeUniqueOption { inherit message; };
emptyValue = type.emptyValue;
getSubOptions = type.getSubOptions;
getSubModules = type.getSubModules;
substSubModules = m: uniq (type.substSubModules m);
functor = (defaultFunctor name) // { wrapped = type; };
nestedTypes.elemType = type;
};

# Null or value of ...
nullOr = elemType: mkOptionType rec {
name = "nullOr";
Expand Down Expand Up @@ -599,6 +611,7 @@ rec {
# A value from a set of allowed ones.
enum = values:
let
inherit (lib.lists) unique;
show = v:
if builtins.isString v then ''"${v}"''
else if builtins.isInt v then builtins.toString v
Expand Down
6 changes: 6 additions & 0 deletions nixos/doc/manual/development/option-types.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,12 @@ Composed types are types that take a type as parameter. `listOf
: Ensures that type *`t`* cannot be merged. It is used to ensure option
definitions are declared only once.

`types.unique` `{ message = m }` *`t`*

: Ensures that type *`t`* cannot be merged. Prints the message *`m`*, after
the line `The option <option path> is defined multiple times.` and before
a list of definition locations.

`types.either` *`t1 t2`*

: Type *`t1`* or type *`t2`*, e.g. `with types; either int str`.
Expand Down
16 changes: 16 additions & 0 deletions nixos/doc/manual/from_md/development/option-types.section.xml
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,22 @@
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>types.unique</literal>
<literal>{ message = m }</literal>
<emphasis><literal>t</literal></emphasis>
</term>
<listitem>
<para>
Ensures that type <emphasis><literal>t</literal></emphasis>
cannot be merged. Prints the message
<emphasis><literal>m</literal></emphasis>, after the line
<literal>The option &lt;option path&gt; is defined multiple times.</literal>
and before a list of definition locations.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>types.either</literal>
Expand Down
57 changes: 45 additions & 12 deletions nixos/modules/system/activation/top-level.nix
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,7 @@ let
utillinux = pkgs.util-linux;

kernelParams = config.boot.kernelParams;
installBootLoader =
config.system.build.installBootLoader
or "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true";
installBootLoader = config.system.build.installBootLoader;
activationScript = config.system.activationScripts.script;
dryActivationScript = config.system.dryActivationScript;
nixosLabel = config.system.nixos.label;
Expand All @@ -135,25 +133,27 @@ let
pkgs.replaceDependency { inherit oldDependency newDependency drv; }
) baseSystemAssertWarn config.system.replaceRuntimeDependencies;

/* Workaround until https://github.com/NixOS/nixpkgs/pull/156533
Call can be replaced by argument when that's merged.
*/
tmpFixupSubmoduleBoundary = subopts:
lib.mkOption {
type = lib.types.submoduleWith {
modules = [ { options = subopts; } ];
};
};

in

{
imports = [
../build.nix
(mkRemovedOptionModule [ "nesting" "clone" ] "Use `specialisation.«name» = { inheritParentConfig = true; configuration = { ... }; }` instead.")
(mkRemovedOptionModule [ "nesting" "children" ] "Use `specialisation.«name».configuration = { ... }` instead.")
];

options = {

system.build = mkOption {
internal = true;
default = {};
type = with types; lazyAttrsOf (uniq unspecified);
description = ''
Attribute set of derivations used to setup the system.
'';
};

specialisation = mkOption {
default = {};
example = lib.literalExpression "{ fewJobsManyCores.configuration = { nix.buildCores = 0; nix.maxJobs = 1; }; }";
Expand Down Expand Up @@ -224,6 +224,39 @@ in
'';
};

system.build = tmpFixupSubmoduleBoundary {
installBootLoader = mkOption {
internal = true;
# "; true" => make the `$out` argument from switch-to-configuration.pl
# go to `true` instead of `echo`, hiding the useless path
# from the log.
default = "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true";
description = ''
A program that writes a bootloader installation script to the path passed in the first command line argument.

See <literal>nixos/modules/system/activation/switch-to-configuration.pl</literal>.
'';
type = types.unique {
message = ''
Only one bootloader can be enabled at a time. This requirement has not
been checked until NixOS 22.05. Earlier versions defaulted to the last
definition. Change your configuration to enable only one bootloader.
'';
} (types.either types.str types.package);
};

toplevel = mkOption {
type = types.package;
readOnly = true;
description = ''
This option contains the store path that typically represents a NixOS system.

You can read this path in a custom deployment tool for example.
'';
};
};


system.copySystemConfiguration = mkOption {
type = types.bool;
default = false;
Expand Down
21 changes: 21 additions & 0 deletions nixos/modules/system/build.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{ lib, ... }:
let
inherit (lib) mkOption types;
in
{
options = {

system.build = mkOption {
default = {};
description = ''
Attribute set of derivations used to set up the system.
'';
type = types.submoduleWith {
modules = [{
freeformType = with types; lazyAttrsOf (uniq unspecified);
}];
};
};

};
}