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).
This commit is contained in:
		
							parent
							
								
									98f3c75a0e
								
							
						
					
					
						commit
						69d82e5c58
					
				
					 8 changed files with 162 additions and 27 deletions
				
			
		| 
						 | 
				
			
			@ -1566,7 +1566,7 @@ string EvalState::copyPathToStore(PathSet & context, const Path & path)
 | 
			
		|||
        dstPath = srcToStore[path];
 | 
			
		||||
    else {
 | 
			
		||||
        dstPath = settings.readOnlyMode
 | 
			
		||||
            ? store->computeStorePathForPath(checkSourcePath(path)).first
 | 
			
		||||
            ? store->computeStorePathForPath(baseNameOf(path), checkSourcePath(path)).first
 | 
			
		||||
            : store->addToStore(baseNameOf(path), checkSourcePath(path), true, htSHA256, defaultPathFilter, repair);
 | 
			
		||||
        srcToStore[path] = dstPath;
 | 
			
		||||
        printMsg(lvlChatty, format("copied source '%1%' -> '%2%'")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue