From 25604d3e68e4cdfc4d4b6e565eca66d3b32905e0 Mon Sep 17 00:00:00 2001 From: Eman Resu <78693624+quatquatt@users.noreply.github.com> Date: Mon, 11 May 2026 11:53:43 -0400 Subject: [PATCH 01/17] lib.strings.makeSearchPath: use concatMap rather than map and filter --- lib/strings.nix | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/strings.nix b/lib/strings.nix index 4c78c909b4be..a05a8ed1c192 100644 --- a/lib/strings.nix +++ b/lib/strings.nix @@ -16,6 +16,7 @@ rec { inherit (builtins) compareVersions + concatMap elem elemAt filter @@ -564,7 +565,10 @@ rec { ::: */ makeSearchPath = - subDir: paths: concatStringsSep ":" (map (path: path + "/" + subDir) (filter (x: x != null) paths)); + subDir: paths: + concatStringsSep ":" ( + concatMap (path: if path != null then [ (path + "/" + subDir) ] else [ ]) paths + ); /** Construct a Unix-style search path by appending the given From 00f9c30eaa178d8fe43584c8bc950c549eedbfd6 Mon Sep 17 00:00:00 2001 From: Eman Resu <78693624+quatquatt@users.noreply.github.com> Date: Mon, 11 May 2026 13:02:22 -0400 Subject: [PATCH 02/17] lib.strings.makeSearchPathOutput: only map once --- lib/strings.nix | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/strings.nix b/lib/strings.nix index a05a8ed1c192..3fb5d7994440 100644 --- a/lib/strings.nix +++ b/lib/strings.nix @@ -606,8 +606,14 @@ rec { ::: */ makeSearchPathOutput = - output: subDir: pkgs: - makeSearchPath subDir (map (lib.getOutput output) pkgs); + output: + let + getOutput' = lib.getOutput output; + in + subDir: pkgs: + concatStringsSep ":" ( + concatMap (path: if path != null then [ (getOutput' path + "/" + subDir) ] else [ ]) pkgs + ); /** Construct a library search path (such as RPATH) containing the From f765e4f33e141e1d542d9af71098a111b2f01737 Mon Sep 17 00:00:00 2001 From: Eman Resu <78693624+quatquatt@users.noreply.github.com> Date: Mon, 11 May 2026 12:25:37 -0400 Subject: [PATCH 03/17] lib.strings.splitStringBy: bring map into the inner loop --- lib/strings.nix | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/strings.nix b/lib/strings.nix index 3fb5d7994440..f6ba4ca7e07b 100644 --- a/lib/strings.nix +++ b/lib/strings.nix @@ -1804,31 +1804,27 @@ rec { predicate: keepSplit: str: let len = stringLength str; + withContext = addContextFrom str; # Helper function that processes the string character by character go = pos: currentPart: result: # Base case: reached end of string if pos == len then - result ++ [ currentPart ] + result ++ [ (withContext currentPart) ] else let currChar = substring pos 1 str; prevChar = if pos > 0 then substring (pos - 1) 1 str else ""; - isSplit = predicate prevChar currChar; in - if isSplit then + if predicate prevChar currChar then # Split here - add current part to results and start a new one - let - newResult = result ++ [ currentPart ]; - newCurrentPart = if keepSplit then currChar else ""; - in - go (pos + 1) newCurrentPart newResult + go (pos + 1) (if keepSplit then currChar else "") (result ++ [ (withContext currentPart) ]) else # Keep building current part go (pos + 1) (currentPart + currChar) result; in - if len == 0 then [ (addContextFrom str "") ] else map (addContextFrom str) (go 0 "" [ ]); + if len == 0 then [ (withContext "") ] else go 0 "" [ ]; /** Returns a string without the specified prefix, if the prefix matches. From de312c12a24051baf1667f6a49859942b4a9e68d Mon Sep 17 00:00:00 2001 From: Eman Resu <78693624+quatquatt@users.noreply.github.com> Date: Mon, 11 May 2026 13:02:50 -0400 Subject: [PATCH 04/17] lib.strings.toShellVar: only check validity of name once --- lib/strings.nix | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/strings.nix b/lib/strings.nix index f6ba4ca7e07b..aec7c852efa8 100644 --- a/lib/strings.nix +++ b/lib/strings.nix @@ -1313,8 +1313,11 @@ rec { ::: */ toShellVar = - name: value: - lib.throwIfNot (isValidPosixName name) "toShellVar: ${name} is not a valid shell variable name" ( + name: + if (!isValidPosixName name) then + throw "toShellVar: ${name} is not a valid shell variable name" + else + value: if isAttrs value && !isStringLike value then "declare -A ${name}=(${ concatStringsSep " " (lib.mapAttrsToList (n: v: "[${escapeShellArg n}]=${escapeShellArg v}") value) @@ -1322,8 +1325,7 @@ rec { else if isList value then "declare -a ${name}=(${escapeShellArgs value})" else - "${name}=${escapeShellArg value}" - ); + "${name}=${escapeShellArg value}"; /** Translate an attribute set `vars` into corresponding shell variable declarations From 9f5c9ea4a8e8a85e040bbe2e66eb724747cd1fc1 Mon Sep 17 00:00:00 2001 From: Eman Resu <78693624+quatquatt@users.noreply.github.com> Date: Mon, 11 May 2026 12:32:02 -0400 Subject: [PATCH 05/17] lib.strings: throw instead of warning when passed a path We've had this warning for years at this point. Doing this will also give us a free performance win in the next commits. --- lib/strings.nix | 99 ++++++++++++++++++++++--------------------------- 1 file changed, 44 insertions(+), 55 deletions(-) diff --git a/lib/strings.nix b/lib/strings.nix index aec7c852efa8..00119e09db3c 100644 --- a/lib/strings.nix +++ b/lib/strings.nix @@ -6,8 +6,6 @@ let inherit (builtins) length; - inherit (lib.trivial) warnIf; - asciiTable = import ./ascii-table.nix; in @@ -732,15 +730,12 @@ rec { */ normalizePath = s: - warnIf (isPath s) - '' + if isPath s then + throw '' lib.strings.normalizePath: The argument (${toString s}) is a path value, but only strings are supported. - Path values are always normalised in Nix, so there's no need to call this function on them. - This function also copies the path to the Nix store and returns the store path, the same as "''${path}" will, which may not be what you want. - This behavior is deprecated and will throw an error in the future.'' - ( - builtins.foldl' (x: y: if y == "/" && hasSuffix "/" x then x else x + y) "" (stringToCharacters s) - ); + Path values are always normalised in Nix, so there's no need to call this function on them.'' + else + builtins.foldl' (x: y: if y == "/" && hasSuffix "/" x then x else x + y) "" (stringToCharacters s); /** Depending on the boolean `cond`, return either the given string @@ -809,14 +804,12 @@ rec { pref: str: # Before 23.05, paths would be copied to the store before converting them # to strings and comparing. This was surprising and confusing. - warnIf (isPath pref) - '' + if isPath pref then + throw '' lib.strings.hasPrefix: The first argument (${toString pref}) is a path value, but only strings are supported. - There is almost certainly a bug in the calling code, since this function always returns `false` in such a case. - This function also copies the path to the Nix store, which may not be what you want. - This behavior is deprecated and will throw an error in the future. You might want to use `lib.path.hasPrefix` instead, which correctly supports paths.'' - (substring 0 (stringLength pref) str == pref); + else + substring 0 (stringLength pref) str == pref; /** Determine whether a string has given suffix. @@ -856,13 +849,13 @@ rec { in # Before 23.05, paths would be copied to the store before converting them # to strings and comparing. This was surprising and confusing. - warnIf (isPath suffix) - '' + if isPath suffix then + throw '' lib.strings.hasSuffix: The first argument (${toString suffix}) is a path value, but only strings are supported. - There is almost certainly a bug in the calling code, since this function always returns `false` in such a case. - This function also copies the path to the Nix store, which may not be what you want. - This behavior is deprecated and will throw an error in the future.'' - (lenContent >= lenSuffix && substring (lenContent - lenSuffix) lenContent content == suffix); + There is almost certainly a bug in the calling code, since this function always returns `false` in such a case. + This function also copies the path to the Nix store, which may not be what you want.'' + else + lenContent >= lenSuffix && substring (lenContent - lenSuffix) lenContent content == suffix; /** Determine whether a string contains the given infix @@ -902,13 +895,13 @@ rec { infix: content: # Before 23.05, paths would be copied to the store before converting them # to strings and comparing. This was surprising and confusing. - warnIf (isPath infix) - '' + if isPath infix then + throw '' lib.strings.hasInfix: The first argument (${toString infix}) is a path value, but only strings are supported. There is almost certainly a bug in the calling code, since this function always returns `false` in such a case. - This function also copies the path to the Nix store, which may not be what you want. - This behavior is deprecated and will throw an error in the future.'' - (builtins.match ".*${escapeRegex infix}.*" "${content}" != null); + This function also copies the path to the Nix store, which may not be what you want.'' + else + builtins.match ".*${escapeRegex infix}.*" "${content}" != null; /** Convert a string `s` to a list of characters (i.e. singleton strings). @@ -1862,22 +1855,20 @@ rec { prefix: str: # Before 23.05, paths would be copied to the store before converting them # to strings and comparing. This was surprising and confusing. - warnIf (isPath prefix) - '' + if isPath prefix then + throw '' lib.strings.removePrefix: The first argument (${toString prefix}) is a path value, but only strings are supported. There is almost certainly a bug in the calling code, since this function never removes any prefix in such a case. - This function also copies the path to the Nix store, which may not be what you want. - This behavior is deprecated and will throw an error in the future.'' - ( - let - preLen = stringLength prefix; - in - if substring 0 preLen str == prefix then - # -1 will take the string until the end - substring preLen (-1) str - else - str - ); + This function also copies the path to the Nix store, which may not be what you want.'' + else + let + preLen = stringLength prefix; + in + if substring 0 preLen str == prefix then + # -1 will take the string until the end + substring preLen (-1) str + else + str; /** Returns a string without the specified suffix, if the suffix matches. @@ -1913,22 +1904,20 @@ rec { suffix: str: # Before 23.05, paths would be copied to the store before converting them # to strings and comparing. This was surprising and confusing. - warnIf (isPath suffix) - '' + if isPath suffix then + throw '' lib.strings.removeSuffix: The first argument (${toString suffix}) is a path value, but only strings are supported. There is almost certainly a bug in the calling code, since this function never removes any suffix in such a case. - This function also copies the path to the Nix store, which may not be what you want. - This behavior is deprecated and will throw an error in the future.'' - ( - let - sufLen = stringLength suffix; - sLen = stringLength str; - in - if sufLen <= sLen && suffix == substring (sLen - sufLen) sufLen str then - substring 0 (sLen - sufLen) str - else - str - ); + This function also copies the path to the Nix store, which may not be what you want.'' + else + let + sufLen = stringLength suffix; + sLen = stringLength str; + in + if sufLen <= sLen && suffix == substring (sLen - sufLen) sufLen str then + substring 0 (sLen - sufLen) str + else + str; /** Returns true if string `v1` denotes a version older than `v2`. From 94148625b00d3a1d00ffc1ba6f5114a208658739 Mon Sep 17 00:00:00 2001 From: Eman Resu <78693624+quatquatt@users.noreply.github.com> Date: Mon, 11 May 2026 13:09:09 -0400 Subject: [PATCH 06/17] lib.strings: check if throw is necessary before taking str If these functions are called with partial function application (like `lib.hasPrefix ".nix"`), then we can avoid performing the isPath check multiple times, and only confirm it once. This gives 12% less primops when recursively importing all nix files in nixpkgs. --- lib/strings.nix | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/lib/strings.nix b/lib/strings.nix index 00119e09db3c..c69319b3c9e5 100644 --- a/lib/strings.nix +++ b/lib/strings.nix @@ -801,15 +801,15 @@ rec { ::: */ hasPrefix = - pref: str: - # Before 23.05, paths would be copied to the store before converting them - # to strings and comparing. This was surprising and confusing. + pref: if isPath pref then + # Before 23.05, paths would be copied to the store before converting them + # to strings and comparing. This was surprising and confusing. throw '' lib.strings.hasPrefix: The first argument (${toString pref}) is a path value, but only strings are supported. You might want to use `lib.path.hasPrefix` instead, which correctly supports paths.'' else - substring 0 (stringLength pref) str == pref; + str: substring 0 (stringLength pref) str == pref; /** Determine whether a string has given suffix. @@ -842,19 +842,20 @@ rec { ::: */ hasSuffix = - suffix: content: - let - lenContent = stringLength content; - lenSuffix = stringLength suffix; - in - # Before 23.05, paths would be copied to the store before converting them - # to strings and comparing. This was surprising and confusing. + suffix: if isPath suffix then + # Before 23.05, paths would be copied to the store before converting them + # to strings and comparing. This was surprising and confusing. throw '' lib.strings.hasSuffix: The first argument (${toString suffix}) is a path value, but only strings are supported. There is almost certainly a bug in the calling code, since this function always returns `false` in such a case. This function also copies the path to the Nix store, which may not be what you want.'' else + content: + let + lenContent = stringLength content; + lenSuffix = stringLength suffix; + in lenContent >= lenSuffix && substring (lenContent - lenSuffix) lenContent content == suffix; /** @@ -892,16 +893,16 @@ rec { ::: */ hasInfix = - infix: content: - # Before 23.05, paths would be copied to the store before converting them - # to strings and comparing. This was surprising and confusing. + infix: if isPath infix then + # Before 23.05, paths would be copied to the store before converting them + # to strings and comparing. This was surprising and confusing. throw '' lib.strings.hasInfix: The first argument (${toString infix}) is a path value, but only strings are supported. There is almost certainly a bug in the calling code, since this function always returns `false` in such a case. This function also copies the path to the Nix store, which may not be what you want.'' else - builtins.match ".*${escapeRegex infix}.*" "${content}" != null; + content: builtins.match ".*${escapeRegex infix}.*" "${content}" != null; /** Convert a string `s` to a list of characters (i.e. singleton strings). @@ -1852,15 +1853,16 @@ rec { ::: */ removePrefix = - prefix: str: - # Before 23.05, paths would be copied to the store before converting them - # to strings and comparing. This was surprising and confusing. + prefix: if isPath prefix then + # Before 23.05, paths would be copied to the store before converting them + # to strings and comparing. This was surprising and confusing. throw '' lib.strings.removePrefix: The first argument (${toString prefix}) is a path value, but only strings are supported. There is almost certainly a bug in the calling code, since this function never removes any prefix in such a case. This function also copies the path to the Nix store, which may not be what you want.'' else + str: let preLen = stringLength prefix; in @@ -1901,15 +1903,16 @@ rec { ::: */ removeSuffix = - suffix: str: - # Before 23.05, paths would be copied to the store before converting them - # to strings and comparing. This was surprising and confusing. + suffix: if isPath suffix then + # Before 23.05, paths would be copied to the store before converting them + # to strings and comparing. This was surprising and confusing. throw '' lib.strings.removeSuffix: The first argument (${toString suffix}) is a path value, but only strings are supported. There is almost certainly a bug in the calling code, since this function never removes any suffix in such a case. This function also copies the path to the Nix store, which may not be what you want.'' else + str: let sufLen = stringLength suffix; sLen = stringLength str; From 35ba022673a1b3f7e227f33a9c18e14003eb0829 Mon Sep 17 00:00:00 2001 From: Eman Resu <78693624+quatquatt@users.noreply.github.com> Date: Mon, 11 May 2026 12:47:41 -0400 Subject: [PATCH 07/17] lib.strings: compute prefix and suffix lengths early Meaningful if these functions are partially applied. Saves 16% primops, 12% thunks, and 6% memory bytes when recursively importing all the nix files in nixpkgs. Slightly increases memory usage when partial function application isn't done, but I think the benefits outweigh the losses here. --- lib/strings.nix | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/strings.nix b/lib/strings.nix index c69319b3c9e5..e1c360fc151f 100644 --- a/lib/strings.nix +++ b/lib/strings.nix @@ -802,6 +802,9 @@ rec { */ hasPrefix = pref: + let + lenPrefix = stringLength pref; + in if isPath pref then # Before 23.05, paths would be copied to the store before converting them # to strings and comparing. This was surprising and confusing. @@ -809,7 +812,7 @@ rec { lib.strings.hasPrefix: The first argument (${toString pref}) is a path value, but only strings are supported. You might want to use `lib.path.hasPrefix` instead, which correctly supports paths.'' else - str: substring 0 (stringLength pref) str == pref; + str: substring 0 lenPrefix str == pref; /** Determine whether a string has given suffix. @@ -843,6 +846,9 @@ rec { */ hasSuffix = suffix: + let + lenSuffix = stringLength suffix; + in if isPath suffix then # Before 23.05, paths would be copied to the store before converting them # to strings and comparing. This was surprising and confusing. @@ -854,7 +860,6 @@ rec { content: let lenContent = stringLength content; - lenSuffix = stringLength suffix; in lenContent >= lenSuffix && substring (lenContent - lenSuffix) lenContent content == suffix; @@ -1854,6 +1859,9 @@ rec { */ removePrefix = prefix: + let + preLen = stringLength prefix; + in if isPath prefix then # Before 23.05, paths would be copied to the store before converting them # to strings and comparing. This was surprising and confusing. @@ -1863,9 +1871,6 @@ rec { This function also copies the path to the Nix store, which may not be what you want.'' else str: - let - preLen = stringLength prefix; - in if substring 0 preLen str == prefix then # -1 will take the string until the end substring preLen (-1) str @@ -1904,6 +1909,9 @@ rec { */ removeSuffix = suffix: + let + sufLen = stringLength suffix; + in if isPath suffix then # Before 23.05, paths would be copied to the store before converting them # to strings and comparing. This was surprising and confusing. @@ -1914,7 +1922,6 @@ rec { else str: let - sufLen = stringLength suffix; sLen = stringLength str; in if sufLen <= sLen && suffix == substring (sLen - sufLen) sufLen str then From 28a5dc3806d241582006e182d401714f59de7597 Mon Sep 17 00:00:00 2001 From: Eman Resu <78693624+quatquatt@users.noreply.github.com> Date: Fri, 15 May 2026 23:29:49 -0400 Subject: [PATCH 08/17] lib.strings.normalizePath: use our hasSuffix optimization --- lib/strings.nix | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/strings.nix b/lib/strings.nix index e1c360fc151f..0db098662787 100644 --- a/lib/strings.nix +++ b/lib/strings.nix @@ -729,13 +729,18 @@ rec { ::: */ normalizePath = + let + startsWithSlash = hasSuffix "/"; + in s: if isPath s then throw '' lib.strings.normalizePath: The argument (${toString s}) is a path value, but only strings are supported. Path values are always normalised in Nix, so there's no need to call this function on them.'' else - builtins.foldl' (x: y: if y == "/" && hasSuffix "/" x then x else x + y) "" (stringToCharacters s); + builtins.foldl' (x: y: if y == "/" && startsWithSlash x then x else x + y) "" ( + stringToCharacters s + ); /** Depending on the boolean `cond`, return either the given string From 7a0811c24b36c60d6254271aa003e13f37ced55d Mon Sep 17 00:00:00 2001 From: Eman Resu <78693624+quatquatt@users.noreply.github.com> Date: Fri, 15 May 2026 23:30:21 -0400 Subject: [PATCH 09/17] lib.strings.hasInfix: compute escaped infix early --- lib/strings.nix | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/strings.nix b/lib/strings.nix index 0db098662787..747d15d119c0 100644 --- a/lib/strings.nix +++ b/lib/strings.nix @@ -904,6 +904,9 @@ rec { */ hasInfix = infix: + let + escapedInfix = escapeRegex infix; + in if isPath infix then # Before 23.05, paths would be copied to the store before converting them # to strings and comparing. This was surprising and confusing. @@ -912,7 +915,7 @@ rec { There is almost certainly a bug in the calling code, since this function always returns `false` in such a case. This function also copies the path to the Nix store, which may not be what you want.'' else - content: builtins.match ".*${escapeRegex infix}.*" "${content}" != null; + content: builtins.match ".*${escapedInfix}.*" "${content}" != null; /** Convert a string `s` to a list of characters (i.e. singleton strings). From 9ff4fe0e9394f47391c8fc99a38a1c8e7f10147c Mon Sep 17 00:00:00 2001 From: Eman Resu <78693624+quatquatt@users.noreply.github.com> Date: Fri, 15 May 2026 23:31:25 -0400 Subject: [PATCH 10/17] lib.strings.splitString: use builtins from global scope These are all already inherited. --- lib/strings.nix | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/strings.nix b/lib/strings.nix index 747d15d119c0..9268f6453820 100644 --- a/lib/strings.nix +++ b/lib/strings.nix @@ -1742,9 +1742,7 @@ rec { splitString = sep: s: let - splits = builtins.filter builtins.isString ( - builtins.split (escapeRegex (toString sep)) (toString s) - ); + splits = filter isString (split (escapeRegex (toString sep)) (toString s)); in map (addContextFrom s) splits; From f908b7a78c0751e56a88e0381caf4084c7537540 Mon Sep 17 00:00:00 2001 From: Eman Resu <78693624+quatquatt@users.noreply.github.com> Date: Fri, 15 May 2026 23:32:05 -0400 Subject: [PATCH 11/17] lib.strings.versionAtLeast: avoid an extra function call --- lib/strings.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/strings.nix b/lib/strings.nix index 9268f6453820..f9b653ff1670 100644 --- a/lib/strings.nix +++ b/lib/strings.nix @@ -1999,7 +1999,7 @@ rec { ::: */ - versionAtLeast = v1: v2: !versionOlder v1 v2; + versionAtLeast = v1: v2: compareVersions v2 v1 != 1; /** This function takes an argument `x` that's either a derivation or a From 4a438d1a8ae75fdc07da2aa22cde2491af299f82 Mon Sep 17 00:00:00 2001 From: Eman Resu <78693624+quatquatt@users.noreply.github.com> Date: Fri, 15 May 2026 23:38:09 -0400 Subject: [PATCH 12/17] lib.strings.toSentenceCase: use -1 trick instead of computing stringLength --- lib/strings.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/strings.nix b/lib/strings.nix index f9b653ff1670..53dc515ac3f8 100644 --- a/lib/strings.nix +++ b/lib/strings.nix @@ -1594,7 +1594,7 @@ rec { ( let firstChar = substring 0 1 str; - rest = substring 1 (stringLength str) str; + rest = substring 1 (-1) str; # -1 takes till the end of the string in addContextFrom str (toUpper firstChar + toLower rest) ); From a709ca8240d4ecd960f282bd3b64ab6256684f41 Mon Sep 17 00:00:00 2001 From: Eman Resu <78693624+quatquatt@users.noreply.github.com> Date: Sat, 16 May 2026 00:29:39 -0400 Subject: [PATCH 13/17] lib.strings.splitString: compute escaped separator early Thanks to moving the existing let variable out, this should have identical performance for non-memoised calls, and improved performance when caching the separator. --- lib/strings.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/strings.nix b/lib/strings.nix index 53dc515ac3f8..5425a4a66590 100644 --- a/lib/strings.nix +++ b/lib/strings.nix @@ -1740,11 +1740,11 @@ rec { ::: */ splitString = - sep: s: + sep: let - splits = filter isString (split (escapeRegex (toString sep)) (toString s)); + escapedSep = escapeRegex (toString sep); in - map (addContextFrom s) splits; + s: map (addContextFrom s) (filter isString (split escapedSep (toString s))); /** Splits a string into substrings based on a predicate that examines adjacent characters. From 184d899ccf450c6584b23ca700a195874c9f53df Mon Sep 17 00:00:00 2001 From: Eman Resu <78693624+quatquatt@users.noreply.github.com> Date: Sat, 16 May 2026 00:38:18 -0400 Subject: [PATCH 14/17] lib.strings: remove duplicate cmake and meson assertions These are already performed by cmakeOptionType and mesonOption - doing them twice isn't necessary. --- lib/strings.nix | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/strings.nix b/lib/strings.nix index 5425a4a66590..ca232cc655b8 100644 --- a/lib/strings.nix +++ b/lib/strings.nix @@ -2199,7 +2199,6 @@ rec { */ cmakeBool = condition: flag: - assert (lib.isString condition); assert (lib.isBool flag); cmakeOptionType "bool" condition (lib.toUpper (lib.boolToString flag)); @@ -2233,11 +2232,7 @@ rec { ::: */ - cmakeFeature = - feature: value: - assert (lib.isString feature); - assert (lib.isString value); - cmakeOptionType "string" feature value; + cmakeFeature = feature: value: cmakeOptionType "string" feature value; /** Create a `"-D="` string that can be passed to typical Meson @@ -2307,7 +2302,6 @@ rec { */ mesonBool = condition: flag: - assert (lib.isString condition); assert (lib.isBool flag); mesonOption condition (lib.boolToString flag); @@ -2344,7 +2338,6 @@ rec { */ mesonEnable = feature: flag: - assert (lib.isString feature); assert (lib.isBool flag); mesonOption feature (if flag then "enabled" else "disabled"); From f9c9f70a40a99eccafcf2f5fb408bd2958befa6c Mon Sep 17 00:00:00 2001 From: Eman Resu <78693624+quatquatt@users.noreply.github.com> Date: Sat, 16 May 2026 00:43:10 -0400 Subject: [PATCH 15/17] lib.strings.cmakeBool: inline two function calls I can live with a function like boolToString being used, but if we're immediately modifying its output, we should homeroll it. --- lib/strings.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/strings.nix b/lib/strings.nix index ca232cc655b8..0bb8e80799f8 100644 --- a/lib/strings.nix +++ b/lib/strings.nix @@ -2200,7 +2200,7 @@ rec { cmakeBool = condition: flag: assert (lib.isBool flag); - cmakeOptionType "bool" condition (lib.toUpper (lib.boolToString flag)); + cmakeOptionType "bool" condition (if flag then "TRUE" else "FALSE"); /** Create a `"-D:STRING="` string that can be passed to typical From 4755ddd1262901bc7091595b00bc011a58a3112d Mon Sep 17 00:00:00 2001 From: Eman Resu <78693624+quatquatt@users.noreply.github.com> Date: Sat, 16 May 2026 00:58:33 -0400 Subject: [PATCH 16/17] lib.strings.{toSentenceCase,toCamelCase}: don't add context unnecessarily builtins.substring doesn't destroy context - it's not necessary to add it back. --- lib/strings.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/strings.nix b/lib/strings.nix index 0bb8e80799f8..f57df9d24380 100644 --- a/lib/strings.nix +++ b/lib/strings.nix @@ -1596,7 +1596,7 @@ rec { firstChar = substring 0 1 str; rest = substring 1 (-1) str; # -1 takes till the end of the string in - addContextFrom str (toUpper firstChar + toLower rest) + toUpper firstChar + toLower rest ); /** @@ -1653,7 +1653,7 @@ rec { first = if length parts > 0 then toLower (head parts) else ""; rest = if length parts > 1 then map toSentenceCase (tail parts) else [ ]; in - concatStrings (map (addContextFrom str) ([ first ] ++ rest)) + concatStrings ([ first ] ++ rest) ); /** From ff92c9d15a51dfe2f01341290b79a22090809dd0 Mon Sep 17 00:00:00 2001 From: Eman Resu <78693624+quatquatt@users.noreply.github.com> Date: Wed, 24 Jun 2026 16:53:31 -0400 Subject: [PATCH 17/17] lib.strings.cmakeFeature: beta reduce --- lib/strings.nix | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/strings.nix b/lib/strings.nix index f57df9d24380..b81a04a6666b 100644 --- a/lib/strings.nix +++ b/lib/strings.nix @@ -2162,8 +2162,9 @@ rec { "LIST" ]; in - type: feature: value: + type: assert (elem (toUpper type) types); + feature: value: assert (isString feature); assert (isString value); "-D${feature}:${toUpper type}=${value}"; @@ -2232,7 +2233,7 @@ rec { ::: */ - cmakeFeature = feature: value: cmakeOptionType "string" feature value; + cmakeFeature = cmakeOptionType "string"; /** Create a `"-D="` string that can be passed to typical Meson