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); | ||||
|  |  | |||
|  | @ -222,11 +222,10 @@ Path Store::makeTextPath(const string & name, const Hash & hash, | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| std::pair<Path, Hash> Store::computeStorePathForPath(const Path & srcPath, | ||||
|     bool recursive, HashType hashAlgo, PathFilter & filter) const | ||||
| std::pair<Path, Hash> Store::computeStorePathForPath(const string & name, | ||||
|     const Path & srcPath, bool recursive, HashType hashAlgo, PathFilter & filter) const | ||||
| { | ||||
|     Hash h = recursive ? hashPath(hashAlgo, srcPath, filter).first : hashFile(hashAlgo, srcPath); | ||||
|     string name = baseNameOf(srcPath); | ||||
|     Path dstPath = makeFixedOutputPath(recursive, h, name); | ||||
|     return std::pair<Path, Hash>(dstPath, h); | ||||
| } | ||||
|  |  | |||
|  | @ -305,9 +305,9 @@ public: | |||
|     /* This is the preparatory part of addToStore(); it computes the
 | ||||
|        store path to which srcPath is to be copied.  Returns the store | ||||
|        path and the cryptographic hash of the contents of srcPath. */ | ||||
|     std::pair<Path, Hash> computeStorePathForPath(const Path & srcPath, | ||||
|         bool recursive = true, HashType hashAlgo = htSHA256, | ||||
|         PathFilter & filter = defaultPathFilter) const; | ||||
|     std::pair<Path, Hash> computeStorePathForPath(const string & name, | ||||
|         const Path & srcPath, bool recursive = true, | ||||
|         HashType hashAlgo = htSHA256, PathFilter & filter = defaultPathFilter) const; | ||||
| 
 | ||||
|     /* Preparatory part of addTextToStore().
 | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue