diff options
Diffstat (limited to 'src')
47 files changed, 704 insertions, 420 deletions
diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc index 8c9133c17..ff8ba2724 100644 --- a/src/build-remote/build-remote.cc +++ b/src/build-remote/build-remote.cc @@ -300,7 +300,7 @@ connected: std::set<Realisation> missingRealisations; StorePathSet missingPaths; - if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) && !derivationHasKnownOutputPaths(drv.type())) { + if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) && !drv.type().hasKnownOutputPaths()) { for (auto & outputName : wantedOutputs) { auto thisOutputHash = outputHashes.at(outputName); auto thisOutputId = DrvOutput{ thisOutputHash, outputName }; diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index b7623d4ba..784117569 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -134,7 +134,9 @@ SourceExprCommand::SourceExprCommand() addFlag({ .longName = "file", .shortName = 'f', - .description = "Interpret installables as attribute paths relative to the Nix expression stored in *file*.", + .description = + "Interpret installables as attribute paths relative to the Nix expression stored in *file*. " + "If *file* is the character -, then a Nix expression will be read from standard input.", .category = installablesCategory, .labels = {"file"}, .handler = {&file}, @@ -695,7 +697,10 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables( auto state = getEvalState(); auto vFile = state->allocValue(); - if (file) + if (file == "-") { + auto e = state->parseStdin(); + state->eval(e, *vFile); + } else if (file) state->evalFile(lookupFileArg(*state, *file), *vFile); else { auto e = state->parseExprFromString(*expr, absPath(".")); diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 7630c5ff4..bb7e77b61 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -1,6 +1,7 @@ #include "get-drvs.hh" #include "util.hh" #include "eval-inline.hh" +#include "derivations.hh" #include "store-api.hh" #include "path-with-outputs.hh" @@ -102,7 +103,7 @@ StorePath DrvInfo::queryOutPath() const } -DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall) +DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall) { if (outputs.empty()) { /* Get the ‘outputs’ list. */ @@ -112,20 +113,24 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall) /* For each output... */ for (auto elem : i->value->listItems()) { - /* Evaluate the corresponding set. */ - std::string name(state->forceStringNoCtx(*elem, *i->pos)); - Bindings::iterator out = attrs->find(state->symbols.create(name)); - if (out == attrs->end()) continue; // FIXME: throw error? - state->forceAttrs(*out->value, *i->pos); - - /* And evaluate its ‘outPath’ attribute. */ - Bindings::iterator outPath = out->value->attrs->find(state->sOutPath); - if (outPath == out->value->attrs->end()) continue; // FIXME: throw error? - PathSet context; - outputs.emplace(name, state->coerceToStorePath(*outPath->pos, *outPath->value, context)); + std::string output(state->forceStringNoCtx(*elem, *i->pos)); + + if (withPaths) { + /* Evaluate the corresponding set. */ + Bindings::iterator out = attrs->find(state->symbols.create(output)); + if (out == attrs->end()) continue; // FIXME: throw error? + state->forceAttrs(*out->value, *i->pos); + + /* And evaluate its ‘outPath’ attribute. */ + Bindings::iterator outPath = out->value->attrs->find(state->sOutPath); + if (outPath == out->value->attrs->end()) continue; // FIXME: throw error? + PathSet context; + outputs.emplace(output, state->coerceToStorePath(*outPath->pos, *outPath->value, context)); + } else + outputs.emplace(output, std::nullopt); } } else - outputs.emplace("out", queryOutPath()); + outputs.emplace("out", withPaths ? std::optional{queryOutPath()} : std::nullopt); } if (!onlyOutputsToInstall || !attrs) return outputs; diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh index 3ca6f1fca..7cc1abef2 100644 --- a/src/libexpr/get-drvs.hh +++ b/src/libexpr/get-drvs.hh @@ -13,7 +13,7 @@ namespace nix { struct DrvInfo { public: - typedef std::map<std::string, StorePath> Outputs; + typedef std::map<std::string, std::optional<StorePath>> Outputs; private: EvalState * state; @@ -46,8 +46,9 @@ public: StorePath requireDrvPath() const; StorePath queryOutPath() const; std::string queryOutputName() const; - /** Return the list of outputs. The "outputs to install" are determined by `meta.outputsToInstall`. */ - Outputs queryOutputs(bool onlyOutputsToInstall = false); + /** Return the unordered map of output names to (optional) output paths. + * The "outputs to install" are determined by `meta.outputsToInstall`. */ + Outputs queryOutputs(bool withPaths = true, bool onlyOutputsToInstall = false); StringSet queryMetaNames(); Value * queryMeta(const std::string & name); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 566cbdfdb..2d16a8d7a 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1163,26 +1163,24 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * auto outPath = state.store->makeFixedOutputPath(ingestionMethod, 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 = ingestionMethod, + .hash = std::move(h), }, - }); + }); } else if (contentAddressed) { HashType ht = parseHashType(outputHashAlgo); for (auto & i : outputs) { drv.env[i] = hashPlaceholder(i); - drv.outputs.insert_or_assign(i, DerivationOutput { - .output = DerivationOutputCAFloating { + drv.outputs.insert_or_assign(i, + DerivationOutput::CAFloating { .method = ingestionMethod, .hashType = ht, - }, - }); + }); } } @@ -1196,43 +1194,36 @@ 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); + auto hashModulo = hashDerivationModulo(*state.store, 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), - }, - }); + [&](const DrvHash & drvHash) { + auto & h = drvHash.hash; + switch (drvHash.kind) { + case DrvHash::Kind::Deferred: + /* Outputs already deferred, nothing to do */ + break; + case DrvHash::Kind::Regular: + for (auto & [outputName, output] : drv.outputs) { + auto outPath = state.store->makeOutputPath(outputName, h, drvName); + drv.env[outputName] = state.store->printStorePath(outPath); + output = DerivationOutput::InputAddressed { + .path = std::move(outPath), + }; + } + break; } }, - [&](CaOutputHashes &) { + [&](const 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); + hashModulo.raw()); } @@ -1244,12 +1235,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); } diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index 6ec5e4a74..cc74c7f58 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -1,5 +1,6 @@ #include "primops.hh" #include "eval-inline.hh" +#include "derivations.hh" #include "store-api.hh" namespace nix { diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index a1430f087..58b6e7c04 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -390,7 +390,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme ref_uri = line.substr(ref_index+5, line.length()-1); } else - ref_uri = fmt("refs/heads/%s", ref); + ref_uri = fmt("refs/(heads|tags)/%s", ref); auto file = store->toRealPath( downloadFile(store, fmt("%s/info/refs", base_url), "source", false, headers).storePath); @@ -399,9 +399,11 @@ struct SourceHutInputScheme : GitArchiveInputScheme std::string line; std::string id; while(getline(is, line)) { - auto index = line.find(ref_uri); - if (index != std::string::npos) { - id = line.substr(0, index-1); + // Append $ to avoid partial name matches + std::regex pattern(fmt("%s$", ref_uri)); + + if (std::regex_search(line, pattern)) { + id = line.substr(0, line.find('\t')); break; } } diff --git a/src/libfetchers/path.cc b/src/libfetchers/path.cc index 59e228e97..f0ef97da5 100644 --- a/src/libfetchers/path.cc +++ b/src/libfetchers/path.cc @@ -1,5 +1,6 @@ #include "fetchers.hh" #include "store-api.hh" +#include "archive.hh" namespace nix::fetchers { @@ -80,8 +81,9 @@ struct PathInputScheme : InputScheme // nothing to do } - std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) override + std::pair<StorePath, Input> fetch(ref<Store> store, const Input & _input) override { + Input input(_input); std::string absPath; auto path = getStrAttr(input.attrs, "path"); @@ -111,9 +113,15 @@ struct PathInputScheme : InputScheme if (storePath) store->addTempRoot(*storePath); - if (!storePath || storePath->name() != "source" || !store->isValidPath(*storePath)) + time_t mtime = 0; + if (!storePath || storePath->name() != "source" || !store->isValidPath(*storePath)) { // FIXME: try to substitute storePath. - storePath = store->addToStore("source", absPath); + auto src = sinkToSource([&](Sink & sink) { + mtime = dumpPathAndGetMtime(absPath, sink, defaultPathFilter); + }); + storePath = store->addToStoreFromDump(*src, "source"); + } + input.attrs.insert_or_assign("lastModified", uint64_t(mtime)); return {std::move(*storePath), input}; } diff --git a/src/libstore/binary-cache-store.hh b/src/libstore/binary-cache-store.hh index 9603a8caa..ca538b3cb 100644 --- a/src/libstore/binary-cache-store.hh +++ b/src/libstore/binary-cache-store.hh @@ -2,6 +2,7 @@ #include "crypto.hh" #include "store-api.hh" +#include "log-store.hh" #include "pool.hh" @@ -28,7 +29,9 @@ struct BinaryCacheStoreConfig : virtual StoreConfig "other than -1 which we reserve to indicate Nix defaults should be used"}; }; -class BinaryCacheStore : public virtual BinaryCacheStoreConfig, public virtual Store +class BinaryCacheStore : public virtual BinaryCacheStoreConfig, + public virtual Store, + public virtual LogStore { private: diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index afed9bf16..40c445836 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -204,7 +204,7 @@ void DerivationGoal::haveDerivation() { trace("have derivation"); - if (drv->type() == DerivationType::CAFloating) + if (!drv->type().hasKnownOutputPaths()) settings.requireExperimentalFeature(Xp::CaDerivations); retrySubstitution = false; @@ -440,9 +440,28 @@ void DerivationGoal::inputsRealised() if (useDerivation) { auto & fullDrv = *dynamic_cast<Derivation *>(drv.get()); - if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) && - ((!fullDrv.inputDrvs.empty() && derivationIsCA(fullDrv.type())) - || fullDrv.type() == DerivationType::DeferredInputAddressed)) { + auto drvType = fullDrv.type(); + bool resolveDrv = std::visit(overloaded { + [&](const DerivationType::InputAddressed & ia) { + /* must resolve if deferred. */ + return ia.deferred; + }, + [&](const DerivationType::ContentAddressed & ca) { + return !fullDrv.inputDrvs.empty() && ( + ca.fixed + /* Can optionally resolve if fixed, which is good + for avoiding unnecessary rebuilds. */ + ? settings.isExperimentalFeatureEnabled(Xp::CaDerivations) + /* Must resolve if floating and there are any inputs + drvs. */ + : true); + }, + }, drvType.raw()); + + if (resolveDrv) + { + settings.requireExperimentalFeature(Xp::CaDerivations); + /* We are be able to resolve this derivation based on the now-known results of dependencies. If so, we become a stub goal aliasing that resolved derivation goal */ @@ -501,7 +520,7 @@ void DerivationGoal::inputsRealised() /* Don't repeat fixed-output derivations since they're already verified by their output hash.*/ - nrRounds = derivationIsFixed(derivationType) ? 1 : settings.buildRepeat + 1; + nrRounds = derivationType.isFixed() ? 1 : settings.buildRepeat + 1; /* Okay, try to build. Note that here we don't wait for a build slot to become available, since we don't need one if there is a @@ -908,7 +927,7 @@ void DerivationGoal::buildDone() st = dynamic_cast<NotDeterministic*>(&e) ? BuildResult::NotDeterministic : statusOk(status) ? BuildResult::OutputRejected : - derivationIsImpure(derivationType) || diskFull ? BuildResult::TransientFailure : + derivationType.isImpure() || diskFull ? BuildResult::TransientFailure : BuildResult::PermanentFailure; } @@ -1221,7 +1240,7 @@ void DerivationGoal::flushLine() std::map<std::string, std::optional<StorePath>> DerivationGoal::queryPartialDerivationOutputMap() { - if (!useDerivation || drv->type() != DerivationType::CAFloating) { + if (!useDerivation || drv->type().hasKnownOutputPaths()) { std::map<std::string, std::optional<StorePath>> res; for (auto & [name, output] : drv->outputs) res.insert_or_assign(name, output.path(worker.store, drv->name, name)); @@ -1233,7 +1252,7 @@ std::map<std::string, std::optional<StorePath>> DerivationGoal::queryPartialDeri OutputPathMap DerivationGoal::queryDerivationOutputMap() { - if (!useDerivation || drv->type() != DerivationType::CAFloating) { + if (!useDerivation || drv->type().hasKnownOutputPaths()) { OutputPathMap res; for (auto & [name, output] : drv->outputsAndOptPaths(worker.store)) res.insert_or_assign(name, *output.second); diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index a372728f5..75e7e6ca3 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -395,7 +395,7 @@ void LocalDerivationGoal::startBuilder() else if (settings.sandboxMode == smDisabled) useChroot = false; else if (settings.sandboxMode == smRelaxed) - useChroot = !(derivationIsImpure(derivationType)) && !noChroot; + useChroot = !(derivationType.isImpure()) && !noChroot; } auto & localStore = getLocalStore(); @@ -608,7 +608,7 @@ void LocalDerivationGoal::startBuilder() "nogroup:x:65534:\n", sandboxGid())); /* Create /etc/hosts with localhost entry. */ - if (!(derivationIsImpure(derivationType))) + if (!(derivationType.isImpure())) writeFile(chrootRootDir + "/etc/hosts", "127.0.0.1 localhost\n::1 localhost\n"); /* Make the closure of the inputs available in the chroot, @@ -796,7 +796,7 @@ void LocalDerivationGoal::startBuilder() us. */ - if (!(derivationIsImpure(derivationType))) + if (!(derivationType.isImpure())) privateNetwork = true; userNamespaceSync.create(); @@ -1049,7 +1049,7 @@ void LocalDerivationGoal::initEnv() derivation, tell the builder, so that for instance `fetchurl' can skip checking the output. On older Nixes, this environment variable won't be set, so `fetchurl' will do the check. */ - if (derivationIsFixed(derivationType)) env["NIX_OUTPUT_CHECKED"] = "1"; + if (derivationType.isFixed()) env["NIX_OUTPUT_CHECKED"] = "1"; /* *Only* if this is a fixed-output derivation, propagate the values of the environment variables specified in the @@ -1060,7 +1060,7 @@ void LocalDerivationGoal::initEnv() to the builder is generally impure, but the output of fixed-output derivations is by definition pure (since we already know the cryptographic hash of the output). */ - if (derivationIsImpure(derivationType)) { + if (derivationType.isImpure()) { for (auto & i : parsedDrv->getStringsAttr("impureEnvVars").value_or(Strings())) env[i] = getEnv(i).value_or(""); } @@ -1340,6 +1340,12 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo next->queryMissing(allowed, willBuild, willSubstitute, unknown, downloadSize, narSize); } + + virtual std::optional<std::string> getBuildLog(const StorePath & path) override + { return std::nullopt; } + + virtual void addBuildLog(const StorePath & path, std::string_view log) override + { unsupported("addBuildLog"); } }; @@ -1668,7 +1674,7 @@ void LocalDerivationGoal::runChild() /* Fixed-output derivations typically need to access the network, so give them access to /etc/resolv.conf and so on. */ - if (derivationIsImpure(derivationType)) { + if (derivationType.isImpure()) { // Only use nss functions to resolve hosts and // services. Don’t use it for anything else that may // be configured for this system. This limits the @@ -1912,7 +1918,7 @@ void LocalDerivationGoal::runChild() sandboxProfile += "(import \"sandbox-defaults.sb\")\n"; - if (derivationIsImpure(derivationType)) + if (derivationType.isImpure()) sandboxProfile += "(import \"sandbox-network.sb\")\n"; /* Add the output paths we'll use at build-time to the chroot */ @@ -2273,7 +2279,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs() return res; }; - auto newInfoFromCA = [&](const DerivationOutputCAFloating outputHash) -> ValidPathInfo { + auto newInfoFromCA = [&](const DerivationOutput::CAFloating outputHash) -> ValidPathInfo { auto & st = outputStats.at(outputName); if (outputHash.method == FileIngestionMethod::Flat) { /* The output path should be a regular file without execute permission. */ @@ -2340,7 +2346,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs() ValidPathInfo newInfo = std::visit(overloaded { - [&](const DerivationOutputInputAddressed & output) { + [&](const DerivationOutput::InputAddressed & output) { /* input-addressed case */ auto requiredFinalPath = output.path; /* Preemptively add rewrite rule for final hash, as that is @@ -2360,8 +2366,8 @@ DrvOutputs LocalDerivationGoal::registerOutputs() return newInfo0; }, - [&](const DerivationOutputCAFixed & dof) { - auto newInfo0 = newInfoFromCA(DerivationOutputCAFloating { + [&](const DerivationOutput::CAFixed & dof) { + auto newInfo0 = newInfoFromCA(DerivationOutput::CAFloating { .method = dof.hash.method, .hashType = dof.hash.hash.type, }); @@ -2383,17 +2389,17 @@ DrvOutputs LocalDerivationGoal::registerOutputs() return newInfo0; }, - [&](DerivationOutputCAFloating & dof) { + [&](const DerivationOutput::CAFloating & dof) { return newInfoFromCA(dof); }, - [&](DerivationOutputDeferred) -> ValidPathInfo { + [&](const DerivationOutput::Deferred &) -> ValidPathInfo { // No derivation should reach that point without having been // rewritten first assert(false); }, - }, output.output); + }, output.raw()); /* FIXME: set proper permissions in restorePath() so we don't have to do another traversal. */ diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index e6760664c..de69b50ee 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -3,7 +3,9 @@ #include "worker-protocol.hh" #include "build-result.hh" #include "store-api.hh" +#include "store-cast.hh" #include "gc-store.hh" +#include "log-store.hh" #include "path-with-outputs.hh" #include "finally.hh" #include "archive.hh" @@ -558,6 +560,8 @@ static void performOp(TunnelLogger * logger, ref<Store> store, BuildMode buildMode = (BuildMode) readInt(from); logger->startWork(); + auto drvType = drv.type(); + /* Content-addressed derivations are trustless because their output paths are verified by their content alone, so any derivation is free to try to produce such a path. @@ -590,12 +594,12 @@ static void performOp(TunnelLogger * logger, ref<Store> store, derivations, we throw out the precomputed output paths and just store the hashes, so there aren't two competing sources of truth an attacker could exploit. */ - if (drv.type() == DerivationType::InputAddressed && !trusted) + if (!(drvType.isCA() || trusted)) throw Error("you are not privileged to build input-addressed derivations"); /* Make sure that the non-input-addressed derivations that got this far are in fact content-addressed if we don't trust them. */ - assert(derivationIsCA(drv.type()) || trusted); + assert(drvType.isCA() || trusted); /* Recompute the derivation path when we cannot trust the original. */ if (!trusted) { @@ -604,7 +608,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store, original not-necessarily-resolved derivation to verify the drv derivation as adequate claim to the input-addressed output paths. */ - assert(derivationIsCA(drv.type())); + assert(drvType.isCA()); Derivation drv2; static_cast<BasicDerivation &>(drv2) = drv; @@ -645,7 +649,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store, Path path = absPath(readString(from)); logger->startWork(); - auto & gcStore = requireGcStore(*store); + auto & gcStore = require<GcStore>(*store); gcStore.addIndirectRoot(path); logger->stopWork(); @@ -663,7 +667,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store, case wopFindRoots: { logger->startWork(); - auto & gcStore = requireGcStore(*store); + auto & gcStore = require<GcStore>(*store); Roots roots = gcStore.findRoots(!trusted); logger->stopWork(); @@ -695,7 +699,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store, logger->startWork(); if (options.ignoreLiveness) throw Error("you are not allowed to ignore liveness"); - auto & gcStore = requireGcStore(*store); + auto & gcStore = require<GcStore>(*store); gcStore.collectGarbage(options, results); logger->stopWork(); @@ -953,11 +957,12 @@ static void performOp(TunnelLogger * logger, ref<Store> store, logger->startWork(); if (!trusted) throw Error("you are not privileged to add logs"); + auto & logStore = require<LogStore>(*store); { FramedSource source(from); StringSink sink; source.drainInto(sink); - store->addBuildLog(path, sink.s); + logStore.addBuildLog(path, sink.s); } logger->stopWork(); to << 1; diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index a49be0057..7fed80387 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -11,72 +11,71 @@ namespace nix { std::optional<StorePath> DerivationOutput::path(const Store & store, std::string_view drvName, std::string_view outputName) const { return std::visit(overloaded { - [](const DerivationOutputInputAddressed & doi) -> std::optional<StorePath> { + [](const DerivationOutput::InputAddressed & doi) -> std::optional<StorePath> { return { doi.path }; }, - [&](const DerivationOutputCAFixed & dof) -> std::optional<StorePath> { + [&](const DerivationOutput::CAFixed & dof) -> std::optional<StorePath> { return { dof.path(store, drvName, outputName) }; }, - [](const DerivationOutputCAFloating & dof) -> std::optional<StorePath> { + [](const DerivationOutput::CAFloating & dof) -> std::optional<StorePath> { return std::nullopt; }, - [](const DerivationOutputDeferred &) -> std::optional<StorePath> { + [](const DerivationOutput::Deferred &) -> std::optional<StorePath> { return std::nullopt; }, - }, output); + }, raw()); } -StorePath DerivationOutputCAFixed::path(const Store & store, std::string_view drvName, std::string_view outputName) const { +StorePath DerivationOutput::CAFixed::path(const Store & store, std::string_view drvName, std::string_view outputName) const { return store.makeFixedOutputPath( hash.method, hash.hash, outputPathName(drvName, outputName)); } -bool derivationIsCA(DerivationType dt) { - switch (dt) { - case DerivationType::InputAddressed: return false; - case DerivationType::CAFixed: return true; - case DerivationType::CAFloating: return true; - case DerivationType::DeferredInputAddressed: return false; - }; - // Since enums can have non-variant values, but making a `default:` would - // disable exhaustiveness warnings. - assert(false); +bool DerivationType::isCA() const { + /* Normally we do the full `std::visit` to make sure we have + exhaustively handled all variants, but so long as there is a + variant called `ContentAddressed`, it must be the only one for + which `isCA` is true for this to make sense!. */ + return std::holds_alternative<ContentAddressed>(raw()); } -bool derivationIsFixed(DerivationType dt) { - switch (dt) { - case DerivationType::InputAddressed: return false; - case DerivationType::CAFixed: return true; - case DerivationType::CAFloating: return false; - case DerivationType::DeferredInputAddressed: return false; - }; - assert(false); +bool DerivationType::isFixed() const { + return std::visit(overloaded { + [](const InputAddressed & ia) { + return false; + }, + [](const ContentAddressed & ca) { + return ca.fixed; + }, + }, raw()); } -bool derivationHasKnownOutputPaths(DerivationType dt) { - switch (dt) { - case DerivationType::InputAddressed: return true; - case DerivationType::CAFixed: return true; - case DerivationType::CAFloating: return false; - case DerivationType::DeferredInputAddressed: return false; - }; - assert(false); +bool DerivationType::hasKnownOutputPaths() const { + return std::visit(overloaded { + [](const InputAddressed & ia) { + return !ia.deferred; + }, + [](const ContentAddressed & ca) { + return ca.fixed; + }, + }, raw()); } -bool derivationIsImpure(DerivationType dt) { - switch (dt) { - case DerivationType::InputAddressed: return false; - case DerivationType::CAFixed: return true; - case DerivationType::CAFloating: return false; - case DerivationType::DeferredInputAddressed: return false; - }; - assert(false); +bool DerivationType::isImpure() const { + return std::visit(overloaded { + [](const InputAddressed & ia) { + return false; + }, + [](const ContentAddressed & ca) { + return !ca.pure; + }, + }, raw()); } @@ -179,35 +178,27 @@ static DerivationOutput parseDerivationOutput(const Store & store, const auto hashType = parseHashType(hashAlgo); if (hash != "") { validatePath(pathS); - return DerivationOutput { - .output = DerivationOutputCAFixed { - .hash = FixedOutputHash { - .method = std::move(method), - .hash = Hash::parseNonSRIUnprefixed(hash, hashType), - }, + return DerivationOutput::CAFixed { + .hash = FixedOutputHash { + .method = std::move(method), + .hash = Hash::parseNonSRIUnprefixed(hash, hashType), }, }; } else { settings.requireExperimentalFeature(Xp::CaDerivations); assert(pathS == ""); - return DerivationOutput { - .output = DerivationOutputCAFloating { - .method = std::move(method), - .hashType = std::move(hashType), - }, + return DerivationOutput::CAFloating { + .method = std::move(method), + .hashType = std::move(hashType), }; } } else { if (pathS == "") { - return DerivationOutput { - .output = DerivationOutputDeferred { } - }; + return DerivationOutput::Deferred { }; } validatePath(pathS); - return DerivationOutput { - .output = DerivationOutputInputAddressed { - .path = store.parseStorePath(pathS), - } + return DerivationOutput::InputAddressed { + .path = store.parseStorePath(pathS), }; } } @@ -335,27 +326,27 @@ std::string Derivation::unparse(const Store & store, bool maskOutputs, if (first) first = false; else s += ','; s += '('; printUnquotedString(s, i.first); std::visit(overloaded { - [&](const DerivationOutputInputAddressed & doi) { + [&](const DerivationOutput::InputAddressed & doi) { s += ','; printUnquotedString(s, maskOutputs ? "" : store.printStorePath(doi.path)); s += ','; printUnquotedString(s, ""); s += ','; printUnquotedString(s, ""); }, - [&](const DerivationOutputCAFixed & dof) { + [&](const DerivationOutput::CAFixed & dof) { s += ','; printUnquotedString(s, maskOutputs ? "" : store.printStorePath(dof.path(store, name, i.first))); s += ','; printUnquotedString(s, dof.hash.printMethodAlgo()); s += ','; printUnquotedString(s, dof.hash.hash.to_string(Base16, false)); }, - [&](const DerivationOutputCAFloating & dof) { + [&](const DerivationOutput::CAFloating & dof) { s += ','; printUnquotedString(s, ""); s += ','; printUnquotedString(s, makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType)); s += ','; printUnquotedString(s, ""); }, - [&](const DerivationOutputDeferred &) { + [&](const DerivationOutput::Deferred &) { s += ','; printUnquotedString(s, ""); s += ','; printUnquotedString(s, ""); s += ','; printUnquotedString(s, ""); } - }, i.second.output); + }, i.second.raw()); s += ')'; } @@ -423,13 +414,13 @@ DerivationType BasicDerivation::type() const std::optional<HashType> floatingHashType; for (auto & i : outputs) { std::visit(overloaded { - [&](const DerivationOutputInputAddressed &) { + [&](const DerivationOutput::InputAddressed &) { inputAddressedOutputs.insert(i.first); }, - [&](const DerivationOutputCAFixed &) { + [&](const DerivationOutput::CAFixed &) { fixedCAOutputs.insert(i.first); }, - [&](const DerivationOutputCAFloating & dof) { + [&](const DerivationOutput::CAFloating & dof) { floatingCAOutputs.insert(i.first); if (!floatingHashType) { floatingHashType = dof.hashType; @@ -438,27 +429,37 @@ DerivationType BasicDerivation::type() const throw Error("All floating outputs must use the same hash type"); } }, - [&](const DerivationOutputDeferred &) { + [&](const DerivationOutput::Deferred &) { deferredIAOutputs.insert(i.first); }, - }, i.second.output); + }, i.second.raw()); } if (inputAddressedOutputs.empty() && fixedCAOutputs.empty() && floatingCAOutputs.empty() && deferredIAOutputs.empty()) { throw Error("Must have at least one output"); } else if (! inputAddressedOutputs.empty() && fixedCAOutputs.empty() && floatingCAOutputs.empty() && deferredIAOutputs.empty()) { - return DerivationType::InputAddressed; + return DerivationType::InputAddressed { + .deferred = false, + }; } else if (inputAddressedOutputs.empty() && ! fixedCAOutputs.empty() && floatingCAOutputs.empty() && deferredIAOutputs.empty()) { if (fixedCAOutputs.size() > 1) // FIXME: Experimental feature? throw Error("Only one fixed output is allowed for now"); if (*fixedCAOutputs.begin() != "out") throw Error("Single fixed output must be named \"out\""); - return DerivationType::CAFixed; + return DerivationType::ContentAddressed { + .pure = false, + .fixed = true, + }; } else if (inputAddressedOutputs.empty() && fixedCAOutputs.empty() && ! floatingCAOutputs.empty() && deferredIAOutputs.empty()) { - return DerivationType::CAFloating; + return DerivationType::ContentAddressed { + .pure = true, + .fixed = false, + }; } else if (inputAddressedOutputs.empty() && fixedCAOutputs.empty() && floatingCAOutputs.empty() && !deferredIAOutputs.empty()) { - return DerivationType::DeferredInputAddressed; + return DerivationType::InputAddressed { + .deferred = true, + }; } else { throw Error("Can't mix derivation output types"); } @@ -510,13 +511,13 @@ static const DrvHashModulo pathDerivationModulo(Store & store, const StorePath & */ DrvHashModulo hashDerivationModulo(Store & store, const Derivation & drv, bool maskOutputs) { - bool isDeferred = false; + auto type = drv.type(); + /* Return a fixed hash for fixed-output derivations. */ - switch (drv.type()) { - case DerivationType::CAFixed: { + if (type.isFixed()) { std::map<std::string, Hash> outputHashes; for (const auto & i : drv.outputs) { - auto & dof = std::get<DerivationOutputCAFixed>(i.second.output); + auto & dof = std::get<DerivationOutput::CAFixed>(i.second.raw()); auto hash = hashString(htSHA256, "fixed:out:" + dof.hash.printMethodAlgo() + ":" + dof.hash.hash.to_string(Base16, false) + ":" @@ -525,33 +526,37 @@ DrvHashModulo hashDerivationModulo(Store & store, const Derivation & drv, bool m } return outputHashes; } - case DerivationType::CAFloating: - isDeferred = true; - break; - case DerivationType::InputAddressed: - break; - case DerivationType::DeferredInputAddressed: - break; - } + + auto kind = std::visit(overloaded { + [](const DerivationType::InputAddressed & ia) { + /* This might be a "pesimistically" deferred output, so we don't + "taint" the kind yet. */ + return DrvHash::Kind::Regular; + }, + [](const DerivationType::ContentAddressed & ca) { + return ca.fixed + ? DrvHash::Kind::Regular + : DrvHash::Kind::Deferred; + }, + }, drv.type().raw()); /* For other derivations, replace the inputs paths with recursive calls to this function. */ std::map<std::string, StringSet> inputs2; - for (auto & i : drv.inputDrvs) { - const auto & res = pathDerivationModulo(store, i.first); + for (auto & [drvPath, inputOutputs0] : drv.inputDrvs) { + // Avoid lambda capture restriction with standard / Clang + auto & inputOutputs = inputOutputs0; + const auto & res = pathDerivationModulo(store, drvPath); std::visit(overloaded { // Regular non-CA derivation, replace derivation - [&](const Hash & drvHash) { - inputs2.insert_or_assign(drvHash.to_string(Base16, false), i.second); - }, - [&](const DeferredHash & deferredHash) { - isDeferred = true; - inputs2.insert_or_assign(deferredHash.hash.to_string(Base16, false), i.second); + [&](const DrvHash & drvHash) { + kind |= drvHash.kind; + inputs2.insert_or_assign(drvHash.hash.to_string(Base16, false), inputOutputs); }, // CA derivation's output hashes [&](const CaOutputHashes & outputHashes) { std::set<std::string> justOut = { "out" }; - for (auto & output : i.second) { + for (auto & output : inputOutputs) { /* Put each one in with a single "out" output.. */ const auto h = outputHashes.at(output); inputs2.insert_or_assign( @@ -559,15 +564,24 @@ DrvHashModulo hashDerivationModulo(Store & store, const Derivation & drv, bool m justOut); } }, - }, res); + }, res.raw()); } auto hash = hashString(htSHA256, drv.unparse(store, maskOutputs, &inputs2)); - if (isDeferred) - return DeferredHash { hash }; - else - return hash; + return DrvHash { .hash = hash, .kind = kind }; +} + + +void operator |= (DrvHash::Kind & self, const DrvHash::Kind & other) noexcept +{ + switch (other) { + case DrvHash::Kind::Regular: + break; + case DrvHash::Kind::Deferred: + self = other; + break; + } } @@ -575,20 +589,15 @@ std::map<std::string, Hash> staticOutputHashes(Store & store, const Derivation & { std::map<std::string, Hash> res; std::visit(overloaded { - [&](const Hash & drvHash) { + [&](const DrvHash & drvHash) { for (auto & outputName : drv.outputNames()) { - res.insert({outputName, drvHash}); - } - }, - [&](const DeferredHash & deferredHash) { - for (auto & outputName : drv.outputNames()) { - res.insert({outputName, deferredHash.hash}); + res.insert({outputName, drvHash.hash}); } }, [&](const CaOutputHashes & outputHashes) { res = outputHashes; }, - }, hashDerivationModulo(store, drv, true)); + }, hashDerivationModulo(store, drv, true).raw()); return res; } @@ -669,27 +678,27 @@ void writeDerivation(Sink & out, const Store & store, const BasicDerivation & dr for (auto & i : drv.outputs) { out << i.first; std::visit(overloaded { - [&](const DerivationOutputInputAddressed & doi) { + [&](const DerivationOutput::InputAddressed & doi) { out << store.printStorePath(doi.path) << "" << ""; }, - [&](const DerivationOutputCAFixed & dof) { + [&](const DerivationOutput::CAFixed & dof) { out << store.printStorePath(dof.path(store, drv.name, i.first)) << dof.hash.printMethodAlgo() << dof.hash.hash.to_string(Base16, false); }, - [&](const DerivationOutputCAFloating & dof) { + [&](const DerivationOutput::CAFloating & dof) { out << "" << (makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType)) << ""; }, - [&](const DerivationOutputDeferred &) { + [&](const DerivationOutput::Deferred &) { out << "" << "" << ""; }, - }, i.second.output); + }, i.second.raw()); } worker_proto::write(store, out, drv.inputSrcs); out << drv.platform << drv.builder << drv.args; @@ -737,45 +746,63 @@ static void rewriteDerivation(Store & store, BasicDerivation & drv, const String auto hashModulo = hashDerivationModulo(store, Derivation(drv), true); for (auto & [outputName, output] : drv.outputs) { - if (std::holds_alternative<DerivationOutputDeferred>(output.output)) { - Hash h = std::get<Hash>(hashModulo); + if (std::holds_alternative<DerivationOutput::Deferred>(output.raw())) { + auto & h = hashModulo.requireNoFixedNonDeferred(); auto outPath = store.makeOutputPath(outputName, h, drv.name); drv.env[outputName] = store.printStorePath(outPath); - output = DerivationOutput { - .output = DerivationOutputInputAddressed { - .path = std::move(outPath), - }, + output = DerivationOutput::InputAddressed { + .path = std::move(outPath), }; } } } +const Hash & DrvHashModulo::requireNoFixedNonDeferred() const { + auto * drvHashOpt = std::get_if<DrvHash>(&raw()); + assert(drvHashOpt); + assert(drvHashOpt->kind == DrvHash::Kind::Regular); + return drvHashOpt->hash; +} + +static bool tryResolveInput( + Store & store, StorePathSet & inputSrcs, StringMap & inputRewrites, + const StorePath & inputDrv, const StringSet & inputOutputs) +{ + auto inputDrvOutputs = store.queryPartialDerivationOutputMap(inputDrv); + + auto getOutput = [&](const std::string & outputName) { + auto & actualPathOpt = inputDrvOutputs.at(outputName); + if (!actualPathOpt) + warn("output %s of input %s missing, aborting the resolving", + outputName, + store.printStorePath(inputDrv) + ); + return actualPathOpt; + }; + + for (auto & outputName : inputOutputs) { + auto actualPathOpt = getOutput(outputName); + if (!actualPathOpt) return false; + auto actualPath = *actualPathOpt; + inputRewrites.emplace( + downstreamPlaceholder(store, inputDrv, outputName), + store.printStorePath(actualPath)); + inputSrcs.insert(std::move(actualPath)); + } + + return true; +} + std::optional<BasicDerivation> Derivation::tryResolve(Store & store) { BasicDerivation resolved { *this }; // Input paths that we'll want to rewrite in the derivation StringMap inputRewrites; - for (auto & input : inputDrvs) { - auto inputDrvOutputs = store.queryPartialDerivationOutputMap(input.first); - StringSet newOutputNames; - for (auto & outputName : input.second) { - auto actualPathOpt = inputDrvOutputs.at(outputName); - if (!actualPathOpt) { - warn("output %s of input %s missing, aborting the resolving", - outputName, - store.printStorePath(input.first) - ); - return std::nullopt; - } - auto actualPath = *actualPathOpt; - inputRewrites.emplace( - downstreamPlaceholder(store, input.first, outputName), - store.printStorePath(actualPath)); - resolved.inputSrcs.insert(std::move(actualPath)); - } - } + for (auto & [inputDrv, inputOutputs] : inputDrvs) + if (!tryResolveInput(store, resolved.inputSrcs, inputRewrites, inputDrv, inputOutputs)) + return std::nullopt; rewriteDerivation(store, resolved, inputRewrites); diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh index 132de82b6..8dea90abf 100644 --- a/src/libstore/derivations.hh +++ b/src/libstore/derivations.hh @@ -4,6 +4,7 @@ #include "types.hh" #include "hash.hh" #include "content-address.hh" +#include "repair-flag.hh" #include "sync.hh" #include <map> @@ -44,19 +45,31 @@ struct DerivationOutputCAFloating */ struct DerivationOutputDeferred {}; -struct DerivationOutput +typedef std::variant< + DerivationOutputInputAddressed, + DerivationOutputCAFixed, + DerivationOutputCAFloating, + DerivationOutputDeferred +> _DerivationOutputRaw; + +struct DerivationOutput : _DerivationOutputRaw { - std::variant< - DerivationOutputInputAddressed, - DerivationOutputCAFixed, - DerivationOutputCAFloating, - DerivationOutputDeferred - > output; + using Raw = _DerivationOutputRaw; + using Raw::Raw; + + using InputAddressed = DerivationOutputInputAddressed; + using CAFixed = DerivationOutputCAFixed; + using CAFloating = DerivationOutputCAFloating; + using Deferred = DerivationOutputDeferred; /* Note, when you use this function you should make sure that you're passing the right derivation name. When in doubt, you should use the safer interface provided by BasicDerivation::outputsAndOptPaths */ std::optional<StorePath> path(const Store & store, std::string_view drvName, std::string_view outputName) const; + + inline const Raw & raw() const { + return static_cast<const Raw &>(*this); + } }; typedef std::map<std::string, DerivationOutput> DerivationOutputs; @@ -72,30 +85,50 @@ typedef std::map<std::string, std::pair<DerivationOutput, std::optional<StorePat output IDs we are interested in. */ typedef std::map<StorePath, StringSet> DerivationInputs; -enum struct DerivationType : uint8_t { - InputAddressed, - DeferredInputAddressed, - CAFixed, - CAFloating, +struct DerivationType_InputAddressed { + bool deferred; }; -/* Do the outputs of the derivation have paths calculated from their content, - or from the derivation itself? */ -bool derivationIsCA(DerivationType); - -/* Is the content of the outputs fixed a-priori via a hash? Never true for - non-CA derivations. */ -bool derivationIsFixed(DerivationType); - -/* Is the derivation impure and needs to access non-deterministic resources, or - pure and can be sandboxed? Note that whether or not we actually sandbox the - derivation is controlled separately. Never true for non-CA derivations. */ -bool derivationIsImpure(DerivationType); +struct DerivationType_ContentAddressed { + bool pure; + bool fixed; +}; -/* Does the derivation knows its own output paths? - * Only true when there's no floating-ca derivation involved in the closure. - */ -bool derivationHasKnownOutputPaths(DerivationType); +typedef std::variant< + DerivationType_InputAddressed, + DerivationType_ContentAddressed +> _DerivationTypeRaw; + +struct DerivationType : _DerivationTypeRaw { + using Raw = _DerivationTypeRaw; + using Raw::Raw; + using InputAddressed = DerivationType_InputAddressed; + using ContentAddressed = DerivationType_ContentAddressed; + + + /* Do the outputs of the derivation have paths calculated from their content, + or from the derivation itself? */ + bool isCA() const; + + /* Is the content of the outputs fixed a-priori via a hash? Never true for + non-CA derivations. */ + bool isFixed() const; + + /* Is the derivation impure and needs to access non-deterministic resources, or + pure and can be sandboxed? Note that whether or not we actually sandbox the + derivation is controlled separately. Never true for non-CA derivations. */ + bool isImpure() const; + + /* Does the derivation knows its own output paths? + Only true when there's no floating-ca derivation involved in the + closure, or if fixed output. + */ + bool hasKnownOutputPaths() const; + + inline const Raw & raw() const { + return static_cast<const Raw &>(*this); + } +}; struct BasicDerivation { @@ -150,8 +183,6 @@ struct Derivation : BasicDerivation class Store; -enum RepairFlag : bool { NoRepair = false, Repair = true }; - /* Write a derivation to the Nix store, and return its path. */ StorePath writeDerivation(Store & store, const Derivation & drv, @@ -175,13 +206,43 @@ std::string outputPathName(std::string_view drvName, std::string_view outputName // whose output hashes are always known since they are fixed up-front. typedef std::map<std::string, Hash> CaOutputHashes; -struct DeferredHash { Hash hash; }; +struct DrvHash { + Hash hash; + + enum struct Kind: bool { + // Statically determined derivations. + // This hash will be directly used to compute the output paths + Regular, + // Floating-output derivations (and their reverse dependencies). + Deferred, + }; + + Kind kind; +}; + +void operator |= (DrvHash::Kind & self, const DrvHash::Kind & other) noexcept; typedef std::variant< - Hash, // regular DRV normalized hash - CaOutputHashes, // Fixed-output derivation hashes - DeferredHash // Deferred hashes for floating outputs drvs and their dependencies -> DrvHashModulo; + // Regular normalized derivation hash, and whether it was deferred (because + // an ancestor derivation is a floating content addressed derivation). + DrvHash, + // Fixed-output derivation hashes + CaOutputHashes +> _DrvHashModuloRaw; + +struct DrvHashModulo : _DrvHashModuloRaw { + using Raw = _DrvHashModuloRaw; + using Raw::Raw; + + /* Get hash, throwing if it is per-output CA hashes or a + deferred Drv hash. + */ + const Hash & requireNoFixedNonDeferred() const; + + inline const Raw & raw() const { + return static_cast<const Raw &>(*this); + } +}; /* Returns hashes with the details of fixed-output subderivations expunged. diff --git a/src/libstore/derived-path.cc b/src/libstore/derived-path.cc index 194489580..319b1c790 100644 --- a/src/libstore/derived-path.cc +++ b/src/libstore/derived-path.cc @@ -1,4 +1,5 @@ #include "derived-path.hh" +#include "derivations.hh" #include "store-api.hh" #include <nlohmann/json.hpp> @@ -11,6 +12,21 @@ nlohmann::json DerivedPath::Opaque::toJSON(ref<Store> store) const { return res; } +nlohmann::json DerivedPath::Built::toJSON(ref<Store> store) const { + nlohmann::json res; + res["drvPath"] = store->printStorePath(drvPath); + // Fallback for the input-addressed derivation case: We expect to always be + // able to print the output paths, so let’s do it + auto knownOutputs = store->queryPartialDerivationOutputMap(drvPath); + for (const auto& output : outputs) { + if (knownOutputs.at(output)) + res["outputs"][output] = store->printStorePath(knownOutputs.at(output).value()); + else + res["outputs"][output] = nullptr; + } + return res; +} + nlohmann::json BuiltPath::Built::toJSON(ref<Store> store) const { nlohmann::json res; res["drvPath"] = store->printStorePath(drvPath); @@ -35,16 +51,22 @@ StorePathSet BuiltPath::outPaths() const ); } -nlohmann::json derivedPathsWithHintsToJSON(const BuiltPaths & buildables, ref<Store> store) { +template<typename T> +nlohmann::json stuffToJSON(const std::vector<T> & ts, ref<Store> store) { auto res = nlohmann::json::array(); - for (const BuiltPath & buildable : buildables) { - std::visit([&res, store](const auto & buildable) { - res.push_back(buildable.toJSON(store)); - }, buildable.raw()); + for (const T & t : ts) { + std::visit([&res, store](const auto & t) { + res.push_back(t.toJSON(store)); + }, t.raw()); } return res; } +nlohmann::json derivedPathsWithHintsToJSON(const BuiltPaths & buildables, ref<Store> store) +{ return stuffToJSON<BuiltPath>(buildables, store); } +nlohmann::json derivedPathsToJSON(const DerivedPaths & paths, ref<Store> store) +{ return stuffToJSON<DerivedPath>(paths, store); } + std::string DerivedPath::Opaque::to_string(const Store & store) const { return store.printStorePath(path); diff --git a/src/libstore/derived-path.hh b/src/libstore/derived-path.hh index 9d6ace069..8ca0882a4 100644 --- a/src/libstore/derived-path.hh +++ b/src/libstore/derived-path.hh @@ -45,6 +45,7 @@ struct DerivedPathBuilt { std::string to_string(const Store & store) const; static DerivedPathBuilt parse(const Store & store, std::string_view); + nlohmann::json toJSON(ref<Store> store) const; }; using _DerivedPathRaw = std::variant< @@ -119,5 +120,6 @@ typedef std::vector<DerivedPath> DerivedPaths; typedef std::vector<BuiltPath> BuiltPaths; nlohmann::json derivedPathsWithHintsToJSON(const BuiltPaths & buildables, ref<Store> store); +nlohmann::json derivedPathsToJSON(const DerivedPaths & , ref<Store> store); } diff --git a/src/libstore/gc-store.cc b/src/libstore/gc-store.cc deleted file mode 100644 index 3dbdec53b..000000000 --- a/src/libstore/gc-store.cc +++ /dev/null @@ -1,13 +0,0 @@ -#include "gc-store.hh" - -namespace nix { - -GcStore & requireGcStore(Store & store) -{ - auto * gcStore = dynamic_cast<GcStore *>(&store); - if (!gcStore) - throw UsageError("Garbage collection not supported by this store"); - return *gcStore; -} - -} diff --git a/src/libstore/gc-store.hh b/src/libstore/gc-store.hh index 829f70dc4..b3cbbad74 100644 --- a/src/libstore/gc-store.hh +++ b/src/libstore/gc-store.hh @@ -61,6 +61,8 @@ struct GCResults struct GcStore : public virtual Store { + inline static std::string operationName = "Garbage collection"; + /* Add an indirect root, which is merely a symlink to `path' from /nix/var/nix/gcroots/auto/<hash of `path'>. `path' is supposed to be a symlink to a store path. The garbage collector will @@ -79,6 +81,4 @@ struct GcStore : public virtual Store virtual void collectGarbage(const GCOptions & options, GCResults & results) = 0; }; -GcStore & requireGcStore(Store & store); - } diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 024da66c1..f65fb1b2e 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -678,7 +678,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) alive.insert(start); try { StorePathSet closure; - computeFSClosure(*path, closure); + computeFSClosure(*path, closure, + /* flipDirection */ false, gcKeepOutputs, gcKeepDerivations); for (auto & p : closure) alive.insert(p); } catch (InvalidPath &) { } @@ -841,7 +842,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) if (unlink(path.c_str()) == -1) throw SysError("deleting '%1%'", path); - results.bytesFreed += st.st_size; + /* Do not accound for deleted file here. Rely on deletePath() + accounting. */ } struct stat st; diff --git a/src/libstore/local-fs-store.hh b/src/libstore/local-fs-store.hh index fbd49dc2c..e6fb3201a 100644 --- a/src/libstore/local-fs-store.hh +++ b/src/libstore/local-fs-store.hh @@ -2,6 +2,7 @@ #include "store-api.hh" #include "gc-store.hh" +#include "log-store.hh" namespace nix { @@ -24,7 +25,10 @@ struct LocalFSStoreConfig : virtual StoreConfig "physical path to the Nix store"}; }; -class LocalFSStore : public virtual LocalFSStoreConfig, public virtual Store, virtual GcStore +class LocalFSStore : public virtual LocalFSStoreConfig, + public virtual Store, + public virtual GcStore, + public virtual LogStore { public: diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 1ee71b1c0..46a547db1 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -698,11 +698,11 @@ void LocalStore::checkDerivationOutputs(const StorePath & drvPath, const Derivat std::optional<Hash> h; for (auto & i : drv.outputs) { std::visit(overloaded { - [&](const DerivationOutputInputAddressed & doia) { + [&](const DerivationOutput::InputAddressed & doia) { if (!h) { // somewhat expensive so we do lazily - auto temp = hashDerivationModulo(*this, drv, true); - h = std::get<Hash>(temp); + auto h0 = hashDerivationModulo(*this, drv, true); + h = h0.requireNoFixedNonDeferred(); } StorePath recomputed = makeOutputPath(i.first, *h, drvName); if (doia.path != recomputed) @@ -710,16 +710,17 @@ void LocalStore::checkDerivationOutputs(const StorePath & drvPath, const Derivat printStorePath(drvPath), printStorePath(doia.path), printStorePath(recomputed)); envHasRightPath(doia.path, i.first); }, - [&](const DerivationOutputCAFixed & dof) { + [&](const DerivationOutput::CAFixed & dof) { StorePath path = makeFixedOutputPath(dof.hash.method, dof.hash.hash, drvName); envHasRightPath(path, i.first); }, - [&](const DerivationOutputCAFloating &) { + [&](const DerivationOutput::CAFloating &) { /* Nothing to check */ }, - [&](const DerivationOutputDeferred &) { + [&](const DerivationOutput::Deferred &) { + /* Nothing to check */ }, - }, i.second.output); + }, i.second.raw()); } } diff --git a/src/libstore/log-store.hh b/src/libstore/log-store.hh new file mode 100644 index 000000000..ff1b92e17 --- /dev/null +++ b/src/libstore/log-store.hh @@ -0,0 +1,21 @@ +#pragma once + +#include "store-api.hh" + + +namespace nix { + +struct LogStore : public virtual Store +{ + inline static std::string operationName = "Build log storage and retrieval"; + + /* Return the build log of the specified store path, if available, + or null otherwise. */ + virtual std::optional<std::string> getBuildLog(const StorePath & path) = 0; + + virtual void addBuildLog(const StorePath & path, std::string_view log) = 0; + + static LogStore & require(Store & store); +}; + +} diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index 6409874ff..1f0bae7fe 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -87,7 +87,7 @@ std::optional<ContentAddress> getDerivationCA(const BasicDerivation & drv) { auto out = drv.outputs.find("out"); if (out != drv.outputs.end()) { - if (auto v = std::get_if<DerivationOutputCAFixed>(&out->second.output)) + if (const auto * v = std::get_if<DerivationOutput::CAFixed>(&out->second.raw())) return v->hash; } return std::nullopt; diff --git a/src/libstore/parsed-derivations.cc b/src/libstore/parsed-derivations.cc index 8c65053e4..f2288a04e 100644 --- a/src/libstore/parsed-derivations.cc +++ b/src/libstore/parsed-derivations.cc @@ -93,7 +93,7 @@ StringSet ParsedDerivation::getRequiredSystemFeatures() const StringSet res; for (auto & i : getStringsAttr("requiredSystemFeatures").value_or(Strings())) res.insert(i); - if (!derivationHasKnownOutputPaths(drv.type())) + if (!drv.type().hasKnownOutputPaths()) res.insert("ca-derivations"); return res; } diff --git a/src/libstore/parsed-derivations.hh b/src/libstore/parsed-derivations.hh index effcf099d..95bec21e8 100644 --- a/src/libstore/parsed-derivations.hh +++ b/src/libstore/parsed-derivations.hh @@ -1,5 +1,6 @@ #pragma once +#include "derivations.hh" #include "store-api.hh" #include <nlohmann/json_fwd.hpp> diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index 9f6f50593..8493be6fc 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -5,6 +5,7 @@ #include "store-api.hh" #include "gc-store.hh" +#include "log-store.hh" namespace nix { @@ -30,7 +31,10 @@ struct RemoteStoreConfig : virtual StoreConfig /* FIXME: RemoteStore is a misnomer - should be something like DaemonStore. */ -class RemoteStore : public virtual RemoteStoreConfig, public virtual Store, public virtual GcStore +class RemoteStore : public virtual RemoteStoreConfig, + public virtual Store, + public virtual GcStore, + public virtual LogStore { public: diff --git a/src/libstore/repair-flag.hh b/src/libstore/repair-flag.hh new file mode 100644 index 000000000..a13cda312 --- /dev/null +++ b/src/libstore/repair-flag.hh @@ -0,0 +1,7 @@ +#pragma once + +namespace nix { + +enum RepairFlag : bool { NoRepair = false, Repair = true }; + +} diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc index bb03daef4..62daa838c 100644 --- a/src/libstore/ssh-store.cc +++ b/src/libstore/ssh-store.cc @@ -52,6 +52,10 @@ public: bool sameMachine() override { return false; } + // FIXME extend daemon protocol, move implementation to RemoteStore + std::optional<std::string> getBuildLog(const StorePath & path) override + { unsupported("getBuildLog"); } + private: struct Connection : RemoteStore::Connection diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 86fa6a211..59937be4d 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -1,6 +1,7 @@ #include "crypto.hh" #include "fs-accessor.hh" #include "globals.hh" +#include "derivations.hh" #include "store-api.hh" #include "util.hh" #include "nar-info-disk-cache.hh" diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index e99a3f2cb..0c8a4db56 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -10,8 +10,8 @@ #include "sync.hh" #include "globals.hh" #include "config.hh" -#include "derivations.hh" #include "path-info.hh" +#include "repair-flag.hh" #include <atomic> #include <limits> @@ -62,6 +62,8 @@ MakeError(BadStorePath, Error); MakeError(InvalidStoreURI, Error); +struct BasicDerivation; +struct Derivation; class FSAccessor; class NarInfoDiskCache; class Store; @@ -605,14 +607,6 @@ public: */ StorePathSet exportReferences(const StorePathSet & storePaths, const StorePathSet & inputPaths); - /* Return the build log of the specified store path, if available, - or null otherwise. */ - virtual std::optional<std::string> getBuildLog(const StorePath & path) - { return std::nullopt; } - - virtual void addBuildLog(const StorePath & path, std::string_view log) - { unsupported("addBuildLog"); } - /* Hack to allow long-running processes like hydra-queue-runner to occasionally flush their path info cache. */ void clearPathInfoCache() diff --git a/src/libstore/store-cast.hh b/src/libstore/store-cast.hh new file mode 100644 index 000000000..ff62fc359 --- /dev/null +++ b/src/libstore/store-cast.hh @@ -0,0 +1,16 @@ +#pragma once + +#include "store-api.hh" + +namespace nix { + +template<typename T> +T & require(Store & store) +{ + auto * castedStore = dynamic_cast<T *>(&store); + if (!castedStore) + throw UsageError("%s not supported by store '%s'", T::operationName, store.getUri()); + return *castedStore; +} + +} diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index eda004756..30b471af5 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -64,11 +64,12 @@ static void dumpContents(const Path & path, off_t size, } -static void dump(const Path & path, Sink & sink, PathFilter & filter) +static time_t dump(const Path & path, Sink & sink, PathFilter & filter) { checkInterrupt(); auto st = lstat(path); + time_t result = st.st_mtime; sink << "("; @@ -103,7 +104,10 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter) for (auto & i : unhacked) if (filter(path + "/" + i.first)) { sink << "entry" << "(" << "name" << i.first << "node"; - dump(path + "/" + i.second, sink, filter); + auto tmp_mtime = dump(path + "/" + i.second, sink, filter); + if (tmp_mtime > result) { + result = tmp_mtime; + } sink << ")"; } } @@ -114,13 +118,20 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter) else throw Error("file '%1%' has an unsupported type", path); sink << ")"; + + return result; } -void dumpPath(const Path & path, Sink & sink, PathFilter & filter) +time_t dumpPathAndGetMtime(const Path & path, Sink & sink, PathFilter & filter) { sink << narVersionMagic1; - dump(path, sink, filter); + return dump(path, sink, filter); +} + +void dumpPath(const Path & path, Sink & sink, PathFilter & filter) +{ + dumpPathAndGetMtime(path, sink, filter); } diff --git a/src/libutil/archive.hh b/src/libutil/archive.hh index fca351605..79ce08df0 100644 --- a/src/libutil/archive.hh +++ b/src/libutil/archive.hh @@ -48,6 +48,10 @@ namespace nix { void dumpPath(const Path & path, Sink & sink, PathFilter & filter = defaultPathFilter); +/* Same as `void dumpPath()`, but returns the last modified date of the path */ +time_t dumpPathAndGetMtime(const Path & path, Sink & sink, + PathFilter & filter = defaultPathFilter); + void dumpString(std::string_view s, Sink & sink); /* FIXME: fix this API, it sucks. */ diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 9f13d5f02..70eaf4f9c 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -406,8 +406,29 @@ static void _deletePath(int parentfd, const Path & path, uint64_t & bytesFreed) throw SysError("getting status of '%1%'", path); } - if (!S_ISDIR(st.st_mode) && st.st_nlink == 1) - bytesFreed += st.st_size; + if (!S_ISDIR(st.st_mode)) { + /* We are about to delete a file. Will it likely free space? */ + + switch (st.st_nlink) { + /* Yes: last link. */ + case 1: + bytesFreed += st.st_size; + break; + /* Maybe: yes, if 'auto-optimise-store' or manual optimisation + was performed. Instead of checking for real let's assume + it's an optimised file and space will be freed. + + In worst case we will double count on freed space for files + with exactly two hardlinks for unoptimised packages. + */ + case 2: + bytesFreed += st.st_size; + break; + /* No: 3+ links. */ + default: + break; + } + } if (S_ISDIR(st.st_mode)) { /* Make the directory accessible. */ diff --git a/src/nix-collect-garbage/nix-collect-garbage.cc b/src/nix-collect-garbage/nix-collect-garbage.cc index 4b28ea6a4..af6f1c88c 100644 --- a/src/nix-collect-garbage/nix-collect-garbage.cc +++ b/src/nix-collect-garbage/nix-collect-garbage.cc @@ -1,4 +1,5 @@ #include "store-api.hh" +#include "store-cast.hh" #include "gc-store.hh" #include "profiles.hh" #include "shared.hh" @@ -81,7 +82,7 @@ static int main_nix_collect_garbage(int argc, char * * argv) // Run the actual garbage collector. if (!dryRun) { auto store = openStore(); - auto & gcStore = requireGcStore(*store); + auto & gcStore = require<GcStore>(*store); options.action = GCOptions::gcDeleteDead; GCResults results; PrintFreed freed(true, results); diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 40c3c5d65..9a68899cd 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -128,7 +128,12 @@ static void getAllExprs(EvalState & state, if (hasSuffix(attrName, ".nix")) attrName = std::string(attrName, 0, attrName.size() - 4); if (!seen.insert(attrName).second) { - printError("warning: name collision in input Nix expressions, skipping '%1%'", path2); + std::string suggestionMessage = ""; + if (path2.find("channels") != std::string::npos && path.find("channels") != std::string::npos) { + suggestionMessage = fmt("\nsuggestion: remove '%s' from either the root channels or the user channels", attrName); + } + printError("warning: name collision in input Nix expressions, skipping '%1%'" + "%2%", path2, suggestionMessage); continue; } /* Load the expression on demand. */ @@ -918,12 +923,17 @@ static void queryJSON(Globals & globals, std::vector<DrvInfo> & elems, bool prin pkgObj.attr("pname", drvName.name); pkgObj.attr("version", drvName.version); pkgObj.attr("system", i.querySystem()); + pkgObj.attr("outputName", i.queryOutputName()); - if (printOutPath) { - DrvInfo::Outputs outputs = i.queryOutputs(); + { + DrvInfo::Outputs outputs = i.queryOutputs(printOutPath); JSONObject outputObj = pkgObj.object("outputs"); - for (auto & j : outputs) - outputObj.attr(j.first, globals.state->store->printStorePath(j.second)); + for (auto & j : outputs) { + if (j.second) + outputObj.attr(j.first, globals.state->store->printStorePath(*j.second)); + else + outputObj.attr(j.first, nullptr); + } } if (printMeta) { @@ -1052,6 +1062,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) /* Print the desired columns, or XML output. */ if (jsonOutput) { queryJSON(globals, elems, printOutPath, printMeta); + cout << '\n'; return; } @@ -1154,13 +1165,16 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) columns.push_back(drvPath ? store.printStorePath(*drvPath) : "-"); } + if (xmlOutput) + attrs["outputName"] = i.queryOutputName(); + if (printOutPath && !xmlOutput) { DrvInfo::Outputs outputs = i.queryOutputs(); std::string s; for (auto & j : outputs) { if (!s.empty()) s += ';'; if (j.first != "out") { s += j.first; s += "="; } - s += store.printStorePath(j.second); + s += store.printStorePath(*j.second); } columns.push_back(s); } @@ -1174,71 +1188,67 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) } if (xmlOutput) { - if (printOutPath || printMeta) { - XMLOpenElement item(xml, "item", attrs); - if (printOutPath) { - DrvInfo::Outputs outputs = i.queryOutputs(); - for (auto & j : outputs) { - XMLAttrs attrs2; - attrs2["name"] = j.first; - attrs2["path"] = store.printStorePath(j.second); - xml.writeEmptyElement("output", attrs2); - } - } - if (printMeta) { - StringSet metaNames = i.queryMetaNames(); - for (auto & j : metaNames) { - XMLAttrs attrs2; - attrs2["name"] = j; - Value * v = i.queryMeta(j); - if (!v) - printError( - "derivation '%s' has invalid meta attribute '%s'", - i.queryName(), j); - else { - if (v->type() == nString) { - attrs2["type"] = "string"; - attrs2["value"] = v->string.s; - xml.writeEmptyElement("meta", attrs2); - } else if (v->type() == nInt) { - attrs2["type"] = "int"; - attrs2["value"] = (format("%1%") % v->integer).str(); - xml.writeEmptyElement("meta", attrs2); - } else if (v->type() == nFloat) { - attrs2["type"] = "float"; - attrs2["value"] = (format("%1%") % v->fpoint).str(); - xml.writeEmptyElement("meta", attrs2); - } else if (v->type() == nBool) { - attrs2["type"] = "bool"; - attrs2["value"] = v->boolean ? "true" : "false"; - xml.writeEmptyElement("meta", attrs2); - } else if (v->type() == nList) { - attrs2["type"] = "strings"; - XMLOpenElement m(xml, "meta", attrs2); - for (auto elem : v->listItems()) { - if (elem->type() != nString) continue; - XMLAttrs attrs3; - attrs3["value"] = elem->string.s; - xml.writeEmptyElement("string", attrs3); - } - } else if (v->type() == nAttrs) { - attrs2["type"] = "strings"; - XMLOpenElement m(xml, "meta", attrs2); - Bindings & attrs = *v->attrs; - for (auto &i : attrs) { - Attr & a(*attrs.find(i.name)); - if(a.value->type() != nString) continue; - XMLAttrs attrs3; - attrs3["type"] = i.name; - attrs3["value"] = a.value->string.s; - xml.writeEmptyElement("string", attrs3); + XMLOpenElement item(xml, "item", attrs); + DrvInfo::Outputs outputs = i.queryOutputs(printOutPath); + for (auto & j : outputs) { + XMLAttrs attrs2; + attrs2["name"] = j.first; + if (j.second) + attrs2["path"] = store.printStorePath(*j.second); + xml.writeEmptyElement("output", attrs2); + } + if (printMeta) { + StringSet metaNames = i.queryMetaNames(); + for (auto & j : metaNames) { + XMLAttrs attrs2; + attrs2["name"] = j; + Value * v = i.queryMeta(j); + if (!v) + printError( + "derivation '%s' has invalid meta attribute '%s'", + i.queryName(), j); + else { + if (v->type() == nString) { + attrs2["type"] = "string"; + attrs2["value"] = v->string.s; + xml.writeEmptyElement("meta", attrs2); + } else if (v->type() == nInt) { + attrs2["type"] = "int"; + attrs2["value"] = (format("%1%") % v->integer).str(); + xml.writeEmptyElement("meta", attrs2); + } else if (v->type() == nFloat) { + attrs2["type"] = "float"; + attrs2["value"] = (format("%1%") % v->fpoint).str(); + xml.writeEmptyElement("meta", attrs2); + } else if (v->type() == nBool) { + attrs2["type"] = "bool"; + attrs2["value"] = v->boolean ? "true" : "false"; + xml.writeEmptyElement("meta", attrs2); + } else if (v->type() == nList) { + attrs2["type"] = "strings"; + XMLOpenElement m(xml, "meta", attrs2); + for (auto elem : v->listItems()) { + if (elem->type() != nString) continue; + XMLAttrs attrs3; + attrs3["value"] = elem->string.s; + xml.writeEmptyElement("string", attrs3); } - } + } else if (v->type() == nAttrs) { + attrs2["type"] = "strings"; + XMLOpenElement m(xml, "meta", attrs2); + Bindings & attrs = *v->attrs; + for (auto &i : attrs) { + Attr & a(*attrs.find(i.name)); + if(a.value->type() != nString) continue; + XMLAttrs attrs3; + attrs3["type"] = i.name; + attrs3["value"] = a.value->string.s; + xml.writeEmptyElement("string", attrs3); + } } } } - } else - xml.writeEmptyElement("item", attrs); + } } else table.push_back(columns); diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index af4f350ff..bcc7736ac 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -56,7 +56,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, output paths, and optionally the derivation path, as well as the meta attributes. */ std::optional<StorePath> drvPath = keepDerivations ? i.queryDrvPath() : std::nullopt; - DrvInfo::Outputs outputs = i.queryOutputs(true); + DrvInfo::Outputs outputs = i.queryOutputs(true, true); StringSet metaNames = i.queryMetaNames(); auto attrs = state.buildBindings(7 + outputs.size()); @@ -76,15 +76,15 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, for (const auto & [m, j] : enumerate(outputs)) { (vOutputs.listElems()[m] = state.allocValue())->mkString(j.first); auto outputAttrs = state.buildBindings(2); - outputAttrs.alloc(state.sOutPath).mkString(state.store->printStorePath(j.second)); + outputAttrs.alloc(state.sOutPath).mkString(state.store->printStorePath(*j.second)); attrs.alloc(j.first).mkAttrs(outputAttrs); /* This is only necessary when installing store paths, e.g., `nix-env -i /nix/store/abcd...-foo'. */ - state.store->addTempRoot(j.second); - state.store->ensurePath(j.second); + state.store->addTempRoot(*j.second); + state.store->ensurePath(*j.second); - references.insert(j.second); + references.insert(*j.second); } // Copy the meta attributes. diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 8ebaf9387..153b84137 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -3,7 +3,9 @@ #include "dotgraph.hh" #include "globals.hh" #include "build-result.hh" +#include "store-cast.hh" #include "gc-store.hh" +#include "log-store.hh" #include "local-store.hh" #include "monitor-fd.hh" #include "serve-protocol.hh" @@ -429,7 +431,7 @@ static void opQuery(Strings opFlags, Strings opArgs) store->computeFSClosure( args, referrers, true, settings.gcKeepOutputs, settings.gcKeepDerivations); - auto & gcStore = requireGcStore(*store); + auto & gcStore = require<GcStore>(*store); Roots roots = gcStore.findRoots(false); for (auto & [target, links] : roots) if (referrers.find(target) != referrers.end()) @@ -474,13 +476,15 @@ static void opReadLog(Strings opFlags, Strings opArgs) { if (!opFlags.empty()) throw UsageError("unknown flag"); + auto & logStore = require<LogStore>(*store); + RunPager pager; for (auto & i : opArgs) { - auto path = store->followLinksToStorePath(i); - auto log = store->getBuildLog(path); + auto path = logStore.followLinksToStorePath(i); + auto log = logStore.getBuildLog(path); if (!log) - throw Error("build log of derivation '%s' is not available", store->printStorePath(path)); + throw Error("build log of derivation '%s' is not available", logStore.printStorePath(path)); std::cout << *log; } } @@ -590,7 +594,7 @@ static void opGC(Strings opFlags, Strings opArgs) if (!opArgs.empty()) throw UsageError("no arguments expected"); - auto & gcStore = requireGcStore(*store); + auto & gcStore = require<GcStore>(*store); if (printRoots) { Roots roots = gcStore.findRoots(false); @@ -629,7 +633,7 @@ static void opDelete(Strings opFlags, Strings opArgs) for (auto & i : opArgs) options.pathsToDelete.insert(store->followLinksToStorePath(i)); - auto & gcStore = requireGcStore(*store); + auto & gcStore = require<GcStore>(*store); GCResults results; PrintFreed freed(true, results); diff --git a/src/nix/app.cc b/src/nix/app.cc index a4e0df06a..803d028f0 100644 --- a/src/nix/app.cc +++ b/src/nix/app.cc @@ -4,6 +4,7 @@ #include "eval-cache.hh" #include "names.hh" #include "command.hh" +#include "derivations.hh" namespace nix { diff --git a/src/nix/build.cc b/src/nix/build.cc index 680db1c60..840c7ca38 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -52,15 +52,26 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile void run(ref<Store> store) override { + if (dryRun) { + std::vector<DerivedPath> pathsToBuild; + + for (auto & i : installables) { + auto b = i->toDerivedPaths(); + pathsToBuild.insert(pathsToBuild.end(), b.begin(), b.end()); + } + printMissing(store, pathsToBuild, lvlError); + if (json) + logger->cout("%s", derivedPathsToJSON(pathsToBuild, store).dump()); + return; + } + auto buildables = Installable::build( getEvalStore(), store, - dryRun ? Realise::Derivation : Realise::Outputs, + Realise::Outputs, installables, buildMode); if (json) logger->cout("%s", derivedPathsWithHintsToJSON(buildables, store).dump()); - if (dryRun) return; - if (outLink != "") if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>()) for (const auto & [_i, buildable] : enumerate(buildables)) { diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 8af5da9d0..d2f9b5a6a 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -196,21 +196,22 @@ static StorePath getDerivationEnvironment(ref<Store> store, ref<Store> evalStore drv.inputSrcs.insert(std::move(getEnvShPath)); if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) { for (auto & output : drv.outputs) { - output.second = { - .output = DerivationOutputDeferred{}, - }; + output.second = DerivationOutput::Deferred {}, drv.env[output.first] = hashPlaceholder(output.first); } } else { for (auto & output : drv.outputs) { - output.second = { .output = DerivationOutputInputAddressed { .path = StorePath::dummy } }; + output.second = DerivationOutput::Deferred { }; drv.env[output.first] = ""; } - Hash h = std::get<0>(hashDerivationModulo(*evalStore, drv, true)); + auto h0 = hashDerivationModulo(*evalStore, drv, true); + const Hash & h = h0.requireNoFixedNonDeferred(); for (auto & output : drv.outputs) { auto outPath = store->makeOutputPath(output.first, h, drv.name); - output.second = { .output = DerivationOutputInputAddressed { .path = outPath } }; + output.second = DerivationOutput::InputAddressed { + .path = outPath, + }; drv.env[output.first] = store->printStorePath(outPath); } } diff --git a/src/nix/log.cc b/src/nix/log.cc index fd3c1d787..72d02ef11 100644 --- a/src/nix/log.cc +++ b/src/nix/log.cc @@ -2,6 +2,7 @@ #include "common-args.hh" #include "shared.hh" #include "store-api.hh" +#include "log-store.hh" #include "progress-bar.hh" using namespace nix; @@ -34,17 +35,24 @@ struct CmdLog : InstallableCommand RunPager pager; for (auto & sub : subs) { + auto * logSubP = dynamic_cast<LogStore *>(&*sub); + if (!logSubP) { + printInfo("Skipped '%s' which does not support retrieving build logs", sub->getUri()); + continue; + } + auto & logSub = *logSubP; + auto log = std::visit(overloaded { [&](const DerivedPath::Opaque & bo) { - return sub->getBuildLog(bo.path); + return logSub.getBuildLog(bo.path); }, [&](const DerivedPath::Built & bfd) { - return sub->getBuildLog(bfd.drvPath); + return logSub.getBuildLog(bfd.drvPath); }, }, b.raw()); if (!log) continue; stopProgressBar(); - printInfo("got build log for '%s' from '%s'", installable->what(), sub->getUri()); + printInfo("got build log for '%s' from '%s'", installable->what(), logSub.getUri()); std::cout << *log; return; } diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 3a51a13e6..916353d8c 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -25,6 +25,7 @@ extern "C" { #include "eval-inline.hh" #include "attr-path.hh" #include "store-api.hh" +#include "log-store.hh" #include "common-eval-args.hh" #include "get-drvs.hh" #include "derivations.hh" @@ -526,9 +527,16 @@ bool NixRepl::processLine(std::string line) bool foundLog = false; RunPager pager; for (auto & sub : subs) { - auto log = sub->getBuildLog(drvPath); + auto * logSubP = dynamic_cast<LogStore *>(&*sub); + if (!logSubP) { + printInfo("Skipped '%s' which does not support retrieving build logs", sub->getUri()); + continue; + } + auto & logSub = *logSubP; + + auto log = logSub.getBuildLog(drvPath); if (log) { - printInfo("got build log for '%s' from '%s'", drvPathRaw, sub->getUri()); + printInfo("got build log for '%s' from '%s'", drvPathRaw, logSub.getUri()); logger->writeToStdout(*log); foundLog = true; break; diff --git a/src/nix/show-derivation.cc b/src/nix/show-derivation.cc index 61a02c9b3..0d9655732 100644 --- a/src/nix/show-derivation.cc +++ b/src/nix/show-derivation.cc @@ -65,19 +65,19 @@ struct CmdShowDerivation : InstallablesCommand auto & outputName = _outputName; // work around clang bug auto outputObj { outputsObj.object(outputName) }; std::visit(overloaded { - [&](const DerivationOutputInputAddressed & doi) { + [&](const DerivationOutput::InputAddressed & doi) { outputObj.attr("path", store->printStorePath(doi.path)); }, - [&](const DerivationOutputCAFixed & dof) { + [&](const DerivationOutput::CAFixed & dof) { outputObj.attr("path", store->printStorePath(dof.path(*store, drv.name, outputName))); outputObj.attr("hashAlgo", dof.hash.printMethodAlgo()); outputObj.attr("hash", dof.hash.hash.to_string(Base16, false)); }, - [&](const DerivationOutputCAFloating & dof) { + [&](const DerivationOutput::CAFloating & dof) { outputObj.attr("hashAlgo", makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType)); }, - [&](const DerivationOutputDeferred &) {}, - }, output.output); + [&](const DerivationOutput::Deferred &) {}, + }, output.raw()); } } diff --git a/src/nix/store-copy-log.cc b/src/nix/store-copy-log.cc index 079cd6b3e..2e288f743 100644 --- a/src/nix/store-copy-log.cc +++ b/src/nix/store-copy-log.cc @@ -1,6 +1,8 @@ #include "command.hh" #include "shared.hh" #include "store-api.hh" +#include "store-cast.hh" +#include "log-store.hh" #include "sync.hh" #include "thread-pool.hh" @@ -26,7 +28,10 @@ struct CmdCopyLog : virtual CopyCommand, virtual InstallablesCommand void run(ref<Store> srcStore) override { + auto & srcLogStore = require<LogStore>(*srcStore); + auto dstStore = getDstStore(); + auto & dstLogStore = require<LogStore>(*dstStore); StorePathSet drvPaths; @@ -35,8 +40,8 @@ struct CmdCopyLog : virtual CopyCommand, virtual InstallablesCommand drvPaths.insert(drvPath); for (auto & drvPath : drvPaths) { - if (auto log = srcStore->getBuildLog(drvPath)) - dstStore->addBuildLog(drvPath, *log); + if (auto log = srcLogStore.getBuildLog(drvPath)) + dstLogStore.addBuildLog(drvPath, *log); else throw Error("build log for '%s' is not available", srcStore->printStorePath(drvPath)); } diff --git a/src/nix/store-delete.cc b/src/nix/store-delete.cc index aa7a8b12f..ca43f1530 100644 --- a/src/nix/store-delete.cc +++ b/src/nix/store-delete.cc @@ -2,6 +2,7 @@ #include "common-args.hh" #include "shared.hh" #include "store-api.hh" +#include "store-cast.hh" #include "gc-store.hh" using namespace nix; @@ -33,7 +34,7 @@ struct CmdStoreDelete : StorePathsCommand void run(ref<Store> store, std::vector<StorePath> && storePaths) override { - auto & gcStore = requireGcStore(*store); + auto & gcStore = require<GcStore>(*store); for (auto & path : storePaths) options.pathsToDelete.insert(path); diff --git a/src/nix/store-gc.cc b/src/nix/store-gc.cc index 21718dc0c..8b9b5d164 100644 --- a/src/nix/store-gc.cc +++ b/src/nix/store-gc.cc @@ -2,6 +2,7 @@ #include "common-args.hh" #include "shared.hh" #include "store-api.hh" +#include "store-cast.hh" #include "gc-store.hh" using namespace nix; @@ -34,7 +35,7 @@ struct CmdStoreGC : StoreCommand, MixDryRun void run(ref<Store> store) override { - auto & gcStore = requireGcStore(*store); + auto & gcStore = require<GcStore>(*store); options.action = dryRun ? GCOptions::gcReturnDead : GCOptions::gcDeleteDead; GCResults results; |