about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libexpr/get-drvs.cc100
-rw-r--r--src/libexpr/get-drvs.hh3
-rw-r--r--src/nix-instantiate/main.cc15
3 files changed, 87 insertions, 31 deletions
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index eaab867a6f..fbcbb90140 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -53,49 +53,96 @@ bool getDerivation(EvalState & state, Expr e, DrvInfo & drv)
 
 
 static void getDerivations(EvalState & state, Expr e,
-    DrvInfos & drvs, Exprs & doneExprs)
+    DrvInfos & drvs, Exprs & doneExprs, const string & attrPath)
 {
+    /* Automatically call functions that have defaults for all
+       arguments. */
+    ATermList formals;
+    ATerm body, pos;
+    if (matchFunction(e, formals, body, pos)) {
+        for (ATermIterator i(formals); i; ++i) {
+            Expr name, def;
+            if (matchNoDefFormal(*i, name))
+                throw Error(format("expression evaluates to a function with no-default arguments (`%1%')")
+                    % aterm2String(name));
+            else if (!matchDefFormal(*i, name, def))
+                abort(); /* can't happen */
+        }
+        getDerivations(state,
+            makeCall(e, makeAttrs(ATermMap())),
+            drvs, doneExprs, attrPath);
+        return;
+    }
+
+    /* Parse the start of attrPath. */
+    enum { apNone, apAttr, apIndex } apType;
+    string attrPathRest;
+    string attr;
+    int attrIndex;
+    Error attrError =
+        Error(format("attribute selection path `%1%' does not match expression") % attrPath);
+
+    if (attrPath.empty())
+        apType = apNone;
+    else {
+        string::size_type dot = attrPath.find(".");
+        if (dot == string::npos) {
+            attrPathRest = "";
+            attr = attrPath;
+        } else {
+            attrPathRest = string(attrPath, dot + 1);
+            attr = string(attrPath, 0, dot);
+        }
+        apType = apAttr;
+        if (string2Int(attr, attrIndex)) apType = apIndex;
+    }
+
+    /* Process the expression. */
     ATermList es;
     DrvInfo drv;
 
     e = evalExpr(state, e);
 
-    if (getDerivation(state, e, drv)) {
-        drvs.push_back(drv);
+    if (getDerivation(state, e, drvs, doneExprs)) {
+        if (apType != apNone) throw attrError;
         return;
     }
 
     if (matchAttrs(e, es)) {
+        if (apType != apNone && apType != apAttr) throw attrError;
         ATermMap drvMap;
         queryAllAttrs(e, drvMap);
-        for (ATermIterator i(drvMap.keys()); i; ++i) {
-            debug(format("evaluating attribute `%1%'") % aterm2String(*i));
-            getDerivation(state, drvMap.get(*i), drvs, doneExprs);
+        if (apType == apNone) {
+            for (ATermIterator i(drvMap.keys()); i; ++i) {
+                debug(format("evaluating attribute `%1%'") % aterm2String(*i));
+                getDerivation(state, drvMap.get(*i), drvs, doneExprs);
+            }
+        } else {
+            Expr e2 = drvMap.get(attr);
+            if (!e2) throw Error(format("attribute `%1%' in selection path not found") % attr);
+            debug(format("evaluating attribute `%1%'") % attr);
+            getDerivation(state, e2, drvs, doneExprs);
+            if (!attrPath.empty())
+                getDerivations(state, e2, drvs, doneExprs, attrPathRest);
         }
         return;
     }
 
     if (matchList(e, es)) {
-        for (ATermIterator i(es); i; ++i) {
+        if (apType != apNone && apType != apIndex) throw attrError;
+        if (apType == apNone) {
+            for (ATermIterator i(es); i; ++i) {
+                debug(format("evaluating list element"));
+                if (!getDerivation(state, *i, drvs, doneExprs))
+                    getDerivations(state, *i, drvs, doneExprs, attrPathRest);
+            }
+        } else {
+            Expr e2 = ATelementAt(es, attrIndex);
+            if (!e2) throw Error(format("list index %1% in selection path not found") % attrIndex);
             debug(format("evaluating list element"));
-            if (!getDerivation(state, *i, drvs, doneExprs))
-                getDerivations(state, *i, drvs, doneExprs);
-        }
-        return;
-    }
-
-    ATermList formals;
-    ATerm body, pos;
-    if (matchFunction(e, formals, body, pos)) {
-        for (ATermIterator i(formals); i; ++i) {
-            Expr name, def;
-            if (matchNoDefFormal(*i, name))
-                throw Error(format("expression evaluates to a function with no-default arguments (`%1%')")
-                    % aterm2String(name));
-            else if (!matchDefFormal(*i, name, def))
-                abort(); /* can't happen */
+            if (!getDerivation(state, e2, drvs, doneExprs))
+                getDerivations(state, e2, drvs, doneExprs, attrPathRest);
         }
-        getDerivations(state, makeCall(e, makeAttrs(ATermMap())), drvs, doneExprs);
         return;
     }
 
@@ -103,8 +150,9 @@ static void getDerivations(EvalState & state, Expr e,
 }
 
 
-void getDerivations(EvalState & state, Expr e, DrvInfos & drvs)
+void getDerivations(EvalState & state, Expr e, DrvInfos & drvs,
+    const string & attrPath)
 {
     Exprs doneExprs;
-    getDerivations(state, e, drvs, doneExprs);
+    getDerivations(state, e, drvs, doneExprs, attrPath);
 }
diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh
index bfcff8cca3..5b1c0e6d4c 100644
--- a/src/libexpr/get-drvs.hh
+++ b/src/libexpr/get-drvs.hh
@@ -58,7 +58,8 @@ typedef list<DrvInfo> DrvInfos;
    Otherwise, return false. */
 bool getDerivation(EvalState & state, Expr e, DrvInfo & drv);
 
-void getDerivations(EvalState & state, Expr e, DrvInfos & drvs);
+void getDerivations(EvalState & state, Expr e, DrvInfos & drvs,
+    const string & attrPath = "");
 
 
 #endif /* !__GET_DRVS_H */
diff --git a/src/nix-instantiate/main.cc b/src/nix-instantiate/main.cc
index 75d03f329b..7668958fca 100644
--- a/src/nix-instantiate/main.cc
+++ b/src/nix-instantiate/main.cc
@@ -33,13 +33,14 @@ static int rootNr = 0;
 static bool indirectRoot = false;
 
 
-static void printResult(EvalState & state, Expr e, bool evalOnly)
+static void printResult(EvalState & state, Expr e, bool evalOnly,
+    const string & attrPath)
 {
     if (evalOnly)
         cout << format("%1%\n") % e;
     else {
         DrvInfos drvs;
-        getDerivations(state, e, drvs);
+        getDerivations(state, e, drvs, attrPath);
         for (DrvInfos::iterator i = drvs.begin(); i != drvs.end(); ++i) {
             Path drvPath = i->queryDrvPath(state);
             if (gcRoot == "")
@@ -61,6 +62,7 @@ void run(Strings args)
     bool readStdin = false;
     bool evalOnly = false;
     bool parseOnly = false;
+    string attrPath;
 
     for (Strings::iterator i = args.begin();
          i != args.end(); )
@@ -82,6 +84,11 @@ void run(Strings args)
                 throw UsageError("`--add-root requires an argument");
             gcRoot = absPath(*i++);
         }
+        else if (arg == "--attr") {
+            if (i == args.end())
+                throw UsageError("`--attr requires an argument");
+            attrPath = *i++;
+        }
         else if (arg == "--indirect")
             indirectRoot = true;
         else if (arg[0] == '-')
@@ -94,7 +101,7 @@ void run(Strings args)
 
     if (readStdin) {
         Expr e = evalStdin(state, parseOnly);
-        printResult(state, e, evalOnly);
+        printResult(state, e, evalOnly, attrPath);
     }
 
     for (Strings::iterator i = files.begin();
@@ -104,7 +111,7 @@ void run(Strings args)
         Expr e = parseOnly
             ? parseExprFromFile(state, path)
             : evalFile(state, path);
-        printResult(state, e, evalOnly);
+        printResult(state, e, evalOnly, attrPath);
     }
 
     printEvalStats(state);