 da18b92635
			
		
	
	
		da18b92635
		
	
	
	
	
		
			
			Documents functions in `lib.debug` for docs generation with nixdoc. Note that type signatures and clearer descriptions are still missing on some of these functions, but this is good enough for a first run.
		
			
				
	
	
		
			254 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			254 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
| /* 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 isAttrs isList isInt
 | |
|           head substring attrNames;
 | |
|   inherit (lib) id elem isFunction;
 | |
| in
 | |
| 
 | |
| rec {
 | |
| 
 | |
|   # -- TRACING --
 | |
| 
 | |
|   /* Conditionally trace the supplied message, based on a predicate.
 | |
| 
 | |
|      Type: traceIf :: bool -> string -> a -> a
 | |
| 
 | |
|      Example:
 | |
|        traceIf true "hello" 3
 | |
|        trace: hello
 | |
|        => 3
 | |
|   */
 | |
|   traceIf =
 | |
|     # Predicate to check
 | |
|     pred:
 | |
|     # Message that should be traced
 | |
|     msg:
 | |
|     # Value to return
 | |
|     x: if pred then trace msg x else x;
 | |
| 
 | |
|   /* Trace the supplied value after applying a function to it, and
 | |
|      return the original value.
 | |
| 
 | |
|      Type: traceValFn :: (a -> b) -> a -> a
 | |
| 
 | |
|      Example:
 | |
|        traceValFn (v: "mystring ${v}") "foo"
 | |
|        trace: mystring foo
 | |
|        => "foo"
 | |
|   */
 | |
|   traceValFn =
 | |
|     # Function to apply
 | |
|     f:
 | |
|     # Value to trace and return
 | |
|     x: trace (f x) x;
 | |
| 
 | |
|   /* Trace the supplied value and return it.
 | |
| 
 | |
|      Type: traceVal :: a -> a
 | |
| 
 | |
|      Example:
 | |
|        traceVal 42
 | |
|        # trace: 42
 | |
|        => 42
 | |
|   */
 | |
|   traceVal = traceValFn id;
 | |
| 
 | |
|   /* `builtins.trace`, but the value is `builtins.deepSeq`ed first.
 | |
| 
 | |
|      Type: traceSeq :: a -> b -> b
 | |
| 
 | |
|      Example:
 | |
|        trace { a.b.c = 3; } null
 | |
|        trace: { a = <CODE>; }
 | |
|        => null
 | |
|        traceSeq { a.b.c = 3; } null
 | |
|        trace: { a = { b = { c = 3; }; }; }
 | |
|        => null
 | |
|   */
 | |
|   traceSeq =
 | |
|     # The value to trace
 | |
|     x:
 | |
|     # The value to return
 | |
|     y: trace (builtins.deepSeq x x) y;
 | |
| 
 | |
|   /* 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
 | |
|                   else if isAttrs v then noQuotes "{…}" v
 | |
|                   else v;
 | |
|         noQuotes = str: v: { __pretty = const str; val = v; };
 | |
|         modify = n: fn: v: if (n == 0) then fn v
 | |
|                       else if isList  v then map (modify (n - 1) fn) v
 | |
|                       else if isAttrs v then mapAttrs
 | |
|                         (const (modify (n - 1) fn)) v
 | |
|                       else v;
 | |
|     in trace (generators.toPretty { allowPrettyValues = true; }
 | |
|                (modify depth snip x)) y;
 | |
| 
 | |
|   /* A combination of `traceVal` and `traceSeq` that applies a
 | |
|      provided function to the value to be traced after `deepSeq`ing
 | |
|      it.
 | |
|   */
 | |
|   traceValSeqFn =
 | |
|     # Function to apply
 | |
|     f:
 | |
|     # Value to trace
 | |
|     v: traceValFn f (builtins.deepSeq v v);
 | |
| 
 | |
|   /* A combination of `traceVal` and `traceSeq`. */
 | |
|   traceValSeq = traceValSeqFn id;
 | |
| 
 | |
|   /* A combination of `traceVal` and `traceSeqN` that applies a
 | |
|   provided function to the value to be traced. */
 | |
|   traceValSeqNFn =
 | |
|     # Function to apply
 | |
|     f:
 | |
|     depth:
 | |
|     # Value to trace
 | |
|     v: traceSeqN depth (f v) v;
 | |
| 
 | |
|   /* A combination of `traceVal` and `traceSeqN`. */
 | |
|   traceValSeqN = traceValSeqNFn id;
 | |
| 
 | |
| 
 | |
|   # -- TESTING --
 | |
| 
 | |
|   /* Evaluate a set of tests.  A test is an attribute set `{expr,
 | |
|      expected}`, denoting an expression and its expected result.  The
 | |
|      result is a list of failed tests, each represented as `{name,
 | |
|      expected, actual}`, denoting the attribute name of the failing
 | |
|      test and its expected and actual results.
 | |
| 
 | |
|      Used for regression testing of the functions in lib; see
 | |
|      tests.nix for an example. Only tests having names starting with
 | |
|      "test" are run.
 | |
| 
 | |
|      Add attr { tests = ["testName"]; } to run these tests only.
 | |
|   */
 | |
|   runTests =
 | |
|     # Tests to run
 | |
|     tests: lib.concatLists (lib.attrValues (lib.mapAttrs (name: test:
 | |
|     let testsToRun = if tests ? tests then tests.tests else [];
 | |
|     in if (substring 0 4 name == "test" ||  elem name testsToRun)
 | |
|        && ((testsToRun == []) || elem name tests.tests)
 | |
|        && (test.expr != test.expected)
 | |
| 
 | |
|       then [ { inherit name; expected = test.expected; result = test.expr; } ]
 | |
|       else [] ) tests));
 | |
| 
 | |
|   /* Create a test assuming that list elements are `true`.
 | |
| 
 | |
|      Example:
 | |
|        { testX = allTrue [ true ]; }
 | |
|   */
 | |
|   testAllTrue = expr: { inherit expr; expected = map (x: true) expr; };
 | |
| 
 | |
| 
 | |
|   # -- 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
 | |
|   # this forces deep evaluation of all arguments and the result!
 | |
|   # note: if result doesn't evaluate you'll get no trace at all (FIXME)
 | |
|   #       args should be printed in any case
 | |
|   traceCallXml = a:
 | |
|     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 (builtins.seq arg arg)}" (expr arg)
 | |
|             )
 | |
|           else
 | |
|             let r = builtins.seq expr expr;
 | |
|             in trace "${str}\n result:\n${builtins.toXML r}" r
 | |
|       ));
 | |
| }
 |