Describe the bug
calling lib.strings.splitString on the linux kernel config throws error: stack overflow (possible infinite recursion).
To Reproduce
in nix repl from
nix-repl> a = builtins.readFile linux.configfile
nix-repl> lib.splitString "\n" a
error: stack overflow (possible infinite recursion)
Additional context
I am working on improving the linux kernel integration into nixos. I have sexy features ready except for that crash.
Metadata
Please run nix run nixpkgs.nix-info -c nix-info -m and paste the result.
- system: `"x86_64-linux"`
- host os: `Linux 4.19.71, NixOS, 19.09.git.3f633fa (Loris)`
- multi-user?: `yes`
- sandbox: `yes`
- version: `nix-env (Nix) 2.3`
- channels(teto): `""`
- channels(root): `""`
- nixpkgs: `/home/teto/nixpkgs`
It probably fails because your input string is too big. builtins.split should perform better in this case.
that looks much better indeed thank you, I usually look up for the builtins at top of lib/strings.nix
inherit (builtins) stringLength substring head tail isString replaceStrings;
and couldn't find a split. Not qsure what to do with the issue, I still find the error annoying. Maybe splitString could now rely on split since it seems like a not so recent addition https://github.com/NixOS/nix/pull/1516
The problem with reimplementing splitString using builtins.split is that the latter accepts regex'es, so we would need to filter out regex syntax to stay compatible. Another solution would be to just deprecate splitString.
hum just in case anyone has some insight, I've arranged my code in different ways (listToAttrs + namedValuePair) but everytime it fails with
error: stack overflow (possible infinite recursion)
The code in question
```
/*
Converts the kernel final configuration file into a structured nix config
Example:
loadConfig linux.configfile
returns
{ IDE = "y"; ... }
*/
loadConfig = configFilename: let
# readLines = builtins.trace "splitting strings" splitString "\n" (builtins.readFile configFilename);
lines = filter (x: builtins.typeOf x == "string")
(builtins.split "\n" (builtins.readFile configFilename));
# lines = [
# ''CONFIG_NLS_DEFAULT="utf8"''
# ''CONFIG_THREAD_INFO_IN_TASK=y''
# ''# CONFIG_LOCALVERSION_AUTO is not set''
# ];
parseLine = line:
let
# String options have double quotes, e.g. 'CONFIG_NLS_DEFAULT="utf8"' and allow escaping.
match_freeform = builtins.match ''^CONFIG_([A-Za-z0-9_]+)="(.*)"$'' line;
match_tristate = builtins.match ''^CONFIG_([A-Za-z0-9_]+)=(.*)$'' line;
match_unset = builtins.match ''^# CONFIG_([A-Za-z0-9_]+) is not set$'' line;
match = if (match_freeform != null && (length match_freeform == 2) ) then
# nameValuePair (head match_freeform) (last match_freeform)
{ "${head match_freeform}" = last match_freeform; }
else if (match_tristate != null && (length match_tristate == 2)) then
# nameValuePair (head match_tristate) (last match_tristate)
{ "${head match_tristate}" = last match_tristate; }
else if (match_unset != null) then
# nameValuePair (head match_unset) "n"
{ "${head match_unset}" = "n"; }
else
# null
{}
;
in
match;
in
foldr (line: prev: (lib.traceVal (parseLine line)) // prev) {} lines ;
``
and I test it in a repl via:
lib.kernel.loadConfig linux.configfile`
There is another recursion in foldr. Try replacing it with builtins.foldl'.
@veprbl you know you are a lovely chap ?! that fixed it thank you !
as you said, it's doubtful the splitString behavior is going to change anytime soon but at least it's now documented with this issue, as well as possible workarounds thanks to your help. Closing.
Most helpful comment
@veprbl you know you are a lovely chap ?! that fixed it thank you !