* Allow string concatenations involving derivations, e.g.,

configureFlags = "--with-freetype2-library="
      + freetype + "/lib";
This commit is contained in:
Eelco Dolstra 2006-05-01 09:56:56 +00:00
parent cce31b739c
commit 6cecad2be0
8 changed files with 130 additions and 18 deletions

View file

@ -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. */

View file

@ -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);

View file

@ -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;

View file

@ -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

View file

@ -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)));
}

View file

@ -18,8 +18,9 @@ let {
name = "dependencies";
system = "@system@";
builder = "@shell@";
args = ["-e" "-x" ./dependencies.builder0.sh];
inherit input1 input2;
args = ["-e" "-x" (./dependencies.builder0.sh + "/FOOBAR/../.")];
input1 = input1 + "/.";
inherit input2;
};
}

View file

@ -1 +1 @@
Str("foobar/a/b/c/d")
Str("foobar/a/b/c/d/foo/xyzzy/foo.txt/../foo/x/y")

View file

@ -1 +1 @@
"foo" + "bar" + toString (/a/b + /c/d)
"foo" + "bar" + toString (/a/b + /c/d) + (/foo/bar + "/../xyzzy/." + "/foo.txt") + ("/../foo" + /x/y)