about summary refs log tree commit diff
path: root/src/nix-env
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2010-05-12T13·59+0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2010-05-12T13·59+0000
commit8032f26ca0bd2233de066ce5786ff976bbd641ae (patch)
tree954b2ecdce037dcf47b0376616ac05dbad8542ab /src/nix-env
parent4750065ada362bd46e85879975a3148e18df5b0c (diff)
parentbd25ac2260267abd2181324e1650820da70e5e60 (diff)
* Merged the `fast-eval' branch.
Diffstat (limited to 'src/nix-env')
-rw-r--r--src/nix-env/Makefile.am7
-rw-r--r--src/nix-env/nix-env.cc227
-rw-r--r--src/nix-env/profiles.cc16
-rw-r--r--src/nix-env/profiles.hh15
-rw-r--r--src/nix-env/user-env.cc257
-rw-r--r--src/nix-env/user-env.hh20
6 files changed, 342 insertions, 200 deletions
diff --git a/src/nix-env/Makefile.am b/src/nix-env/Makefile.am
index 900524f76ed1..7dfa7425a062 100644
--- a/src/nix-env/Makefile.am
+++ b/src/nix-env/Makefile.am
@@ -1,9 +1,10 @@
 bin_PROGRAMS = nix-env
 
-nix_env_SOURCES = nix-env.cc profiles.cc profiles.hh help.txt
+nix_env_SOURCES = nix-env.cc profiles.cc profiles.hh user-env.cc user-env.hh help.txt
+
 nix_env_LDADD = ../libmain/libmain.la ../libexpr/libexpr.la \
  ../libstore/libstore.la ../libutil/libutil.la \
- ../boost/format/libformat.la ${aterm_lib} @ADDITIONAL_NETWORK_LIBS@
+ ../boost/format/libformat.la @ADDITIONAL_NETWORK_LIBS@
 
 nix-env.o: help.txt.hh
 
@@ -11,6 +12,6 @@ nix-env.o: help.txt.hh
 	../bin2c/bin2c helpText < $< > $@ || (rm $@ && exit 1)
 
 AM_CXXFLAGS = \
- -I$(srcdir)/.. ${aterm_include} \
+ -I$(srcdir)/.. \
  -I$(srcdir)/../libutil -I$(srcdir)/../libstore \
  -I$(srcdir)/../libexpr -I$(srcdir)/../libmain -I../libexpr
diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc
index 35caf687bf87..12a256a091f9 100644
--- a/src/nix-env/nix-env.cc
+++ b/src/nix-env/nix-env.cc
@@ -6,13 +6,12 @@
 #include "parser.hh"
 #include "eval.hh"
 #include "help.txt.hh"
-#include "nixexpr-ast.hh"
 #include "get-drvs.hh"
 #include "attr-path.hh"
-#include "pathlocks.hh"
 #include "common-opts.hh"
 #include "xml-writer.hh"
 #include "store-api.hh"
+#include "user-env.hh"
 #include "util.hh"
 
 #include <cerrno>
@@ -47,7 +46,7 @@ struct InstallSourceInfo
     Path profile; /* for srcProfile */
     string systemFilter; /* for srcNixExprDrvs */
     bool prebuiltOnly;
-    ATermMap autoArgs;
+    Bindings autoArgs;
     InstallSourceInfo() : prebuiltOnly(false) { };
 };
 
@@ -112,7 +111,7 @@ static bool isNixExpr(const Path & path)
 
 
 static void getAllExprs(EvalState & state,
-    const Path & path, ATermMap & attrs)
+    const Path & path, ExprAttrs & attrs)
 {
     Strings names = readDirectory(path);
     StringSet namesSorted(names.begin(), names.end());
@@ -132,8 +131,8 @@ static void getAllExprs(EvalState & state,
             string attrName = *i;
             if (hasSuffix(attrName, ".nix"))
                 attrName = string(attrName, 0, attrName.size() - 4);
-            attrs.set(toATerm(attrName), makeAttrRHS(
-                    parseExprFromFile(state, absPath(path2)), makeNoPos()));
+            attrs.attrs[state.symbols.create(attrName)] =
+                ExprAttrs::Attr(parseExprFromFile(state, absPath(path2)), noPos);
         }
         else
             /* `path2' is a directory (with no default.nix in it);
@@ -143,7 +142,7 @@ static void getAllExprs(EvalState & state,
 }
 
 
-static Expr loadSourceExpr(EvalState & state, const Path & path)
+static Expr * loadSourceExpr(EvalState & state, const Path & path)
 {
     if (isNixExpr(path)) return parseExprFromFile(state, absPath(path));
 
@@ -153,20 +152,22 @@ static Expr loadSourceExpr(EvalState & state, const Path & path)
        (but keep the attribute set flat, not nested, to make it easier
        for a user to have a ~/.nix-defexpr directory that includes
        some system-wide directory). */
-    ATermMap attrs;
-    attrs.set(toATerm("_combineChannels"), makeAttrRHS(makeList(ATempty), makeNoPos()));
-    getAllExprs(state, path, attrs);
-    return makeAttrs(attrs);
+    ExprAttrs * attrs = new ExprAttrs;
+    attrs->attrs[state.symbols.create("_combineChannels")] =
+        ExprAttrs::Attr(new ExprList(), noPos);
+    getAllExprs(state, path, *attrs);
+    return attrs;
 }
 
 
 static void loadDerivations(EvalState & state, Path nixExprPath,
-    string systemFilter, const ATermMap & autoArgs,
+    string systemFilter, const Bindings & autoArgs,
     const string & pathPrefix, DrvInfos & elems)
 {
-    getDerivations(state,
-        findAlongAttrPath(state, pathPrefix, autoArgs, loadSourceExpr(state, nixExprPath)),
-        pathPrefix, autoArgs, elems);
+    Value v;
+    findAlongAttrPath(state, pathPrefix, autoArgs, loadSourceExpr(state, nixExprPath), v);
+    
+    getDerivations(state, v, pathPrefix, autoArgs, elems);
 
     /* Filter out all derivations not applicable to the current
        system. */
@@ -192,172 +193,6 @@ static Path getDefNixExprPath()
 }
 
 
