diff options
Diffstat (limited to 'lib/debug.nix')
-rw-r--r-- | lib/debug.nix | 184 |
1 files changed, 128 insertions, 56 deletions
diff --git a/lib/debug.nix b/lib/debug.nix index d163e60b6957..91a9265a6b5e 100644 --- a/lib/debug.nix +++ b/lib/debug.nix @@ -1,34 +1,67 @@ +/* Collection of functions useful for debugging + broken nix expressions. + + * `trace`-like functions take two values, print + the first to stderr and return the second. + * `traceVal`-like functions take one argument + which both printed and returned. + * `traceSeq`-like functions fully evaluate their + traced value before printing (not just to “weak + head normal form” like trace does by default). + * Functions that end in `-Fn` take an additional + function as their first argument, which is applied + to the traced value before it is printed. +*/ { lib }: - let - -inherit (builtins) trace attrNamesToStr isAttrs isList isInt - isString isBool head substring attrNames; - -inherit (lib) all id mapAttrsFlatten elem isFunction; - + inherit (builtins) trace isAttrs isList isInt + head substring attrNames; + inherit (lib) id elem isFunction; in rec { - inherit (builtins) addErrorContext; - - addErrorContextToAttrs = lib.mapAttrs (a: v: lib.addErrorContext "while evaluating ${a}" v); + # -- TRACING -- - traceIf = p: msg: x: if p then trace msg x else x; + /* Trace msg, but only if pred is true. - traceVal = x: trace x x; - traceXMLVal = x: trace (builtins.toXML x) x; - traceXMLValMarked = str: x: trace (str + builtins.toXML x) x; + Example: + traceIf true "hello" 3 + trace: hello + => 3 + */ + traceIf = pred: msg: x: if pred then trace msg x else x; - # strict trace functions (traced structure is fully evaluated and printed) + /* Trace the value and also return it. - /* `builtins.trace`, but the value is `builtins.deepSeq`ed first. */ + Example: + traceValFn (v: "mystring ${v}") "foo" + trace: mystring foo + => "foo" + */ + traceValFn = f: x: trace (f x) x; + traceVal = traceValFn id; + + /* `builtins.trace`, but the value is `builtins.deepSeq`ed first. + + Example: + trace { a.b.c = 3; } null + trace: { a = <CODE>; } + => null + traceSeq { a.b.c = 3; } null + trace: { a = { b = { c = 3; }; }; } + => null + */ traceSeq = x: y: trace (builtins.deepSeq x x) y; - /* Like `traceSeq`, but only down to depth n. - * This is very useful because lots of `traceSeq` usages - * lead to an infinite recursion. + /* Like `traceSeq`, but only evaluate down to depth n. + This is very useful because lots of `traceSeq` usages + lead to an infinite recursion. + + Example: + traceSeqN 2 { a.b.c = 3; } null + trace: { a = { b = {…}; }; } + => null */ traceSeqN = depth: x: y: with lib; let snip = v: if isList v then noQuotes "[…]" v @@ -43,39 +76,16 @@ rec { in trace (generators.toPretty { allowPrettyValues = true; } (modify depth snip x)) y; - /* `traceSeq`, but the same value is traced and returned */ - traceValSeq = v: traceVal (builtins.deepSeq v v); - /* `traceValSeq` but with fixed depth */ - traceValSeqN = depth: v: traceSeqN depth v v; + /* A combination of `traceVal` and `traceSeq` */ + traceValSeqFn = f: v: traceVal f (builtins.deepSeq v v); + traceValSeq = traceValSeqFn id; + /* A combination of `traceVal` and `traceSeqN`. */ + traceValSeqNFn = f: depth: v: traceSeqN depth (f v) v; + traceValSeqN = traceValSeqNFn id; - # this can help debug your code as well - designed to not produce thousands of lines - traceShowVal = x: trace (showVal x) x; - traceShowValMarked = str: x: trace (str + showVal x) x; - attrNamesToStr = a: lib.concatStringsSep "; " (map (x: "${x}=") (attrNames a)); - showVal = x: - if isAttrs x then - if x ? outPath then "x is a derivation, name ${if x ? name then x.name else "<no name>"}, { ${attrNamesToStr x} }" - else "x is attr set { ${attrNamesToStr x} }" - else if isFunction x then "x is a function" - else if x == [] then "x is an empty list" - else if isList x then "x is a list, first element is: ${showVal (head x)}" - else if x == true then "x is boolean true" - else if x == false then "x is boolean false" - else if x == null then "x is null" - else if isInt x then "x is an integer `${toString x}'" - else if isString x then "x is a string `${substring 0 50 x}...'" - else "x is probably a path `${substring 0 50 (toString x)}...'"; - # trace the arguments passed to function and its result - # maybe rewrite these functions in a traceCallXml like style. Then one function is enough - traceCall = n: f: a: let t = n2: x: traceShowValMarked "${n} ${n2}:" x; in t "result" (f (t "arg 1" a)); - traceCall2 = n: f: a: b: let t = n2: x: traceShowValMarked "${n} ${n2}:" x; in t "result" (f (t "arg 1" a) (t "arg 2" b)); - traceCall3 = n: f: a: b: c: let t = n2: x: traceShowValMarked "${n} ${n2}:" x; in t "result" (f (t "arg 1" a) (t "arg 2" b) (t "arg 3" c)); - - # FIXME: rename this? - traceValIfNot = c: x: - if c x then true else trace (showVal x) false; + # -- TESTING -- /* Evaluate a set of tests. A test is an attribute set {expr, expected}, denoting an expression and its expected result. The @@ -99,9 +109,68 @@ rec { # usage: { testX = allTrue [ true ]; } testAllTrue = expr: { inherit expr; expected = map (x: true) expr; }; - strict = v: - trace "Warning: strict is deprecated and will be removed in the next release" - (builtins.seq v v); + + # -- DEPRECATED -- + + traceShowVal = x: trace (showVal x) x; + traceShowValMarked = str: x: trace (str + showVal x) x; + + attrNamesToStr = a: + trace ( "Warning: `attrNamesToStr` is deprecated " + + "and will be removed in the next release. " + + "Please use more specific concatenation " + + "for your uses (`lib.concat(Map)StringsSep`)." ) + (lib.concatStringsSep "; " (map (x: "${x}=") (attrNames a))); + + showVal = with lib; + trace ( "Warning: `showVal` is deprecated " + + "and will be removed in the next release, " + + "please use `traceSeqN`" ) + (let + modify = v: + let pr = f: { __pretty = f; val = v; }; + in if isDerivation v then pr + (drv: "<δ:${drv.name}:${concatStringsSep "," + (attrNames drv)}>") + else if [] == v then pr (const "[]") + else if isList v then pr (l: "[ ${go (head l)}, … ]") + else if isAttrs v then pr + (a: "{ ${ concatStringsSep ", " (attrNames a)} }") + else v; + go = x: generators.toPretty + { allowPrettyValues = true; } + (modify x); + in go); + + traceXMLVal = x: + trace ( "Warning: `traceXMLVal` is deprecated " + + "and will be removed in the next release. " + + "Please use `traceValFn builtins.toXML`." ) + (trace (builtins.toXML x) x); + traceXMLValMarked = str: x: + trace ( "Warning: `traceXMLValMarked` is deprecated " + + "and will be removed in the next release. " + + "Please use `traceValFn (x: str + builtins.toXML x)`." ) + (trace (str + builtins.toXML x) x); + + # trace the arguments passed to function and its result + # maybe rewrite these functions in a traceCallXml like style. Then one function is enough + traceCall = n: f: a: let t = n2: x: traceShowValMarked "${n} ${n2}:" x; in t "result" (f (t "arg 1" a)); + traceCall2 = n: f: a: b: let t = n2: x: traceShowValMarked "${n} ${n2}:" x; in t "result" (f (t "arg 1" a) (t "arg 2" b)); + traceCall3 = n: f: a: b: c: let t = n2: x: traceShowValMarked "${n} ${n2}:" x; in t "result" (f (t "arg 1" a) (t "arg 2" b) (t "arg 3" c)); + + traceValIfNot = c: x: + trace ( "Warning: `traceValIfNot` is deprecated " + + "and will be removed in the next release. " + + "Please use `if/then/else` and `traceValSeq 1`.") + (if c x then true else traceSeq (showVal x) false); + + + addErrorContextToAttrs = attrs: + trace ( "Warning: `addErrorContextToAttrs` is deprecated " + + "and will be removed in the next release. " + + "Please use `builtins.addErrorContext` directly." ) + (lib.mapAttrs (a: v: lib.addErrorContext "while evaluating ${a}" v) attrs); # example: (traceCallXml "myfun" id 3) will output something like # calling myfun arg 1: 3 result: 3 @@ -109,17 +178,20 @@ rec { # note: if result doesn't evaluate you'll get no trace at all (FIXME) # args should be printed in any case traceCallXml = a: - if !isInt a then + trace ( "Warning: `traceCallXml` is deprecated " + + "and will be removed in the next release. " + + "Please complain if you use the function regularly." ) + (if !isInt a then traceCallXml 1 "calling ${a}\n" else let nr = a; in (str: expr: if isFunction expr then (arg: - traceCallXml (builtins.add 1 nr) "${str}\n arg ${builtins.toString nr} is \n ${builtins.toXML (strict arg)}" (expr arg) + traceCallXml (builtins.add 1 nr) "${str}\n arg ${builtins.toString nr} is \n ${builtins.toXML (builtins.seq arg arg)}" (expr arg) ) else - let r = strict expr; + let r = builtins.seq expr expr; in trace "${str}\n result:\n${builtins.toXML r}" r - ); + )); } |