about summary refs log tree commit diff
path: root/src/libexpr/eval.cc
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2006-05-01T09·56+0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2006-05-01T09·56+0000
commit6cecad2be0f7ced82658ec2a86bcf61583487959 (patch)
treee412fb161e0cfac4bffeae6bfd96cff674a0f947 /src/libexpr/eval.cc
parentcce31b739c6d3e381824ac6fde3f06ccb02782af (diff)
* Allow string concatenations involving derivations, e.g.,
    configureFlags = "--with-freetype2-library="
      + freetype + "/lib";

Diffstat (limited to 'src/libexpr/eval.cc')
-rw-r--r--src/libexpr/eval.cc114
1 files changed, 103 insertions, 11 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index fc51590b20cc..1c2aafd91fc9 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -149,6 +149,103 @@ ATermList evalList(EvalState & state, Expr e)
 }
 
 
+/* String concatenation and context nodes: in order to allow users to
+   write things like
+
+     "--with-freetype2-library=" + freetype + "/lib"
+
+   where `freetype' is a derivation, we automatically coerce
+   derivations into their output path (e.g.,
+   /nix/store/hashcode-freetype) in concatenations.  However, if we do
+   this naively, we could introduce an undeclared dependency: when the
+   string is used in another derivation, that derivation would not
+   have an explicitly dependency on `freetype' in its inputDrvs
+   field.  Thus `freetype' would not necessarily be built.
+
+   To prevent this, we wrap the string resulting from the
+   concatenation in a *context node*, like this:
+
+     Context([freetype],
+       Str("--with-freetype2-library=/nix/store/hashcode-freetype/lib"))
+
+   Thus the context is the list of all derivations used in the
+   computation of a value.  These contexts are propagated through
+   further concatenations.  In processBinding() in primops.cc, context
+   nodes are unwrapped and added to inputDrvs.
+
+   !!! Should the ordering of the context list have a canonical form?
+
+   !!! Contexts are not currently recognised in most places in the
+   evaluator. */
+
+
+/* Coerce a value to a string, keeping track of contexts. */
+string coerceToStringWithContext(EvalState & state,
+    ATermList & context, Expr e, bool & isPath)
+{
+    isPath = false;
+    
+    e = evalExpr(state, e);
+
+    ATermList es;
+    ATerm e2;
+    if (matchContext(e, es, e2)) {
+        e = e2;
+        context = ATconcat(es, context);
+    }
+    
+    ATerm s;
+    if (matchStr(e, s) || matchUri(e, s))
+        return aterm2String(s);
+    
+    if (matchPath(e, s)) {
+        isPath = true;
+        return aterm2String(s);
+    }
+
+    if (matchAttrs(e, es)) {
+        ATermMap attrs;
+        queryAllAttrs(e, attrs, false);
+
+        Expr a = attrs.get("type");
+        if (a && evalString(state, a) == "derivation") {
+            a = attrs.get("outPath");
+            if (!a) throw Error("output path missing from derivation");
+            context = ATinsert(context, e);
+            return evalPath(state, a);
+        }
+    }
+    
+    throw Error("cannot coerce value to string");
+}
+
+
+/* Wrap an expression in a context if the context is not empty. */
+Expr wrapInContext(ATermList context, Expr e)
+{
+    return context == ATempty ? e : makeContext(context, e);
+}
+
+
+static ATerm concatStrings(EvalState & state, const ATermVector & args)
+{
+    ATermList context = ATempty;
+    ostringstream s;
+    bool isPath;
+
+    for (ATermVector::const_iterator i = args.begin(); i != args.end(); ++i) {
+        bool isPath2;
+        s << coerceToStringWithContext(state, context, *i, isPath2);
+        if (i == args.begin()) isPath = isPath2;
+    }
+
+    Expr result = isPath
+        ? makePath(toATerm(canonPath(s.str())))
+        : makeStr(toATerm(s.str()));
+    return wrapInContext(context, result);
+}
+
+
 Expr evalExpr2(EvalState & state, Expr e)
 {
     Expr e1, e2, e3, e4;
@@ -167,7 +264,8 @@ Expr evalExpr2(EvalState & state, Expr e)
         sym == symFunction1 ||
         sym == symAttrs ||
         sym == symList ||
-        sym == symPrimOp)
+        sym == symPrimOp ||
+        sym == symContext)
         return e;
     
     /* The `Closed' constructor is just a way to prevent substitutions
@@ -338,16 +436,10 @@ Expr evalExpr2(EvalState & state, Expr e)
 
     /* String or path concatenation. */
     if (matchOpPlus(e, e1, e2)) {
-        e1 = evalExpr(state, e1);
-        e2 = evalExpr(state, e2);
-        ATerm s1, s2;
-        if (matchStr(e1, s1) && matchStr(e2, s2))
-            return makeStr(toATerm(
-                (string) aterm2String(s1) + (string) aterm2String(s2)));
-        else if (matchPath(e1, s1) && matchPath(e2, s2))
-            return makePath(toATerm(canonPath(
-                (string) aterm2String(s1) + "/" + (string) aterm2String(s2))));
-        else throw Error("wrong argument types in `+' operator");
+        ATermVector args;
+        args.push_back(e1);
+        args.push_back(e2);
+        return concatStrings(state, args);
     }
 
     /* List concatenation. */