diff options
author | Alyssa Ross <hi@alyssa.is> | 2022-03-15 10:36:38 +0000 |
---|---|---|
committer | Alyssa Ross <hi@alyssa.is> | 2022-03-16 11:37:19 +0000 |
commit | d435710923ac6e6f9fc155534800745004f2ce93 (patch) | |
tree | 386f9401476f96bdc6ec25173a090198942b5d5b /nixpkgs/lib | |
parent | c725f0011e91ae49d351b981690eb66b862b6104 (diff) | |
parent | 3239fd2b8f728106491154b44625662e10259af2 (diff) | |
download | nixlib-d435710923ac6e6f9fc155534800745004f2ce93.tar nixlib-d435710923ac6e6f9fc155534800745004f2ce93.tar.gz nixlib-d435710923ac6e6f9fc155534800745004f2ce93.tar.bz2 nixlib-d435710923ac6e6f9fc155534800745004f2ce93.tar.lz nixlib-d435710923ac6e6f9fc155534800745004f2ce93.tar.xz nixlib-d435710923ac6e6f9fc155534800745004f2ce93.tar.zst nixlib-d435710923ac6e6f9fc155534800745004f2ce93.zip |
Merge commit '3239fd2b8f728106491154b44625662e10259af2'
Conflicts: nixpkgs/pkgs/applications/window-managers/sway/default.nix
Diffstat (limited to 'nixpkgs/lib')
-rw-r--r-- | nixpkgs/lib/attrsets.nix | 2 | ||||
-rw-r--r-- | nixpkgs/lib/default.nix | 2 | ||||
-rw-r--r-- | nixpkgs/lib/modules.nix | 15 | ||||
-rw-r--r-- | nixpkgs/lib/systems/parse.nix | 4 | ||||
-rwxr-xr-x | nixpkgs/lib/tests/modules.sh | 30 | ||||
-rw-r--r-- | nixpkgs/lib/tests/modules/adhoc-freeformType-survives-type-merge.nix | 14 | ||||
-rw-r--r-- | nixpkgs/lib/tests/modules/emptyValues.nix | 36 | ||||
-rw-r--r-- | nixpkgs/lib/tests/modules/freeform-submodules.nix | 22 | ||||
-rw-r--r-- | nixpkgs/lib/tests/modules/optionTypeFile.nix | 28 | ||||
-rw-r--r-- | nixpkgs/lib/tests/modules/optionTypeMerging.nix | 27 | ||||
-rw-r--r-- | nixpkgs/lib/tests/modules/raw.nix | 30 | ||||
-rw-r--r-- | nixpkgs/lib/types.nix | 58 |
12 files changed, 250 insertions, 18 deletions
diff --git a/nixpkgs/lib/attrsets.nix b/nixpkgs/lib/attrsets.nix index a88947b45858..c0d3ede73d0e 100644 --- a/nixpkgs/lib/attrsets.nix +++ b/nixpkgs/lib/attrsets.nix @@ -327,7 +327,7 @@ rec { isDerivation "foobar" => false */ - isDerivation = x: isAttrs x && x ? type && x.type == "derivation"; + isDerivation = x: x.type or null == "derivation"; /* Converts a store path to a fake derivation. */ toDerivation = path: diff --git a/nixpkgs/lib/default.nix b/nixpkgs/lib/default.nix index 3e43733ad20f..3fead03a4636 100644 --- a/nixpkgs/lib/default.nix +++ b/nixpkgs/lib/default.nix @@ -126,7 +126,7 @@ let getValues getFiles optionAttrSetToDocList optionAttrSetToDocList' scrubOptionValue literalExpression literalExample literalDocBook - showOption showFiles unknownModule mkOption; + showOption showFiles unknownModule mkOption mkPackageOption; inherit (self.types) isType setType defaultTypeMerge defaultFunctor isOptionType mkOptionType; inherit (self.asserts) diff --git a/nixpkgs/lib/modules.nix b/nixpkgs/lib/modules.nix index f1125aca0a35..4c4d9f994dae 100644 --- a/nixpkgs/lib/modules.nix +++ b/nixpkgs/lib/modules.nix @@ -138,7 +138,7 @@ rec { # support for that, in turn it's lazy in its values. This means e.g. # a `_module.args.pkgs = import (fetchTarball { ... }) {}` won't # start a download when `pkgs` wasn't evaluated. - type = types.lazyAttrsOf types.unspecified; + type = types.lazyAttrsOf types.raw; internal = true; description = "Arguments passed to each module."; }; @@ -151,8 +151,7 @@ rec { }; _module.freeformType = mkOption { - # Disallow merging for now, but could be implemented nicely with a `types.optionType` - type = types.nullOr (types.uniq types.attrs); + type = types.nullOr types.optionType; internal = true; default = null; description = '' @@ -762,13 +761,13 @@ rec { options = opt.options or (throw "Option `${showOption loc}' has type optionSet but has no option attribute, in ${showFiles opt.declarations}."); f = tp: - let optionSetIn = type: (tp.name == type) && (tp.functor.wrapped.name == "optionSet"); - in if tp.name == "option set" || tp.name == "submodule" then throw "The option ${showOption loc} uses submodules without a wrapping type, in ${showFiles opt.declarations}." - else if optionSetIn "attrsOf" then types.attrsOf (types.submodule options) - else if optionSetIn "listOf" then types.listOf (types.submodule options) - else if optionSetIn "nullOr" then types.nullOr (types.submodule options) + else if (tp.functor.wrapped.name or null) == "optionSet" then + if tp.name == "attrsOf" then types.attrsOf (types.submodule options) + else if tp.name == "listOf" then types.listOf (types.submodule options) + else if tp.name == "nullOr" then types.nullOr (types.submodule options) + else tp else tp; in if opt.type.getSubModules or null == null diff --git a/nixpkgs/lib/systems/parse.nix b/nixpkgs/lib/systems/parse.nix index 8a88d8cfbe87..f0e87c30e473 100644 --- a/nixpkgs/lib/systems/parse.nix +++ b/nixpkgs/lib/systems/parse.nix @@ -364,8 +364,8 @@ rec { musleabihf = { float = "hard"; }; musl = {}; - uclibceabihf = { float = "soft"; }; - uclibceabi = { float = "hard"; }; + uclibceabi = { float = "soft"; }; + uclibceabihf = { float = "hard"; }; uclibc = {}; unknown = {}; diff --git a/nixpkgs/lib/tests/modules.sh b/nixpkgs/lib/tests/modules.sh index 590937da5b8f..350fe85e7487 100755 --- a/nixpkgs/lib/tests/modules.sh +++ b/nixpkgs/lib/tests/modules.sh @@ -240,6 +240,11 @@ checkConfigOutput '^"24"$' config.foo ./freeform-attrsOf.nix ./freeform-str-dep- checkConfigError 'infinite recursion encountered' config.foo ./freeform-attrsOf.nix ./freeform-unstr-dep-str.nix checkConfigError 'The option .* is used but not defined' config.foo ./freeform-lazyAttrsOf.nix ./freeform-unstr-dep-str.nix checkConfigOutput '^"24"$' config.foo ./freeform-lazyAttrsOf.nix ./freeform-unstr-dep-str.nix ./define-value-string.nix +# submodules in freeformTypes should have their locations annotated +checkConfigOutput '/freeform-submodules.nix"$' config.fooDeclarations.0 ./freeform-submodules.nix +# freeformTypes can get merged using `types.type`, including submodules +checkConfigOutput '^10$' config.free.xxx.foo ./freeform-submodules.nix +checkConfigOutput '^10$' config.free.yyy.bar ./freeform-submodules.nix ## types.anything # Check that attribute sets are merged recursively @@ -284,6 +289,31 @@ checkConfigOutput '^"a b"$' config.resultFoo ./declare-variants.nix ./define-var checkConfigOutput '^"a y z"$' config.resultFooBar ./declare-variants.nix ./define-variant.nix checkConfigOutput '^"a b c"$' config.resultFooFoo ./declare-variants.nix ./define-variant.nix +## emptyValue's +checkConfigOutput "[ ]" config.list.a ./emptyValues.nix +checkConfigOutput "{ }" config.attrs.a ./emptyValues.nix +checkConfigOutput "null" config.null.a ./emptyValues.nix +checkConfigOutput "{ }" config.submodule.a ./emptyValues.nix +# These types don't have empty values +checkConfigError 'The option .int.a. is used but not defined' config.int.a ./emptyValues.nix +checkConfigError 'The option .nonEmptyList.a. is used but not defined' config.nonEmptyList.a ./emptyValues.nix + +## types.raw +checkConfigOutput "{ foo = <CODE>; }" config.unprocessedNesting ./raw.nix +checkConfigOutput "10" config.processedToplevel ./raw.nix +checkConfigError "The option .multiple. is defined multiple times" config.multiple ./raw.nix +checkConfigOutput "bar" config.priorities ./raw.nix + +# Test that types.optionType merges types correctly +checkConfigOutput '^10$' config.theOption.int ./optionTypeMerging.nix +checkConfigOutput '^"hello"$' config.theOption.str ./optionTypeMerging.nix + +# Test that types.optionType correctly annotates option locations +checkConfigError 'The option .theOption.nested. in .other.nix. is already declared in .optionTypeFile.nix.' config.theOption.nested ./optionTypeFile.nix + +# Test that types.optionType leaves types untouched as long as they don't need to be merged +checkConfigOutput 'ok' config.freeformItems.foo.bar ./adhoc-freeformType-survives-type-merge.nix + cat <<EOF ====== module tests ====== $pass Pass diff --git a/nixpkgs/lib/tests/modules/adhoc-freeformType-survives-type-merge.nix b/nixpkgs/lib/tests/modules/adhoc-freeformType-survives-type-merge.nix new file mode 100644 index 000000000000..3cefb543c256 --- /dev/null +++ b/nixpkgs/lib/tests/modules/adhoc-freeformType-survives-type-merge.nix @@ -0,0 +1,14 @@ +{ lib, ... }: { + options.dummy = lib.mkOption { type = lib.types.anything; default = {}; }; + freeformType = + let + a = lib.types.attrsOf (lib.types.submodule { options.bar = lib.mkOption { }; }); + in + # modifying types like this breaks type merging. + # This test makes sure that type merging is not performed when only a single declaration exists. + # Don't modify types in practice! + a // { + merge = loc: defs: { freeformItems = a.merge loc defs; }; + }; + config.foo.bar = "ok"; +} diff --git a/nixpkgs/lib/tests/modules/emptyValues.nix b/nixpkgs/lib/tests/modules/emptyValues.nix new file mode 100644 index 000000000000..77825de3281a --- /dev/null +++ b/nixpkgs/lib/tests/modules/emptyValues.nix @@ -0,0 +1,36 @@ +{ lib, ... }: +let + inherit (lib) types; +in { + + options = { + int = lib.mkOption { + type = types.lazyAttrsOf types.int; + }; + list = lib.mkOption { + type = types.lazyAttrsOf (types.listOf types.int); + }; + nonEmptyList = lib.mkOption { + type = types.lazyAttrsOf (types.nonEmptyListOf types.int); + }; + attrs = lib.mkOption { + type = types.lazyAttrsOf (types.attrsOf types.int); + }; + null = lib.mkOption { + type = types.lazyAttrsOf (types.nullOr types.int); + }; + submodule = lib.mkOption { + type = types.lazyAttrsOf (types.submodule {}); + }; + }; + + config = { + int.a = lib.mkIf false null; + list.a = lib.mkIf false null; + nonEmptyList.a = lib.mkIf false null; + attrs.a = lib.mkIf false null; + null.a = lib.mkIf false null; + submodule.a = lib.mkIf false null; + }; + +} diff --git a/nixpkgs/lib/tests/modules/freeform-submodules.nix b/nixpkgs/lib/tests/modules/freeform-submodules.nix new file mode 100644 index 000000000000..3910435a7b5c --- /dev/null +++ b/nixpkgs/lib/tests/modules/freeform-submodules.nix @@ -0,0 +1,22 @@ +{ lib, options, ... }: with lib.types; { + + options.fooDeclarations = lib.mkOption { + default = (options.free.type.getSubOptions [])._freeformOptions.foo.declarations; + }; + + options.free = lib.mkOption { + type = submodule { + config._module.freeformType = lib.mkMerge [ + (attrsOf (submodule { + options.foo = lib.mkOption {}; + })) + (attrsOf (submodule { + options.bar = lib.mkOption {}; + })) + ]; + }; + }; + + config.free.xxx.foo = 10; + config.free.yyy.bar = 10; +} diff --git a/nixpkgs/lib/tests/modules/optionTypeFile.nix b/nixpkgs/lib/tests/modules/optionTypeFile.nix new file mode 100644 index 000000000000..6015d59a72c9 --- /dev/null +++ b/nixpkgs/lib/tests/modules/optionTypeFile.nix @@ -0,0 +1,28 @@ +{ config, lib, ... }: { + + _file = "optionTypeFile.nix"; + + options.theType = lib.mkOption { + type = lib.types.optionType; + }; + + options.theOption = lib.mkOption { + type = config.theType; + default = {}; + }; + + config.theType = lib.mkMerge [ + (lib.types.submodule { + options.nested = lib.mkOption { + type = lib.types.int; + }; + }) + (lib.types.submodule { + _file = "other.nix"; + options.nested = lib.mkOption { + type = lib.types.str; + }; + }) + ]; + +} diff --git a/nixpkgs/lib/tests/modules/optionTypeMerging.nix b/nixpkgs/lib/tests/modules/optionTypeMerging.nix new file mode 100644 index 000000000000..74a620c4620c --- /dev/null +++ b/nixpkgs/lib/tests/modules/optionTypeMerging.nix @@ -0,0 +1,27 @@ +{ config, lib, ... }: { + + options.theType = lib.mkOption { + type = lib.types.optionType; + }; + + options.theOption = lib.mkOption { + type = config.theType; + }; + + config.theType = lib.mkMerge [ + (lib.types.submodule { + options.int = lib.mkOption { + type = lib.types.int; + default = 10; + }; + }) + (lib.types.submodule { + options.str = lib.mkOption { + type = lib.types.str; + }; + }) + ]; + + config.theOption.str = "hello"; + +} diff --git a/nixpkgs/lib/tests/modules/raw.nix b/nixpkgs/lib/tests/modules/raw.nix new file mode 100644 index 000000000000..418e671ed076 --- /dev/null +++ b/nixpkgs/lib/tests/modules/raw.nix @@ -0,0 +1,30 @@ +{ lib, ... }: { + + options = { + processedToplevel = lib.mkOption { + type = lib.types.raw; + }; + unprocessedNesting = lib.mkOption { + type = lib.types.raw; + }; + multiple = lib.mkOption { + type = lib.types.raw; + }; + priorities = lib.mkOption { + type = lib.types.raw; + }; + }; + + config = { + processedToplevel = lib.mkIf true 10; + unprocessedNesting.foo = throw "foo"; + multiple = lib.mkMerge [ + "foo" + "foo" + ]; + priorities = lib.mkMerge [ + "foo" + (lib.mkForce "bar") + ]; + }; +} diff --git a/nixpkgs/lib/types.nix b/nixpkgs/lib/types.nix index f2f9b2bca985..3078615f5ddc 100644 --- a/nixpkgs/lib/types.nix +++ b/nixpkgs/lib/types.nix @@ -61,7 +61,11 @@ let boolToString ; - inherit (lib.modules) mergeDefinitions; + inherit (lib.modules) + mergeDefinitions + fixupOptionType + mergeOptionDecls + ; outer_types = rec { isType = type: x: (x._type or "") == type; @@ -162,6 +166,13 @@ rec { # nixos/doc/manual/development/option-types.xml! types = rec { + raw = mkOptionType rec { + name = "raw"; + description = "raw value"; + check = value: true; + merge = mergeOneOption; + }; + anything = mkOptionType { name = "anything"; description = "anything"; @@ -357,13 +368,21 @@ rec { emptyValue = { value = {}; }; }; - # derivation is a reserved keyword. + # A package is a top-level store path (/nix/store/hash-name). This includes: + # - derivations + # - more generally, attribute sets with an `outPath` or `__toString` attribute + # pointing to a store path, e.g. flake inputs + # - strings with context, e.g. "${pkgs.foo}" or (toString pkgs.foo) + # - hardcoded store path literals (/nix/store/hash-foo) or strings without context + # ("/nix/store/hash-foo"). These get a context added to them using builtins.storePath. package = mkOptionType { name = "package"; check = x: isDerivation x || isStorePath x; merge = loc: defs: let res = mergeOneOption loc defs; - in if isDerivation res then res else toDerivation res; + in if builtins.isPath res || (builtins.isString res && ! builtins.hasContext res) + then toDerivation res + else res; }; shellPackage = package // { @@ -390,7 +409,7 @@ rec { ).optionalValue ) def.value ) defs))); - emptyValue = { value = {}; }; + emptyValue = { value = []; }; getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["*"]); getSubModules = elemType.getSubModules; substSubModules = m: listOf (elemType.substSubModules m); @@ -402,7 +421,7 @@ rec { let list = addCheck (types.listOf elemType) (l: l != []); in list // { description = "non-empty " + list.description; - # Note: emptyValue is left as is, because another module may define an element. + emptyValue = { }; # no .value attr, meaning unset }; attrsOf = elemType: mkOptionType rec { @@ -503,7 +522,7 @@ rec { functionTo = elemType: mkOptionType { name = "functionTo"; - description = "function that evaluates to a(n) ${elemType.name}"; + description = "function that evaluates to a(n) ${elemType.description}"; check = isFunction; merge = loc: defs: fnArgs: (mergeDefinitions (loc ++ [ "[function body]" ]) elemType (map (fn: { inherit (fn) file; value = fn.value fnArgs; }) defs)).mergedValue; @@ -518,6 +537,33 @@ rec { modules = toList modules; }; + # The type of a type! + optionType = mkOptionType { + name = "optionType"; + description = "optionType"; + check = value: value._type or null == "option-type"; + merge = loc: defs: + if length defs == 1 + then (head defs).value + else let + # Prepares the type definitions for mergeOptionDecls, which + # annotates submodules types with file locations + optionModules = map ({ value, file }: + { + _file = file; + # There's no way to merge types directly from the module system, + # but we can cheat a bit by just declaring an option with the type + options = lib.mkOption { + type = value; + }; + } + ) defs; + # Merges all the types into a single one, including submodule merging. + # This also propagates file information to all submodules + mergedOption = fixupOptionType loc (mergeOptionDecls loc optionModules); + in mergedOption.type; + }; + submoduleWith = { modules , specialArgs ? {} |