about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2013-11-19T13·09+0100
committerEelco Dolstra <eelco.dolstra@logicblox.com>2013-11-19T13·09+0100
commit0f24400d90daf65cf20142a662f8245008437e2c (patch)
tree0daf395a2ff226203b9560edc74ebe1efd89862c
parent990126cde03428509191bed132f38050034d342e (diff)
Generalise meta attributes
-rw-r--r--src/libexpr/get-drvs.cc159
-rw-r--r--src/libexpr/get-drvs.hh46
-rw-r--r--src/nix-env/nix-env.cc147
-rw-r--r--src/nix-env/user-env.cc52
-rw-r--r--src/nix-instantiate/nix-instantiate.cc4
5 files changed, 204 insertions, 204 deletions
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index f5d7c189ce..42776b22ac 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -2,116 +2,155 @@
 #include "util.hh"
 #include "eval-inline.hh"
 
+#include <cstring>
+
 
 namespace nix {
 
 
-string DrvInfo::queryDrvPath(EvalState & state) const
+string DrvInfo::queryDrvPath()
 {
     if (drvPath == "" && attrs) {
-        Bindings::iterator i = attrs->find(state.sDrvPath);
+        Bindings::iterator i = attrs->find(state->sDrvPath);
         PathSet context;
-        (string &) drvPath = i != attrs->end() ? state.coerceToPath(*i->value, context) : "";
+        drvPath = i != attrs->end() ? state->coerceToPath(*i->value, context) : "";
     }
     return drvPath;
 }
 
 
-string DrvInfo::queryOutPath(EvalState & state) const
+string DrvInfo::queryOutPath()
 {
     if (outPath == "" && attrs) {
-        Bindings::iterator i = attrs->find(state.sOutPath);
+        Bindings::iterator i = attrs->find(state->sOutPath);
         PathSet context;
-        (string &) outPath = i != attrs->end() ? state.coerceToPath(*i->value, context) : "";
+        outPath = i != attrs->end() ? state->coerceToPath(*i->value, context) : "";
     }
     return outPath;
 }
 
 
-DrvInfo::Outputs DrvInfo::queryOutputs(EvalState & state)
+DrvInfo::Outputs DrvInfo::queryOutputs()
 {
     if (outputs.empty()) {
         /* Get the ‘outputs’ list. */
-        Bindings::iterator i = attrs->find(state.sOutputs);
-
-        if (i == attrs->end())
-            outputs["out"] = queryOutPath(state);
-        else {
-            state.forceList(*i->value);
+        Bindings::iterator i;
+        if (attrs && (i = attrs->find(state->sOutputs)) != attrs->end()) {
+            state->forceList(*i->value);
 
             /* For each output... */
             for (unsigned int j = 0; j < i->value->list.length; ++j) {
                 /* Evaluate the corresponding set. */
-                string name = state.forceStringNoCtx(*i->value->list.elems[j]);
-                Bindings::iterator out = attrs->find(state.symbols.create(name));
+                string name = state->forceStringNoCtx(*i->value->list.elems[j]);
+                Bindings::iterator out = attrs->find(state->symbols.create(name));
                 if (out == attrs->end()) continue; // FIXME: throw error?
-                state.forceAttrs(*out->value);
+                state->forceAttrs(*out->value);
 
                 /* And evaluate its ‘outPath’ attribute. */
-                Bindings::iterator outPath = out->value->attrs->find(state.sOutPath);
+                Bindings::iterator outPath = out->value->attrs->find(state->sOutPath);
                 if (outPath == out->value->attrs->end()) continue; // FIXME: throw error?
                 PathSet context;
-                outputs[name] = state.coerceToPath(*outPath->value, context);
+                outputs[name] = state->coerceToPath(*outPath->value, context);
             }
-        }
+        } else
+            outputs["out"] = queryOutPath();
     }
     return outputs;
 }
 
 
-string DrvInfo::queryOutputName(EvalState & state) const
+string DrvInfo::queryOutputName()
 {
     if (outputName == "" && attrs) {
-        Bindings::iterator i = attrs->find(state.sOutputName);
-        (string &) outputName = i != attrs->end() ? state.forceStringNoCtx(*i->value) : "";
+        Bindings::iterator i = attrs->find(state->sOutputName);
+        outputName = i != attrs->end() ? state->forceStringNoCtx(*i->value) : "";
     }
     return outputName;
 }
 
 
-MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const
+Bindings * DrvInfo::getMeta()
 {
-    if (metaInfoRead) return meta;
+    if (meta) return meta;
+    if (!attrs) return 0;
+    Bindings::iterator a = attrs->find(state->sMeta);
+    if (a == attrs->end()) return 0;
+    state->forceAttrs(*a->value);
+    meta = a->value->attrs;
+    return meta;
+}
 
-    (bool &) metaInfoRead = true;
 
-    Bindings::iterator a = attrs->find(state.sMeta);
-    if (a == attrs->end()) return meta; /* fine, empty meta information */
+StringSet DrvInfo::queryMetaNames()
+{
+    StringSet res;
+    if (!getMeta()) return res;
+    foreach (Bindings::iterator, i, *meta)
+        res.insert(i->name);
+    return res;
+}
 
-    state.forceAttrs(*a->value);
 
-    foreach (Bindings::iterator, i, *a->value->attrs) {
-        MetaValue value;
-        state.forceValue(*i->value);
-        if (i->value->type == tString) {
-            value.type = MetaValue::tpString;
-            value.stringValue = i->value->string.s;
-        } else if (i->value->type == tInt) {
-            value.type = MetaValue::tpInt;
-            value.intValue = i->value->integer;
-        } else if (i->value->type == tList) {
-            value.type = MetaValue::tpStrings;
-            for (unsigned int j = 0; j < i->value->list.length; ++j)
-                value.stringValues.push_back(state.forceStringNoCtx(*i->value->list.elems[j]));
-        } else continue;
-        ((MetaInfo &) meta)[i->name] = value;
-    }
+Value * DrvInfo::queryMeta(const string & name)
+{
+    if (!getMeta()) return 0;
+    Bindings::iterator a = meta->find(state->symbols.create(name));
+    if (a == meta->end()) return 0;
+    state->forceValue(*a->value);
+    return a->value;
+}
 
-    return meta;
+
+string DrvInfo::queryMetaString(const string & name)
+{
+    Value * v = queryMeta(name);
+    if (!v || v->type != tString) return "";
+    return v->string.s;
 }
 
 
-MetaValue DrvInfo::queryMetaInfo(EvalState & state, const string & name) const
+int DrvInfo::queryMetaInt(const string & name, int def)
 {
-    /* !!! evaluates all meta attributes => inefficient */
-    return queryMetaInfo(state)[name];
+    Value * v = queryMeta(name);
+    if (!v) return def;
+    if (v->type == tInt) return v->integer;
+    if (v->type == tString) {
+        /* Backwards compatibility with before we had support for
+           integer meta fields. */
+        int n;
+        if (string2Int(v->string.s, n)) return n;
+    }
+    return def;
 }
 
 
-void DrvInfo::setMetaInfo(const MetaInfo & meta)
+bool DrvInfo::queryMetaBool(const string & name, bool def)
 {
-    metaInfoRead = true;
-    this->meta = meta;
+    Value * v = queryMeta(name);
+    if (!v) return def;
+    if (v->type == tBool) return v->boolean;
+    if (v->type == tString) {
+        /* Backwards compatibility with before we had support for
+           Boolean meta fields. */
+        if (strcmp(v->string.s, "true") == 0) return true;
+        if (strcmp(v->string.s, "false") == 0) return false;
+    }
+    return def;
+}
+
+
+void DrvInfo::setMeta(const string & name, Value * v)
+{
+    getMeta();
+    Bindings * old = meta;
+    meta = new Bindings();
+    Symbol sym = state->symbols.create(name);
+    if (old)
+        foreach (Bindings::iterator, i, *old)
+            if (i->name != sym)
+                meta->push_back(*i);
+    if (v) meta->push_back(Attr(sym, v));
+    meta->sort();
 }
 
 
@@ -136,22 +175,18 @@ static bool getDerivation(EvalState & state, Value & v,
         if (done.find(v.attrs) != done.end()) return false;
         done.insert(v.attrs);
 
-        DrvInfo drv;
-
         Bindings::iterator i = v.attrs->find(state.sName);
         /* !!! We really would like to have a decent back trace here. */
         if (i == v.attrs->end()) throw TypeError("derivation name missing");
-        drv.name = state.forceStringNoCtx(*i->value);
 
         Bindings::iterator i2 = v.attrs->find(state.sSystem);
-        if (i2 == v.attrs->end())
-            drv.system = "unknown";
-        else
-            drv.system = state.forceStringNoCtx(*i2->value);
 
-        drv.attrs = v.attrs;
-
-        drv.attrPath = attrPath;
+        DrvInfo drv(
+            state,
+            state.forceStringNoCtx(*i->value),
+            attrPath,
+            i2 == v.attrs->end() ? "unknown" : state.forceStringNoCtx(*i2->value),
+            v.attrs);
 
         drvs.push_back(drv);
         return false;
@@ -190,8 +225,6 @@ static void getDerivations(EvalState & state, Value & vIn,
     state.autoCallFunction(autoArgs, vIn, v);
 
     /* Process the expression. */
-    DrvInfo drv;
-
     if (!getDerivation(state, v, pathPrefix, drvs, done, ignoreAssertionFailures)) ;
 
     else if (v.type == tAttrs) {
diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh
index af3998e400..b5aebc1883 100644
--- a/src/libexpr/get-drvs.hh
+++ b/src/libexpr/get-drvs.hh
@@ -11,50 +11,50 @@
 namespace nix {
 
 
-struct MetaValue
-{
-    enum { tpNone, tpString, tpStrings, tpInt } type;
-    string stringValue;
-    Strings stringValues;
-    int intValue;
-};
-
-
-typedef std::map<string, MetaValue> MetaInfo;
-
-
 struct DrvInfo
 {
 public:
     typedef std::map<string, Path> Outputs;
 
 private:
+    EvalState * state;
+
     string drvPath;
     string outPath;
     string outputName;
     Outputs outputs;
 
-    bool metaInfoRead;
-    MetaInfo meta;
-
     bool failed; // set if we get an AssertionError
 
+    Bindings * attrs, * meta;
+
+    Bindings * getMeta();
+
 public:
     string name;
     string attrPath; /* path towards the derivation */
     string system;
 
-    /* !!! make this private */
-    Bindings * attrs;
+    DrvInfo(EvalState & state) : state(&state), failed(false), attrs(0), meta(0) { };
+    DrvInfo(EvalState & state, const string & name, const string & attrPath, const string & system, Bindings * attrs)
+        : state(&state), failed(false), attrs(attrs), meta(0), name(name), attrPath(attrPath), system(system) { };
+
+    string queryDrvPath();
+    string queryOutPath();
+    string queryOutputName();
+    Outputs queryOutputs();
 
-    DrvInfo() : metaInfoRead(false), failed(false), attrs(0) { };
+    StringSet queryMetaNames();
+    Value * queryMeta(const string & name);
+    string queryMetaString(const string & name);
+    int queryMetaInt(const string & name, int def);
+    bool queryMetaBool(const string & name, bool def);
+    void setMeta(const string & name, Value * v);
 
-    string queryDrvPath(EvalState & state) const;
-    string queryOutPath(EvalState & state) const;
-    string queryOutputName(EvalState & state) const;
-    Outputs queryOutputs(EvalState & state);
+    /*
     MetaInfo queryMetaInfo(EvalState & state) const;
     MetaValue queryMetaInfo(EvalState & state, const string & name) const;
+    */
 
     void setDrvPath(const string & s)
     {
@@ -66,8 +66,6 @@ public:
         outPath = s;
     }
 
-    void setMetaInfo(const MetaInfo & meta);
-
     void setFailed() { failed = true; };
     bool hasFailed() { return failed; };
 };
diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc
index f0c833c0b2..2d548ba1ab 100644
--- a/src/nix-env/nix-env.cc
+++ b/src/nix-env/nix-env.cc
@@ -210,21 +210,13 @@ static Path getDefNixExprPath()
 }
 
 
-static int getPriority(EvalState & state, const DrvInfo & drv)
+static int getPriority(EvalState & state, DrvInfo & drv)
 {
-    MetaValue value = drv.queryMetaInfo(state, "priority");
-    int prio = 0;
-    if (value.type == MetaValue::tpInt) prio = value.intValue;
-    else if (value.type == MetaValue::tpString)
-        /* Backwards compatibility.  Priorities used to be strings
-           before we had support for integer meta field. */
-        string2Int(value.stringValue, prio);
-    return prio;
+    return drv.queryMetaInt("priority", 0);
 }
 
 
-static int comparePriorities(EvalState & state,
-    const DrvInfo & drv1, const DrvInfo & drv2)
+static int comparePriorities(EvalState & state, DrvInfo & drv1, DrvInfo & drv2)
 {
     return getPriority(state, drv2) - getPriority(state, drv1);
 }
@@ -232,9 +224,9 @@ static int comparePriorities(EvalState & state,
 
 // FIXME: this function is rather slow since it checks a single path
 // at a time.
-static bool isPrebuilt(EvalState & state, const DrvInfo & elem)
+static bool isPrebuilt(EvalState & state, DrvInfo & elem)
 {
-    Path path = elem.queryOutPath(state);
+    Path path = elem.queryOutPath();
     if (store->isValidPath(path)) return true;
     PathSet ps = store->querySubstitutablePaths(singleton<PathSet>(path));
     return ps.find(path) != ps.end();
@@ -296,7 +288,8 @@ static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems,
                 }
 
                 if (d > 0) {
-                    newest[drvName.name] = *j;
+                    newest.erase(drvName.name);
+                    newest.insert(Newest::value_type(drvName.name, *j));
                     multiple.erase(j->first.name);
                 } else if (d == 0) {
                     multiple.insert(j->first.name);
@@ -392,18 +385,16 @@ static void queryInstSources(EvalState & state,
            derivations). */
         case srcStorePaths: {
 
-            for (Strings::const_iterator i = args.begin();
-                 i != args.end(); ++i)
-            {
+            foreach (Strings::const_iterator, i, args) {
                 Path path = followLinksToStorePath(*i);
 
-                DrvInfo elem;
-                elem.attrs = new Bindings;
                 string name = baseNameOf(path);
                 string::size_type dash = name.find('-');
                 if (dash != string::npos)
                     name = string(name, dash + 1);
 
+                DrvInfo elem(state, name, "", "", 0);
+
                 if (isDerivation(path)) {
                     elem.setDrvPath(path);
                     elem.setOutPath(findOutput(derivationFromPath(*store, path), "out"));
@@ -413,8 +404,6 @@ static void queryInstSources(EvalState & state,
                 }
                 else elem.setOutPath(path);
 
-                elem.name = name;
-
                 elems.push_back(elem);
             }
 
@@ -444,25 +433,24 @@ static void queryInstSources(EvalState & state,
 }
 
 
-static void printMissing(EvalState & state, const DrvInfos & elems)
+static void printMissing(EvalState & state, DrvInfos & elems)
 {
     PathSet targets;
-    foreach (DrvInfos::const_iterator, i, elems) {
-        Path drvPath = i->queryDrvPath(state);
+    foreach (DrvInfos::iterator, i, elems) {
+        Path drvPath = i->queryDrvPath();
         if (drvPath != "")
             targets.insert(drvPath);
         else
-            targets.insert(i->queryOutPath(state));
+            targets.insert(i->queryOutPath());
     }
 
     printMissing(*store, targets);
 }
 
 
-static bool keep(MetaInfo & meta)
+static bool keep(DrvInfo & drv)
 {
-    MetaValue value = meta["keep"];
-    return value.type == MetaValue::tpString && value.stringValue == "true";
+    return drv.queryMetaBool("keep", false);
 }
 
 
@@ -504,10 +492,9 @@ static void installDerivations(Globals & globals,
 
             foreach (DrvInfos::iterator, i, installedElems) {
                 DrvName drvName(i->name);
-                MetaInfo meta = i->queryMetaInfo(globals.state);
                 if (!globals.preserveInstalled &&
                     newNames.find(drvName.name) != newNames.end() &&
-                    !keep(meta))
+                    !keep(*i))
                     printMsg(lvlInfo, format("replacing old `%1%'") % i->name);
                 else
                     allElems.push_back(*i);
@@ -573,8 +560,7 @@ static void upgradeDerivations(Globals & globals,
 
             try {
 
-                MetaInfo meta = i->queryMetaInfo(globals.state);
-                if (keep(meta)) {
+                if (keep(*i)) {
                     newElems.push_back(*i);
                     continue;
                 }
@@ -611,8 +597,8 @@ static void upgradeDerivations(Globals & globals,
                 }
 
                 if (bestElem != availElems.end() &&
-                    i->queryOutPath(globals.state) !=
-                    bestElem->queryOutPath(globals.state))
+                    i->queryOutPath() !=
+                    bestElem->queryOutPath())
                 {
                     printMsg(lvlInfo,
                         format("upgrading `%1%' to `%2%'")
@@ -657,12 +643,9 @@ static void opUpgrade(Globals & globals,
 static void setMetaFlag(EvalState & state, DrvInfo & drv,
     const string & name, const string & value)
 {
-    MetaInfo meta = drv.queryMetaInfo(state);
-    MetaValue v;
-    v.type = MetaValue::tpString;
-    v.stringValue = value;
-    meta[name] = v;
-    drv.setMetaInfo(meta);
+    Value * v = state.allocValue();
+    mkString(*v, value.c_str());
+    drv.setMeta(name, v);
 }
 
 
@@ -689,8 +672,7 @@ static void opSetFlag(Globals & globals,
             DrvName drvName(i->name);
             foreach (DrvNames::iterator, j, selectors)
                 if (j->matches(drvName)) {
-                    printMsg(lvlInfo,
-                        format("setting flag on `%1%'") % i->name);
+                    printMsg(lvlInfo, format("setting flag on `%1%'") % i->name);
                     setMetaFlag(globals.state, *i, flagName, flagValue);
                     break;
                 }
@@ -720,20 +702,20 @@ static void opSet(Globals & globals,
 
     DrvInfo & drv(elems.front());
 
-    if (drv.queryDrvPath(globals.state) != "") {
-        PathSet paths = singleton<PathSet>(drv.queryDrvPath(globals.state));
+    if (drv.queryDrvPath() != "") {
+        PathSet paths = singleton<PathSet>(drv.queryDrvPath());
         printMissing(*store, paths);
         if (globals.dryRun) return;
         store->buildPaths(paths, globals.state.repair);
     }
     else {
-        printMissing(*store, singleton<PathSet>(drv.queryOutPath(globals.state)));
+        printMissing(*store, singleton<PathSet>(drv.queryOutPath()));
         if (globals.dryRun) return;
-        store->ensurePath(drv.queryOutPath(globals.state));
+        store->ensurePath(drv.queryOutPath());
     }
 
     debug(format("switching to new user environment"));
-    Path generation = createGeneration(globals.profile, drv.queryOutPath(globals.state));
+    Path generation = createGeneration(globals.profile, drv.queryOutPath());
     switchLink(globals.profile, generation);
 }
 
@@ -753,7 +735,7 @@ static void uninstallDerivations(Globals & globals, Strings & selectors,
             foreach (Strings::iterator, j, selectors)
                 /* !!! the repeated calls to followLinksToStorePath()
                    are expensive, should pre-compute them. */
-                if ((isPath(*j) && i->queryOutPath(globals.state) == followLinksToStorePath(*j))
+                if ((isPath(*j) && i->queryOutPath() == followLinksToStorePath(*j))
                     || DrvName(*j).matches(drvName))
                 {
                     printMsg(lvlInfo, format("uninstalling `%1%'") % i->name);
@@ -887,18 +869,12 @@ static void queryJSON(Globals & globals, vector<DrvInfo> & elems)
 
         pkgObj.attr("meta");
         JSONObject metaObj(cout);
-        MetaInfo meta = i->queryMetaInfo(globals.state);
-        foreach (MetaInfo::iterator, j, meta) {
-            metaObj.attr(j->first);
-            if (j->second.type == MetaValue::tpString) {
-                escapeJSON(cout, j->second.stringValue);
-            } else if (j->second.type == MetaValue::tpInt) {
-                cout << j->second.intValue;
-            } else if (j->second.type == MetaValue::tpStrings) {
-                JSONList l(cout);
-                foreach (Strings::iterator, k, j->second.stringValues)
-                    l.elem(*k);
-            }
+        StringSet metaNames = i->queryMetaNames();
+        foreach (StringSet::iterator, j, metaNames) {
+            metaObj.attr(*j);
+            Value * v = i->queryMeta(*j);
+            PathSet context;
+            printValueAsJSON(globals.state, true, *v, cout, context);
         }
     }
 }
@@ -983,7 +959,7 @@ static void opQuery(Globals & globals,
     if (printStatus) {
         for (DrvInfos::iterator i = installedElems.begin();
              i != installedElems.end(); ++i)
-            installed.insert(i->queryOutPath(globals.state));
+            installed.insert(i->queryOutPath());
     }
 
 
@@ -993,7 +969,7 @@ static void opQuery(Globals & globals,
         PathSet paths;
         foreach (vector<DrvInfo>::iterator, i, elems)
             try {
-                paths.insert(i->queryOutPath(globals.state));
+                paths.insert(i->queryOutPath());
             } catch (AssertionError & e) {
                 printMsg(lvlTalkative, format("skipping derivation named `%1%' which gives an assertion failure") % i->name);
                 i->setFailed();
@@ -1021,8 +997,8 @@ static void opQuery(Globals & globals,
             startNest(nest, lvlDebug, format("outputting query result `%1%'") % i->attrPath);
 
             if (globals.prebuiltOnly &&
-                validPaths.find(i->queryOutPath(globals.state)) == validPaths.end() &&
-                substitutablePaths.find(i->queryOutPath(globals.state)) == substitutablePaths.end())
+                validPaths.find(i->queryOutPath()) == validPaths.end() &&
+                substitutablePaths.find(i->queryOutPath()) == substitutablePaths.end())
                 continue;
 
             /* For table output. */
@@ -1032,7 +1008,7 @@ static void opQuery(Globals & globals,
             XMLAttrs attrs;
 
             if (printStatus) {
-                Path outPath = i->queryOutPath(globals.state);
+                Path outPath = i->queryOutPath();
                 bool hasSubs = substitutablePaths.find(outPath) != substitutablePaths.end();
                 bool isInstalled = installed.find(outPath) != installed.end();
                 bool isValid = validPaths.find(outPath) != validPaths.end();
@@ -1093,7 +1069,7 @@ static void opQuery(Globals & globals,
                 columns.push_back(i->system);
 
             if (printDrvPath) {
-                string drvPath = i->queryDrvPath(globals.state);
+                string drvPath = i->queryDrvPath();
                 if (xmlOutput) {
                     if (drvPath != "") attrs["drvPath"] = drvPath;
                 } else
@@ -1101,7 +1077,7 @@ static void opQuery(Globals & globals,
             }
 
             if (printOutPath && !xmlOutput) {
-                DrvInfo::Outputs outputs = i->queryOutputs(globals.state);
+                DrvInfo::Outputs outputs = i->queryOutputs();
                 string s;
                 foreach (DrvInfo::Outputs::iterator, j, outputs) {
                     if (!s.empty()) s += ';';
@@ -1112,9 +1088,7 @@ static void opQuery(Globals & globals,
             }
 
             if (printDescription) {
-                MetaInfo meta = i->queryMetaInfo(globals.state);
-                MetaValue value = meta["description"];
-                string descr = value.type == MetaValue::tpString ? value.stringValue : "";
+                string descr = i->queryMetaString("description");
                 if (xmlOutput) {
                     if (descr != "") attrs["description"] = descr;
                 } else
@@ -1125,7 +1099,7 @@ static void opQuery(Globals & globals,
                 if (printOutPath || printMeta) {
                     XMLOpenElement item(xml, "item", attrs);
                     if (printOutPath) {
-                        DrvInfo::Outputs outputs = i->queryOutputs(globals.state);
+                        DrvInfo::Outputs outputs = i->queryOutputs();
                         foreach (DrvInfo::Outputs::iterator, j, outputs) {
                             XMLAttrs attrs2;
                             attrs2["name"] = j->first;
@@ -1134,26 +1108,32 @@ static void opQuery(Globals & globals,
                         }
                     }
                     if (printMeta) {
-                        MetaInfo meta = i->queryMetaInfo(globals.state);
-                        foreach (MetaInfo::iterator, j, meta) {
+                        StringSet metaNames = i->queryMetaNames();
+                        foreach (StringSet::iterator, j, metaNames) {
                             XMLAttrs attrs2;
-                            attrs2["name"] = j->first;
-                            if (j->second.type == MetaValue::tpString) {
+                            attrs2["name"] = *j;
+                            Value & v(*i->queryMeta(*j));
+                            if (v.type == tString) {
                                 attrs2["type"] = "string";
-                                attrs2["value"] = j->second.stringValue;
+                                attrs2["value"] = v.string.s;
                                 xml.writeEmptyElement("meta", attrs2);
-                            } else if (j->second.type == MetaValue::tpInt) {
+                            } else if (v.type == tInt) {
                                 attrs2["type"] = "int";
-                                attrs2["value"] = (format("%1%") % j->second.intValue).str();
+                                attrs2["value"] = (format("%1%") % v.integer).str();
+                                xml.writeEmptyElement("meta", attrs2);
+                            } else if (v.type == tBool) {
+                                attrs2["type"] = "bool";
+                                attrs2["value"] = v.boolean ? "true" : "false";
                                 xml.writeEmptyElement("meta", attrs2);
-                            } else if (j->second.type == MetaValue::tpStrings) {
+                            } else if (v.type == tList) {
                                 attrs2["type"] = "strings";
                                 XMLOpenElement m(xml, "meta", attrs2);
-                                foreach (Strings::iterator, k, j->second.stringValues) {
+                                for (unsigned int j = 0; j < v.list.length; ++j) {
+                                    string s = globals.state.forceStringNoCtx(*v.list.elems[j]);
                                     XMLAttrs attrs3;
-                                    attrs3["value"] = *k;
+                                    attrs3["value"] = s;
                                     xml.writeEmptyElement("string", attrs3);
-                               }
+                                }
                             }
                         }
                     }
@@ -1166,6 +1146,9 @@ static void opQuery(Globals & globals,
 
         } catch (AssertionError & e) {
             printMsg(lvlTalkative, format("skipping derivation named `%1%' which gives an assertion failure") % i->name);
+        } catch (Error & e) {
+            e.addPrefix(format("while querying the derivation named `%1%':\n") % i->name);
+            throw;
         }
     }
 
diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc
index 64ddaadb99..0ef28a12e7 100644
--- a/src/nix-env/user-env.cc
+++ b/src/nix-env/user-env.cc
@@ -5,6 +5,7 @@
 #include "globals.hh"
 #include "shared.hh"
 #include "eval.hh"
+#include "eval-inline.hh"
 #include "profiles.hh"
 
 
@@ -32,9 +33,9 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
     /* 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));
+    foreach (DrvInfos::iterator, i, elems)
+        if (i->queryDrvPath() != "")
+            drvsToBuild.insert(i->queryDrvPath());
 
     debug(format("building user environment dependencies"));
     store->buildPaths(drvsToBuild, state.repair);
@@ -48,7 +49,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
         /* Create a pseudo-derivation containing the name, system,
            output paths, and optionally the derivation path, as well
            as the meta attributes. */
-        Path drvPath = keepDerivations ? i->queryDrvPath(state) : "";
+        Path drvPath = keepDerivations ? i->queryDrvPath() : "";
 
         Value & v(*state.allocValue());
         manifest.list.elems[n++] = &v;
@@ -58,12 +59,12 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
         mkString(*state.allocAttr(v, state.sName), i->name);
         if (!i->system.empty())
             mkString(*state.allocAttr(v, state.sSystem), i->system);
-        mkString(*state.allocAttr(v, state.sOutPath), i->queryOutPath(state));
+        mkString(*state.allocAttr(v, state.sOutPath), i->queryOutPath());
         if (drvPath != "")
-            mkString(*state.allocAttr(v, state.sDrvPath), i->queryDrvPath(state));
+            mkString(*state.allocAttr(v, state.sDrvPath), i->queryDrvPath());
 
         // Copy each output.
-        DrvInfo::Outputs outputs = i->queryOutputs(state);
+        DrvInfo::Outputs outputs = i->queryOutputs();
         Value & vOutputs = *state.allocAttr(v, state.sOutputs);
         state.mkList(vOutputs, outputs.size());
         unsigned int m = 0;
@@ -84,28 +85,12 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
         // Copy the meta attributes.
         Value & vMeta = *state.allocAttr(v, state.sMeta);
         state.mkAttrs(vMeta, 16);
-
-        MetaInfo meta = i->queryMetaInfo(state);
-
-        foreach (MetaInfo::const_iterator, j, meta) {
-            Value & v2(*state.allocAttr(vMeta, 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.allocValue();
-                        mkString(*v2.list.elems[m++], *k);
-                    }
-                    break;
-                }
-                default: abort();
-            }
+        StringSet metaNames = i->queryMetaNames();
+        foreach (StringSet::iterator, j, metaNames) {
+            Value * v = i->queryMeta(*j);
+            state.strictForceValue(*v); // FIXME
+            vMeta.attrs->push_back(Attr(state.symbols.create(*j), v));
         }
-
-        vMeta.attrs->sort();
         v.attrs->sort();
 
         if (drvPath != "") references.insert(drvPath);
@@ -133,13 +118,14 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
 
     /* Evaluate it. */
     debug("evaluating user environment builder");
-    DrvInfo topLevelDrv;
-    if (!getDerivation(state, topLevel, topLevelDrv, false))
-        abort();
+    state.forceValue(topLevel);
+    PathSet context;
+    Path topLevelDrv = state.coerceToPath(*topLevel.attrs->find(state.sDrvPath)->value, context);
+    Path topLevelOut = state.coerceToPath(*topLevel.attrs->find(state.sOutPath)->value, context);
 
     /* Realise the resulting store expression. */
     debug("building user environment");
-    store->buildPaths(singleton<PathSet>(topLevelDrv.queryDrvPath(state)), state.repair);
+    store->buildPaths(singleton<PathSet>(topLevelDrv), state.repair);
 
     /* Switch the current user environment to the output path. */
     PathLocks lock;
@@ -152,7 +138,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
     }
 
     debug(format("switching to new user environment"));
-    Path generation = createGeneration(profile, topLevelDrv.queryOutPath(state));
+    Path generation = createGeneration(profile, topLevelOut);
     switchLink(profile, generation);
 
     return true;
diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc
index fd2c04eae4..b7f944062c 100644
--- a/src/nix-instantiate/nix-instantiate.cc
+++ b/src/nix-instantiate/nix-instantiate.cc
@@ -63,10 +63,10 @@ void processExpr(EvalState & state, const Strings & attrPaths,
             DrvInfos drvs;
             getDerivations(state, v, "", autoArgs, drvs, false);
             foreach (DrvInfos::iterator, i, drvs) {
-                Path drvPath = i->queryDrvPath(state);
+                Path drvPath = i->queryDrvPath();
 
                 /* What output do we want? */
-                string outputName = i->queryOutputName(state);
+                string outputName = i->queryOutputName();
                 if (outputName == "")
                     throw Error(format("derivation `%1%' lacks an `outputName' attribute ") % drvPath);