diff options
Diffstat (limited to 'src/nix-env/user-env.cc')
-rw-r--r-- | src/nix-env/user-env.cc | 137 |
1 files changed, 132 insertions, 5 deletions
diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index f040f8c11..a0e51cae1 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -1,5 +1,12 @@ #include "util.hh" #include "get-drvs.hh" +#include "derivations.hh" +#include "store-api.hh" +#include "globals.hh" +#include "shared.hh" +#include "eval.hh" +#include "parser.hh" +#include "profiles.hh" namespace nix { @@ -11,18 +18,138 @@ static void readLegacyManifest(const Path & path, DrvInfos & elems); DrvInfos queryInstalled(EvalState & state, const Path & userEnv) { DrvInfos elems; - - Path path = userEnv + "/manifest"; - if (!pathExists(path)) - return DrvInfos(); /* not an error, assume nothing installed */ + Path manifestFile = userEnv + "/manifest.nix"; + Path oldManifestFile = userEnv + "/manifest"; - readLegacyManifest(path, elems); + if (pathExists(manifestFile)) { + Value v; + state.eval(parseExprFromFile(state, manifestFile), v); + getDerivations(state, v, "", Bindings(), elems); + } else if (pathExists(oldManifestFile)) + readLegacyManifest(oldManifestFile, elems); return elems; } +bool createUserEnv(EvalState & state, DrvInfos & elems, + const Path & profile, bool keepDerivations, + const string & lockToken) +{ + /* Build the components in the user environment, if they don't + exist already. */ + PathSet drvsToBuild; + foreach (DrvInfos::const_iterator, i, elems) + if (i->queryDrvPath(state) != "") + drvsToBuild.insert(i->queryDrvPath(state)); + + debug(format("building user environment dependencies")); + store->buildDerivations(drvsToBuild); + + /* Construct the whole top level derivation. */ + PathSet references; + Value manifest; + state.mkList(manifest, elems.size()); + unsigned int n = 0; + foreach (DrvInfos::iterator, i, elems) { + /* Create a pseudo-derivation containing the name, system, + output path, and optionally the derivation path, as well as + the meta attributes. */ + Path drvPath = keepDerivations ? i->queryDrvPath(state) : ""; + + Value & v(*state.allocValues(1)); + manifest.list.elems[n++] = &v; + state.mkAttrs(v); + + mkString((*v.attrs)[state.sType], "derivation"); + mkString((*v.attrs)[state.sName], i->name); + mkString((*v.attrs)[state.sSystem], i->system); + mkString((*v.attrs)[state.sOutPath], i->queryOutPath(state)); + if (drvPath != "") + mkString((*v.attrs)[state.sDrvPath], i->queryDrvPath(state)); + + state.mkAttrs((*v.attrs)[state.sMeta]); + + MetaInfo meta = i->queryMetaInfo(state); + + foreach (MetaInfo::const_iterator, j, meta) { + Value & v2((*(*v.attrs)[state.sMeta].attrs)[state.symbols.create(j->first)]); + switch (j->second.type) { + case MetaValue::tpInt: mkInt(v2, j->second.intValue); break; + case MetaValue::tpString: mkString(v2, j->second.stringValue); break; + case MetaValue::tpStrings: { + state.mkList(v2, j->second.stringValues.size()); + unsigned int m = 0; + foreach (Strings::const_iterator, k, j->second.stringValues) { + v2.list.elems[m] = state.allocValues(1); + mkString(*v2.list.elems[m++], *k); + } + break; + } + default: abort(); + } + } + + /* This is only necessary when installing store paths, e.g., + `nix-env -i /nix/store/abcd...-foo'. */ + store->addTempRoot(i->queryOutPath(state)); + store->ensurePath(i->queryOutPath(state)); + + references.insert(i->queryOutPath(state)); + 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. */ + Path manifestFile = store->addTextToStore("env-manifest.nix", + (format("%1%") % manifest).str(), references); + + printMsg(lvlError, manifestFile); + + /* Get the environment builder expression. */ + Value envBuilder; + state.eval(parseExprFromFile(state, nixDataDir + "/nix/corepkgs/buildenv"), envBuilder); + + /* Construct a Nix expression that calls the user environment + builder with the manifest as argument. */ + Value args, topLevel; + state.mkAttrs(args); + mkString((*args.attrs)[state.sSystem], thisSystem); + mkString((*args.attrs)[state.symbols.create("manifest")], + manifestFile, singleton<PathSet>(manifestFile)); + (*args.attrs)[state.symbols.create("derivations")] = manifest; + mkApp(topLevel, envBuilder, args); + + /* Evaluate it. */ + debug("evaluating user environment builder"); + DrvInfo topLevelDrv; + if (!getDerivation(state, topLevel, topLevelDrv)) + abort(); + + /* Realise the resulting store expression. */ + debug("building user environment"); + store->buildDerivations(singleton<PathSet>(topLevelDrv.queryDrvPath(state))); + + /* Switch the current user environment to the output path. */ + PathLocks lock; + lockProfile(lock, profile); + + Path lockTokenCur = optimisticLockProfile(profile); + if (lockToken != lockTokenCur) { + printMsg(lvlError, format("profile `%1%' changed while we were busy; restarting") % profile); + return false; + } + + debug(format("switching to new user environment")); + Path generation = createGeneration(profile, topLevelDrv.queryOutPath(state)); + switchLink(profile, generation); + + return true; +} + + /* Code for parsing manifests in the old textual ATerm format. */ static string parseStr(std::istream & str) |