git.iko.soy/typeable/nixpkgs
on 8a9d1aa help sign in
269 lines 8.5 kB history raw
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.
14*/
15{ lib }:
16let
17 inherit (lib)
18 isInt
19 attrNames
20 isList
21 isAttrs
22 substring
23 addErrorContext
24 attrValues
25 concatLists
26 concatStringsSep
27 const
28 elem
29 generators
30 head
31 id
32 isDerivation
33 isFunction
34 mapAttrs
35 trace;
36in
38rec {
40 # -- TRACING --
42 /* Conditionally trace the supplied message, based on a predicate.
44 Type: traceIf :: bool -> string -> a -> a
46 Example:
47 traceIf true "hello" 3
48 trace: hello
49 => 3
50 */
51 traceIf =
52 # Predicate to check
53 pred:
54 # Message that should be traced
55 msg:
56 # Value to return
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
64 Example:
65 traceValFn (v: "mystring ${v}") "foo"
66 trace: mystring foo
67 => "foo"
68 */
69 traceValFn =
70 # Function to apply
71 f:
72 # Value to trace and return
73 x: trace (f x) x;
75 /* Trace the supplied value and return it.
77 Type: traceVal :: a -> a
79 Example:
80 traceVal 42
81 # trace: 42
82 => 42
83 */
84 traceVal = traceValFn id;
86 /* `builtins.trace`, but the value is `builtins.deepSeq`ed first.
88 Type: traceSeq :: a -> b -> b
90 Example:
91 trace { a.b.c = 3; } null
92 trace: { a = <CODE>; }
93 => null
94 traceSeq { a.b.c = 3; } null
95 trace: { a = { b = { c = 3; }; }; }
96 => null
97 */
98 traceSeq =
99 # The value to trace
100 x:
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.
108 Example:
109 traceSeqN 2 { a.b.c = 3; } null
110 trace: { a = { b = {…}; }; }
111 => null
112 */
113 traceSeqN = depth: x: y:
114 let snip = v: if isList v then noQuotes "[…]" v
115 else if isAttrs v then noQuotes "{…}" v
116 else 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
122 else 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
128 it.
129 */
130 traceValSeqFn =
131 # Function to apply
132 f:
133 # Value to trace
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. */
141 traceValSeqNFn =
142 # Function to apply
143 f:
144 depth:
145 # Value to trace
146 v: traceSeqN depth (f v) v;
148 /* A combination of `traceVal` and `traceSeqN`. */
149 traceValSeqN = traceValSeqNFn id;
152 # -- TESTING --
154 /* Evaluate a set of tests. A test is an attribute set `{expr,
155 expected}`, denoting an expression and its expected result. The
156 result is a list of failed tests, each represented as `{name,
157 expected, actual}`, denoting the attribute name of the failing
158 test and its expected and actual results.
160 Used for regression testing of the functions in lib; see
161 tests.nix for an example. Only tests having names starting with
162 "test" are run.
164 Add attr { tests = ["testName"]; } to run these tests only.
165 */
166 runTests =
167 # Tests to run
168 tests: concatLists (attrValues (mapAttrs (name: test:
169 let testsToRun = if tests ? tests then tests.tests else [];
170 in if (substring 0 4 name == "test" || elem name testsToRun)
171 && ((testsToRun == []) || elem name tests.tests)
172 && (test.expr != test.expected)
174 then [ { inherit name; expected = test.expected; result = test.expr; } ]
175 else [] ) tests));
177 /* Create a test assuming that list elements are `true`.
179 Example:
180 { testX = allTrue [ true ]; }
181 */
182 testAllTrue = expr: { inherit expr; expected = map (x: true) expr; };
185 # -- DEPRECATED --
187 traceShowVal = x: trace (showVal x) x;
188 traceShowValMarked = str: x: trace (str + showVal x) x;
190 attrNamesToStr = a:
191 trace ( "Warning: `attrNamesToStr` is deprecated "
192 + "and will be removed in the next release. "
193 + "Please use more specific concatenation "
194 + "for your uses (`lib.concat(Map)StringsSep`)." )
195 (concatStringsSep "; " (map (x: "${x}=") (attrNames a)));
197 showVal =
198 trace ( "Warning: `showVal` is deprecated "
199 + "and will be removed in the next release, "
200 + "please use `traceSeqN`" )
201 (let
202 modify = v:
203 let pr = f: { __pretty = f; val = v; };
204 in if isDerivation v then pr
205 (drv: "<δ:${drv.name}:${concatStringsSep ","
206 (attrNames drv)}>")
207 else if [] == v then pr (const "[]")
208 else if isList v then pr (l: "[ ${go (head l)}, … ]")
209 else if isAttrs v then pr
210 (a: "{ ${ concatStringsSep ", " (attrNames a)} }")
211 else v;
212 go = x: generators.toPretty
213 { allowPrettyValues = true; }
214 (modify x);
215 in go);
217 traceXMLVal = x:
218 trace ( "Warning: `traceXMLVal` is deprecated "
219 + "and will be removed in the next release. "
220 + "Please use `traceValFn builtins.toXML`." )
221 (trace (builtins.toXML x) x);
222 traceXMLValMarked = str: x:
223 trace ( "Warning: `traceXMLValMarked` is deprecated "
224 + "and will be removed in the next release. "
225 + "Please use `traceValFn (x: str + builtins.toXML x)`." )
226 (trace (str + builtins.toXML x) x);
228 # trace the arguments passed to function and its result
229 # maybe rewrite these functions in a traceCallXml like style. Then one function is enough
230 traceCall = n: f: a: let t = n2: x: traceShowValMarked "${n} ${n2}:" x; in t "result" (f (t "arg 1" a));
231 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));
232 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));
234 traceValIfNot = c: x:
235 trace ( "Warning: `traceValIfNot` is deprecated "
236 + "and will be removed in the next release. "
237 + "Please use `if/then/else` and `traceValSeq 1`.")
238 (if c x then true else traceSeq (showVal x) false);
241 addErrorContextToAttrs = attrs:
242 trace ( "Warning: `addErrorContextToAttrs` is deprecated "
243 + "and will be removed in the next release. "
244 + "Please use `builtins.addErrorContext` directly." )
245 (mapAttrs (a: v: addErrorContext "while evaluating ${a}" v) attrs);
247 # example: (traceCallXml "myfun" id 3) will output something like
248 # calling myfun arg 1: 3 result: 3
249 # this forces deep evaluation of all arguments and the result!
250 # note: if result doesn't evaluate you'll get no trace at all (FIXME)
251 # args should be printed in any case
252 traceCallXml = a:
253 trace ( "Warning: `traceCallXml` is deprecated "
254 + "and will be removed in the next release. "
255 + "Please complain if you use the function regularly." )
256 (if !isInt a then
257 traceCallXml 1 "calling ${a}\n"
258 else
259 let nr = a;
260 in (str: expr:
261 if isFunction expr then
262 (arg:
263 traceCallXml (builtins.add 1 nr) "${str}\n arg ${builtins.toString nr} is \n ${builtins.toXML (builtins.seq arg arg)}" (expr arg)
264 )
265 else
266 let r = builtins.seq expr expr;
267 in trace "${str}\n result:\n${builtins.toXML r}" r
268 ));