diff options
author | Eelco Dolstra <e.dolstra@tudelft.nl> | 2006-05-01 09:56:56 +0000 |
---|---|---|
committer | Eelco Dolstra <e.dolstra@tudelft.nl> | 2006-05-01 09:56:56 +0000 |
commit | 6cecad2be0f7ced82658ec2a86bcf61583487959 (patch) | |
tree | e412fb161e0cfac4bffeae6bfd96cff674a0f947 /src/libexpr/eval.cc | |
parent | cce31b739c6d3e381824ac6fde3f06ccb02782af (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.cc | 114 |
1 files changed, 103 insertions, 11 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. */ |