* Another experimental feature: a way to truly override attributes in

a rec.  This will be very useful to allow end-user customisation of
  all-packages.nix, for instance globally overriding GCC or some other
  dependency.  The // operator doesn't cut it: you could replace the
  "gcc" attribute, but all other attributes would continue to
  reference the original value due to the substitution semantics of
  rec.

  The syntax is a bit hacky but this is to allow backwards
  compatibility.
This commit is contained in:
Eelco Dolstra 2008-08-14 16:59:37 +00:00
parent 9279174dde
commit ca07f3e370
2 changed files with 25 additions and 2 deletions

View File

@ -170,16 +170,18 @@ static Expr substArgs(EvalState & state,
to attributes substituted with selection expressions on the to attributes substituted with selection expressions on the
original set. E.g., e = `rec {x = f x y; y = x;}' becomes `{x = f original set. E.g., e = `rec {x = f x y; y = x;}' becomes `{x = f
(e.x) (e.y); y = e.x;}'. */ (e.x) (e.y); y = e.x;}'. */
LocalNoInline(ATerm expandRec(ATerm e, ATermList rbnds, ATermList nrbnds)) LocalNoInline(ATerm expandRec(EvalState & state, ATerm e, ATermList rbnds, ATermList nrbnds))
{ {
ATerm name; ATerm name;
Expr e2; Expr e2;
Pos pos; Pos pos;
Expr eOverrides = 0;
/* Create the substitution list. */ /* Create the substitution list. */
ATermMap subs(ATgetLength(rbnds) + ATgetLength(nrbnds)); ATermMap subs(ATgetLength(rbnds) + ATgetLength(nrbnds));
for (ATermIterator i(rbnds); i; ++i) { for (ATermIterator i(rbnds); i; ++i) {
if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */
if (name == sOverrides) eOverrides = e2;
subs.set(name, makeSelect(e, name)); subs.set(name, makeSelect(e, name));
} }
for (ATermIterator i(nrbnds); i; ++i) { for (ATermIterator i(nrbnds); i; ++i) {
@ -187,6 +189,22 @@ LocalNoInline(ATerm expandRec(ATerm e, ATermList rbnds, ATermList nrbnds))
subs.set(name, e2); subs.set(name, e2);
} }
/* If the rec contains an attribute called `__overrides', then
evaluate it, and add the attributes in that set to the rec.
This allows overriding of recursive attributes, which is
otherwise not possible. (You can use the // operator to
replace an attribute, but other attributes in the rec will
still reference the original value, because that value has been
substituted into the bodies of the other attributes. Hence we
need __overrides.) */
ATermMap overrides;
if (eOverrides) {
eOverrides = evalExpr(state, eOverrides);
queryAllAttrs(overrides, overrides, false);
foreach (ATermMap::const_iterator, i, overrides)
subs.set(i->key, i->value);
}
Substitution subs_(0, &subs); Substitution subs_(0, &subs);
/* Create the non-recursive set. */ /* Create the non-recursive set. */
@ -196,6 +214,10 @@ LocalNoInline(ATerm expandRec(ATerm e, ATermList rbnds, ATermList nrbnds))
as.set(name, makeAttrRHS(substitute(subs_, e2), pos)); as.set(name, makeAttrRHS(substitute(subs_, e2), pos));
} }
if (eOverrides)
foreach (ATermMap::const_iterator, i, overrides)
as.set(i->key, makeAttrRHS(i->value, makeNoPos()));
/* Copy the non-recursive bindings. !!! inefficient */ /* Copy the non-recursive bindings. !!! inefficient */
for (ATermIterator i(nrbnds); i; ++i) { for (ATermIterator i(nrbnds); i; ++i) {
if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */ if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */
@ -667,7 +689,7 @@ Expr evalExpr2(EvalState & state, Expr e)
/* Mutually recursive sets. */ /* Mutually recursive sets. */
ATermList rbnds, nrbnds; ATermList rbnds, nrbnds;
if (matchRec(e, rbnds, nrbnds)) if (matchRec(e, rbnds, nrbnds))
return expandRec(e, rbnds, nrbnds); return expandRec(state, e, rbnds, nrbnds);
/* Conditionals. */ /* Conditionals. */
if (matchIf(e, e1, e2, e3)) if (matchIf(e, e1, e2, e3))

View File

@ -93,3 +93,4 @@ AttrRHS | Expr Pos | ATerm |
eTrue = makeBool(makeTrue()) eTrue = makeBool(makeTrue())
eFalse = makeBool(makeFalse()) eFalse = makeBool(makeFalse())
sOverrides = toATerm("__overrides")