Allow builtins.{path,filterSource} on paths with a context

We now build the context (so this has the side-effect of making
builtins.{path,filterSource} work on derivations outputs, if IFD is
enabled) and then check that the path has no references (which is what
we really care about).
This commit is contained in:
Eelco Dolstra 2021-10-07 13:43:17 +02:00
parent 66c4b20d8b
commit 4806f2f6b0
2 changed files with 43 additions and 20 deletions

View file

@ -1843,12 +1843,43 @@ static RegisterPrimOp primop_toFile({
.fun = prim_toFile, .fun = prim_toFile,
}); });
static void addPath(EvalState & state, const Pos & pos, const string & name, const Path & path_, static void addPath(
Value * filterFun, FileIngestionMethod method, const std::optional<Hash> expectedHash, Value & v) EvalState & state,
const Pos & pos,
const string & name,
const Path & path_,
Value * filterFun,
FileIngestionMethod method,
const std::optional<Hash> expectedHash,
Value & v,
const PathSet & context)
{ {
const auto path = evalSettings.pureEval && expectedHash ? try {
path_ : // FIXME: handle CA derivation outputs (where path_ needs to
state.checkSourcePath(path_); // be rewritten to the actual output).
state.realiseContext(context);
} catch (InvalidPathError & e) {
throw EvalError({
.msg = hintfmt("cannot add path '%s', since path '%s' is not valid", path_, e.path),
.errPos = pos
});
} catch (Error & e) {
e.addTrace(pos, "while adding path '%s'", path_);
throw;
}
const auto path = evalSettings.pureEval && expectedHash
? path_
: state.checkSourcePath(path_);
if (state.store->isInStore(path)) {
auto storePath = state.store->toStorePath(path).first;
auto info = state.store->queryPathInfo(storePath);
if (!info->references.empty())
throw EvalError("store path '%s' is not allowed to have references",
state.store->printStorePath(storePath));
}
PathFilter filter = filterFun ? ([&](const Path & path) { PathFilter filter = filterFun ? ([&](const Path & path) {
auto st = lstat(path); auto st = lstat(path);
@ -1876,6 +1907,7 @@ static void addPath(EvalState & state, const Pos & pos, const string & name, con
std::optional<StorePath> expectedStorePath; std::optional<StorePath> expectedStorePath;
if (expectedHash) if (expectedHash)
expectedStorePath = state.store->makeFixedOutputPath(method, *expectedHash, name); expectedStorePath = state.store->makeFixedOutputPath(method, *expectedHash, name);
Path dstPath; Path dstPath;
if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) { if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) {
dstPath = state.store->printStorePath(settings.readOnlyMode dstPath = state.store->printStorePath(settings.readOnlyMode
@ -1896,11 +1928,6 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
{ {
PathSet context; PathSet context;
Path path = state.coerceToPath(pos, *args[1], context); Path path = state.coerceToPath(pos, *args[1], context);
if (!context.empty())
throw EvalError({
.msg = hintfmt("string '%1%' cannot refer to other paths", path),
.errPos = pos
});
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
if (args[0]->type() != nFunction) if (args[0]->type() != nFunction)
@ -1911,7 +1938,7 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
.errPos = pos .errPos = pos
}); });
addPath(state, pos, std::string(baseNameOf(path)), path, args[0], FileIngestionMethod::Recursive, std::nullopt, v); addPath(state, pos, std::string(baseNameOf(path)), path, args[0], FileIngestionMethod::Recursive, std::nullopt, v, context);
} }
static RegisterPrimOp primop_filterSource({ static RegisterPrimOp primop_filterSource({
@ -1977,18 +2004,13 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
Value * filterFun = nullptr; Value * filterFun = nullptr;
auto method = FileIngestionMethod::Recursive; auto method = FileIngestionMethod::Recursive;
std::optional<Hash> expectedHash; std::optional<Hash> expectedHash;
PathSet context;
for (auto & attr : *args[0]->attrs) { for (auto & attr : *args[0]->attrs) {
const string & n(attr.name); const string & n(attr.name);
if (n == "path") { if (n == "path")
PathSet context;
path = state.coerceToPath(*attr.pos, *attr.value, context); path = state.coerceToPath(*attr.pos, *attr.value, context);
if (!context.empty()) else if (attr.name == state.sName)
throw EvalError({
.msg = hintfmt("string '%1%' cannot refer to other paths", path),
.errPos = *attr.pos
});
} else if (attr.name == state.sName)
name = state.forceStringNoCtx(*attr.value, *attr.pos); name = state.forceStringNoCtx(*attr.value, *attr.pos);
else if (n == "filter") { else if (n == "filter") {
state.forceValue(*attr.value, pos); state.forceValue(*attr.value, pos);
@ -2011,7 +2033,7 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
if (name.empty()) if (name.empty())
name = baseNameOf(path); name = baseNameOf(path);
addPath(state, pos, name, path, filterFun, method, expectedHash, v); addPath(state, pos, name, path, filterFun, method, expectedHash, v, context);
} }
static RegisterPrimOp primop_path({ static RegisterPrimOp primop_path({

View file

@ -262,6 +262,7 @@ cat > $flake3Dir/flake.nix <<EOF
inherit system; inherit system;
name = "fnord"; name = "fnord";
dummy = builtins.readFile (builtins.path { name = "source"; path = ./.; filter = path: type: baseNameOf path == "config.nix"; } + "/config.nix"); dummy = builtins.readFile (builtins.path { name = "source"; path = ./.; filter = path: type: baseNameOf path == "config.nix"; } + "/config.nix");
dummy2 = builtins.readFile (builtins.path { name = "source"; path = inputs.flake1; filter = path: type: baseNameOf path == "simple.nix"; } + "/simple.nix");
buildCommand = '' buildCommand = ''
cat \${inputs.nonFlake}/README.md > \$out cat \${inputs.nonFlake}/README.md > \$out
''; '';