Files
lix/subprojects/nix-eval-jobs/src/drv.cc
T
Maximilian BoschandJörg Thalheim d6b0b8b382 nix-eval-jobs: add --no-instantiate flag
Closes #987

The patch adds a flag `--no-instantiate` which only performs evaluation
without instantiating any derivations. Hence, GC root creation is also
skipped. To achieve that, Lix is also put in read-only mode and all
operations that require reading a derivation (e.g. constituents or
listing input derivations) are disabled fallback values are set.

This is a port of an upstream PR[1]. Given the divergence of the
codebases (different restructurings on both ends, no more CA derivations)
I decided to redo large portions from scratch instead of
cherry-picking the patches. Hence, the authorship.

Additionally the clean up of casts down to a local store are removed or
guarded behind an if, as done in the upstream PR.

[1] https://github.com/nix-community/nix-eval-jobs/pull/379

Co-authored-by: Jörg Thalheim <joerg@thalheim.io>
Change-Id: Ib84f44e7799bc5577fd2ee98912458f16ebeab81
2025-09-16 13:03:53 +02:00

157 lines
5.3 KiB
C++

#include <lix/config.h> // IWYU pragma: keep
#include <lix/libstore/path-with-outputs.hh>
#include <lix/libstore/store-api.hh>
#include <lix/libstore/local-fs-store.hh>
#include <lix/libexpr/value-to-json.hh>
#include <lix/libstore/derivations.hh>
#include <stdint.h>
#include <lix/libexpr/eval.hh>
#include <lix/libexpr/get-drvs.hh>
#include <lix/libexpr/nixexpr.hh>
#include <lix/libstore/path.hh>
#include <lix/libutil/json.hh>
#include <lix/libutil/ref.hh>
#include <lix/libexpr/value/context.hh>
#include <exception>
#include <memory>
#include <sstream>
#include <utility>
#include <vector>
#include "drv.hh"
#include "eval-args.hh"
#include "lix/libutil/async.hh"
static bool
queryIsCached(nix::AsyncIoRoot &aio,
nix::Store &store,
std::map<std::string, std::optional<std::string>> &outputs) {
uint64_t downloadSize, narSize;
nix::StorePathSet willBuild, willSubstitute, unknown;
std::vector<nix::StorePathWithOutputs> paths;
for (auto const &[key, val] : outputs) {
if (val) {
paths.push_back(followLinksToStorePathWithOutputs(store, *val));
}
}
aio.blockOn(store.queryMissing(toDerivedPaths(paths), willBuild, willSubstitute,
unknown, downloadSize, narSize));
return willBuild.empty() && unknown.empty();
}
/* The fields of a derivation that are printed in json form */
Drv::Drv(std::string &attrPath, nix::EvalState &state, nix::DrvInfo &drvInfo,
MyArgs &args, std::optional<Constituents> constituents)
: constituents(constituents) {
auto localStore = state.ctx.store.try_cast_shared<nix::LocalFSStore>();
auto canReadDerivation = localStore && !nix::settings.readOnlyMode;
try {
for (auto &[outputName, optOutputPath] :
drvInfo.queryOutputs(state, true)) {
assert(optOutputPath);
outputs[outputName] = state.ctx.store->printStorePath(*optOutputPath);
}
} catch (const std::exception &e) { // NOLINT(lix-foreign-exceptions)
state.ctx.errors.make<nix::EvalError>(
"derivation '%s' does not have valid outputs: %s",
attrPath, e.what()
).debugThrow();
}
if (args.meta) {
nix::JSON meta_;
for (auto &metaName : drvInfo.queryMetaNames(state)) {
nix::NixStringContext context;
std::stringstream ss;
auto metaValue = drvInfo.queryMeta(state, metaName);
// Skip non-serialisable types
// TODO: Fix serialisation of derivations to store paths
if (metaValue == 0) {
continue;
}
nix::printValueAsJSON(state, true, *metaValue, nix::noPos, ss,
context);
meta_[metaName] = nix::json::parse(ss.str());
}
meta = meta_;
}
// !canReadDerivation together with checkCacheStatus is rejected in main().
if (args.checkCacheStatus) {
cacheStatus = queryIsCached(state.aio, *localStore, outputs)
? Drv::CacheStatus::Cached
: Drv::CacheStatus::Uncached;
} else {
cacheStatus = Drv::CacheStatus::Unknown;
}
drvPath = state.ctx.store->printStorePath(drvInfo.requireDrvPath(state));
name = drvInfo.queryName(state);
if (canReadDerivation) {
auto drv = state.aio.blockOn(localStore->readDerivation(drvInfo.requireDrvPath(state)));
for (const auto &[inputDrvPath, inputNode] : drv.inputDrvs) {
std::set<std::string> inputDrvOutputs;
for (auto &outputName : inputNode) {
inputDrvOutputs.insert(outputName);
}
inputDrvs[localStore->printStorePath(inputDrvPath)] = inputDrvOutputs;
}
system = drv.platform;
} else {
system = drvInfo.querySystem(state);
}
}
void to_json(nix::JSON &json, const Drv &drv) {
std::map<std::string, nix::JSON> outputsJson;
for (auto &[name, optPath] : drv.outputs) {
outputsJson[name] =
optPath ? nix::JSON(*optPath) : nix::JSON(nullptr);
}
json = nix::JSON{{"name", drv.name},
{"system", drv.system},
{"drvPath", drv.drvPath},
{"outputs", outputsJson},
{"inputDrvs", drv.inputDrvs}};
if (drv.meta.has_value()) {
json["meta"] = drv.meta.value();
}
if (auto constituents = drv.constituents) {
json["constituents"] = constituents->constituents;
json["namedConstituents"] = constituents->namedConstituents;
}
if (drv.cacheStatus != Drv::CacheStatus::Unknown) {
json["isCached"] = drv.cacheStatus == Drv::CacheStatus::Cached;
}
}
void register_gc_root(nix::Path &gcRootsDir, std::string &drvPath, const nix::ref<nix::Store> &store,
nix::AsyncIoRoot &aio) {
if (!gcRootsDir.empty() && !nix::settings.readOnlyMode) {
nix::Path root =
gcRootsDir + "/" +
std::string(nix::baseNameOf(drvPath));
if (!nix::pathExists(root)) {
auto localStore = store.try_cast_shared<nix::LocalFSStore>();
if (localStore) {
auto storePath =
localStore->parseStorePath(drvPath);
aio.blockOn(localStore->addPermRoot(storePath, root));
}
}
}
}