diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/default.nix | 5 | ||||
-rw-r--r-- | lib/meta.nix | 14 | ||||
-rw-r--r-- | lib/modules.nix | 13 | ||||
-rw-r--r-- | lib/strings.nix | 65 | ||||
-rw-r--r-- | lib/systems/doubles.nix | 2 | ||||
-rw-r--r-- | lib/systems/inspect.nix | 3 | ||||
-rw-r--r-- | lib/systems/platforms.nix | 2 | ||||
-rw-r--r-- | lib/tests/misc.nix | 20 | ||||
-rwxr-xr-x | lib/tests/modules.sh | 6 | ||||
-rw-r--r-- | lib/tests/modules/extendModules-168767-imports.nix | 41 | ||||
-rw-r--r-- | lib/tests/systems.nix | 3 | ||||
-rw-r--r-- | lib/types.nix | 10 |
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 |