1 /* Collection of functions useful for debugging
2 broken nix expressions.
4 * `trace`-like functions take two values, print
5 the first to stderr and return the second.
6 * `traceVal`-like functions take one argument
7 which both printed and returned.
8 * `traceSeq`-like functions fully evaluate their
9 traced value before printing (not just to “weak
10 head normal form” like trace does by default).
11 * Functions that end in `-Fn` take an additional
12 function as their first argument, which is applied
13 to the traced value before it is printed.
42 /* Conditionally trace the supplied message, based on a predicate.
44 Type: traceIf :: bool -> string -> a -> a
47 traceIf true "hello" 3
54 # Message that should be traced
57 x: if pred then trace msg x else x;
59 /* Trace the supplied value after applying a function to it, and
60 return the original value.
62 Type: traceValFn :: (a -> b) -> a -> a
65 traceValFn (v: "mystring ${v}") "foo"
72 # Value to trace and return
75 /* Trace the supplied value and return it.
77 Type: traceVal :: a -> a
84 traceVal = traceValFn id;
86 /* `builtins.trace`, but the value is `builtins.deepSeq`ed first.
88 Type: traceSeq :: a -> b -> b
91 trace { a.b.c = 3; } null
92 trace: { a = <CODE>; }
94 traceSeq { a.b.c = 3; } null
95 trace: { a = { b = { c = 3; }; }; }
101 # The value to return
102 y: trace (builtins.deepSeq x x) y;
104 /* Like `traceSeq`, but only evaluate down to depth n.
105 This is very useful because lots of `traceSeq` usages
106 lead to an infinite recursion.
109 traceSeqN 2 { a.b.c = 3; } null
110 trace: { a = { b = {…}; }; }
113 traceSeqN = depth: x: y:
114 let snip = v: if isList v then noQuotes "[…]" v
115 else if isAttrs v then noQuotes "{…}" v
117 noQuotes = str: v: { __pretty = const str; val = v; };
118 modify = n: fn: v: if (n == 0) then fn v
119 else if isList v then map (modify (n - 1) fn) v
120 else if isAttrs v then mapAttrs
121 (const (modify (n - 1) fn)) v
123 in trace (generators.toPretty { allowPrettyValues = true; }
124 (modify depth snip x)) y;
126 /* A combination of `traceVal` and `traceSeq` that applies a
127 provided function to the value to be traced after `deepSeq`ing
134 v: traceValFn f (builtins.deepSeq v v);
136 /* A combination of `traceVal` and `traceSeq`. */
137 traceValSeq = traceValSeqFn id;
139 /* A combination of `traceVal` and `traceSeqN` that applies a
140 provided function to the value to be traced. */
146 v: traceSeqN depth (f v) v;
148 /* A combination of `traceVal` and `traceSeqN`. */
149 traceValSeqN = traceValSeqNFn id;
151 /* Trace the input and output of a function `f` named `name`,
152 both down to `depth`.
154 This is useful for adding around a function call,
155 to see the before/after of values as they are transformed.
158 traceFnSeqN 2 "id" (x: x) { a.b.c = 3; }
159 trace: { fn = "id"; from = { a.b = {…}; }; to = { a.b = {…}; }; }
162 traceFnSeqN = depth: name: f: v:
176 /* Evaluate a set of tests. A test is an attribute set `{expr,
177 expected}`, denoting an expression and its expected result. The
178 result is a list of failed tests, each represented as `{name,
179 expected, actual}`, denoting the attribute name of the failing
180 test and its expected and actual results.
182 Used for regression testing of the functions in lib; see
183 tests.nix for an example. Only tests having names starting with
186 Add attr { tests = ["testName"]; } to run these tests only.
190 tests: concatLists (attrValues (mapAttrs (name: test:
191 let testsToRun = if tests ? tests then tests.tests else [];
192 in if (substring 0 4 name == "test" || elem name testsToRun)
193 && ((testsToRun == []) || elem name tests.tests)
194 && (test.expr != test.expected)
196 then [ { inherit name; expected = test.expected; result = test.expr; } ]
199 /* Create a test assuming that list elements are `true`.
202 { testX = allTrue [ true ]; }
204 testAllTrue = expr: { inherit expr; expected = map (x: true) expr; };
209 traceShowVal = x: trace (showVal x) x;
210 traceShowValMarked = str: x: trace (str + showVal x) x;
213 trace ( "Warning: `attrNamesToStr` is deprecated "
214 + "and will be removed in the next release. "
215 + "Please use more specific concatenation "
216 + "for your uses (`lib.concat(Map)StringsSep`)." )
217 (concatStringsSep "; " (map (x: "${x}=") (attrNames a)));
220 trace ( "Warning: `showVal` is deprecated "
221 + "and will be removed in the next release, "
222 + "please use `traceSeqN`" )
225 let pr = f: { __pretty = f; val = v; };
226 in if isDerivation v then pr
227 (drv: "<δ:${drv.name}:${concatStringsSep ","
229 else if [] == v then pr (const "[]")
230 else if isList v then pr (l: "[ ${go (head l)}, … ]")
231 else if isAttrs v then pr
232 (a: "{ ${ concatStringsSep ", " (attrNames a)} }")
234 go = x: generators.toPretty
235 { allowPrettyValues = true; }
240 trace ( "Warning: `traceXMLVal` is deprecated "
241 + "and will be removed in the next release. "
242 + "Please use `traceValFn builtins.toXML`." )
243 (trace (builtins.toXML x) x);
244 traceXMLValMarked = str: x:
245 trace ( "Warning: `traceXMLValMarked` is deprecated "
246 + "and will be removed in the next release. "
247 + "Please use `traceValFn (x: str + builtins.toXML x)`." )
248 (trace (str + builtins.toXML x) x);
250 # trace the arguments passed to function and its result
251 # maybe rewrite these functions in a traceCallXml like style. Then one function is enough
252 traceCall = n: f: a: let t = n2: x: traceShowValMarked "${n} ${n2}:" x; in t "result" (f (t "arg 1" a));
253 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));
254 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));
256 traceValIfNot = c: x:
257 trace ( "Warning: `traceValIfNot` is deprecated "
258 + "and will be removed in the next release. "
259 + "Please use `if/then/else` and `traceValSeq 1`.")
260 (if c x then true else traceSeq (showVal x) false);
263 addErrorContextToAttrs = attrs:
264 trace ( "Warning: `addErrorContextToAttrs` is deprecated "
265 + "and will be removed in the next release. "
266 + "Please use `builtins.addErrorContext` directly." )
267 (mapAttrs (a: v: addErrorContext "while evaluating ${a}" v) attrs);
269 # example: (traceCallXml "myfun" id 3) will output something like
270 # calling myfun arg 1: 3 result: 3
271 # this forces deep evaluation of all arguments and the result!
272 # note: if result doesn't evaluate you'll get no trace at all (FIXME)
273 # args should be printed in any case
275 trace ( "Warning: `traceCallXml` is deprecated "
276 + "and will be removed in the next release. "
277 + "Please complain if you use the function regularly." )
279 traceCallXml 1 "calling ${a}\n"
283 if isFunction expr then
285 traceCallXml (builtins.add 1 nr) "${str}\n arg ${builtins.toString nr} is \n ${builtins.toXML (builtins.seq arg arg)}" (expr arg)
288 let r = builtins.seq expr expr;
289 in trace "${str}\n result:\n${builtins.toXML r}" r