about summary refs log tree commit diff
diff options
context:
space:
mode:
-rwxr-xr-xcorepkgs/buildenv/builder.pl.in2
-rw-r--r--src/libexpr/eval.cc11
-rw-r--r--src/libexpr/eval.hh11
-rw-r--r--src/libexpr/get-drvs.cc23
-rw-r--r--src/libexpr/get-drvs.hh2
-rw-r--r--src/libexpr/primops.cc31
-rw-r--r--src/nix-env/nix-env.cc136
-rw-r--r--src/nix-env/profiles.cc16
-rw-r--r--src/nix-env/profiles.hh15
-rw-r--r--src/nix-env/user-env.cc137
-rw-r--r--src/nix-env/user-env.hh4
11 files changed, 205 insertions, 183 deletions
diff --git a/corepkgs/buildenv/builder.pl.in b/corepkgs/buildenv/builder.pl.in
index 9932ea577d..9eb9f7bb08 100755
--- a/corepkgs/buildenv/builder.pl.in
+++ b/corepkgs/buildenv/builder.pl.in
@@ -160,4 +160,4 @@ while (scalar(keys %postponed) > 0) {
 print STDERR "created $symlinks symlinks in user environment\n";
 
 
-symlink($ENV{"manifest"}, "$out/manifest") or die "cannot create manifest";
+symlink($ENV{"manifest"}, "$out/manifest.nix") or die "cannot create manifest";
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index d259d58a37..f59ea99e5d 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -98,6 +98,7 @@ EvalState::EvalState()
     , sType(symbols.create("type"))
     , sMeta(symbols.create("meta"))
     , sName(symbols.create("name"))
+    , sSystem(symbols.create("system"))
     , baseEnv(allocEnv(128))
     , baseEnvDispl(0)
     , staticBaseEnv(false, 0)
@@ -131,12 +132,13 @@ void EvalState::addPrimOp(const string & name,
     unsigned int arity, PrimOp primOp)
 {
     Value v;
+    string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
     v.type = tPrimOp;
     v.primOp.arity = arity;
     v.primOp.fun = primOp;
+    v.primOp.name = strdup(name2.c_str());
     staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
     baseEnv.values[baseEnvDispl++] = v;
-    string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
     (*baseEnv.values[0].attrs)[symbols.create(name2)] = v;
 }
 
@@ -550,7 +552,12 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
                 vArgs[n--] = arg->primOpApp.right;
 
             /* And call the primop. */
-            primOp->primOp.fun(*this, vArgs, v);
+            try {
+                primOp->primOp.fun(*this, vArgs, v);
+            } catch (Error & e) {
+                addErrorPrefix(e, "while evaluating the builtin function `%1%':\n", primOp->primOp.name);
+                throw;
+            }
         } else {
             Value * v2 = allocValues(2);
             v2[0] = fun;
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 6cdc171f56..a730dc2977 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -92,6 +92,7 @@ struct Value
         Value * val;
         struct {
             PrimOp fun;
+            char * name;
             unsigned int arity;
         } primOp;
         struct {
@@ -138,6 +139,14 @@ static inline void mkCopy(Value & v, Value & src)
 }
 
 
+static inline void mkApp(Value & v, Value & left, Value & right)
+{
+    v.type = tApp;
+    v.app.left = &left;
+    v.app.right = &right;
+}
+
+
 void mkString(Value & v, const char * s);
 void mkString(Value & v, const string & s, const PathSet & context = PathSet());
 void mkPath(Value & v, const char * s);
@@ -162,7 +171,7 @@ public:
 
     SymbolTable symbols;
 
-    const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName;
+    const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sSystem;
 
 private:
     SrcToStore srcToStore; 
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index e9f1063d95..e0ad91d8a5 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -70,27 +70,6 @@ void DrvInfo::setMetaInfo(const MetaInfo & meta)
 {
     metaInfoRead = true;
     this->meta = meta;
-    
-#if 0
-    Value * metaAttrs = state.allocValues(1);
-    foreach (MetaInfo::const_iterator, i, meta) {
-        Expr e;
-        switch (i->second.type) {
-            case MetaValue::tpInt: e = makeInt(i->second.intValue); break;
-            case MetaValue::tpString: e = makeStr(i->second.stringValue); break;
-            case MetaValue::tpStrings: {
-                ATermList es = ATempty;
-                foreach (Strings::const_iterator, j, i->second.stringValues)
-                    es = ATinsert(es, makeStr(*j));
-                e = makeList(ATreverse(es));
-                break;
-            }
-            default: abort();
-        }
-        metaAttrs.set(toATerm(i->first), makeAttrRHS(e, makeNoPos()));
-    }
-    attrs->set(toATerm("meta"), makeAttrs(metaAttrs));
-#endif
 }
 
 
@@ -122,7 +101,7 @@ static bool getDerivation(EvalState & state, Value & v,
         if (i == v.attrs->end()) throw TypeError("derivation name missing");
         drv.name = state.forceStringNoCtx(i->second);
 
-        i = v.attrs->find(state.symbols.create("system"));
+        i = v.attrs->find(state.sSystem);
         if (i == v.attrs->end())
             drv.system = "unknown";
         else
diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh
index 6f3c381f8e..ca7d980027 100644
--- a/src/libexpr/get-drvs.hh
+++ b/src/libexpr/get-drvs.hh
@@ -41,7 +41,7 @@ public:
     /* !!! make this private */
     Bindings * attrs;
 
-    DrvInfo() : metaInfoRead(false) { };
+    DrvInfo() : metaInfoRead(false), attrs(0) { };
 
     string queryDrvPath(EvalState & state) const;
     string queryOutPath(EvalState & state) const;
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index b282015933..a228398e06 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -89,24 +89,29 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
 {
     startNest(nest, lvlDebug, "finding dependencies");
 
-    Expr attrs = evalExpr(state, args[0]);
+    state.forceAttrs(*args[0]);
 
     /* Get the start set. */
-    Expr startSet = queryAttr(attrs, "startSet");
-    if (!startSet) throw EvalError("attribute `startSet' required");
-    ATermList startSet2 = evalList(state, startSet);
+    Bindings::iterator startSet =
+        args[0]->attrs->find(state.symbols.create("startSet"));
+    if (startSet == args[0]->attrs->end())
+        throw EvalError("attribute `startSet' required");
+    state.forceList(startSet->second);
 
-    set<Expr> workSet; // !!! gc roots
-    for (ATermIterator i(startSet2); i; ++i) workSet.insert(*i);
+    list<Value> workSet;
+    for (unsigned int n = 0; n < startSet->second.list.length; ++n)
+        workSet.push_back(*startSet->second.list.elems[n]);
 
     /* Get the operator. */
-    Expr op = queryAttr(attrs, "operator");
-    if (!op) throw EvalError("attribute `operator' required");
+    Bindings::iterator op =
+        args[0]->attrs->find(state.symbols.create("operator"));
+    if (op == args[0]->attrs->end())
+        throw EvalError("attribute `operator' required");
     
     /* Construct the closure by applying the operator to element of
        `workSet', adding the result to `workSet', continuing until
        no new elements are found. */
-    ATermList res = ATempty;
+    list<Value> res;
     set<Expr> doneKeys; // !!! gc roots
     while (!workSet.empty()) {
 	Expr e = *(workSet.begin());
@@ -322,8 +327,8 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
                 string s = state.coerceToString(i->second, context, true);
                 drv.env[key] = s;
                 if (key == "builder") drv.builder = s;
-                else if (key == "system") drv.platform = s;
-                else if (key == "name") drvName = s;
+                else if (i->first == state.sSystem) drv.platform = s;
+                else if (i->first == state.sName) drvName = s;
                 else if (key == "outputHash") outputHash = s;
                 else if (key == "outputHashAlgo") outputHashAlgo = s;
                 else if (key == "outputHashMode") {
@@ -830,9 +835,7 @@ static void prim_map(EvalState & state, Value * * args, Value & v)
 
     for (unsigned int n = 0; n < v.list.length; ++n) {
         v.list.elems[n] = &vs[n];
-        vs[n].type = tApp;
-        vs[n].app.left = args[0];
-        vs[n].app.right = args[1]->list.elems[n];
+        mkApp(vs[n], *args[0], *args[1]->list.elems[n]);
     }
 }
 
diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc
index e298c4003b..4a9df454d4 100644
--- a/src/nix-env/nix-env.cc
+++ b/src/nix-env/nix-env.cc
@@ -8,7 +8,6 @@
 #include "help.txt.hh"
 #include "get-drvs.hh"
 #include "attr-path.hh"
-#include "pathlocks.hh"
 #include "common-opts.hh"
 #include "xml-writer.hh"
 #include "store-api.hh"
@@ -193,141 +192,6 @@ static Path getDefNixExprPath()
 }
 
 
-/* Ensure exclusive access to a profile.  Any command that modifies
-   the profile first acquires this lock. */
-static void lockProfile(PathLocks & lock, const Path & profile)
-{
-    lock.lockPaths(singleton<PathSet>(profile),
-        (format("waiting for lock on profile `%1%'") % profile).str());
-    lock.setDeletion(true);
-}
-
-
-/* Optimistic locking is used by long-running operations like `nix-env
-   -i'.  Instead of acquiring the exclusive lock for the entire
-   duration of the operation, we just perform the operation
-   optimistically (without an exclusive lock), and check at the end
-   whether the profile changed while we were busy (i.e., the symlink
-   target changed).  If so, the operation is restarted.  Restarting is
-   generally cheap, since the build results are still in the Nix
-   store.  Most of the time, only the user environment has to be
-   rebuilt. */
-static string optimisticLockProfile(const Path & profile)
-{
-    return pathExists(profile) ? readLink(profile) : "";
-}
-
-
-static bool createUserEnv(EvalState & state, DrvInfos & elems,
-    const Path & profile, bool keepDerivations,
-    const string & lockToken)
-{
-    throw Error("not implemented");
-#if 0
-    /* Build the components in the user environment, if they don't
-       exist already. */
-    PathSet drvsToBuild;
-    foreach (DrvInfos::const_iterator, i, elems)
-        /* Call to `isDerivation' is for compatibility with Nix <= 0.7
-           user environments. */
-        if (i->queryDrvPath(state) != "" &&
-            isDerivation(i->queryDrvPath(state)))
-            drvsToBuild.insert(i->queryDrvPath(state));
-
-    debug(format("building user environment dependencies"));
-    store->buildDerivations(drvsToBuild);
-
-    /* Get the environment builder expression. */
-    Expr envBuilder = parseExprFromFile(state,
-        nixDataDir + "/nix/corepkgs/buildenv"); /* !!! */
-
-    /* Construct the whole top level derivation. */
-    PathSet references;
-    ATermList manifest = ATempty;
-    ATermList inputs = ATempty;
-    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) : "";
-
-        /* Round trip to get rid of "bad" meta values (like
-           functions). */
-        MetaInfo meta = i->queryMetaInfo(state);
-        i->setMetaInfo(meta);
-        
-        ATermList as = ATmakeList5(
-            makeBind(toATerm("type"),
-                makeStr("derivation"), makeNoPos()),
-            makeBind(toATerm("name"),
-                makeStr(i->name), makeNoPos()),
-            makeBind(toATerm("system"),
-                makeStr(i->system), makeNoPos()),
-            makeBind(toATerm("outPath"),
-                makeStr(i->queryOutPath(state)), makeNoPos()), 
-            makeBind(toATerm("meta"),
-                i->attrs->get(toATerm("meta")), makeNoPos()));
-        
-        if (drvPath != "") as = ATinsert(as, 
-            makeBind(toATerm("drvPath"),
-                makeStr(drvPath), makeNoPos()));
-        
-        manifest = ATinsert(manifest, makeAttrs(as));
-        
-        inputs = ATinsert(inputs, makeStr(i->queryOutPath(state)));
-
-        /* 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 inputs to the store; we need
-       it for future modifications of the environment. */
-    Path manifestFile = store->addTextToStore("env-manifest",
-        atPrint(canonicaliseExpr(makeList(ATreverse(manifest)))), references);
-
-    Expr topLevel = makeCall(envBuilder, makeAttrs(ATmakeList3(
-        makeBind(toATerm("system"),
-            makeStr(thisSystem), makeNoPos()),
-        makeBind(toATerm("derivations"),
-            makeList(ATreverse(manifest)), makeNoPos()),
-        makeBind(toATerm("manifest"),
-            makeStr(manifestFile, singleton<PathSet>(manifestFile)), makeNoPos())
-        )));
-
-    /* Instantiate it. */
-    debug(format("evaluating builder expression `%1%'") % topLevel);
-    DrvInfo topLevelDrv;
-    if (!getDerivation(state, topLevel, topLevelDrv))
-        abort();
-    
-    /* Realise the resulting store expression. */
-    debug(format("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;
-#endif
-}
-
-
 static int getPriority(EvalState & state, const DrvInfo & drv)
 {
     MetaValue value = drv.queryMetaInfo(state, "priority");
diff --git a/src/nix-env/profiles.cc b/src/nix-env/profiles.cc
index 75585b1b29..60576f1ae7 100644
--- a/src/nix-env/profiles.cc
+++ b/src/nix-env/profiles.cc
@@ -130,6 +130,20 @@ void switchLink(Path link, Path target)
         throw SysError(format("renaming `%1%' to `%2%'") % tmp % link);
 }
 
- 
+
+void lockProfile(PathLocks & lock, const Path & profile)
+{
+    lock.lockPaths(singleton<PathSet>(profile),
+        (format("waiting for lock on profile `%1%'") % profile).str());
+    lock.setDeletion(true);
+}
+
+
+string optimisticLockProfile(const Path & profile)
+{
+    return pathExists(profile) ? readLink(profile) : "";
+}
+
+
 }
 
diff --git a/src/nix-env/profiles.hh b/src/nix-env/profiles.hh
index 99c20f42d1..a64258dae2 100644
--- a/src/nix-env/profiles.hh
+++ b/src/nix-env/profiles.hh
@@ -2,6 +2,7 @@
 #define __PROFILES_H
 
 #include "types.hh"
+#include "pathlocks.hh"
 
 #include <time.h>
 
@@ -37,6 +38,20 @@ void deleteGeneration(const Path & profile, unsigned int gen);
 
 void switchLink(Path link, Path target);
 
+/* Ensure exclusive access to a profile.  Any command that modifies
+   the profile first acquires this lock. */
+void lockProfile(PathLocks & lock, const Path & profile);
+
+/* Optimistic locking is used by long-running operations like `nix-env
+   -i'.  Instead of acquiring the exclusive lock for the entire
+   duration of the operation, we just perform the operation
+   optimistically (without an exclusive lock), and check at the end
+   whether the profile changed while we were busy (i.e., the symlink
+   target changed).  If so, the operation is restarted.  Restarting is
+   generally cheap, since the build results are still in the Nix
+   store.  Most of the time, only the user environment has to be
+   rebuilt. */
+string optimisticLockProfile(const Path & profile);
 
 }
 
diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc
index f040f8c115..a0e51cae19 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)
diff --git a/src/nix-env/user-env.hh b/src/nix-env/user-env.hh
index 6675014f1a..4125d82173 100644
--- a/src/nix-env/user-env.hh
+++ b/src/nix-env/user-env.hh
@@ -7,6 +7,10 @@ namespace nix {
 
 DrvInfos queryInstalled(EvalState & state, const Path & userEnv);
 
+bool createUserEnv(EvalState & state, DrvInfos & elems,
+    const Path & profile, bool keepDerivations,
+    const string & lockToken);
+
 }
 
 #endif /* !__USER_ENV_H */