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
157 lines
5.3 KiB
C++
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));
|
|
}
|
|
}
|
|
}
|
|
}
|