summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/default.nix5
-rw-r--r--lib/meta.nix14
-rw-r--r--lib/modules.nix13
-rw-r--r--lib/strings.nix65
-rw-r--r--lib/systems/doubles.nix2
-rw-r--r--lib/systems/inspect.nix3
-rw-r--r--lib/systems/platforms.nix2
-rw-r--r--lib/tests/misc.nix20
-rwxr-xr-xlib/tests/modules.sh6
-rw-r--r--lib/tests/modules/extendModules-168767-imports.nix41
-rw-r--r--lib/tests/systems.nix3
-rw-r--r--lib/types.nix10
12 files changed, 172 insertions, 12 deletions
diff --git a/lib/default.nix b/lib/default.nix
index 8d7cc20ae482..22eb5440c282 100644
--- a/lib/default.nix
+++ b/lib/default.nix
@@ -98,7 +98,8 @@ let
concatImapStringsSep makeSearchPath makeSearchPathOutput
makeLibraryPath makeBinPath optionalString
hasInfix hasPrefix hasSuffix stringToCharacters stringAsChars escape
- escapeShellArg escapeShellArgs escapeRegex escapeXML replaceChars lowerChars
+ escapeShellArg escapeShellArgs isValidPosixName toShellVar toShellVars
+ escapeRegex escapeXML replaceChars lowerChars
upperChars toLower toUpper addContextFrom splitString
removePrefix removeSuffix versionOlder versionAtLeast
getName getVersion
@@ -112,7 +113,7 @@ let
makeScope makeScopeWithSplicing;
inherit (self.meta) addMetaAttrs dontDistribute setName updateName
appendToName mapDerivationAttrset setPrio lowPrio lowPrioSet hiPrio
- hiPrioSet getLicenseFromSpdxId;
+ hiPrioSet getLicenseFromSpdxId getExe;
inherit (self.sources) pathType pathIsDirectory cleanSourceFilter
cleanSource sourceByRegex sourceFilesBySuffices
commitIdFromGitRepo cleanSourceWith pathHasContext
diff --git a/lib/meta.nix b/lib/meta.nix
index 5b1f7ee5ff2d..74b94211552b 100644
--- a/lib/meta.nix
+++ b/lib/meta.nix
@@ -126,4 +126,18 @@ rec {
lib.warn "getLicenseFromSpdxId: No license matches the given SPDX ID: ${licstr}"
{ shortName = licstr; }
);
+
+ /* Get the path to the main program of a derivation with either
+ meta.mainProgram or pname or name
+
+ Type: getExe :: derivation -> string
+
+ Example:
+ getExe pkgs.hello
+ => "/nix/store/g124820p9hlv4lj8qplzxw1c44dxaw1k-hello-2.12/bin/hello"
+ getExe pkgs.mustache-go
+ => "/nix/store/am9ml4f4ywvivxnkiaqwr0hyxka1xjsf-mustache-go-1.3.0/bin/mustache"
+ */
+ getExe = x:
+ "${lib.getBin x}/bin/${x.meta.mainProgram or (lib.getName x)}";
}
diff --git a/lib/modules.nix b/lib/modules.nix
index abcdd1a894cc..0bbf9d43c575 100644
--- a/lib/modules.nix
+++ b/lib/modules.nix
@@ -113,6 +113,10 @@ rec {
args ? {}
, # This would be remove in the future, Prefer _module.check option instead.
check ? true
+ # Internal variable to avoid `_key` collisions regardless
+ # of `extendModules`. Used in `submoduleWith`.
+ # Test case: lib/tests/modules, "168767"
+ , extensionOffset ? 0
}:
let
withWarnings = x:
@@ -156,7 +160,10 @@ rec {
type = types.lazyAttrsOf types.raw;
# Only render documentation once at the root of the option tree,
# not for all individual submodules.
- internal = prefix != [];
+ # Allow merging option decls to make this internal regardless.
+ ${if prefix == []
+ then null # unset => visible
+ else "internal"} = true;
# TODO: Change the type of this option to a submodule with a
# freeformType, so that individual arguments can be documented
# separately
@@ -338,15 +345,17 @@ rec {
modules ? [],
specialArgs ? {},
prefix ? [],
+ extensionOffset ? length modules,
}:
evalModules (evalModulesArgs // {
modules = regularModules ++ modules;
specialArgs = evalModulesArgs.specialArgs or {} // specialArgs;
prefix = extendArgs.prefix or evalModulesArgs.prefix;
+ inherit extensionOffset;
});
type = lib.types.submoduleWith {
- inherit modules specialArgs;
+ inherit modules specialArgs extensionOffset;
};
result = withWarnings {
diff --git a/lib/strings.nix b/lib/strings.nix
index 820d1901f945..d3ef748fb71e 100644
--- a/lib/strings.nix
+++ b/lib/strings.nix
@@ -17,6 +17,7 @@ rec {
head
isInt
isList
+ isAttrs
isString
match
parseDrvName
@@ -253,10 +254,7 @@ rec {
=> false
*/
hasInfix = infix: content:
- let
- drop = x: substring 1 (stringLength x) x;
- in hasPrefix infix content
- || content != "" && hasInfix infix (drop content);
+ builtins.match ".*${escapeRegex infix}.*" content != null;
/* Convert a string to a list of characters (i.e. singleton strings).
This allows you to, e.g., map a function over each character. However,
@@ -327,6 +325,65 @@ rec {
*/
escapeShellArgs = concatMapStringsSep " " escapeShellArg;
+ /* Test whether the given name is a valid POSIX shell variable name.
+
+ Type: string -> bool
+
+ Example:
+ isValidPosixName "foo_bar000"
+ => true
+ isValidPosixName "0-bad.jpg"
+ => false
+ */
+ isValidPosixName = name: match "[a-zA-Z_][a-zA-Z0-9_]*" name != null;
+
+ /* Translate a Nix value into a shell variable declaration, with proper escaping.
+
+ Supported value types are strings (mapped to regular variables), lists of strings
+ (mapped to Bash-style arrays) and attribute sets of strings (mapped to Bash-style
+ associative arrays). Note that "strings" include string-coercible values like paths.
+
+ Strings are translated into POSIX sh-compatible code; lists and attribute sets
+ assume a shell that understands Bash syntax (e.g. Bash or ZSH).
+
+ Type: string -> (string | listOf string | attrsOf string) -> string
+
+ Example:
+ ''
+ ${toShellVar "foo" "some string"}
+ [[ "$foo" == "some string" ]]
+ ''
+ */
+ toShellVar = name: value:
+ lib.throwIfNot (isValidPosixName name) "toShellVar: ${name} is not a valid shell variable name" (
+ if isAttrs value then
+ "declare -A ${name}=(${
+ concatStringsSep " " (lib.mapAttrsToList (n: v:
+ "[${escapeShellArg n}]=${escapeShellArg v}"
+ ) value)
+ })"
+ else if isList value then
+ "declare -a ${name}=(${escapeShellArgs value})"
+ else
+ "${name}=${escapeShellArg value}"
+ );
+
+ /* Translate an attribute set into corresponding shell variable declarations
+ using `toShellVar`.
+
+ Type: attrsOf (string | listOf string | attrsOf string) -> string
+
+ Example:
+ let
+ foo = "value";
+ bar = foo;
+ in ''
+ ${toShellVars { inherit foo bar; }}
+ [[ "$foo" == "$bar" ]]
+ ''
+ */
+ toShellVars = vars: concatStringsSep "\n" (lib.mapAttrsToList toShellVar vars);
+
/* Turn a string into a Nix expression representing that string
Type: string -> string
diff --git a/lib/systems/doubles.nix b/lib/systems/doubles.nix
index 27cdaf6a7233..3cdebbc07c1f 100644
--- a/lib/systems/doubles.nix
+++ b/lib/systems/doubles.nix
@@ -74,6 +74,8 @@ in {
mips = filterDoubles predicates.isMips;
mmix = filterDoubles predicates.isMmix;
riscv = filterDoubles predicates.isRiscV;
+ riscv32 = filterDoubles predicates.isRiscV32;
+ riscv64 = filterDoubles predicates.isRiscV64;
vc4 = filterDoubles predicates.isVc4;
or1k = filterDoubles predicates.isOr1k;
m68k = filterDoubles predicates.isM68k;
diff --git a/lib/systems/inspect.nix b/lib/systems/inspect.nix
index 89cac575c67d..27c25deafec3 100644
--- a/lib/systems/inspect.nix
+++ b/lib/systems/inspect.nix
@@ -13,6 +13,7 @@ rec {
isx86_64 = { cpu = { family = "x86"; bits = 64; }; };
isPowerPC = { cpu = cpuTypes.powerpc; };
isPower = { cpu = { family = "power"; }; };
+ isPower64 = { cpu = { family = "power"; bits = 64; }; };
isx86 = { cpu = { family = "x86"; }; };
isAarch32 = { cpu = { family = "arm"; bits = 32; }; };
isAarch64 = { cpu = { family = "arm"; bits = 64; }; };
@@ -23,6 +24,8 @@ rec {
isMips64n64 = { cpu = { family = "mips"; bits = 64; }; abi = { abi = "64"; }; };
isMmix = { cpu = { family = "mmix"; }; };
isRiscV = { cpu = { family = "riscv"; }; };
+ isRiscV32 = { cpu = { family = "riscv"; bits = 32; }; };
+ isRiscV64 = { cpu = { family = "riscv"; bits = 64; }; };
isSparc = { cpu = { family = "sparc"; }; };
isWasm = { cpu = { family = "wasm"; }; };
isMsp430 = { cpu = { family = "msp430"; }; };
diff --git a/lib/systems/platforms.nix b/lib/systems/platforms.nix
index 04d55416242e..d65ff6487b7a 100644
--- a/lib/systems/platforms.nix
+++ b/lib/systems/platforms.nix
@@ -500,7 +500,7 @@ rec {
# based on:
# https://www.mail-archive.com/qemu-discuss@nongnu.org/msg05179.html
# https://gmplib.org/~tege/qemu.html#mips64-debian
- mips64el-qemu-linux-gnuabi64 = (import ./examples).mips64el-linux-gnuabi64 // {
+ mips64el-qemu-linux-gnuabi64 = {
linux-kernel = {
name = "mips64el";
baseConfig = "64r2el_defconfig";
diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix
index dcb56d0c1fc6..f7e62a71eee1 100644
--- a/lib/tests/misc.nix
+++ b/lib/tests/misc.nix
@@ -260,6 +260,26 @@ runTests {
expected = ""test" 'test' < & >";
};
+ testToShellVars = {
+ expr = ''
+ ${toShellVars {
+ STRing01 = "just a 'string'";
+ _array_ = [ "with" "more strings" ];
+ assoc."with some" = ''
+ strings
+ possibly newlines
+ '';
+ }}
+ '';
+ expected = ''
+ STRing01='just a '\'''string'\''''
+ declare -a _array_=('with' 'more strings')
+ declare -A assoc=(['with some']='strings
+ possibly newlines
+ ')
+ '';
+ };
+
# LISTS
testFilter = {
diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh
index 8050c6539fc2..cc13a8d38e37 100755
--- a/lib/tests/modules.sh
+++ b/lib/tests/modules.sh
@@ -293,7 +293,7 @@ checkConfigOutput '^"a c"$' config.result ./functionTo/merging-attrs.nix
# moduleType
checkConfigOutput '^"a b"$' config.resultFoo ./declare-variants.nix ./define-variant.nix
-checkConfigOutput '^"a y z"$' config.resultFooBar ./declare-variants.nix ./define-variant.nix
+checkConfigOutput '^"a b y z"$' config.resultFooBar ./declare-variants.nix ./define-variant.nix
checkConfigOutput '^"a b c"$' config.resultFooFoo ./declare-variants.nix ./define-variant.nix
## emptyValue's
@@ -327,6 +327,10 @@ checkConfigError 'The option .theOption.nested. in .other.nix. is already declar
# 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
+# Anonymous submodules don't get nixed by import resolution/deduplication
+# because of an `extendModules` bug, issue 168767.
+checkConfigOutput '^1$' config.sub.specialisation.value ./extendModules-168767-imports.nix
+
cat <<EOF
====== module tests ======
$pass Pass
diff --git a/lib/tests/modules/extendModules-168767-imports.nix b/lib/tests/modules/extendModules-168767-imports.nix
new file mode 100644
index 000000000000..489e6b5a5d83
--- /dev/null
+++ b/lib/tests/modules/extendModules-168767-imports.nix
@@ -0,0 +1,41 @@
+{ lib
+, extendModules
+, ...
+}:
+with lib;
+{
+ imports = [
+
+ {
+ options.sub = mkOption {
+ default = { };
+ type = types.submodule (
+ { config
+ , extendModules
+ , ...
+ }:
+ {
+ options.value = mkOption {
+ type = types.int;
+ };
+
+ options.specialisation = mkOption {
+ default = { };
+ inherit
+ (extendModules {
+ modules = [{
+ specialisation = mkOverride 0 { };
+ }];
+ })
+ type;
+ };
+ }
+ );
+ };
+ }
+
+ { config.sub.value = 1; }
+
+
+ ];
+}
diff --git a/lib/tests/systems.nix b/lib/tests/systems.nix
index c88adbf4651a..46e7bd992f1e 100644
--- a/lib/tests/systems.nix
+++ b/lib/tests/systems.nix
@@ -19,6 +19,9 @@ with lib.systems.doubles; lib.runTests {
testi686 = mseteq i686 [ "i686-linux" "i686-freebsd" "i686-genode" "i686-netbsd" "i686-openbsd" "i686-cygwin" "i686-windows" "i686-none" "i686-darwin" ];
testmips = mseteq mips [ "mips64el-linux" "mipsel-linux" "mipsel-netbsd" ];
testmmix = mseteq mmix [ "mmix-mmixware" ];
+ testriscv = mseteq riscv [ "riscv32-linux" "riscv64-linux" "riscv32-netbsd" "riscv64-netbsd" "riscv32-none" "riscv64-none" ];
+ testriscv32 = mseteq riscv32 [ "riscv32-linux" "riscv32-netbsd" "riscv32-none" ];
+ testriscv64 = mseteq riscv64 [ "riscv64-linux" "riscv64-netbsd" "riscv64-none" ];
testx86_64 = mseteq x86_64 [ "x86_64-linux" "x86_64-darwin" "x86_64-freebsd" "x86_64-genode" "x86_64-redox" "x86_64-openbsd" "x86_64-netbsd" "x86_64-cygwin" "x86_64-solaris" "x86_64-windows" "x86_64-none" ];
testcygwin = mseteq cygwin [ "i686-cygwin" "x86_64-cygwin" ];
diff --git a/lib/types.nix b/lib/types.nix
index 5c4b96310617..91b040d24553 100644
--- a/lib/types.nix
+++ b/lib/types.nix
@@ -568,6 +568,11 @@ rec {
{ modules
, specialArgs ? {}
, shorthandOnlyDefinesConfig ? false
+
+ # Internal variable to avoid `_key` collisions regardless
+ # of `extendModules`. Wired through by `evalModules`.
+ # Test case: lib/tests/modules, "168767"
+ , extensionOffset ? 0
}@attrs:
let
inherit (lib.modules) evalModules;
@@ -579,11 +584,11 @@ rec {
allModules = defs: imap1 (n: { value, file }:
if isFunction value
then setFunctionArgs
- (args: lib.modules.unifyModuleSyntax file "${toString file}-${toString n}" (value args))
+ (args: lib.modules.unifyModuleSyntax file "${toString file}-${toString (n + extensionOffset)}" (value args))
(functionArgs value)
else if isAttrs value
then
- lib.modules.unifyModuleSyntax file "${toString file}-${toString n}" (shorthandToModule value)
+ lib.modules.unifyModuleSyntax file "${toString file}-${toString (n + extensionOffset)}" (shorthandToModule value)
else value
) defs;
@@ -620,6 +625,7 @@ rec {
(base.extendModules {
modules = [ { _module.args.name = last loc; } ] ++ allModules defs;
prefix = loc;
+ extensionOffset = extensionOffset + length defs;
}).config;
emptyValue = { value = {}; };
getSubOptions = prefix: (base.extendModules