about summary refs log tree commit diff
path: root/src/nix-env/user-env.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/nix-env/user-env.cc')
-rw-r--r--src/nix-env/user-env.cc137
1 files changed, 132 insertions, 5 deletions
diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc
index f040f8c11570..a0e51cae19c3 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)