forked from lix-project/lix
* Allow string concatenations involving derivations, e.g.,
configureFlags = "--with-freetype2-library=" + freetype + "/lib";
This commit is contained in:
parent
cce31b739c
commit
6cecad2be0
|
@ -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 evalExpr2(EvalState & state, Expr e)
|
||||||
{
|
{
|
||||||
Expr e1, e2, e3, e4;
|
Expr e1, e2, e3, e4;
|
||||||
|
@ -167,7 +264,8 @@ Expr evalExpr2(EvalState & state, Expr e)
|
||||||
sym == symFunction1 ||
|
sym == symFunction1 ||
|
||||||
sym == symAttrs ||
|
sym == symAttrs ||
|
||||||
sym == symList ||
|
sym == symList ||
|
||||||
sym == symPrimOp)
|
sym == symPrimOp ||
|
||||||
|
sym == symContext)
|
||||||
return e;
|
return e;
|
||||||
|
|
||||||
/* The `Closed' constructor is just a way to prevent substitutions
|
/* The `Closed' constructor is just a way to prevent substitutions
|
||||||
|
@ -338,16 +436,10 @@ Expr evalExpr2(EvalState & state, Expr e)
|
||||||
|
|
||||||
/* String or path concatenation. */
|
/* String or path concatenation. */
|
||||||
if (matchOpPlus(e, e1, e2)) {
|
if (matchOpPlus(e, e1, e2)) {
|
||||||
e1 = evalExpr(state, e1);
|
ATermVector args;
|
||||||
e2 = evalExpr(state, e2);
|
args.push_back(e1);
|
||||||
ATerm s1, s2;
|
args.push_back(e2);
|
||||||
if (matchStr(e1, s1) && matchStr(e2, s2))
|
return concatStrings(state, args);
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* List concatenation. */
|
/* List concatenation. */
|
||||||
|
|
|
@ -59,6 +59,11 @@ bool evalBool(EvalState & state, Expr e);
|
||||||
ATermList evalList(EvalState & state, Expr e);
|
ATermList evalList(EvalState & state, Expr e);
|
||||||
ATerm coerceToString(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. */
|
/* Print statistics. */
|
||||||
void printEvalStats(EvalState & state);
|
void printEvalStats(EvalState & state);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
typedef set<Expr> Exprs;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ Closed | Expr | Expr |
|
||||||
Rec | ATermList ATermList | Expr |
|
Rec | ATermList ATermList | Expr |
|
||||||
Bool | ATerm | Expr |
|
Bool | ATerm | Expr |
|
||||||
Null | | Expr |
|
Null | | Expr |
|
||||||
|
Context | ATermList Expr | Expr |
|
||||||
|
|
||||||
Bind | string Expr Pos | ATerm |
|
Bind | string Expr Pos | ATerm |
|
||||||
Bind | string Expr | ATerm | Bind2
|
Bind | string Expr | ATerm | Bind2
|
||||||
|
|
|
@ -109,6 +109,14 @@ static void processBinding(EvalState & state, Expr e, Derivation & drv,
|
||||||
int n;
|
int n;
|
||||||
Expr e1, e2;
|
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));
|
if (matchStr(e, s)) ss.push_back(aterm2String(s));
|
||||||
else if (matchUri(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");
|
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. */
|
/* Convert the argument (which can be a path or a uri) to a string. */
|
||||||
static Expr primToString(EvalState & state, const ATermVector & args)
|
static Expr primToString(EvalState & state, const ATermVector & args)
|
||||||
{
|
{
|
||||||
ATerm s = coerceToString(evalExpr(state, args[0]));
|
ATermList context = ATempty;
|
||||||
if (!s) throw Error("cannot coerce value to string");
|
bool dummy;
|
||||||
return makeStr(s);
|
string s = coerceToStringWithContext(state, context, args[0], dummy);
|
||||||
|
return wrapInContext(context, makeStr(toATerm(s)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,9 @@ let {
|
||||||
name = "dependencies";
|
name = "dependencies";
|
||||||
system = "@system@";
|
system = "@system@";
|
||||||
builder = "@shell@";
|
builder = "@shell@";
|
||||||
args = ["-e" "-x" ./dependencies.builder0.sh];
|
args = ["-e" "-x" (./dependencies.builder0.sh + "/FOOBAR/../.")];
|
||||||
inherit input1 input2;
|
input1 = input1 + "/.";
|
||||||
|
inherit input2;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
|
@ -1 +1 @@
|
||||||
Str("foobar/a/b/c/d")
|
Str("foobar/a/b/c/d/foo/xyzzy/foo.txt/../foo/x/y")
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
"foo" + "bar" + toString (/a/b + /c/d)
|
"foo" + "bar" + toString (/a/b + /c/d) + (/foo/bar + "/../xyzzy/." + "/foo.txt") + ("/../foo" + /x/y)
|
||||||
|
|
Loading…
Reference in a new issue