at 18.09-beta 7.4 kB view raw
1/* Collection of functions useful for debugging 2 broken nix expressions. 3 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 (builtins) trace isAttrs isList isInt 18 head substring attrNames; 19 inherit (lib) id elem isFunction; 20in 21 22rec { 23 24 # -- TRACING -- 25 26 /* Trace msg, but only if pred is true. 27 28 Example: 29 traceIf true "hello" 3 30 trace: hello 31 => 3 32 */ 33 traceIf = pred: msg: x: if pred then trace msg x else x; 34 35 /* Trace the value and also return it. 36 37 Example: 38 traceValFn (v: "mystring ${v}") "foo" 39 trace: mystring foo 40 => "foo" 41 */ 42 traceValFn = f: x: trace (f x) x; 43 traceVal = traceValFn id; 44 45 /* `builtins.trace`, but the value is `builtins.deepSeq`ed first. 46 47 Example: 48 trace { a.b.c = 3; } null 49 trace: { a = <CODE>; } 50 => null 51 traceSeq { a.b.c = 3; } null 52 trace: { a = { b = { c = 3; }; }; } 53 => null 54 */ 55 traceSeq = x: y: trace (builtins.deepSeq x x) y; 56 57 /* Like `traceSeq`, but only evaluate down to depth n. 58 This is very useful because lots of `traceSeq` usages 59 lead to an infinite recursion. 60 61 Example: 62 traceSeqN 2 { a.b.c = 3; } null 63 trace: { a = { b = {}; }; } 64 => null 65 */ 66 traceSeqN = depth: x: y: with lib; 67 let snip = v: if isList v then noQuotes "[]" v 68 else if isAttrs v then noQuotes "{}" v 69 else v; 70 noQuotes = str: v: { __pretty = const str; val = v; }; 71 modify = n: fn: v: if (n == 0) then fn v 72 else if isList v then map (modify (n - 1) fn) v 73 else if isAttrs v then mapAttrs 74 (const (modify (n - 1) fn)) v 75 else v; 76 in trace (generators.toPretty { allowPrettyValues = true; } 77 (modify depth snip x)) y; 78 79 /* A combination of `traceVal` and `traceSeq` */ 80 traceValSeqFn = f: v: traceValFn f (builtins.deepSeq v v); 81 traceValSeq = traceValSeqFn id; 82 83 /* A combination of `traceVal` and `traceSeqN`. */ 84 traceValSeqNFn = f: depth: v: traceSeqN depth (f v) v; 85 traceValSeqN = traceValSeqNFn id; 86 87 88 # -- TESTING -- 89 90 /* Evaluate a set of tests. A test is an attribute set {expr, 91 expected}, denoting an expression and its expected result. The 92 result is a list of failed tests, each represented as {name, 93 expected, actual}, denoting the attribute name of the failing 94 test and its expected and actual results. Used for regression 95 testing of the functions in lib; see tests.nix for an example. 96 Only tests having names starting with "test" are run. 97 Add attr { tests = ["testName"]; } to run these test only 98 */ 99 runTests = tests: lib.concatLists (lib.attrValues (lib.mapAttrs (name: test: 100 let testsToRun = if tests ? tests then tests.tests else []; 101 in if (substring 0 4 name == "test" || elem name testsToRun) 102 && ((testsToRun == []) || elem name tests.tests) 103 && (test.expr != test.expected) 104 105 then [ { inherit name; expected = test.expected; result = test.expr; } ] 106 else [] ) tests)); 107 108 # create a test assuming that list elements are true 109 # usage: { testX = allTrue [ true ]; } 110 testAllTrue = expr: { inherit expr; expected = map (x: true) expr; }; 111 112 113 # -- DEPRECATED -- 114 115 traceShowVal = x: trace (showVal x) x; 116 traceShowValMarked = str: x: trace (str + showVal x) x; 117 118 attrNamesToStr = a: 119 trace ( "Warning: `attrNamesToStr` is deprecated " 120 + "and will be removed in the next release. " 121 + "Please use more specific concatenation " 122 + "for your uses (`lib.concat(Map)StringsSep`)." ) 123 (lib.concatStringsSep "; " (map (x: "${x}=") (attrNames a))); 124 125 showVal = with lib; 126 trace ( "Warning: `showVal` is deprecated " 127 + "and will be removed in the next release, " 128 + "please use `traceSeqN`" ) 129 (let 130 modify = v: 131 let pr = f: { __pretty = f; val = v; }; 132 in if isDerivation v then pr 133 (drv: "<δ:${drv.name}:${concatStringsSep "," 134 (attrNames drv)}>") 135 else if [] == v then pr (const "[]") 136 else if isList v then pr (l: "[ ${go (head l)}, ]") 137 else if isAttrs v then pr 138 (a: "{ ${ concatStringsSep ", " (attrNames a)} }") 139 else v; 140 go = x: generators.toPretty 141 { allowPrettyValues = true; } 142 (modify x); 143 in go); 144 145 traceXMLVal = x: 146 trace ( "Warning: `traceXMLVal` is deprecated " 147 + "and will be removed in the next release. " 148 + "Please use `traceValFn builtins.toXML`." ) 149 (trace (builtins.toXML x) x); 150 traceXMLValMarked = str: x: 151 trace ( "Warning: `traceXMLValMarked` is deprecated " 152 + "and will be removed in the next release. " 153 + "Please use `traceValFn (x: str + builtins.toXML x)`." ) 154 (trace (str + builtins.toXML x) x); 155 156 # trace the arguments passed to function and its result 157 # maybe rewrite these functions in a traceCallXml like style. Then one function is enough 158 traceCall = n: f: a: let t = n2: x: traceShowValMarked "${n} ${n2}:" x; in t "result" (f (t "arg 1" a)); 159 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)); 160 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)); 161 162 traceValIfNot = c: x: 163 trace ( "Warning: `traceValIfNot` is deprecated " 164 + "and will be removed in the next release. " 165 + "Please use `if/then/else` and `traceValSeq 1`.") 166 (if c x then true else traceSeq (showVal x) false); 167 168 169 addErrorContextToAttrs = attrs: 170 trace ( "Warning: `addErrorContextToAttrs` is deprecated " 171 + "and will be removed in the next release. " 172 + "Please use `builtins.addErrorContext` directly." ) 173 (lib.mapAttrs (a: v: lib.addErrorContext "while evaluating ${a}" v) attrs); 174 175 # example: (traceCallXml "myfun" id 3) will output something like 176 # calling myfun arg 1: 3 result: 3 177 # this forces deep evaluation of all arguments and the result! 178 # note: if result doesn't evaluate you'll get no trace at all (FIXME) 179 # args should be printed in any case 180 traceCallXml = a: 181 trace ( "Warning: `traceCallXml` is deprecated " 182 + "and will be removed in the next release. " 183 + "Please complain if you use the function regularly." ) 184 (if !isInt a then 185 traceCallXml 1 "calling ${a}\n" 186 else 187 let nr = a; 188 in (str: expr: 189 if isFunction expr then 190 (arg: 191 traceCallXml (builtins.add 1 nr) "${str}\n arg ${builtins.toString nr} is \n ${builtins.toXML (builtins.seq arg arg)}" (expr arg) 192 ) 193 else 194 let r = builtins.seq expr expr; 195 in trace "${str}\n result:\n${builtins.toXML r}" r 196 )); 197}