diff --git a/lib/asserts.nix b/lib/asserts.nix deleted file mode 100644 index 8a5f1fb3feb76..0000000000000 --- a/lib/asserts.nix +++ /dev/null @@ -1,44 +0,0 @@ -{ lib }: - -rec { - - /* Print a trace message if pred is false. - Intended to be used to augment asserts with helpful error messages. - - Example: - assertMsg false "nope" - => false - stderr> trace: nope - - assert (assertMsg ("foo" == "bar") "foo is not bar, silly"); "" - stderr> trace: foo is not bar, silly - stderr> assert failed at … - - Type: - assertMsg :: Bool -> String -> Bool - */ - # TODO(Profpatsch): add tests that check stderr - assertMsg = pred: msg: - if pred - then true - else builtins.trace msg false; - - /* Specialized `assertMsg` for checking if val is one of the elements - of a list. Useful for checking enums. - - Example: - let sslLibrary = "libressl" - in assertOneOf "sslLibrary" sslLibrary [ "openssl" "bearssl" ] - => false - stderr> trace: sslLibrary must be one of "openssl", "bearssl", but is: "libressl" - - Type: - assertOneOf :: String -> ComparableVal -> List ComparableVal -> Bool - */ - assertOneOf = name: val: xs: assertMsg - (lib.elem val xs) - "${name} must be one of ${ - lib.generators.toPretty {} xs}, but is: ${ - lib.generators.toPretty {} val}"; - -} diff --git a/lib/debug.nix b/lib/debug.nix index 383eb32d75d0d..b336d423fec3d 100644 --- a/lib/debug.nix +++ b/lib/debug.nix @@ -1,90 +1,11 @@ -/* 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; + inherit (builtins) attrNames trace substring; + inherit (lib) elem deprecate; in rec { - # -- TRACING -- - - /* Trace msg, but only if pred is true. - - Example: - traceIf true "hello" 3 - trace: hello - => 3 - */ - traceIf = pred: msg: x: if pred then trace msg x else x; - - /* Trace the value and also return it. - - 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 = ; } - => 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 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` */ - traceValSeqFn = f: v: traceValFn 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; - - # -- TESTING -- /* Evaluate a set of tests. A test is an attribute set {expr, @@ -109,89 +30,22 @@ rec { # usage: { 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`)." ) + deprecate { + failingAfter = "18.03"; + what = "attrNamesToStr"; + reason = "please use more specific concatenation function 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." ) + deprecate { + failingAfter = "18.03"; + what = "addErrorContextToAttrs"; + replacement = "builtins.addErrorContext"; + } (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 - )); } diff --git a/lib/default.nix b/lib/default.nix index d7a05fec8338e..c1afc21d404c0 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -11,6 +11,12 @@ let callLibs = file: import file { lib = self; }; in with self; { + # Configuration of lib, can be set by doing + # lib.extend (self: super: { config = ...; }) + # One can get this value through lib.config + # The Nixpkgs configuration will be passed like this + config = {}; + # often used, or depending on very little trivial = callLibs ./trivial.nix; fixedPoints = callLibs ./fixed-points.nix; @@ -38,7 +44,7 @@ let systems = callLibs ./systems; # misc - asserts = callLibs ./asserts.nix; + logging = callLibs ./logging.nix; debug = callLibs ./debug.nix; generators = callLibs ./generators.nix; misc = callLibs ./deprecated.nix; @@ -59,7 +65,7 @@ let stringLength sub substring tail; inherit (trivial) id const concat or and bitAnd bitOr bitXor bitNot boolToString mergeAttrs flip mapNullable inNixShell min max - importJSON warn info nixpkgsVersion version mod compare + importJSON nixpkgsVersion version mod compare splitByAndCompare functionArgs setFunctionArgs isFunction; inherit (fixedPoints) fix fix' extends composeExtensions makeExtensible makeExtensibleWithCustomName; @@ -117,13 +123,14 @@ let unknownModule mkOption; inherit (types) isType setType defaultTypeMerge defaultFunctor isOptionType mkOptionType; - inherit (asserts) - assertMsg assertOneOf; - inherit (debug) addErrorContextToAttrs traceIf traceVal traceValFn + inherit (logging) /* debug */ info warn fail deprecate + assertMsg assertOneOf + traceIf traceVal traceValFn traceXMLVal traceXMLValMarked traceSeq traceSeqN traceValSeq traceValSeqFn traceValSeqN traceValSeqNFn traceShowVal traceShowValMarked showVal traceCall traceCall2 traceCall3 - traceValIfNot runTests testAllTrue traceCallXml attrNamesToStr; + traceValIfNot traceCallXml; + inherit (debug) runTests testAllTrue addErrorContextToAttrs attrNamesToStr; inherit (misc) maybeEnv defaultMergeArg defaultMerge foldArgs defaultOverridableDelayableArgs composedArgsAndFun maybeAttrNullable maybeAttr ifEnable checkFlag getValue diff --git a/lib/logging.nix b/lib/logging.nix new file mode 100644 index 0000000000000..1bfb0f6da43fd --- /dev/null +++ b/lib/logging.nix @@ -0,0 +1,261 @@ +/* Logging, tracing, etc. */ + +{ lib }: + +let + inherit (builtins) trace isAttrs isList isInt + head substring attrNames; + inherit (lib) config id elem isFunction; +in + +rec { + + # -- Logging -- + + DEBUG = 0; + INFO = 1000; + WARNING = 2000; + ERROR = 3000; + + showLogLevel = x: + if x >= ERROR then "ERROR" + else if x >= WARNING then "WARNING" + else if x >= INFO then "INFO" + else "DEBUG"; + + log = level: condition: msg: x: + if condition && level >= (config.throwLevel or ERROR) then throw msg + else if condition && level >= (config.traceLevel or WARNING) then trace "${showLogLevel level}: ${msg}" x + else x; + + debug = log DEBUG true; + info = log INFO true; + warn = log WARNING true; + fail = log ERROR true; + + deprecate = + { silentBefore ? null + , failingAfter ? null + , what + , ...} @ attrs: + assert isNull silentBefore + || isNull failingAfter + || lib.versionOlder silentBefore failingAfter; + assert attrs ? reason || attrs ? replacement; + let + current = config.configVersion or lib.trivial.release; + + explain = lib.concatStringsSep ", " (lib.concatLists [ + (lib.optional (attrs ? reason) attrs.reason) + (lib.optional (attrs ? replacement) "please use `${attrs.replacement}` instead") + ]); + failMessage = "`${what}` is long deprecated and could be removed at any point without further warnings, ${explain}"; + warnMessage = "`${what}` is deprecated, ${explain}"; + warnReason = "ERROR after ${failingAfter}:\n${warnMessage}"; + infoReason = "WARNING after ${silentBefore}: ${warnReason}"; + in + if !isNull failingAfter && lib.versionAtLeast current failingAfter then lib.fail failMessage + else if !isNull silentBefore && lib.versionAtLeast current silentBefore then lib.warn warnReason + else lib.info infoReason; + + # -- Tracing -- + + /* 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. + */ + + /* Trace msg, but only if pred is true. + + Example: + traceIf true "hello" 3 + trace: hello + => 3 + */ + traceIf = pred: msg: x: if pred then trace msg x else x; + + /* Trace the value and also return it. + + 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 = ; } + => 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 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` */ + traceValSeqFn = f: v: traceValFn 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; + + # -- Tracing for asserts -- + + /* Print a trace message if pred is false. + Intended to be used to augment asserts with helpful error messages. + + Example: + assertMsg false "nope" + => false + stderr> trace: nope + + assert (assertMsg ("foo" == "bar") "foo is not bar, silly"); "" + stderr> trace: foo is not bar, silly + stderr> assert failed at … + + Type: + assertMsg :: Bool -> String -> Bool + */ + # TODO(Profpatsch): add tests that check stderr + assertMsg = pred: msg: + if pred + then true + else builtins.trace msg false; + + /* Specialized `assertMsg` for checking if val is one of the elements + of a list. Useful for checking enums. + + Example: + let sslLibrary = "libressl" + in assertOneOf "sslLibrary" sslLibrary [ "openssl" "bearssl" ] + => false + stderr> trace: sslLibrary must be one of "openssl", "bearssl", but is: "libressl" + + Type: + assertOneOf :: String -> ComparableVal -> List ComparableVal -> Bool + */ + assertOneOf = name: val: xs: assertMsg + (lib.elem val xs) + "${name} must be one of ${ + lib.generators.toPretty {} xs}, but is: ${ + lib.generators.toPretty {} val}"; + + # -- DEPRECATED -- + + traceShowVal = x: trace (showVal x) x; + traceShowValMarked = str: x: trace (str + showVal x) x; + + showVal = with lib; + deprecate { + silentBefore = "17.03"; + failingAfter = "18.03"; + what = "showVal"; + replacement = "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: + deprecate { + failingAfter = "18.03"; + what = "traceXMLVal"; + replacement = "traceValFn builtins.toXML"; + } + (trace (builtins.toXML x) x); + traceXMLValMarked = str: x: + deprecate { + failingAfter = "18.03"; + what = "traceXMLValMarked"; + replacement = "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: + deprecate { + failingAfter = "18.03"; + what = "traceValIfNot"; + reason = "please use `if/then/else` and `traceValSeq 1` instead"; + } + (if c x then true else traceSeq (showVal x) false); + + # 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: + deprecate { + failingAfter = "18.03"; + what = "traceCallXml"; + reason = "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 + )); + +} diff --git a/lib/sources.nix b/lib/sources.nix index 704711b20cd94..fb2f8f95e02a7 100644 --- a/lib/sources.nix +++ b/lib/sources.nix @@ -69,7 +69,7 @@ rec { # Get the commit id of a git repo # Example: commitIdFromGitRepo commitIdFromGitRepo = - let readCommitFromFile = path: file: + let readCommitFromFile = file: path: with builtins; let fileName = toString path + "/" + file; packedRefsName = toString path + "/packed-refs"; @@ -81,7 +81,7 @@ rec { matchRef = match "^ref: (.*)$" fileContent; in if isNull matchRef then fileContent - else readCommitFromFile path (lib.head matchRef) + else readCommitFromFile (lib.head matchRef) path # Sometimes, the file isn't there at all and has been packed away in the # packed-refs file, so we have to grep through it: else if lib.pathExists packedRefsName @@ -92,7 +92,7 @@ rec { then throw ("Could not find " + file + " in " + packedRefsName) else lib.head matchRef else throw ("Not a .git directory: " + path); - in lib.flip readCommitFromFile "HEAD"; + in readCommitFromFile "HEAD"; pathHasContext = builtins.hasContext or (lib.hasPrefix builtins.storeDir); diff --git a/lib/trivial.nix b/lib/trivial.nix index b1eea0bf1247e..185b1e5a7542e 100644 --- a/lib/trivial.nix +++ b/lib/trivial.nix @@ -170,25 +170,6 @@ rec { importJSON = path: builtins.fromJSON (builtins.readFile path); - - ## Warnings - - /* See https://github.com/NixOS/nix/issues/749. Eventually we'd like these - to expand to Nix builtins that carry metadata so that Nix can filter out - the INFO messages without parsing the message string. - - Usage: - { - foo = lib.warn "foo is deprecated" oldFoo; - } - - TODO: figure out a clever way to integrate location information from - something like __unsafeGetAttrPos. - */ - warn = msg: builtins.trace "WARNING: ${msg}"; - info = msg: builtins.trace "INFO: ${msg}"; - - ## Function annotations /* Add metadata about expected function arguments to a function. diff --git a/pkgs/top-level/default.nix b/pkgs/top-level/default.nix index da7fc1bed34c5..37cc4fb57adf0 100644 --- a/pkgs/top-level/default.nix +++ b/pkgs/top-level/default.nix @@ -43,7 +43,6 @@ let # Rename the function arguments crossSystem0 = crossSystem; in let - lib = import ../../lib; # Allow both: # { /* the config */ } and @@ -53,6 +52,10 @@ in let then configExpr { inherit pkgs; } else configExpr; + lib = (import ../../lib).extend (self: super: { + inherit config; + }); + # From a minimum of `system` or `config` (actually a target triple, *not* # nixpkgs configuration), infer the other one and platform as needed. localSystem = lib.systems.elaborate (