diff options
Diffstat (limited to 'src/libexpr/primops.cc')
-rw-r--r-- | src/libexpr/primops.cc | 240 |
1 files changed, 133 insertions, 107 deletions
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 9a26bae71..c40c54247 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -43,8 +43,8 @@ StringMap EvalState::realiseContext(const PathSet & context) StringMap res; for (auto & i : context) { - auto [ctxS, outputName] = decodeContext(i); - auto ctx = store->parseStorePath(ctxS); + auto [ctx, outputName] = decodeContext(*store, i); + auto ctxS = store->printStorePath(ctx); if (!store->isValidPath(ctx)) debug_throw(InvalidPathError(store->printStorePath(ctx))); if (!outputName.empty() && ctx.isDerivation()) { @@ -141,7 +141,7 @@ static void mkOutputString( BindingsBuilder & attrs, const StorePath & drvPath, const BasicDerivation & drv, - const std::pair<string, DerivationOutput> & o) + const std::pair<std::string, DerivationOutput> & o) { auto optOutputPath = o.second.path(*state.store, drv.name, o.first); attrs.alloc(o.first).mkString( @@ -314,7 +314,7 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value { auto path = realisePath(state, pos, *args[0]); - string sym(state.forceStringNoCtx(*args[1], pos)); + std::string sym(state.forceStringNoCtx(*args[1], pos)); void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL); if (!handle) @@ -386,7 +386,7 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Value & v) { state.forceValue(*args[0], pos); - string t; + std::string t; switch (args[0]->type()) { case nInt: t = "int"; break; case nBool: t = "bool"; break; @@ -575,9 +575,9 @@ struct CompareValues #if HAVE_BOEHMGC -typedef list<Value *, gc_allocator<Value *> > ValueList; +typedef std::list<Value *, gc_allocator<Value *> > ValueList; #else -typedef list<Value *> ValueList; +typedef std::list<Value *> ValueList; #endif @@ -655,7 +655,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar // `doneKeys' doesn't need to be a GC root, because its values are // reachable from res. auto cmp = CompareValues(state); - set<Value *, decltype(cmp)> doneKeys(cmp); + std::set<Value *, decltype(cmp)> doneKeys(cmp); while (!workSet.empty()) { Value * e = *(workSet.begin()); workSet.pop_front(); @@ -695,7 +695,32 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar static RegisterPrimOp primop_genericClosure(RegisterPrimOp::Info { .name = "__genericClosure", + .args = {"attrset"}, .arity = 1, + .doc = R"( + Take an *attrset* with values named `startSet` and `operator` in order to + return a *list of attrsets* by starting with the `startSet`, recursively + applying the `operator` function to each element. The *attrsets* in the + `startSet` and produced by the `operator` must each contain value named + `key` which are comparable to each other. The result is produced by + repeatedly calling the operator for each element encountered with a + unique key, terminating when no new elements are produced. For example, + + ``` + builtins.genericClosure { + startSet = [ {key = 5;} ]; + operator = item: [{ + key = if (item.key / 2 ) * 2 == item.key + then item.key / 2 + else 3 * item.key + 1; + }]; + } + ``` + evaluates to + ``` + [ { key = 5; } { key = 16; } { key = 8; } { key = 4; } { key = 2; } { key = 1; } ] + ``` + )", .fun = prim_genericClosure, }); @@ -742,7 +767,7 @@ static RegisterPrimOp primop_abort({ .fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - string s = state.coerceToString(pos, *args[0], context).toOwned(); + auto s = state.coerceToString(pos, *args[0], context).toOwned(); state.debug_throw(Abort("evaluation aborted with the following error message: '%1%'", s)); } }); @@ -760,7 +785,7 @@ static RegisterPrimOp primop_throw({ .fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - string s = state.coerceToString(pos, *args[0], context).toOwned(); + auto s = state.coerceToString(pos, *args[0], context).toOwned(); state.debug_throw(ThrownError(s)); } }); @@ -861,7 +886,7 @@ static RegisterPrimOp primop_tryEval({ /* Return an environment variable. Use with care. */ static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v) { - string name(state.forceStringNoCtx(*args[0], pos)); + std::string name(state.forceStringNoCtx(*args[0], pos)); v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or("")); } @@ -970,7 +995,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * pos ); - string drvName; + std::string drvName; Pos & posDrvName(*attr->pos); try { drvName = state.forceStringNoCtx(*attr->value, pos); @@ -999,16 +1024,17 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * PathSet context; bool contentAddressed = false; + bool isImpure = false; std::optional<std::string> outputHash; std::string outputHashAlgo; - auto ingestionMethod = FileIngestionMethod::Flat; + std::optional<FileIngestionMethod> ingestionMethod; StringSet outputs; outputs.insert("out"); for (auto & i : args[0]->attrs->lexicographicOrder()) { if (i->name == state.sIgnoreNulls) continue; - const string & key = i->name; + const std::string & key = i->name; vomit("processing attribute '%1%'", key); auto handleHashMode = [&](const std::string_view s) { @@ -1061,12 +1087,18 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * settings.requireExperimentalFeature(Xp::CaDerivations); } + else if (i->name == state.sImpure) { + isImpure = state.forceBool(*i->value, pos); + if (isImpure) + settings.requireExperimentalFeature(Xp::ImpureDerivations); + } + /* The `args' attribute is special: it supplies the command-line arguments to the builder. */ else if (i->name == state.sArgs) { state.forceList(*i->value, pos); for (auto elem : i->value->listItems()) { - string s = state.coerceToString(posDrvName, *elem, context, true).toOwned(); + auto s = state.coerceToString(posDrvName, *elem, context, true).toOwned(); drv.args.push_back(s); } } @@ -1153,8 +1185,8 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * /* Handle derivation outputs of the form ‘!<name>!<path>’. */ else if (path.at(0) == '!') { - std::pair<string, string> ctx = decodeContext(path); - drv.inputDrvs[state.store->parseStorePath(ctx.first)].insert(ctx.second); + auto ctx = decodeContext(*state.store, path); + drv.inputDrvs[ctx.first].insert(ctx.second); } /* Otherwise it's a source file. */ @@ -1193,31 +1225,44 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * .errPos = posDrvName })); - std::optional<HashType> ht = parseHashTypeOpt(outputHashAlgo); - Hash h = newHashAllowEmpty(*outputHash, ht); + auto h = newHashAllowEmpty(*outputHash, parseHashTypeOpt(outputHashAlgo)); - auto outPath = state.store->makeFixedOutputPath(ingestionMethod, h, drvName); + auto method = ingestionMethod.value_or(FileIngestionMethod::Flat); + auto outPath = state.store->makeFixedOutputPath(method, h, drvName); drv.env["out"] = state.store->printStorePath(outPath); - drv.outputs.insert_or_assign("out", DerivationOutput { - .output = DerivationOutputCAFixed { - .hash = FixedOutputHash { - .method = ingestionMethod, - .hash = std::move(h), - }, + drv.outputs.insert_or_assign("out", + DerivationOutput::CAFixed { + .hash = FixedOutputHash { + .method = method, + .hash = std::move(h), }, - }); + }); } - else if (contentAddressed) { - HashType ht = parseHashType(outputHashAlgo); + else if (contentAddressed || isImpure) { + if (contentAddressed && isImpure) + throw EvalError({ + .msg = hintfmt("derivation cannot be both content-addressed and impure"), + .errPos = posDrvName + }); + + auto ht = parseHashTypeOpt(outputHashAlgo).value_or(htSHA256); + auto method = ingestionMethod.value_or(FileIngestionMethod::Recursive); + for (auto & i : outputs) { drv.env[i] = hashPlaceholder(i); - drv.outputs.insert_or_assign(i, DerivationOutput { - .output = DerivationOutputCAFloating { - .method = ingestionMethod, - .hashType = ht, - }, - }); + if (isImpure) + drv.outputs.insert_or_assign(i, + DerivationOutput::Impure { + .method = method, + .hashType = ht, + }); + else + drv.outputs.insert_or_assign(i, + DerivationOutput::CAFloating { + .method = method, + .hashType = ht, + }); } } @@ -1231,44 +1276,29 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * for (auto & i : outputs) { drv.env[i] = ""; drv.outputs.insert_or_assign(i, - DerivationOutput { - .output = DerivationOutputInputAddressed { - .path = StorePath::dummy, - }, - }); + DerivationOutput::Deferred { }); } - // Regular, non-CA derivation should always return a single hash and not - // hash per output. auto hashModulo = hashDerivationModulo(*state.store, Derivation(drv), true); - std::visit(overloaded { - [&](Hash & h) { - for (auto & i : outputs) { - auto outPath = state.store->makeOutputPath(i, h, drvName); - drv.env[i] = state.store->printStorePath(outPath); - drv.outputs.insert_or_assign(i, - DerivationOutput { - .output = DerivationOutputInputAddressed { - .path = std::move(outPath), - }, - }); - } - }, - [&](CaOutputHashes &) { - // Shouldn't happen as the toplevel derivation is not CA. - assert(false); - }, - [&](DeferredHash &) { - for (auto & i : outputs) { - drv.outputs.insert_or_assign(i, - DerivationOutput { - .output = DerivationOutputDeferred{}, - }); - } - }, - }, - hashModulo); - + switch (hashModulo.kind) { + case DrvHash::Kind::Regular: + for (auto & i : outputs) { + auto h = hashModulo.hashes.at(i); + auto outPath = state.store->makeOutputPath(i, h, drvName); + drv.env[i] = state.store->printStorePath(outPath); + drv.outputs.insert_or_assign( + i, + DerivationOutputInputAddressed { + .path = std::move(outPath), + }); + } + break; + ; + case DrvHash::Kind::Deferred: + for (auto & i : outputs) { + drv.outputs.insert_or_assign(i, DerivationOutputDeferred {}); + } + } } /* Write the resulting term into the Nix store directory. */ @@ -1279,12 +1309,9 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * /* Optimisation, but required in read-only mode! because in that case we don't actually write store derivations, so we can't - read them later. - - However, we don't bother doing this for floating CA derivations because - their "hash modulo" is indeterminate until built. */ - if (drv.type() != DerivationType::CAFloating) { - auto h = hashDerivationModulo(*state.store, Derivation(drv), false); + read them later. */ + { + auto h = hashDerivationModulo(*state.store, drv, false); drvHashes.lock()->insert_or_assign(drvPath, h); } @@ -1475,8 +1502,8 @@ static RegisterPrimOp primop_dirOf({ static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Value & v) { auto path = realisePath(state, pos, *args[0]); - string s = readFile(path); - if (s.find((char) 0) != string::npos) + auto s = readFile(path); + if (s.find((char) 0) != std::string::npos) state.debug_throw(Error("the contents of the file '%1%' cannot be represented as a Nix string", path)); StorePathSet refs; if (state.store->isInStore(path)) { @@ -1509,7 +1536,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va for (auto v2 : args[0]->listItems()) { state.forceAttrs(*v2, pos); - string prefix; + std::string prefix; Bindings::iterator i = v2->attrs->find(state.sPrefix); if (i != v2->attrs->end()) prefix = state.forceStringNoCtx(*i->value, pos); @@ -1523,7 +1550,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va ); PathSet context; - string path = state.coerceToString(pos, *i->value, context, false, false).toOwned(); + auto path = state.coerceToString(pos, *i->value, context, false, false).toOwned(); try { auto rewrites = state.realiseContext(context); @@ -1789,8 +1816,8 @@ static RegisterPrimOp primop_fromJSON({ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - string name(state.forceStringNoCtx(*args[0], pos)); - string contents(state.forceString(*args[1], context, pos)); + std::string name(state.forceStringNoCtx(*args[0], pos)); + std::string contents(state.forceString(*args[1], context, pos)); StorePathSet refs; @@ -1898,7 +1925,7 @@ static RegisterPrimOp primop_toFile({ static void addPath( EvalState & state, const Pos & pos, - const string & name, + const std::string & name, Path path, Value * filterFun, FileIngestionMethod method, @@ -1954,20 +1981,15 @@ static void addPath( if (expectedHash) expectedStorePath = state.store->makeFixedOutputPath(method, *expectedHash, name); - Path dstPath; if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) { - dstPath = state.store->printStorePath(settings.readOnlyMode + StorePath dstPath = settings.readOnlyMode ? state.store->computeStorePathForPath(name, path, method, htSHA256, filter).first - : state.store->addToStore(name, path, method, htSHA256, filter, state.repair, refs)); - if (expectedHash && expectedStorePath != state.store->parseStorePath(dstPath)) + : state.store->addToStore(name, path, method, htSHA256, filter, state.repair, refs); + if (expectedHash && expectedStorePath != dstPath) state.debug_throw(Error("store path mismatch in (possibly filtered) path added from '%s'", path)); + state.allowAndSetStorePathString(dstPath, v); } else - dstPath = state.store->printStorePath(*expectedStorePath); - - v.mkString(dstPath, {dstPath}); - - state.allowPath(dstPath); - + state.allowAndSetStorePathString(*expectedStorePath, v); } catch (Error & e) { e.addTrace(pos, "while adding path '%s'", path); throw; @@ -2051,14 +2073,14 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value { state.forceAttrs(*args[0], pos); Path path; - string name; + std::string name; Value * filterFun = nullptr; auto method = FileIngestionMethod::Recursive; std::optional<Hash> expectedHash; PathSet context; for (auto & attr : *args[0]->attrs) { - const string & n(attr.name); + auto & n(attr.name); if (n == "path") path = state.coerceToPath(*attr.pos, *attr.value, context); else if (attr.name == state.sName) @@ -3651,7 +3673,7 @@ static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * * auto sep = state.forceString(*args[0], context, pos); state.forceList(*args[1], pos); - string res; + std::string res; res.reserve((args[1]->listSize() + 32) * sep.size()); bool first = true; @@ -3684,12 +3706,12 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar .errPos = pos })); - vector<string> from; + std::vector<std::string> from; from.reserve(args[0]->listSize()); for (auto elem : args[0]->listItems()) from.emplace_back(state.forceString(*elem, pos)); - vector<std::pair<string, PathSet>> to; + std::vector<std::pair<std::string, PathSet>> to; to.reserve(args[1]->listSize()); for (auto elem : args[1]->listItems()) { PathSet ctx; @@ -3700,7 +3722,7 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar PathSet context; auto s = state.forceString(*args[2], context, pos); - string res; + std::string res; // Loops one past last character to handle the case where 'from' contains an empty string. for (size_t p = 0; p <= s.size(); ) { bool found = false; @@ -3841,7 +3863,7 @@ RegisterPrimOp::RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun) .name = name, .args = {}, .arity = arity, - .fun = fun + .fun = fun, }); } @@ -3913,13 +3935,17 @@ void EvalState::createBaseEnv() if (RegisterPrimOp::primOps) for (auto & primOp : *RegisterPrimOp::primOps) - addPrimOp({ - .fun = primOp.fun, - .arity = std::max(primOp.args.size(), primOp.arity), - .name = symbols.create(primOp.name), - .args = primOp.args, - .doc = primOp.doc, - }); + if (!primOp.experimentalFeature + || settings.isExperimentalFeatureEnabled(*primOp.experimentalFeature)) + { + addPrimOp({ + .fun = primOp.fun, + .arity = std::max(primOp.args.size(), primOp.arity), + .name = symbols.create(primOp.name), + .args = primOp.args, + .doc = primOp.doc, + }); + } /* Add a wrapper around the derivation primop that computes the `drvPath' and `outPath' attributes lazily. */ |