Get rid of most .at calls (#6393)

Use one of `get` or `getOr` instead which will either return a null-pointer (with a nicer error message) or a default value when the key is missing.
This commit is contained in:
Alain Zscheile 2022-05-04 07:44:32 +02:00 committed by GitHub
parent 9489b4b7ef
commit 1385b20078
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 231 additions and 118 deletions

View file

@ -871,12 +871,13 @@ std::vector<std::pair<std::shared_ptr<Installable>, BuiltPath>> Installable::bui
auto outputHashes = staticOutputHashes(*evalStore, drv); // FIXME: expensive auto outputHashes = staticOutputHashes(*evalStore, drv); // FIXME: expensive
auto drvOutputs = drv.outputsAndOptPaths(*store); auto drvOutputs = drv.outputsAndOptPaths(*store);
for (auto & output : bfd.outputs) { for (auto & output : bfd.outputs) {
if (!outputHashes.count(output)) auto outputHash = get(outputHashes, output);
if (!outputHash)
throw Error( throw Error(
"the derivation '%s' doesn't have an output named '%s'", "the derivation '%s' doesn't have an output named '%s'",
store->printStorePath(bfd.drvPath), output); store->printStorePath(bfd.drvPath), output);
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) { if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
DrvOutput outputId { outputHashes.at(output), output }; DrvOutput outputId { *outputHash, output };
auto realisation = store->queryRealisation(outputId); auto realisation = store->queryRealisation(outputId);
if (!realisation) if (!realisation)
throw Error( throw Error(
@ -887,10 +888,11 @@ std::vector<std::pair<std::shared_ptr<Installable>, BuiltPath>> Installable::bui
} else { } else {
// If ca-derivations isn't enabled, assume that // If ca-derivations isn't enabled, assume that
// the output path is statically known. // the output path is statically known.
assert(drvOutputs.count(output)); auto drvOutput = get(drvOutputs, output);
assert(drvOutputs.at(output).second); assert(drvOutput);
assert(drvOutput->second);
outputs.insert_or_assign( outputs.insert_or_assign(
output, *drvOutputs.at(output).second); output, *drvOutput->second);
} }
} }
res.push_back({installable, BuiltPath::Built { bfd.drvPath, outputs }}); res.push_back({installable, BuiltPath::Built { bfd.drvPath, outputs }});

View file

@ -50,13 +50,11 @@ void ConfigFile::apply()
else else
assert(false); assert(false);
if (!whitelist.count(baseName)) { if (!whitelist.count(baseName) && !nix::fetchSettings.acceptFlakeConfig) {
auto trustedList = readTrustedList();
bool trusted = false; bool trusted = false;
if (nix::fetchSettings.acceptFlakeConfig){ auto trustedList = readTrustedList();
trusted = true; auto tlname = get(trustedList, name);
} else if (auto saved = get(get(trustedList, name).value_or(std::map<std::string, bool>()), valueS)) { if (auto saved = tlname ? get(*tlname, valueS) : nullptr) {
trusted = *saved; trusted = *saved;
warn("Using saved setting for '%s = %s' from ~/.local/share/nix/trusted-settings.json.", name,valueS); warn("Using saved setting for '%s = %s' from ~/.local/share/nix/trusted-settings.json.", name,valueS);
} else { } else {
@ -69,7 +67,6 @@ void ConfigFile::apply()
writeTrustedList(trustedList); writeTrustedList(trustedList);
} }
} }
if (!trusted) { if (!trusted) {
warn("ignoring untrusted flake configuration setting '%s'", name); warn("ignoring untrusted flake configuration setting '%s'", name);
continue; continue;

View file

@ -176,7 +176,7 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
parsedURL.query.insert_or_assign("shallow", "1"); parsedURL.query.insert_or_assign("shallow", "1");
return std::make_pair( return std::make_pair(
FlakeRef(Input::fromURL(parsedURL), get(parsedURL.query, "dir").value_or("")), FlakeRef(Input::fromURL(parsedURL), getOr(parsedURL.query, "dir", "")),
fragment); fragment);
} }
@ -189,7 +189,7 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
if (!hasPrefix(path, "/")) if (!hasPrefix(path, "/"))
throw BadURL("flake reference '%s' is not an absolute path", url); throw BadURL("flake reference '%s' is not an absolute path", url);
auto query = decodeQuery(match[2]); auto query = decodeQuery(match[2]);
path = canonPath(path + "/" + get(query, "dir").value_or("")); path = canonPath(path + "/" + getOr(query, "dir", ""));
} }
fetchers::Attrs attrs; fetchers::Attrs attrs;
@ -208,7 +208,7 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
input.parent = baseDir; input.parent = baseDir;
return std::make_pair( return std::make_pair(
FlakeRef(std::move(input), get(parsedURL.query, "dir").value_or("")), FlakeRef(std::move(input), getOr(parsedURL.query, "dir", "")),
fragment); fragment);
} }
} }

View file

