diff options
Diffstat (limited to 'src/legacy/user-env.cc')
-rw-r--r-- | src/legacy/user-env.cc | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/src/legacy/user-env.cc b/src/legacy/user-env.cc new file mode 100644 index 000000000..f5dbd06ca --- /dev/null +++ b/src/legacy/user-env.cc @@ -0,0 +1,155 @@ +#include "user-env.hh" +#include "derivations.hh" +#include "store-api.hh" +#include "path-with-outputs.hh" +#include "local-fs-store.hh" +#include "globals.hh" +#include "shared.hh" +#include "eval.hh" +#include "eval-inline.hh" +#include "profiles.hh" +#include "print-ambiguous.hh" + +#include <limits> +#include <sstream> + +namespace nix { + + +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. */ + std::vector<StorePathWithOutputs> drvsToBuild; + for (auto & i : elems) + if (auto drvPath = i.queryDrvPath()) + drvsToBuild.push_back({*drvPath}); + + debug("building user environment dependencies"); + state.store->buildPaths( + toDerivedPaths(drvsToBuild), + state.repair ? bmRepair : bmNormal); + + /* Construct the whole top level derivation. */ + StorePathSet references; + Value manifest; + state.mkList(manifest, elems.size()); + size_t 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. */ + std::optional<StorePath> drvPath = keepDerivations ? i.queryDrvPath() : std::nullopt; + DrvInfo::Outputs outputs = i.queryOutputs(true, true); + StringSet metaNames = i.queryMetaNames(); + + auto attrs = state.buildBindings(7 + outputs.size()); + + attrs.alloc(state.sType).mkString("derivation"); + attrs.alloc(state.sName).mkString(i.queryName()); + auto system = i.querySystem(); + if (!system.empty()) + attrs.alloc(state.sSystem).mkString(system); + attrs.alloc(state.sOutPath).mkString(state.store->printStorePath(i.queryOutPath())); + if (drvPath) + attrs.alloc(state.sDrvPath).mkString(state.store->printStorePath(*drvPath)); + + // Copy each output meant for installation. + auto & vOutputs = attrs.alloc(state.sOutputs); + state.mkList(vOutputs, outputs.size()); + 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)); + 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); + + references.insert(*j.second); + } + + // Copy the meta attributes. + auto meta = state.buildBindings(metaNames.size()); + for (auto & j : metaNames) { + Value * v = i.queryMeta(j); + if (!v) continue; + meta.insert(state.symbols.create(j), v); + } + + attrs.alloc(state.sMeta).mkAttrs(meta); + + (manifest.listElems()[n++] = state.allocValue())->mkAttrs(attrs); + + if (drvPath) 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. */ + std::ostringstream str; + printAmbiguous(manifest, state.symbols, str, nullptr, std::numeric_limits<int>::max()); + auto manifestFile = state.store->addTextToStore("env-manifest.nix", + str.str(), references); + + /* Get the environment builder expression. */ + Value envBuilder; + state.eval(state.parseExprFromString( + #include "buildenv.nix.gen.hh" + , state.rootPath(CanonPath::root)), envBuilder); + + /* Construct a Nix expression that calls the user environment + builder with the manifest as argument. */ + auto attrs = state.buildBindings(3); + state.mkStorePathString(manifestFile, attrs.alloc("manifest")); + attrs.insert(state.symbols.create("derivations"), &manifest); + Value args; + args.mkAttrs(attrs); + + Value topLevel; + topLevel.mkApp(&envBuilder, &args); + + /* Evaluate it. */ + debug("evaluating user environment builder"); + state.forceValue(topLevel, topLevel.determinePos(noPos)); + NixStringContext context; + Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath)); + auto topLevelDrv = state.coerceToStorePath(aDrvPath.pos, *aDrvPath.value, context, ""); + Attr & aOutPath(*topLevel.attrs->find(state.sOutPath)); + auto topLevelOut = state.coerceToStorePath(aOutPath.pos, *aOutPath.value, context, ""); + + /* Realise the resulting store expression. */ + debug("building user environment"); + std::vector<StorePathWithOutputs> topLevelDrvs; + topLevelDrvs.push_back({topLevelDrv}); + state.store->buildPaths( + toDerivedPaths(topLevelDrvs), + state.repair ? 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) { + printInfo("profile '%1%' changed while we were busy; restarting", profile); + return false; + } + + debug("switching to new user environment"); + Path generation = createGeneration(*store2, profile, topLevelOut); + switchLink(profile, generation); + } + + return true; +} + + +} |