diff options
Diffstat (limited to 'src/nix-env/user-env.cc')
-rw-r--r-- | src/nix-env/user-env.cc | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc new file mode 100644 index 000000000000..72e13fceb191 --- /dev/null +++ b/src/nix-env/user-env.cc @@ -0,0 +1,257 @@ +#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 { + + +static void readLegacyManifest(const Path & path, DrvInfos & elems); + + +DrvInfos queryInstalled(EvalState & state, const Path & userEnv) +{ + DrvInfos elems; + + Path manifestFile = userEnv + "/manifest.nix"; + Path oldManifestFile = userEnv + "/manifest"; + + 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].value, "derivation"); + mkString((*v.attrs)[state.sName].value, i->name); + mkString((*v.attrs)[state.sSystem].value, i->system); + mkString((*v.attrs)[state.sOutPath].value, i->queryOutPath(state)); + if (drvPath != "") + mkString((*v.attrs)[state.sDrvPath].value, i->queryDrvPath(state)); + + state.mkAttrs((*v.attrs)[state.sMeta].value); + + MetaInfo meta = i->queryMetaInfo(state); + + foreach (MetaInfo::const_iterator, j, meta) { + Value & v2((*(*v.attrs)[state.sMeta].value.attrs)[state.symbols.create(j->first)].value); + 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].value, thisSystem); + mkString((*args.attrs)[state.symbols.create("manifest")].value, + manifestFile, singleton<PathSet>(manifestFile)); + (*args.attrs)[state.symbols.create("derivations")].value = 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) +{ + expect(str, "Str("); + string s = parseString(str); + expect(str, ",[])"); + return s; +} + + +static string parseWord(std::istream & str) +{ + string res; + while (isalpha(str.peek())) + res += str.get(); + return res; +} + + +static MetaInfo parseMeta(std::istream & str) +{ + MetaInfo meta; + + expect(str, "Attrs(["); + while (!endOfList(str)) { + expect(str, "Bind("); + + MetaValue value; + + string name = parseString(str); + expect(str, ","); + + string type = parseWord(str); + + if (type == "Str") { + expect(str, "("); + value.type = MetaValue::tpString; + value.stringValue = parseString(str); + expect(str, ",[])"); + } + + else if (type == "List") { + expect(str, "(["); + value.type = MetaValue::tpStrings; + while (!endOfList(str)) + value.stringValues.push_back(parseStr(str)); + expect(str, ")"); + } + + else throw Error(format("unexpected token `%1%'") % type); + + expect(str, ",NoPos)"); + meta[name] = value; + } + + expect(str, ")"); + + return meta; +} + + +static void readLegacyManifest(const Path & path, DrvInfos & elems) +{ + string manifest = readFile(path); + std::istringstream str(manifest); + expect(str, "List(["); + + unsigned int n = 0; + + while (!endOfList(str)) { + DrvInfo elem; + expect(str, "Attrs(["); + + while (!endOfList(str)) { + expect(str, "Bind("); + string name = parseString(str); + expect(str, ","); + + if (name == "meta") elem.setMetaInfo(parseMeta(str)); + else { + string value = parseStr(str); + if (name == "name") elem.name = value; + else if (name == "outPath") elem.setOutPath(value); + else if (name == "drvPath") elem.setDrvPath(value); + else if (name == "system") elem.system = value; + } + + expect(str, ",NoPos)"); + } + + expect(str, ")"); + + if (elem.name != "") { + elem.attrPath = int2String(n++); + elems.push_back(elem); + } + } + + expect(str, ")"); +} + + +} + |