-struct AddPos : TermFun
-{
-    ATerm operator () (ATerm e)
-    {
-        ATerm x, y;
-        if (matchObsoleteBind(e, x, y))
-            return makeBind(x, y, makeNoPos());
-        if (matchObsoleteStr(e, x))
-            return makeStr(x, ATempty);
-        return e;
-    }
-};
-
-
-static DrvInfos queryInstalled(EvalState & state, const Path & userEnv)
-{
-    Path path = userEnv + "/manifest";
-
-    if (!pathExists(path))
-        return DrvInfos(); /* not an error, assume nothing installed */
-
-    Expr e = ATreadFromNamedFile(path.c_str());
-    if (!e) throw Error(format("cannot read Nix expression from `%1%'") % path);
-
-    /* Compatibility: Bind(x, y) -> Bind(x, y, NoPos). */
-    AddPos addPos;
-    e = bottomupRewrite(addPos, e);
-
-    DrvInfos elems;
-    getDerivations(state, e, "", ATermMap(1), elems);
-    return elems;
-}
-
-
-/* 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)
-{
-    /* 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;
-}
-
-
 static int getPriority(EvalState & state, const DrvInfo & drv)
 {
     MetaValue value = drv.queryMetaInfo(state, "priority");
@@ -516,14 +351,13 @@ static void queryInstSources(EvalState & state,
            (import ./foo.nix)' = `(import ./foo.nix).bar'. */
         case srcNixExprs: {
                 
-            Expr e1 = loadSourceExpr(state, instSource.nixExprPath);
+            Expr * e1 = loadSourceExpr(state, instSource.nixExprPath);
 
-            for (Strings::const_iterator i = args.begin();
-                 i != args.end(); ++i)
-            {
-                Expr e2 = parseExprFromString(state, *i, absPath("."));
-                Expr call = makeCall(e2, e1);
-                getDerivations(state, call, "", instSource.autoArgs, elems);
+            foreach (Strings::const_iterator, i, args) {
+                Expr * e2 = parseExprFromString(state, *i, absPath("."));
+                Expr * call = new ExprApp(e2, e1);
+                Value v; state.eval(call, v);
+                getDerivations(state, v, "", instSource.autoArgs, elems);
             }
             
             break;
@@ -540,7 +374,7 @@ static void queryInstSources(EvalState & state,
                 Path path = followLinksToStorePath(*i);
 
                 DrvInfo elem;
-                elem.attrs = boost::shared_ptr<ATermMap>(new ATermMap(0)); /* ugh... */
+                elem.attrs = new Bindings;
                 string name = baseNameOf(path);
                 string::size_type dash = name.find('-');
                 if (dash != string::npos)
@@ -574,12 +408,12 @@ static void queryInstSources(EvalState & state,
         }
 
         case srcAttrPath: {
-            for (Strings::const_iterator i = args.begin();
-                 i != args.end(); ++i)
-                getDerivations(state,
-                    findAlongAttrPath(state, *i, instSource.autoArgs,
-                        loadSourceExpr(state, instSource.nixExprPath)),
-                    "", instSource.autoArgs, elems);
+            foreach (Strings::const_iterator, i, args) {
+                Value v;
+                findAlongAttrPath(state, *i, instSource.autoArgs,
+                    loadSourceExpr(state, instSource.nixExprPath), v);
+                getDerivations(state, v, "", instSource.autoArgs, elems);
+            }
             break;
         }
     }
@@ -1102,6 +936,7 @@ static void opQuery(Globals & globals,
     
     foreach (vector<DrvInfo>::iterator, i, elems2) {
         try {
+            startNest(nest, lvlDebug, format("outputting query result `%1%'") % i->attrPath);
 
             /* For table output. */
             Strings columns;
@@ -1472,7 +1307,7 @@ void run(Strings args)
 
     op(globals, remaining, opFlags, opArgs);
 
-    printEvalStats(globals.state);
+    globals.state.printStats();
 }
 
 
diff --git a/src/nix-env/profiles.cc b/src/nix-env/profiles.cc
index 75585b1b2907..60576f1ae74b 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 99c20f42d18c..a64258dae224 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
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, ")");
+}
+
+
+}
+
diff --git a/src/nix-env/user-env.hh b/src/nix-env/user-env.hh
new file mode 100644
index 000000000000..4125d821732f
--- /dev/null
+++ b/src/nix-env/user-env.hh
@@ -0,0 +1,20 @@
+#ifndef __USER_ENV_H
+#define __USER_ENV_H
+
+#include "get-drvs.hh"
+
+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 */
+
+
+
+