@ -34,7 +34,7 @@ DrvInfo::DrvInfo(EvalState & state, ref<Store> store, const std::string & drvPat
outputName = outputName =
selectedOutputs.empty() selectedOutputs.empty()
? get(drv.env, "outputName").value_or("out") ? getOr(drv.env, "outputName", "out")
: *selectedOutputs.begin(); : *selectedOutputs.begin();
auto i = drv.outputs.find(outputName); auto i = drv.outputs.find(outputName);

View file

@ -68,14 +68,15 @@ StringMap EvalState::realiseContext(const PathSet & context)
/* Get all the output paths corresponding to the placeholders we had */ /* Get all the output paths corresponding to the placeholders we had */
for (auto & [drvPath, outputs] : drvs) { for (auto & [drvPath, outputs] : drvs) {
auto outputPaths = store->queryDerivationOutputMap(drvPath); const auto outputPaths = store->queryDerivationOutputMap(drvPath);
for (auto & outputName : outputs) { for (auto & outputName : outputs) {
if (outputPaths.count(outputName) == 0) auto outputPath = get(outputPaths, outputName);
if (!outputPath)
throw Error("derivation '%s' does not have an output named '%s'", throw Error("derivation '%s' does not have an output named '%s'",
store->printStorePath(drvPath), outputName); store->printStorePath(drvPath), outputName);
res.insert_or_assign( res.insert_or_assign(
downstreamPlaceholder(*store, drvPath, outputName), downstreamPlaceholder(*store, drvPath, outputName),
store->printStorePath(outputPaths.at(outputName)) store->printStorePath(*outputPath)
); );
} }
} }
@ -1249,8 +1250,13 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * *
switch (hashModulo.kind) { switch (hashModulo.kind) {
case DrvHash::Kind::Regular: case DrvHash::Kind::Regular:
for (auto & i : outputs) { for (auto & i : outputs) {
auto h = hashModulo.hashes.at(i); auto h = get(hashModulo.hashes, i);
auto outPath = state.store->makeOutputPath(i, h, drvName); if (!h)
throw AssertionError({
.msg = hintfmt("derivation produced no hash for output '%s'", i),
.errPos = state.positions[posDrvName],
});
auto outPath = state.store->makeOutputPath(i, *h, drvName);
drv.env[i] = state.store->printStorePath(outPath); drv.env[i] = state.store->printStorePath(outPath);
drv.outputs.insert_or_assign( drv.outputs.insert_or_assign(
i, i,

View file

@ -984,21 +984,28 @@ void DerivationGoal::resolvedFinished()
realWantedOutputs = resolvedDrv.outputNames(); realWantedOutputs = resolvedDrv.outputNames();
for (auto & wantedOutput : realWantedOutputs) { for (auto & wantedOutput : realWantedOutputs) {
assert(initialOutputs.count(wantedOutput) != 0); auto initialOutput = get(initialOutputs, wantedOutput);
assert(resolvedHashes.count(wantedOutput) != 0); auto resolvedHash = get(resolvedHashes, wantedOutput);
auto realisation = resolvedResult.builtOutputs.at( if ((!initialOutput) || (!resolvedHash))
DrvOutput { resolvedHashes.at(wantedOutput), wantedOutput }); throw Error(
"derivation '%s' doesn't have expected output '%s' (derivation-goal.cc/resolvedFinished,resolve)",
worker.store.printStorePath(drvPath), wantedOutput);
auto realisation = get(resolvedResult.builtOutputs, DrvOutput { *resolvedHash, wantedOutput });
if (!realisation)
throw Error(
"derivation '%s' doesn't have expected output '%s' (derivation-goal.cc/resolvedFinished,realisation)",
worker.store.printStorePath(resolvedDrvGoal->drvPath), wantedOutput);
if (drv->type().isPure()) { if (drv->type().isPure()) {
auto newRealisation = realisation; auto newRealisation = *realisation;
newRealisation.id = DrvOutput { initialOutputs.at(wantedOutput).outputHash, wantedOutput }; newRealisation.id = DrvOutput { initialOutput->outputHash, wantedOutput };
newRealisation.signatures.clear(); newRealisation.signatures.clear();
if (!drv->type().isFixed()) if (!drv->type().isFixed())
newRealisation.dependentRealisations = drvOutputReferences(worker.store, *drv, realisation.outPath); newRealisation.dependentRealisations = drvOutputReferences(worker.store, *drv, realisation->outPath);
signRealisation(newRealisation); signRealisation(newRealisation);
worker.store.registerDrvOutput(newRealisation); worker.store.registerDrvOutput(newRealisation);
} }
outputPaths.insert(realisation.outPath); outputPaths.insert(realisation->outPath);
builtOutputs.emplace(realisation.id, realisation); builtOutputs.emplace(realisation->id, *realisation);
} }
runPostBuildHook( runPostBuildHook(
@ -1294,7 +1301,11 @@ std::pair<bool, DrvOutputs> DerivationGoal::checkPathValidity()
DrvOutputs validOutputs; DrvOutputs validOutputs;
for (auto & i : queryPartialDerivationOutputMap()) { for (auto & i : queryPartialDerivationOutputMap()) {
InitialOutput & info = initialOutputs.at(i.first); auto initialOutput = get(initialOutputs, i.first);
if (!initialOutput)
// this is an invalid output, gets catched with (!wantedOutputsLeft.empty())
continue;
auto & info = *initialOutput;
info.wanted = wantOutput(i.first, wantedOutputs); info.wanted = wantOutput(i.first, wantedOutputs);
if (info.wanted) if (info.wanted)
wantedOutputsLeft.erase(i.first); wantedOutputsLeft.erase(i.first);
@ -1309,7 +1320,7 @@ std::pair<bool, DrvOutputs> DerivationGoal::checkPathValidity()
: PathStatus::Corrupt, : PathStatus::Corrupt,
}; };
} }
auto drvOutput = DrvOutput{initialOutputs.at(i.first).outputHash, i.first}; auto drvOutput = DrvOutput{info.outputHash, i.first};
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) { if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
if (auto real = worker.store.queryRealisation(drvOutput)) { if (auto real = worker.store.queryRealisation(drvOutput)) {
info.known = { info.known = {

View file

@ -482,7 +482,7 @@ void LocalDerivationGoal::startBuilder()
temporary build directory. The text files have the format used temporary build directory. The text files have the format used
by `nix-store --register-validity'. However, the deriver by `nix-store --register-validity'. However, the deriver
fields are left empty. */ fields are left empty. */
auto s = get(drv->env, "exportReferencesGraph").value_or(""); auto s = getOr(drv->env, "exportReferencesGraph", "");
Strings ss = tokenizeString<Strings>(s); Strings ss = tokenizeString<Strings>(s);
if (ss.size() % 2 != 0) if (ss.size() % 2 != 0)
throw BuildError("odd number of tokens in 'exportReferencesGraph': '%1%'", s); throw BuildError("odd number of tokens in 'exportReferencesGraph': '%1%'", s);
@ -989,7 +989,7 @@ void LocalDerivationGoal::initTmpDir() {
there is no size constraint). */ there is no size constraint). */
if (!parsedDrv->getStructuredAttrs()) { if (!parsedDrv->getStructuredAttrs()) {
StringSet passAsFile = tokenizeString<StringSet>(get(drv->env, "passAsFile").value_or("")); StringSet passAsFile = tokenizeString<StringSet>(getOr(drv->env, "passAsFile", ""));
for (auto & i : drv->env) { for (auto & i : drv->env) {
if (passAsFile.find(i.first) == passAsFile.end()) { if (passAsFile.find(i.first) == passAsFile.end()) {
env[i.first] = i.second; env[i.first] = i.second;
@ -2128,12 +2128,22 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
std::map<std::string, std::variant<AlreadyRegistered, PerhapsNeedToRegister>> outputReferencesIfUnregistered; std::map<std::string, std::variant<AlreadyRegistered, PerhapsNeedToRegister>> outputReferencesIfUnregistered;
std::map<std::string, struct stat> outputStats; std::map<std::string, struct stat> outputStats;
for (auto & [outputName, _] : drv->outputs) { for (auto & [outputName, _] : drv->outputs) {
auto actualPath = toRealPathChroot(worker.store.printStorePath(scratchOutputs.at(outputName))); auto scratchOutput = get(scratchOutputs, outputName);
if (!scratchOutput)
throw BuildError(
"builder for '%s' has no scratch output for '%s'",
worker.store.printStorePath(drvPath), outputName);
auto actualPath = toRealPathChroot(worker.store.printStorePath(*scratchOutput));
outputsToSort.insert(outputName); outputsToSort.insert(outputName);
/* Updated wanted info to remove the outputs we definitely don't need to register */ /* Updated wanted info to remove the outputs we definitely don't need to register */
auto & initialInfo = initialOutputs.at(outputName); auto initialOutput = get(initialOutputs, outputName);
if (!initialOutput)
throw BuildError(
"builder for '%s' has no initial output for '%s'",
worker.store.printStorePath(drvPath), outputName);
auto & initialInfo = *initialOutput;
/* Don't register if already valid, and not checking */ /* Don't register if already valid, and not checking */
initialInfo.wanted = buildMode == bmCheck initialInfo.wanted = buildMode == bmCheck
@ -2185,6 +2195,11 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
auto sortedOutputNames = topoSort(outputsToSort, auto sortedOutputNames = topoSort(outputsToSort,
{[&](const std::string & name) { {[&](const std::string & name) {
auto orifu = get(outputReferencesIfUnregistered, name);
if (!orifu)
throw BuildError(
"no output reference for '%s' in build of '%s'",
name, worker.store.printStorePath(drvPath));
return std::visit(overloaded { return std::visit(overloaded {
/* Since we'll use the already installed versions of these, we /* Since we'll use the already installed versions of these, we
can treat them as leaves and ignore any references they can treat them as leaves and ignore any references they
@ -2199,7 +2214,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
referencedOutputs.insert(o); referencedOutputs.insert(o);
return referencedOutputs; return referencedOutputs;
}, },
}, outputReferencesIfUnregistered.at(name)); }, *orifu);
}}, }},
{[&](const std::string & path, const std::string & parent) { {[&](const std::string & path, const std::string & parent) {
// TODO with more -vvvv also show the temporary paths for manual inspection. // TODO with more -vvvv also show the temporary paths for manual inspection.
@ -2213,9 +2228,10 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
OutputPathMap finalOutputs; OutputPathMap finalOutputs;
for (auto & outputName : sortedOutputNames) { for (auto & outputName : sortedOutputNames) {
auto output = drv->outputs.at(outputName); auto output = get(drv->outputs, outputName);
auto & scratchPath = scratchOutputs.at(outputName); auto scratchPath = get(scratchOutputs, outputName);
auto actualPath = toRealPathChroot(worker.store.printStorePath(scratchPath)); assert(output && scratchPath);
auto actualPath = toRealPathChroot(worker.store.printStorePath(*scratchPath));
auto finish = [&](StorePath finalStorePath) { auto finish = [&](StorePath finalStorePath) {
/* Store the final path */ /* Store the final path */
@ -2223,10 +2239,13 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
/* The rewrite rule will be used in downstream outputs that refer to /* The rewrite rule will be used in downstream outputs that refer to
use. This is why the topological sort is essential to do first use. This is why the topological sort is essential to do first
before this for loop. */ before this for loop. */
if (scratchPath != finalStorePath) if (*scratchPath != finalStorePath)
outputRewrites[std::string { scratchPath.hashPart() }] = std::string { finalStorePath.hashPart() }; outputRewrites[std::string { scratchPath->hashPart() }] = std::string { finalStorePath.hashPart() };
}; };
auto orifu = get(outputReferencesIfUnregistered, outputName);
assert(orifu);
std::optional<StorePathSet> referencesOpt = std::visit(overloaded { std::optional<StorePathSet> referencesOpt = std::visit(overloaded {
[&](const AlreadyRegistered & skippedFinalPath) -> std::optional<StorePathSet> { [&](const AlreadyRegistered & skippedFinalPath) -> std::optional<StorePathSet> {
finish(skippedFinalPath.path); finish(skippedFinalPath.path);
@ -2235,7 +2254,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
[&](const PerhapsNeedToRegister & r) -> std::optional<StorePathSet> { [&](const PerhapsNeedToRegister & r) -> std::optional<StorePathSet> {
return r.refs; return r.refs;
}, },
}, outputReferencesIfUnregistered.at(outputName)); }, *orifu);
if (!referencesOpt) if (!referencesOpt)
continue; continue;
@ -2268,25 +2287,29 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
for (auto & r : references) { for (auto & r : references) {
auto name = r.name(); auto name = r.name();
auto origHash = std::string { r.hashPart() }; auto origHash = std::string { r.hashPart() };
if (r == scratchPath) if (r == *scratchPath) {
res.first = true; res.first = true;
else if (outputRewrites.count(origHash) == 0) } else if (auto outputRewrite = get(outputRewrites, origHash)) {
res.second.insert(r); std::string newRef = *outputRewrite;
else {
std::string newRef = outputRewrites.at(origHash);
newRef += '-'; newRef += '-';
newRef += name; newRef += name;
res.second.insert(StorePath { newRef }); res.second.insert(StorePath { newRef });
} else {
res.second.insert(r);
} }
} }
return res; return res;
}; };
auto newInfoFromCA = [&](const DerivationOutput::CAFloating outputHash) -> ValidPathInfo { auto newInfoFromCA = [&](const DerivationOutput::CAFloating outputHash) -> ValidPathInfo {
auto & st = outputStats.at(outputName); auto st = get(outputStats, outputName);
if (!st)
throw BuildError(
"output path %1% without valid stats info",
actualPath);
if (outputHash.method == FileIngestionMethod::Flat) { if (outputHash.method == FileIngestionMethod::Flat) {
/* The output path should be a regular file without execute permission. */ /* The output path should be a regular file without execute permission. */
if (!S_ISREG(st.st_mode) || (st.st_mode & S_IXUSR) != 0) if (!S_ISREG(st->st_mode) || (st->st_mode & S_IXUSR) != 0)
throw BuildError( throw BuildError(
"output path '%1%' should be a non-executable regular file " "output path '%1%' should be a non-executable regular file "
"since recursive hashing is not enabled (outputHashMode=flat)", "since recursive hashing is not enabled (outputHashMode=flat)",
@ -2294,7 +2317,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
} }
rewriteOutput(); rewriteOutput();
/* FIXME optimize and deduplicate with addToStore */ /* FIXME optimize and deduplicate with addToStore */
std::string oldHashPart { scratchPath.hashPart() }; std::string oldHashPart { scratchPath->hashPart() };
HashModuloSink caSink { outputHash.hashType, oldHashPart }; HashModuloSink caSink { outputHash.hashType, oldHashPart };
switch (outputHash.method) { switch (outputHash.method) {
case FileIngestionMethod::Recursive: case FileIngestionMethod::Recursive:
@ -2313,7 +2336,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
outputPathName(drv->name, outputName), outputPathName(drv->name, outputName),
refs.second, refs.second,
refs.first); refs.first);
if (scratchPath != finalPath) { if (*scratchPath != finalPath) {
// Also rewrite the output path // Also rewrite the output path
auto source = sinkToSource([&](Sink & nextSink) { auto source = sinkToSource([&](Sink & nextSink) {
StringSink sink; StringSink sink;
@ -2354,9 +2377,9 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
auto requiredFinalPath = output.path; auto requiredFinalPath = output.path;
/* Preemptively add rewrite rule for final hash, as that is /* Preemptively add rewrite rule for final hash, as that is
what the NAR hash will use rather than normalized-self references */ what the NAR hash will use rather than normalized-self references */
if (scratchPath != requiredFinalPath) if (*scratchPath != requiredFinalPath)
outputRewrites.insert_or_assign( outputRewrites.insert_or_assign(
std::string { scratchPath.hashPart() }, std::string { scratchPath->hashPart() },
std::string { requiredFinalPath.hashPart() }); std::string { requiredFinalPath.hashPart() });
rewriteOutput(); rewriteOutput();
auto narHashAndSize = hashPath(htSHA256, actualPath); auto narHashAndSize = hashPath(htSHA256, actualPath);
@ -2409,7 +2432,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
}); });
}, },
}, output.raw()); }, output->raw());
/* FIXME: set proper permissions in restorePath() so /* FIXME: set proper permissions in restorePath() so
we don't have to do another traversal. */ we don't have to do another traversal. */
@ -2425,7 +2448,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
derivations. */ derivations. */
PathLocks dynamicOutputLock; PathLocks dynamicOutputLock;
dynamicOutputLock.setDeletion(true); dynamicOutputLock.setDeletion(true);
auto optFixedPath = output.path(worker.store, drv->name, outputName); auto optFixedPath = output->path(worker.store, drv->name, outputName);
if (!optFixedPath || if (!optFixedPath ||
worker.store.printStorePath(*optFixedPath) != finalDestPath) worker.store.printStorePath(*optFixedPath) != finalDestPath)
{ {
@ -2491,11 +2514,10 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
/* For debugging, print out the referenced and unreferenced paths. */ /* For debugging, print out the referenced and unreferenced paths. */
for (auto & i : inputPaths) { for (auto & i : inputPaths) {
auto j = references.find(i); if (references.count(i))
if (j == references.end())
debug("unreferenced input: '%1%'", worker.store.printStorePath(i));
else
debug("referenced input: '%1%'", worker.store.printStorePath(i)); debug("referenced input: '%1%'", worker.store.printStorePath(i));
else
debug("unreferenced input: '%1%'", worker.store.printStorePath(i));
} }
if (curRound == nrRounds) { if (curRound == nrRounds) {
@ -2612,9 +2634,11 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
DrvOutputs builtOutputs; DrvOutputs builtOutputs;
for (auto & [outputName, newInfo] : infos) { for (auto & [outputName, newInfo] : infos) {
auto oldinfo = get(initialOutputs, outputName);
assert(oldinfo);
auto thisRealisation = Realisation { auto thisRealisation = Realisation {
.id = DrvOutput { .id = DrvOutput {
initialOutputs.at(outputName).outputHash, oldinfo->outputHash,
outputName outputName
}, },
.outPath = newInfo.path .outPath = newInfo.path
@ -2710,9 +2734,10 @@ void LocalDerivationGoal::checkOutputs(const std::map<std::string, ValidPathInfo
for (auto & i : *value) { for (auto & i : *value) {
if (worker.store.isStorePath(i)) if (worker.store.isStorePath(i))
spec.insert(worker.store.parseStorePath(i)); spec.insert(worker.store.parseStorePath(i));
else if (outputs.count(i)) else if (auto output = get(outputs, i))
spec.insert(outputs.at(i).path); spec.insert(output->path);
else throw BuildError("derivation contains an illegal reference specifier '%s'", i); else
throw BuildError("derivation contains an illegal reference specifier '%s'", i);
} }
auto used = recursive auto used = recursive
@ -2751,24 +2776,18 @@ void LocalDerivationGoal::checkOutputs(const std::map<std::string, ValidPathInfo
}; };
if (auto structuredAttrs = parsedDrv->getStructuredAttrs()) { if (auto structuredAttrs = parsedDrv->getStructuredAttrs()) {
auto outputChecks = structuredAttrs->find("outputChecks"); if (auto outputChecks = get(*structuredAttrs, "outputChecks")) {
if (outputChecks != structuredAttrs->end()) { if (auto output = get(*outputChecks, outputName)) {
auto output = outputChecks->find(outputName);
if (output != outputChecks->end()) {
Checks checks; Checks checks;
auto maxSize = output->find("maxSize"); if (auto maxSize = get(*output, "maxSize"))
if (maxSize != output->end())
checks.maxSize = maxSize->get<uint64_t>(); checks.maxSize = maxSize->get<uint64_t>();
auto maxClosureSize = output->find("maxClosureSize"); if (auto maxClosureSize = get(*output, "maxClosureSize"))
if (maxClosureSize != output->end())
checks.maxClosureSize = maxClosureSize->get<uint64_t>(); checks.maxClosureSize = maxClosureSize->get<uint64_t>();
auto get = [&](const std::string & name) -> std::optional<Strings> { auto get_ = [&](const std::string & name) -> std::optional<Strings> {
auto i = output->find(name); if (auto i = get(*output, name)) {
if (i != output->end()) {
Strings res; Strings res;
for (auto j = i->begin(); j != i->end(); ++j) { for (auto j = i->begin(); j != i->end(); ++j) {
if (!j->is_string()) if (!j->is_string())
@ -2781,10 +2800,10 @@ void LocalDerivationGoal::checkOutputs(const std::map<std::string, ValidPathInfo
return {}; return {};
}; };
checks.allowedReferences = get("allowedReferences"); checks.allowedReferences = get_("allowedReferences");
checks.allowedRequisites = get("allowedRequisites"); checks.allowedRequisites = get_("allowedRequisites");
checks.disallowedReferences = get("disallowedReferences"); checks.disallowedReferences = get_("disallowedReferences");
checks.disallowedRequisites = get("disallowedRequisites"); checks.disallowedRequisites = get_("disallowedRequisites");
applyChecks(checks); applyChecks(checks);
} }

View file

@ -380,7 +380,10 @@ void Worker::waitForInput()
std::set<int> fds2(j->fds); std::set<int> fds2(j->fds);
std::vector<unsigned char> buffer(4096); std::vector<unsigned char> buffer(4096);
for (auto & k : fds2) { for (auto & k : fds2) {
if (pollStatus.at(fdToPollStatus.at(k)).revents) { const auto fdPollStatusId = get(fdToPollStatus, k);
assert(fdPollStatusId);
assert(*fdPollStatusId < pollStatus.size());
if (pollStatus.at(*fdPollStatusId).revents) {
ssize_t rd = ::read(k, buffer.data(), buffer.size()); ssize_t rd = ::read(k, buffer.data(), buffer.size());
// FIXME: is there a cleaner way to handle pt close // FIXME: is there a cleaner way to handle pt close
// than EIO? Is this even standard? // than EIO? Is this even standard?

View file

@ -24,7 +24,7 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData)
Path storePath = getAttr("out"); Path storePath = getAttr("out");
auto mainUrl = getAttr("url"); auto mainUrl = getAttr("url");
bool unpack = get(drv.env, "unpack").value_or("") == "1"; bool unpack = getOr(drv.env, "unpack", "") == "1";
/* Note: have to use a fresh fileTransfer here because we're in /* Note: have to use a fresh fileTransfer here because we're in
a forked process. */ a forked process. */

View file

@ -661,8 +661,10 @@ DrvHash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOut
if (res.kind == DrvHash::Kind::Deferred) if (res.kind == DrvHash::Kind::Deferred)
kind = DrvHash::Kind::Deferred; kind = DrvHash::Kind::Deferred;
for (auto & outputName : inputOutputs) { for (auto & outputName : inputOutputs) {
const auto h = res.hashes.at(outputName); const auto h = get(res.hashes, outputName);
inputs2[h.to_string(Base16, false)].insert(outputName); if (!h)
throw Error("no hash for output '%s' of derivation '%s'", outputName, drv.name);
inputs2[h->to_string(Base16, false)].insert(outputName);
} }
} }
@ -836,8 +838,11 @@ static void rewriteDerivation(Store & store, BasicDerivation & drv, const String
auto hashModulo = hashDerivationModulo(store, Derivation(drv), true); auto hashModulo = hashDerivationModulo(store, Derivation(drv), true);
for (auto & [outputName, output] : drv.outputs) { for (auto & [outputName, output] : drv.outputs) {
if (std::holds_alternative<DerivationOutput::Deferred>(output.raw())) { if (std::holds_alternative<DerivationOutput::Deferred>(output.raw())) {
auto & h = hashModulo.hashes.at(outputName); auto h = get(hashModulo.hashes, outputName);
auto outPath = store.makeOutputPath(outputName, h, drv.name); if (!h)
throw Error("derivation '%s' output '%s' has no hash (derivations.cc/rewriteDerivation)",
drv.name, outputName);
auto outPath = store.makeOutputPath(outputName, *h, drv.name);
drv.env[outputName] = store.printStorePath(outPath); drv.env[outputName] = store.printStorePath(outPath);
output = DerivationOutput::InputAddressed { output = DerivationOutput::InputAddressed {
.path = std::move(outPath), .path = std::move(outPath),

View file

@ -4,6 +4,8 @@
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <optional>
namespace nix { namespace nix {
nlohmann::json DerivedPath::Opaque::toJSON(ref<Store> store) const { nlohmann::json DerivedPath::Opaque::toJSON(ref<Store> store) const {
@ -17,12 +19,12 @@ nlohmann::json DerivedPath::Built::toJSON(ref<Store> store) const {
res["drvPath"] = store->printStorePath(drvPath); res["drvPath"] = store->printStorePath(drvPath);
// Fallback for the input-addressed derivation case: We expect to always be // Fallback for the input-addressed derivation case: We expect to always be
// able to print the output paths, so lets do it // able to print the output paths, so lets do it
auto knownOutputs = store->queryPartialDerivationOutputMap(drvPath); const auto knownOutputs = store->queryPartialDerivationOutputMap(drvPath);
for (const auto& output : outputs) { for (const auto& output : outputs) {
if (knownOutputs.at(output)) auto knownOutput = get(knownOutputs, output);
res["outputs"][output] = store->printStorePath(knownOutputs.at(output).value()); res["outputs"][output] = (knownOutput && *knownOutput)
else ? store->printStorePath(**knownOutput)
res["outputs"][output] = nullptr; : nullptr;
} }
return res; return res;
} }
@ -123,10 +125,15 @@ RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const
for (auto& [outputName, outputPath] : p.outputs) { for (auto& [outputName, outputPath] : p.outputs) {
if (settings.isExperimentalFeatureEnabled( if (settings.isExperimentalFeatureEnabled(
Xp::CaDerivations)) { Xp::CaDerivations)) {
auto drvOutput = get(drvHashes, outputName);
if (!drvOutput)
throw Error(
"the derivation '%s' has unrealised output '%s' (derived-path.cc/toRealisedPaths)",
store.printStorePath(p.drvPath), outputName);
auto thisRealisation = store.queryRealisation( auto thisRealisation = store.queryRealisation(
DrvOutput{drvHashes.at(outputName), outputName}); DrvOutput{*drvOutput, outputName});
assert(thisRealisation); // Weve built it, so we must h assert(thisRealisation); // Weve built it, so we must
// ve the realisation // have the realisation
res.insert(*thisRealisation); res.insert(*thisRealisation);
} else { } else {
res.insert(outputPath); res.insert(outputPath);

View file

@ -692,10 +692,10 @@ struct curlFileTransfer : public FileTransfer
#if ENABLE_S3 #if ENABLE_S3
auto [bucketName, key, params] = parseS3Uri(request.uri); auto [bucketName, key, params] = parseS3Uri(request.uri);
std::string profile = get(params, "profile").value_or(""); std::string profile = getOr(params, "profile", "");
std::string region = get(params, "region").value_or(Aws::Region::US_EAST_1); std::string region = getOr(params, "region", Aws::Region::US_EAST_1);
std::string scheme = get(params, "scheme").value_or(""); std::string scheme = getOr(params, "scheme", "");
std::string endpoint = get(params, "endpoint").value_or(""); std::string endpoint = getOr(params, "endpoint", "");
S3Helper s3Helper(profile, region, scheme, endpoint); S3Helper s3Helper(profile, region, scheme, endpoint);

View file

@ -718,7 +718,11 @@ void LocalStore::checkDerivationOutputs(const StorePath & drvPath, const Derivat
// somewhat expensive so we do lazily // somewhat expensive so we do lazily
hashesModulo = hashDerivationModulo(*this, drv, true); hashesModulo = hashDerivationModulo(*this, drv, true);
} }
StorePath recomputed = makeOutputPath(i.first, hashesModulo->hashes.at(i.first), drvName); auto currentOutputHash = get(hashesModulo->hashes, i.first);
if (!currentOutputHash)
throw Error("derivation '%s' has unexpected output '%s' (local-store / hashesModulo) named '%s'",
printStorePath(drvPath), printStorePath(doia.path), i.first);
StorePath recomputed = makeOutputPath(i.first, *currentOutputHash, drvName);
if (doia.path != recomputed) if (doia.path != recomputed)
throw Error("derivation '%s' has incorrect output '%s', should be '%s'", throw Error("derivation '%s' has incorrect output '%s', should be '%s'",
printStorePath(drvPath), printStorePath(doia.path), printStorePath(recomputed)); printStorePath(drvPath), printStorePath(doia.path), printStorePath(recomputed));

View file

@ -278,11 +278,16 @@ std::map<DrvOutput, StorePath> drvOutputReferences(
std::set<Realisation> inputRealisations; std::set<Realisation> inputRealisations;
for (const auto & [inputDrv, outputNames] : drv.inputDrvs) { for (const auto & [inputDrv, outputNames] : drv.inputDrvs) {
auto outputHashes = const auto outputHashes =
staticOutputHashes(store, store.readDerivation(inputDrv)); staticOutputHashes(store, store.readDerivation(inputDrv));
for (const auto & outputName : outputNames) { for (const auto & outputName : outputNames) {
auto outputHash = get(outputHashes, outputName);
if (!outputHash)
throw Error(
"output '%s' of derivation '%s' isn't realised", outputName,
store.printStorePath(inputDrv));
auto thisRealisation = store.queryRealisation( auto thisRealisation = store.queryRealisation(
DrvOutput{outputHashes.at(outputName), outputName}); DrvOutput{*outputHash, outputName});
if (!thisRealisation) if (!thisRealisation)
throw Error( throw Error(
"output '%s' of derivation '%s' isn't built", outputName, "output '%s' of derivation '%s' isn't built", outputName,

View file

@ -853,15 +853,15 @@ std::vector<BuildResult> RemoteStore::buildPathsWithResults(
OutputPathMap outputs; OutputPathMap outputs;
auto drv = evalStore->readDerivation(bfd.drvPath); auto drv = evalStore->readDerivation(bfd.drvPath);
auto outputHashes = staticOutputHashes(*evalStore, drv); // FIXME: expensive const auto outputHashes = staticOutputHashes(*evalStore, drv); // FIXME: expensive
auto drvOutputs = drv.outputsAndOptPaths(*this); const auto drvOutputs = drv.outputsAndOptPaths(*this);
for (auto & output : bfd.outputs) { for (auto & output : bfd.outputs) {
if (!outputHashes.count(output)) auto outputHash = get(outputHashes, output);
if (!outputHash)
throw Error( throw Error(
"the derivation '%s' doesn't have an output named '%s'", "the derivation '%s' doesn't have an output named '%s'",
printStorePath(bfd.drvPath), output); printStorePath(bfd.drvPath), output);
auto outputId = auto outputId = DrvOutput{ *outputHash, output };
DrvOutput{outputHashes.at(output), output};
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) { if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
auto realisation = auto realisation =
queryRealisation(outputId); queryRealisation(outputId);
@ -874,13 +874,14 @@ std::vector<BuildResult> RemoteStore::buildPathsWithResults(
} else { } else {
// If ca-derivations isn't enabled, assume that // If ca-derivations isn't enabled, assume that
// the output path is statically known. // the output path is statically known.
assert(drvOutputs.count(output)); const auto drvOutput = get(drvOutputs, output);
assert(drvOutputs.at(output).second); assert(drvOutput);
assert(drvOutput->second);
res.builtOutputs.emplace( res.builtOutputs.emplace(
outputId, outputId,
Realisation { Realisation {
.id = outputId, .id = outputId,
.outPath = *drvOutputs.at(output).second .outPath = *drvOutput->second,
}); });
} }
} }

View file

@ -1314,7 +1314,7 @@ static bool isNonUriPath(const std::string & spec) {
std::shared_ptr<Store> openFromNonUri(const std::string & uri, const Store::Params & params) std::shared_ptr<Store> openFromNonUri(const std::string & uri, const Store::Params & params)
{ {
if (uri == "" || uri == "auto") { if (uri == "" || uri == "auto") {
auto stateDir = get(params, "state").value_or(settings.nixStateDir); auto stateDir = getOr(params, "state", settings.nixStateDir);
if (access(stateDir.c_str(), R_OK | W_OK) == 0) if (access(stateDir.c_str(), R_OK | W_OK) == 0)
return std::make_shared<LocalStore>(params); return std::make_shared<LocalStore>(params);
else if (pathExists(settings.nixDaemonSocketFile)) else if (pathExists(settings.nixDaemonSocketFile))

View file

@ -35,7 +35,9 @@ const std::optional<ExperimentalFeature> parseExperimentalFeature(const std::str
std::string_view showExperimentalFeature(const ExperimentalFeature feature) std::string_view showExperimentalFeature(const ExperimentalFeature feature)
{ {
return stringifiedXpFeatures.at(feature); const auto ret = get(stringifiedXpFeatures, feature);
assert(ret);
return *ret;
} }
std::set<ExperimentalFeature> parseFeatures(const std::set<std::string> & rawFeatures) std::set<ExperimentalFeature> parseFeatures(const std::set<std::string> & rawFeatures)

View file

@ -548,7 +548,7 @@ namespace nix {
TEST(get, emptyContainer) { TEST(get, emptyContainer) {
StringMap s = { }; StringMap s = { };
auto expected = std::nullopt; auto expected = nullptr;
ASSERT_EQ(get(s, "one"), expected); ASSERT_EQ(get(s, "one"), expected);
} }
@ -559,7 +559,23 @@ namespace nix {
s["two"] = "er"; s["two"] = "er";
auto expected = "yi"; auto expected = "yi";
ASSERT_EQ(get(s, "one"), expected); ASSERT_EQ(*get(s, "one"), expected);
}
TEST(getOr, emptyContainer) {
StringMap s = { };
auto expected = "yi";
ASSERT_EQ(getOr(s, "one", "yi"), expected);
}
TEST(getOr, getFromContainer) {
StringMap s;
s["one"] = "yi";
s["two"] = "er";
auto expected = "yi";
ASSERT_EQ(getOr(s, "one", "nope"), expected);
} }
/* ---------------------------------------------------------------------------- /* ----------------------------------------------------------------------------

View file

@ -1586,6 +1586,20 @@ std::string stripIndentation(std::string_view s)
} }
const nlohmann::json * get(const nlohmann::json & map, const std::string & key)
{
auto i = map.find(key);
if (i == map.end()) return nullptr;
return &*i;
}
nlohmann::json * get(nlohmann::json & map, const std::string & key)
{
auto i = map.find(key);
if (i == map.end()) return nullptr;
return &*i;
}
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////

View file

@ -543,13 +543,34 @@ std::string stripIndentation(std::string_view s);
/* Get a value for the specified key from an associate container. */ /* Get a value for the specified key from an associate container. */
template <class T> template <class T>
std::optional<typename T::mapped_type> get(const T & map, const typename T::key_type & key) const typename T::mapped_type * get(const T & map, const typename T::key_type & key)
{ {
auto i = map.find(key); auto i = map.find(key);
if (i == map.end()) return {}; if (i == map.end()) return nullptr;
return std::optional<typename T::mapped_type>(i->second); return &i->second;
} }
template <class T>
typename T::mapped_type * get(T & map, const typename T::key_type & key)
{
auto i = map.find(key);
if (i == map.end()) return nullptr;
return &i->second;
}
const nlohmann::json * get(const nlohmann::json & map, const std::string & key);
nlohmann::json * get(nlohmann::json & map, const std::string & key);
/* Get a value for the specified key from an associate container, or a default value if the key isn't present. */
template <class T>
const typename T::mapped_type & getOr(T & map,
const typename T::key_type & key,
const typename T::mapped_type & defaultValue)
{
auto i = map.find(key);
if (i == map.end()) return defaultValue;
return i->second;
}
/* Remove and return the first item from a container. */ /* Remove and return the first item from a container. */
template <class T> template <class T>

2
src/nix-build/nix-build.cc Executable file → Normal file
View file

@ -440,7 +440,7 @@ static void main_nix_build(int argc, char * * argv)
env["NIX_STORE"] = store->storeDir; env["NIX_STORE"] = store->storeDir;
env["NIX_BUILD_CORES"] = std::to_string(settings.buildCores); env["NIX_BUILD_CORES"] = std::to_string(settings.buildCores);
auto passAsFile = tokenizeString<StringSet>(get(drv.env, "passAsFile").value_or("")); auto passAsFile = tokenizeString<StringSet>(getOr(drv.env, "passAsFile", ""));
bool keepTmp = false; bool keepTmp = false;
int fileNr = 0; int fileNr = 0;

View file

@ -176,7 +176,7 @@ int main(int argc, char ** argv)
impurePaths.insert(argv[2]); impurePaths.insert(argv[2]);
else { else {
auto drv = store->derivationFromPath(store->parseStorePath(argv[1])); auto drv = store->derivationFromPath(store->parseStorePath(argv[1]));
impurePaths = tokenizeString<StringSet>(get(drv.env, "__impureHostDeps").value_or("")); impurePaths = tokenizeString<StringSet>(getOr(drv.env, "__impureHostDeps", ""));
impurePaths.insert("/usr/lib/libSystem.dylib"); impurePaths.insert("/usr/lib/libSystem.dylib");
} }