#include "nix-env/user-env.hh" #include <glog/logging.h> #include "libexpr/eval-inline.hh" #include "libexpr/eval.hh" #include "libmain/shared.hh" #include "libstore/derivations.hh" #include "libstore/globals.hh" #include "libstore/profiles.hh" #include "libstore/store-api.hh" #include "libutil/status.hh" #include "libutil/util.hh" namespace nix { DrvInfos queryInstalled(EvalState& state, const Path& userEnv) { DrvInfos elems; Path manifestFile = userEnv + "/manifest.nix"; if (pathExists(manifestFile)) { Value v; state.evalFile(manifestFile, v); Bindings& bindings(*Bindings::NewGC()); getDerivations(state, v, "", bindings, elems, false); } return elems; } bool createUserEnv(EvalState& state, DrvInfos& elems, const Path& profile, bool keepDerivations, const std::string& lockToken) { /* Build the components in the user environment, if they don't exist already. */ PathSet drvsToBuild; for (auto& i : elems) { if (!i.queryDrvPath().empty()) { drvsToBuild.insert(i.queryDrvPath()); } } DLOG(INFO) << "building user environment dependencies"; util::OkOrThrow(state.store->buildPaths( drvsToBuild, state.repair != 0u ? bmRepair : bmNormal)); /* Construct the whole top level derivation. */ PathSet references; Value manifest; state.mkList(manifest, elems.size()); unsigned int n = 0; for (auto& i : elems) { /* Create a pseudo-derivation containing the name, system, output paths, and optionally the derivation path, as well as the meta attributes. */ Path drvPath = keepDerivations ? i.queryDrvPath() : ""; Value* v = state.allocValue(); (*manifest.list)[n++] = v; state.mkAttrs(*v, 16); mkString(*state.allocAttr(*v, state.sType), "derivation"); mkString(*state.allocAttr(*v, state.sName), i.queryName()); auto system = i.querySystem(); if (!system.empty()) { mkString(*state.allocAttr(*v, state.sSystem), system); } mkString(*state.allocAttr(*v, state.sOutPath), i.queryOutPath()); if (!drvPath.empty()) { mkString(*state.allocAttr(*v, state.sDrvPath), i.queryDrvPath()); } // Copy each output meant for installation. DrvInfo::Outputs outputs = i.queryOutputs(true); Value& vOutputs = *state.allocAttr(*v, state.sOutputs); state.mkList(vOutputs, outputs.size()); unsigned int m = 0; for (auto& j : outputs) { mkString(*((*vOutputs.list)[m++] = state.allocValue()), j.first); Value& vOutputs = *state.allocAttr(*v, state.symbols.Create(j.first)); state.mkAttrs(vOutputs, 2); mkString(*state.allocAttr(vOutputs, state.sOutPath), j.second); /* 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); references.insert(j.second); } // Copy the meta attributes. Value& vMeta = *state.allocAttr(*v, state.sMeta); state.mkAttrs(vMeta, 16); StringSet metaNames = i.queryMetaNames(); for (auto& j : metaNames) { Value* v = i.queryMeta(j); if (v == nullptr) { continue; } vMeta.attrs->push_back(Attr(state.symbols.Create(j), v)); } if (!drvPath.empty()) { references.insert(drvPath); } } /* Also write a copy of the list of user environment elements to the store; we need it for future modifications of the environment. */ Path manifestFile = state.store->addTextToStore( "env-manifest.nix", (format("%1%") % manifest).str(), references); /* Get the environment builder expression. */ Value envBuilder; state.evalFile(state.findFile("nix/buildenv.nix"), envBuilder); /* Construct a Nix expression that calls the user environment builder with the manifest as argument. */ Value args; Value topLevel; state.mkAttrs(args, 3); mkString(*state.allocAttr(args, state.symbols.Create("manifest")), manifestFile, {manifestFile}); args.attrs->push_back(Attr(state.symbols.Create("derivations"), &manifest)); mkApp(topLevel, envBuilder, args); /* Evaluate it. */ DLOG(INFO) << "evaluating user environment builder"; state.forceValue(topLevel); PathSet context; Attr& aDrvPath(topLevel.attrs->find(state.sDrvPath)->second); Path topLevelDrv = state.coerceToPath(aDrvPath.pos != nullptr ? *(aDrvPath.pos) : noPos, *(aDrvPath.value), context); Attr& aOutPath(topLevel.attrs->find(state.sOutPath)->second); Path topLevelOut = state.coerceToPath(aOutPath.pos != nullptr ? *(aOutPath.pos) : noPos, *(aOutPath.value), context); /* Realise the resulting store expression. */ DLOG(INFO) << "building user environment"; util::OkOrThrow(state.store->buildPaths( {topLevelDrv}, state.repair != 0u ? bmRepair : bmNormal)); /* Switch the current user environment to the output path. */ auto store2 = state.store.dynamic_pointer_cast<LocalFSStore>(); if (store2) { PathLocks lock; lockProfile(lock, profile); Path lockTokenCur = optimisticLockProfile(profile); if (lockToken != lockTokenCur) { LOG(WARNING) << "profile '" << profile << "' changed while we were busy; restarting"; return false; } DLOG(INFO) << "switching to new user environment"; Path generation = createGeneration(ref<LocalFSStore>(store2), profile, topLevelOut); switchLink(profile, generation); } return true; } } // namespace nix