aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorShea Levy <shea@shealevy.com>2019-01-13 12:43:46 -0500
committerShea Levy <shea@shealevy.com>2019-01-14 11:27:10 -0500
commit1d757292d0cb78beec32fcdfe15c2944a4bc4a95 (patch)
tree7c704f46212eb988db98e426b1043d4bc36ab1c7 /src
parent087be7281a02a6d0e548ae56b910771837023d21 (diff)
Add builtins.getContext.
This can be very helpful when debugging, as well as enabling complex black magic like surgically removing a single dependency from a string's context.
Diffstat (limited to 'src')
-rw-r--r--src/libexpr/eval.hh3
-rw-r--r--src/libexpr/primops.cc38
-rw-r--r--src/libexpr/primops/context.cc132
3 files changed, 135 insertions, 38 deletions
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index d0f298e16..9fe387891 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -316,6 +316,9 @@ private:
/* Return a string representing the type of the value `v'. */
string showType(const Value & v);
+/* Decode a context string ‘!<name>!<path>’ into a pair <path,
+ name>. */
+std::pair<string, string> decodeContext(const string & s);
/* If `path' refers to a directory, then append "/default.nix". */
Path resolveExprPath(Path path);
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 113ceaf29..0da9f702f 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -1780,41 +1780,6 @@ static void prim_stringLength(EvalState & state, const Pos & pos, Value * * args
}
-static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
-{
- PathSet context;
- string s = state.coerceToString(pos, *args[0], context);
- mkString(v, s, PathSet());
-}
-
-
-static void prim_hasContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
-{
- PathSet context;
- state.forceString(*args[0], context, pos);
- mkBool(v, !context.empty());
-}
-
-
-/* Sometimes we want to pass a derivation path (i.e. pkg.drvPath) to a
- builder without causing the derivation to be built (for instance,
- in the derivation that builds NARs in nix-push, when doing
- source-only deployment). This primop marks the string context so
- that builtins.derivation adds the path to drv.inputSrcs rather than
- drv.inputDrvs. */
-static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & pos, Value * * args, Value & v)
-{
- PathSet context;
- string s = state.coerceToString(pos, *args[0], context);
-
- PathSet context2;
- for (auto & p : context)
- context2.insert(p.at(0) == '=' ? string(p, 1) : p);
-
- mkString(v, s, context2);
-}
-
-
/* Return the cryptographic hash of a string in base-16. */
static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
@@ -2285,9 +2250,6 @@ void EvalState::createBaseEnv()
addPrimOp("toString", 1, prim_toString);
addPrimOp("__substring", 3, prim_substring);
addPrimOp("__stringLength", 1, prim_stringLength);
- addPrimOp("__hasContext", 1, prim_hasContext);
- addPrimOp("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
- addPrimOp("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
addPrimOp("__hashString", 2, prim_hashString);
addPrimOp("__match", 2, prim_match);
addPrimOp("__split", 2, prim_split);
diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc
new file mode 100644
index 000000000..6849aa261
--- /dev/null
+++ b/src/libexpr/primops/context.cc
@@ -0,0 +1,132 @@
+#include "primops.hh"
+#include "eval-inline.hh"
+
+namespace nix {
+
+static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+ PathSet context;
+ string s = state.coerceToString(pos, *args[0], context);
+ mkString(v, s, PathSet());
+}
+
+static RegisterPrimOp r1("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
+
+
+static void prim_hasContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+ PathSet context;
+ state.forceString(*args[0], context, pos);
+ mkBool(v, !context.empty());
+}
+
+static RegisterPrimOp r2("__hasContext", 1, prim_hasContext);
+
+
+/* Sometimes we want to pass a derivation path (i.e. pkg.drvPath) to a
+ builder without causing the derivation to be built (for instance,
+ in the derivation that builds NARs in nix-push, when doing
+ source-only deployment). This primop marks the string context so
+ that builtins.derivation adds the path to drv.inputSrcs rather than
+ drv.inputDrvs. */
+static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+ PathSet context;
+ string s = state.coerceToString(pos, *args[0], context);
+
+ PathSet context2;
+ for (auto & p : context)
+ context2.insert(p.at(0) == '=' ? string(p, 1) : p);
+
+ mkString(v, s, context2);
+}
+
+static RegisterPrimOp r3("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
+
+
+/* Extract the context of a string as a structured Nix value.
+
+ The context is represented as an attribute set whose keys are the
+ paths in the context set and whose values are attribute sets with
+ the following keys:
+ path: True if the relevant path is in the context as a plain store
+ path (i.e. the kind of context you get when interpolating
+ a Nix path (e.g. ./.) into a string). False if missing.
+ allOutputs: True if the relevant path is a derivation and it is
+ in the context as a drv file with all of its outputs
+ (i.e. the kind of context you get when referencing
+ .drvPath of some derivation). False if missing.
+ outputs: If a non-empty list, the relevant path is a derivation
+ and the provided outputs are referenced in the context
+ (i.e. the kind of context you get when referencing
+ .outPath of some derivation). Empty list if missing.
+ Note that for a given path any combination of the above attributes
+ may be present, but at least one must be set to something other
+ than the default.
+*/
+static void prim_getContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+ struct ContextInfo {
+ bool path = false;
+ bool allOutputs = false;
+ Strings outputs;
+ };
+ PathSet context;
+ state.forceString(*args[0], context, pos);
+ auto contextInfos = std::map<Path, ContextInfo>();
+ for (const auto & p : context) {
+ Path drv;
+ string output;
+ const Path * path = &p;
+ if (p.at(0) == '=') {
+ drv = string(p, 1);
+ path = &drv;
+ } else if (p.at(0) == '!') {
+ std::pair<string, string> ctx = decodeContext(p);
+ drv = ctx.first;
+ output = ctx.second;
+ path = &drv;
+ }
+ auto isPath = drv.empty();
+ auto isAllOutputs = (!drv.empty()) && output.empty();
+
+ auto iter = contextInfos.find(*path);
+ if (iter == contextInfos.end()) {
+ contextInfos.emplace(*path, ContextInfo{isPath, isAllOutputs, output.empty() ? Strings{} : Strings{std::move(output)}});
+ } else {
+ if (isPath)
+ iter->second.path = true;
+ else if (isAllOutputs)
+ iter->second.allOutputs = true;
+ else
+ iter->second.outputs.emplace_back(std::move(output));
+ }
+ }
+
+ state.mkAttrs(v, contextInfos.size());
+
+ auto sPath = state.symbols.create("path");
+ auto sAllOutputs = state.symbols.create("allOutputs");
+ for (const auto & info : contextInfos) {
+ auto & infoVal = *state.allocAttr(v, state.symbols.create(info.first));
+ state.mkAttrs(infoVal, 3);
+ if (info.second.path)
+ mkBool(*state.allocAttr(infoVal, sPath), true);
+ if (info.second.allOutputs)
+ mkBool(*state.allocAttr(infoVal, sAllOutputs), true);
+ if (!info.second.outputs.empty()) {
+ auto & outputsVal = *state.allocAttr(infoVal, state.sOutputs);
+ state.mkList(outputsVal, info.second.outputs.size());
+ size_t i = 0;
+ for (const auto & output : info.second.outputs) {
+ mkString(*(outputsVal.listElems()[i++] = state.allocValue()), output);
+ }
+ }
+ infoVal.attrs->sort();
+ }
+ v.attrs->sort();
+}
+
+static RegisterPrimOp r4("__getContext", 1, prim_getContext);
+
+}