aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr/primops.cc
diff options
context:
space:
mode:
authorShea Levy <shea@shealevy.com>2018-01-25 07:05:57 -0800
committerShea Levy <shea@shealevy.com>2018-02-06 16:48:08 -0500
commit69d82e5c58bf6d7e16fc296f598c352da2a618d0 (patch)
tree22f76d4df9460225f7877c8c4eca4b9c2ecdc9e2 /src/libexpr/primops.cc
parent98f3c75a0e16f5aaaecb25a46f988580efb04d19 (diff)
Add path primop.
builtins.path allows specifying the name of a path (which makes paths with store-illegal names now addable), allows adding paths with flat instead of recursive hashes, allows specifying a filter (so is a generalization of filterSource), and allows specifying an expected hash (enabling safe path adding in pure mode).
Diffstat (limited to 'src/libexpr/primops.cc')
-rw-r--r--src/libexpr/primops.cc93
1 files changed, 75 insertions, 18 deletions
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 975f0e830..5c8dfd9df 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -1009,20 +1009,13 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu
}
-static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void addPath(EvalState & state, const Pos & pos, const string & name, const Path & path_,
+ Value * filterFun, bool recursive, const Hash & expectedHash, Value & v)
{
- PathSet context;
- Path path = state.coerceToPath(pos, *args[1], context);
- if (!context.empty())
- throw EvalError(format("string '%1%' cannot refer to other paths, at %2%") % path % pos);
-
- state.forceValue(*args[0]);
- if (args[0]->type != tLambda)
- throw TypeError(format("first argument in call to 'filterSource' is not a function but %1%, at %2%") % showType(*args[0]) % pos);
-
- path = state.checkSourcePath(path);
-
- PathFilter filter = [&](const Path & path) {
+ const auto path = settings.pureEval && expectedHash ?
+ path_ :
+ state.checkSourcePath(path_);
+ PathFilter filter = filterFun ? ([&](const Path & path) {
auto st = lstat(path);
/* Call the filter function. The first argument is the path,
@@ -1031,7 +1024,7 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
mkString(arg1, path);
Value fun2;
- state.callFunction(*args[0], arg1, fun2, noPos);
+ state.callFunction(*filterFun, arg1, fun2, noPos);
Value arg2;
mkString(arg2,
@@ -1044,16 +1037,79 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
state.callFunction(fun2, arg2, res, noPos);
return state.forceBool(res, pos);
- };
+ }) : defaultPathFilter;
- Path dstPath = settings.readOnlyMode
- ? state.store->computeStorePathForPath(path, true, htSHA256, filter).first
- : state.store->addToStore(baseNameOf(path), path, true, htSHA256, filter, state.repair);
+ Path expectedStorePath;
+ if (expectedHash) {
+ expectedStorePath =
+ state.store->makeFixedOutputPath(recursive, expectedHash, name);
+ }
+ Path dstPath;
+ if (!expectedHash || !state.store->isValidPath(expectedStorePath)) {
+ dstPath = settings.readOnlyMode
+ ? state.store->computeStorePathForPath(name, path, recursive, htSHA256, filter).first
+ : state.store->addToStore(name, path, recursive, htSHA256, filter, state.repair);
+ if (expectedHash && expectedStorePath != dstPath) {
+ throw Error(format("store path mismatch in (possibly filtered) path added from '%1%'") % path);
+ }
+ } else
+ dstPath = expectedStorePath;
mkString(v, dstPath, {dstPath});
}
+static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+ PathSet context;
+ Path path = state.coerceToPath(pos, *args[1], context);
+ if (!context.empty())
+ throw EvalError(format("string '%1%' cannot refer to other paths, at %2%") % path % pos);
+
+ state.forceValue(*args[0]);
+ if (args[0]->type != tLambda)
+ throw TypeError(format("first argument in call to 'filterSource' is not a function but %1%, at %2%") % showType(*args[0]) % pos);
+
+ addPath(state, pos, baseNameOf(path), path, args[0], true, Hash(), v);
+}
+
+static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+ state.forceAttrs(*args[0], pos);
+ Path path;
+ string name;
+ Value * filterFun = nullptr;
+ auto recursive = true;
+ Hash expectedHash;
+
+ for (auto & attr : *args[0]->attrs) {
+ const string & n(attr.name);
+ if (n == "path") {
+ PathSet context;
+ path = state.coerceToPath(*attr.pos, *attr.value, context);
+ if (!context.empty())
+ throw EvalError(format("string '%1%' cannot refer to other paths, at %2%") % path % *attr.pos);
+ } else if (attr.name == state.sName)
+ name = state.forceStringNoCtx(*attr.value, *attr.pos);
+ else if (n == "filter") {
+ state.forceValue(*attr.value);
+ filterFun = attr.value;
+ } else if (n == "recursive")
+ recursive = state.forceBool(*attr.value, *attr.pos);
+ else if (n == "sha256")
+ expectedHash = Hash(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
+ else
+ throw EvalError(format("unsupported argument '%1%' to 'addPath', at %2%") % attr.name % *attr.pos);
+ }
+ if (path.empty())
+ throw EvalError(format("'path' required, at %1%") % pos);
+ if (name.empty())
+ name = baseNameOf(path);
+
+ addPath(state, pos, name, path, filterFun, recursive, expectedHash, v);
+}
+
+
/*************************************************************
* Sets
*************************************************************/
@@ -2071,6 +2127,7 @@ void EvalState::createBaseEnv()
addPrimOp("__fromJSON", 1, prim_fromJSON);
addPrimOp("__toFile", 2, prim_toFile);
addPrimOp("__filterSource", 2, prim_filterSource);
+ addPrimOp("__path", 1, prim_path);
// Sets
addPrimOp("__attrNames", 1, prim_attrNames);