aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2006-05-01 09:56:56 +0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2006-05-01 09:56:56 +0000
commit6cecad2be0f7ced82658ec2a86bcf61583487959 (patch)
treee412fb161e0cfac4bffeae6bfd96cff674a0f947 /src/libexpr
parentcce31b739c6d3e381824ac6fde3f06ccb02782af (diff)
* Allow string concatenations involving derivations, e.g.,
configureFlags = "--with-freetype2-library=" + freetype + "/lib";
Diffstat (limited to 'src/libexpr')
-rw-r--r--src/libexpr/eval.cc114
-rw-r--r--src/libexpr/eval.hh5
-rw-r--r--src/libexpr/get-drvs.cc4
-rw-r--r--src/libexpr/nixexpr-ast.def1
-rw-r--r--src/libexpr/primops.cc15
5 files changed, 125 insertions, 14 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index fc51590b2..1c2aafd91 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. */
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 8070f4884..54a612b36 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -59,6 +59,11 @@ bool evalBool(EvalState & state, Expr e);
ATermList evalList(EvalState & state, Expr e);
ATerm coerceToString(Expr e);
+/* Contexts. */
+string coerceToStringWithContext(EvalState & state,
+ ATermList & context, Expr e, bool & isPath);
+Expr wrapInContext(ATermList context, Expr e);
+
/* Print statistics. */
void printEvalStats(EvalState & state);
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index 78edbd392..b101f2da3 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -45,6 +45,10 @@ MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const
}
+/* Cache for already evaluated derivations. Usually putting ATerms in
+ a STL container is unsafe (they're not scanning for GC roots), but
+ here it doesn't matter; everything in this set is reachable from
+ the stack as well. */
typedef set<Expr> Exprs;
diff --git a/src/libexpr/nixexpr-ast.def b/src/libexpr/nixexpr-ast.def
index b384ff7c0..fab560c99 100644
--- a/src/libexpr/nixexpr-ast.def
+++ b/src/libexpr/nixexpr-ast.def
@@ -35,6 +35,7 @@ Closed | Expr | Expr |
Rec | ATermList ATermList | Expr |
Bool | ATerm | Expr |
Null | | Expr |
+Context | ATermList Expr | Expr |
Bind | string Expr Pos | ATerm |
Bind | string Expr | ATerm | Bind2
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 3a291e007..8935b147e 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -109,6 +109,14 @@ static void processBinding(EvalState & state, Expr e, Derivation & drv,
int n;
Expr e1, e2;
+ if (matchContext(e, es, e2)) {
+ e = e2;
+ for (ATermIterator i(es); i; ++i) {
+ Strings dummy;
+ processBinding(state, *i, drv, dummy);
+ }
+ }
+
if (matchStr(e, s)) ss.push_back(aterm2String(s));
else if (matchUri(e, s)) ss.push_back(aterm2String(s));
else if (e == eTrue) ss.push_back("1");
@@ -408,9 +416,10 @@ ATerm coerceToString(Expr e)
/* Convert the argument (which can be a path or a uri) to a string. */
static Expr primToString(EvalState & state, const ATermVector & args)
{
- ATerm s = coerceToString(evalExpr(state, args[0]));
- if (!s) throw Error("cannot coerce value to string");
- return makeStr(s);
+ ATermList context = ATempty;
+ bool dummy;
+ string s = coerceToStringWithContext(state, context, args[0], dummy);
+ return wrapInContext(context, makeStr(toATerm(s)));
}