about summary refs log tree commit diff
diff options
context:
space:
mode:
authorShea Levy <shea@shealevy.com>2019-01-14T16·34-0500
committerShea Levy <shea@shealevy.com>2019-01-31T13·52-0500
commitb30be6b450f872f8be6dc8afa28f4b030fa8d1d1 (patch)
tree34a70603cf1f640dd277df7963996c3de7bac6a7
parent1d757292d0cb78beec32fcdfe15c2944a4bc4a95 (diff)
Add builtins.appendContext.
A partner of builtins.getContext, useful for the same reasons.
-rw-r--r--src/libexpr/primops/context.cc59
-rw-r--r--tests/lang/eval-okay-context-introspection.nix6
2 files changed, 61 insertions, 4 deletions
diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc
index 6849aa261190..2d79739ea047 100644
--- a/src/libexpr/primops/context.cc
+++ b/src/libexpr/primops/context.cc
@@ -1,5 +1,6 @@
 #include "primops.hh"
 #include "eval-inline.hh"
+#include "derivations.hh"
 
 namespace nix {
 
@@ -61,8 +62,7 @@ static RegisterPrimOp r3("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscar
               (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.
+   may be present.
 */
 static void prim_getContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
 {
@@ -129,4 +129,59 @@ static void prim_getContext(EvalState & state, const Pos & pos, Value * * args,
 
 static RegisterPrimOp r4("__getContext", 1, prim_getContext);
 
+
+/* Append the given context to a given string.
+
+   See the commentary above unsafeGetContext for details of the
+   context representation.
+*/
+static void prim_appendContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+    PathSet context;
+    auto orig = state.forceString(*args[0], context, pos);
+
+    state.forceAttrs(*args[1], pos);
+
+    auto sPath = state.symbols.create("path");
+    auto sAllOutputs = state.symbols.create("allOutputs");
+    for (auto & i : *args[1]->attrs) {
+        if (!state.store->isStorePath(i.name))
+            throw EvalError("Context key '%s' is not a store path, at %s", i.name, i.pos);
+        if (!settings.readOnlyMode)
+            state.store->ensurePath(i.name);
+        state.forceAttrs(*i.value, *i.pos);
+        auto iter = i.value->attrs->find(sPath);
+        if (iter != i.value->attrs->end()) {
+            if (state.forceBool(*iter->value, *iter->pos))
+                context.insert(i.name);
+        }
+
+        iter = i.value->attrs->find(sAllOutputs);
+        if (iter != i.value->attrs->end()) {
+            if (state.forceBool(*iter->value, *iter->pos)) {
+                if (!isDerivation(i.name)) {
+                    throw EvalError("Tried to add all-outputs context of %s, which is not a derivation, to a string, at %s", i.name, i.pos);
+                }
+                context.insert("=" + string(i.name));
+            }
+        }
+
+        iter = i.value->attrs->find(state.sOutputs);
+        if (iter != i.value->attrs->end()) {
+            state.forceList(*iter->value, *iter->pos);
+            if (iter->value->listSize() && !isDerivation(i.name)) {
+                throw EvalError("Tried to add derivation output context of %s, which is not a derivation, to a string, at %s", i.name, i.pos);
+            }
+            for (unsigned int n = 0; n < iter->value->listSize(); ++n) {
+                auto name = state.forceStringNoCtx(*iter->value->listElems()[n], *iter->pos);
+                context.insert("!" + name + "!" + string(i.name));
+            }
+        }
+    }
+
+    mkString(v, orig, context);
+}
+
+static RegisterPrimOp r5("__appendContext", 2, prim_appendContext);
+
 }
diff --git a/tests/lang/eval-okay-context-introspection.nix b/tests/lang/eval-okay-context-introspection.nix
index d9b2ea354990..43178bd2eef9 100644
--- a/tests/lang/eval-okay-context-introspection.nix
+++ b/tests/lang/eval-okay-context-introspection.nix
@@ -18,5 +18,7 @@ let
     };
   };
 
-  legit-context = "${path}${drv.outPath}${drv.foo.outPath}${drv.drvPath}";
-in builtins.getContext legit-context == desired-context
+  legit-context = builtins.getContext "${path}${drv.outPath}${drv.foo.outPath}${drv.drvPath}";
+
+  constructed-context = builtins.getContext (builtins.appendContext "" desired-context);
+in legit-context == constructed-context