forked from lix-project/lix
* Inline some functions and get rid of the indirection through
EvalState::eval(). This gives a 12% speedup on ‘nix-instantiate /etc/nixos/nixos/ -A system --readonly-mode’ (from 1.01s to 0.89s).
This commit is contained in:
parent
2bda12ef3b
commit
f491ae97d4
|
@ -6,7 +6,7 @@ libexpr_la_SOURCES = \
|
||||||
names.cc
|
names.cc
|
||||||
|
|
||||||
pkginclude_HEADERS = \
|
pkginclude_HEADERS = \
|
||||||
nixexpr.hh eval.hh lexer-tab.hh parser-tab.hh \
|
nixexpr.hh eval.hh eval-inline.hh lexer-tab.hh parser-tab.hh \
|
||||||
get-drvs.hh attr-path.hh value-to-xml.hh common-opts.hh \
|
get-drvs.hh attr-path.hh value-to-xml.hh common-opts.hh \
|
||||||
names.hh symbol-table.hh value.hh
|
names.hh symbol-table.hh value.hh
|
||||||
|
|
||||||
|
|
61
src/libexpr/eval-inline.hh
Normal file
61
src/libexpr/eval-inline.hh
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#ifndef __EVAL_INLINE_H
|
||||||
|
#define __EVAL_INLINE_H
|
||||||
|
|
||||||
|
#include "eval.hh"
|
||||||
|
|
||||||
|
#define LocalNoInline(f) static f __attribute__((noinline)); f
|
||||||
|
#define LocalNoInlineNoReturn(f) static f __attribute__((noinline, noreturn)); f
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
|
LocalNoInlineNoReturn(void throwEvalError(const char * s))
|
||||||
|
{
|
||||||
|
throw EvalError(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s2))
|
||||||
|
{
|
||||||
|
throw TypeError(format(s) % s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void EvalState::forceValue(Value & v)
|
||||||
|
{
|
||||||
|
if (v.type == tThunk) {
|
||||||
|
ValueType saved = v.type;
|
||||||
|
try {
|
||||||
|
v.type = tBlackhole;
|
||||||
|
//checkInterrupt();
|
||||||
|
v.thunk.expr->eval(*this, *v.thunk.env, v);
|
||||||
|
} catch (Error & e) {
|
||||||
|
v.type = saved;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (v.type == tApp)
|
||||||
|
callFunction(*v.app.left, *v.app.right, v);
|
||||||
|
else if (v.type == tBlackhole)
|
||||||
|
throwEvalError("infinite recursion encountered");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void EvalState::forceAttrs(Value & v)
|
||||||
|
{
|
||||||
|
forceValue(v);
|
||||||
|
if (v.type != tAttrs)
|
||||||
|
throwTypeError("value is %1% while an attribute set was expected", showType(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void EvalState::forceList(Value & v)
|
||||||
|
{
|
||||||
|
forceValue(v);
|
||||||
|
if (v.type != tList)
|
||||||
|
throwTypeError("value is %1% while a list was expected", showType(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !__EVAL_INLINE_H */
|
|
@ -4,6 +4,7 @@
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
|
#include "eval-inline.hh"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
@ -27,10 +28,6 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#define LocalNoInline(f) static f __attribute__((noinline)); f
|
|
||||||
#define LocalNoInlineNoReturn(f) static f __attribute__((noinline, noreturn)); f
|
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
|
@ -227,11 +224,6 @@ void EvalState::addPrimOp(const string & name,
|
||||||
evaluator. So here are some helper functions for throwing
|
evaluator. So here are some helper functions for throwing
|
||||||
exceptions. */
|
exceptions. */
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const char * s))
|
|
||||||
{
|
|
||||||
throw EvalError(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2))
|
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2))
|
||||||
{
|
{
|
||||||
throw EvalError(format(s) % s2);
|
throw EvalError(format(s) % s2);
|
||||||
|
@ -247,11 +239,6 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s))
|
||||||
throw TypeError(s);
|
throw TypeError(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s2))
|
|
||||||
{
|
|
||||||
throw TypeError(format(s) % s2);
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos, const string & s2))
|
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos, const string & s2))
|
||||||
{
|
{
|
||||||
throw TypeError(format(s) % pos % s2);
|
throw TypeError(format(s) % pos % s2);
|
||||||
|
@ -309,7 +296,7 @@ void mkPath(Value & v, const char * s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Value * EvalState::lookupVar(Env * env, const VarRef & var)
|
inline Value * EvalState::lookupVar(Env * env, const VarRef & var)
|
||||||
{
|
{
|
||||||
for (unsigned int l = var.level; l; --l, env = env->up) ;
|
for (unsigned int l = var.level; l; --l, env = env->up) ;
|
||||||
|
|
||||||
|
@ -435,7 +422,6 @@ Value * ExprPath::maybeThunk(EvalState & state, Env & env)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void EvalState::evalFile(const Path & path, Value & v)
|
void EvalState::evalFile(const Path & path, Value & v)
|
||||||
{
|
{
|
||||||
FileEvalCache::iterator i = fileEvalCache.find(path);
|
FileEvalCache::iterator i = fileEvalCache.find(path);
|
||||||
|
@ -454,61 +440,25 @@ void EvalState::evalFile(const Path & path, Value & v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct RecursionCounter
|
|
||||||
{
|
|
||||||
EvalState & state;
|
|
||||||
RecursionCounter(EvalState & state) : state(state)
|
|
||||||
{
|
|
||||||
state.recursionDepth++;
|
|
||||||
if (state.recursionDepth > state.maxRecursionDepth)
|
|
||||||
state.maxRecursionDepth = state.recursionDepth;
|
|
||||||
}
|
|
||||||
~RecursionCounter()
|
|
||||||
{
|
|
||||||
state.recursionDepth--;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void EvalState::eval(Env & env, Expr * e, Value & v)
|
|
||||||
{
|
|
||||||
/* When changing this function, make sure that you don't cause a
|
|
||||||
(large) increase in stack consumption! */
|
|
||||||
|
|
||||||
/* !!! Disable this eventually. */
|
|
||||||
RecursionCounter r(*this);
|
|
||||||
char x;
|
|
||||||
if (&x < deepestStack) deepestStack = &x;
|
|
||||||
|
|
||||||
//debug(format("eval: %1%") % *e);
|
|
||||||
|
|
||||||
checkInterrupt();
|
|
||||||
|
|
||||||
nrEvaluated++;
|
|
||||||
|
|
||||||
e->eval(*this, env, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void EvalState::eval(Expr * e, Value & v)
|
void EvalState::eval(Expr * e, Value & v)
|
||||||
{
|
{
|
||||||
eval(baseEnv, e, v);
|
e->eval(*this, baseEnv, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool EvalState::evalBool(Env & env, Expr * e)
|
inline bool EvalState::evalBool(Env & env, Expr * e)
|
||||||
{
|
{
|
||||||
Value v;
|
Value v;
|
||||||
eval(env, e, v);
|
e->eval(*this, env, v);
|
||||||
if (v.type != tBool)
|
if (v.type != tBool)
|
||||||
throwTypeError("value is %1% while a Boolean was expected", showType(v));
|
throwTypeError("value is %1% while a Boolean was expected", showType(v));
|
||||||
return v.boolean;
|
return v.boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::evalAttrs(Env & env, Expr * e, Value & v)
|
inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v)
|
||||||
{
|
{
|
||||||
eval(env, e, v);
|
e->eval(*this, env, v);
|
||||||
if (v.type != tAttrs)
|
if (v.type != tAttrs)
|
||||||
throwTypeError("value is %1% while an attribute set was expected", showType(v));
|
throwTypeError("value is %1% while an attribute set was expected", showType(v));
|
||||||
}
|
}
|
||||||
|
@ -622,7 +572,7 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
|
||||||
else
|
else
|
||||||
env2.values[displ++] = i->second.e->maybeThunk(state, env2);
|
env2.values[displ++] = i->second.e->maybeThunk(state, env2);
|
||||||
|
|
||||||
state.eval(env2, body, v);
|
body->eval(state, env2, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -650,7 +600,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
Pos * pos = 0;
|
Pos * pos = 0;
|
||||||
Value * vAttrs = &vTmp;
|
Value * vAttrs = &vTmp;
|
||||||
|
|
||||||
state.eval(env, e, vTmp);
|
e->eval(state, env, vTmp);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
@ -662,7 +612,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
if (vAttrs->type != tAttrs ||
|
if (vAttrs->type != tAttrs ||
|
||||||
(j = vAttrs->attrs->find(*i)) == vAttrs->attrs->end())
|
(j = vAttrs->attrs->find(*i)) == vAttrs->attrs->end())
|
||||||
{
|
{
|
||||||
state.eval(env, def, v);
|
def->eval(state, env, v);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -692,7 +642,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
|
||||||
Value vTmp;
|
Value vTmp;
|
||||||
Value * vAttrs = &vTmp;
|
Value * vAttrs = &vTmp;
|
||||||
|
|
||||||
state.eval(env, e, vTmp);
|
e->eval(state, env, vTmp);
|
||||||
|
|
||||||
foreach (AttrPath::const_iterator, i, attrPath) {
|
foreach (AttrPath::const_iterator, i, attrPath) {
|
||||||
state.forceValue(*vAttrs);
|
state.forceValue(*vAttrs);
|
||||||
|
@ -722,7 +672,7 @@ void ExprLambda::eval(EvalState & state, Env & env, Value & v)
|
||||||
void ExprApp::eval(EvalState & state, Env & env, Value & v)
|
void ExprApp::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
Value vFun;
|
Value vFun;
|
||||||
state.eval(env, e1, vFun);
|
e1->eval(state, env, vFun);
|
||||||
state.callFunction(vFun, *(e2->maybeThunk(state, env)), v);
|
state.callFunction(vFun, *(e2->maybeThunk(state, env)), v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -814,7 +764,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
eval(env2, fun.lambda.fun->body, v);
|
fun.lambda.fun->body->eval(*this, env2, v);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
addErrorPrefix(e, "while evaluating the function at %1%:\n", fun.lambda.fun->pos);
|
addErrorPrefix(e, "while evaluating the function at %1%:\n", fun.lambda.fun->pos);
|
||||||
throw;
|
throw;
|
||||||
|
@ -857,13 +807,13 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v)
|
||||||
env2.values[0] = state.allocValue();
|
env2.values[0] = state.allocValue();
|
||||||
state.evalAttrs(env, attrs, *env2.values[0]);
|
state.evalAttrs(env, attrs, *env2.values[0]);
|
||||||
|
|
||||||
state.eval(env2, body, v);
|
body->eval(state, env2, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExprIf::eval(EvalState & state, Env & env, Value & v)
|
void ExprIf::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
state.eval(env, state.evalBool(env, cond) ? then : else_, v);
|
(state.evalBool(env, cond) ? then : else_)->eval(state, env, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -871,7 +821,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
if (!state.evalBool(env, cond))
|
if (!state.evalBool(env, cond))
|
||||||
throwAssertionError("assertion failed at %1%", pos);
|
throwAssertionError("assertion failed at %1%", pos);
|
||||||
state.eval(env, body, v);
|
body->eval(state, env, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -883,16 +833,16 @@ void ExprOpNot::eval(EvalState & state, Env & env, Value & v)
|
||||||
|
|
||||||
void ExprOpEq::eval(EvalState & state, Env & env, Value & v)
|
void ExprOpEq::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
Value v1; state.eval(env, e1, v1);
|
Value v1; e1->eval(state, env, v1);
|
||||||
Value v2; state.eval(env, e2, v2);
|
Value v2; e2->eval(state, env, v2);
|
||||||
mkBool(v, state.eqValues(v1, v2));
|
mkBool(v, state.eqValues(v1, v2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExprOpNEq::eval(EvalState & state, Env & env, Value & v)
|
void ExprOpNEq::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
Value v1; state.eval(env, e1, v1);
|
Value v1; e1->eval(state, env, v1);
|
||||||
Value v2; state.eval(env, e2, v2);
|
Value v2; e2->eval(state, env, v2);
|
||||||
mkBool(v, !state.eqValues(v1, v2));
|
mkBool(v, !state.eqValues(v1, v2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -954,9 +904,9 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
|
||||||
|
|
||||||
void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
|
void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
Value v1; state.eval(env, e1, v1);
|
Value v1; e1->eval(state, env, v1);
|
||||||
state.forceList(v1);
|
state.forceList(v1);
|
||||||
Value v2; state.eval(env, e2, v2);
|
Value v2; e2->eval(state, env, v2);
|
||||||
state.forceList(v2);
|
state.forceList(v2);
|
||||||
state.mkList(v, v1.list.length + v2.list.length);
|
state.mkList(v, v1.list.length + v2.list.length);
|
||||||
for (unsigned int n = 0; n < v1.list.length; ++n)
|
for (unsigned int n = 0; n < v1.list.length; ++n)
|
||||||
|
@ -975,7 +925,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
Value vStr;
|
Value vStr;
|
||||||
|
|
||||||
foreach (vector<Expr *>::iterator, i, *es) {
|
foreach (vector<Expr *>::iterator, i, *es) {
|
||||||
state.eval(env, *i, vStr);
|
(*i)->eval(state, env, vStr);
|
||||||
|
|
||||||
/* If the first element is a path, then the result will also
|
/* If the first element is a path, then the result will also
|
||||||
be a path, we don't copy anything (yet - that's done later,
|
be a path, we don't copy anything (yet - that's done later,
|
||||||
|
@ -999,25 +949,6 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::forceValue(Value & v)
|
|
||||||
{
|
|
||||||
if (v.type == tThunk) {
|
|
||||||
ValueType saved = v.type;
|
|
||||||
try {
|
|
||||||
v.type = tBlackhole;
|
|
||||||
eval(*v.thunk.env, v.thunk.expr, v);
|
|
||||||
} catch (Error & e) {
|
|
||||||
v.type = saved;
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (v.type == tApp)
|
|
||||||
callFunction(*v.app.left, *v.app.right, v);
|
|
||||||
else if (v.type == tBlackhole)
|
|
||||||
throwEvalError("infinite recursion encountered");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void EvalState::strictForceValue(Value & v)
|
void EvalState::strictForceValue(Value & v)
|
||||||
{
|
{
|
||||||
forceValue(v);
|
forceValue(v);
|
||||||
|
@ -1052,22 +983,6 @@ bool EvalState::forceBool(Value & v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::forceAttrs(Value & v)
|
|
||||||
{
|
|
||||||
forceValue(v);
|
|
||||||
if (v.type != tAttrs)
|
|
||||||
throwTypeError("value is %1% while an attribute set was expected", showType(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void EvalState::forceList(Value & v)
|
|
||||||
{
|
|
||||||
forceValue(v);
|
|
||||||
if (v.type != tList)
|
|
||||||
throwTypeError("value is %1% while a list was expected", showType(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void EvalState::forceFunction(Value & v)
|
void EvalState::forceFunction(Value & v)
|
||||||
{
|
{
|
||||||
forceValue(v);
|
forceValue(v);
|
||||||
|
|
|
@ -138,18 +138,17 @@ public:
|
||||||
/* Evaluate an expression to normal form, storing the result in
|
/* Evaluate an expression to normal form, storing the result in
|
||||||
value `v'. */
|
value `v'. */
|
||||||
void eval(Expr * e, Value & v);
|
void eval(Expr * e, Value & v);
|
||||||
void eval(Env & env, Expr * e, Value & v);
|
|
||||||
|
|
||||||
/* Evaluation the expression, then verify that it has the expected
|
/* Evaluation the expression, then verify that it has the expected
|
||||||
type. */
|
type. */
|
||||||
bool evalBool(Env & env, Expr * e);
|
inline bool evalBool(Env & env, Expr * e);
|
||||||
void evalAttrs(Env & env, Expr * e, Value & v);
|
inline void evalAttrs(Env & env, Expr * e, Value & v);
|
||||||
|
|
||||||
/* If `v' is a thunk, enter it and overwrite `v' with the result
|
/* If `v' is a thunk, enter it and overwrite `v' with the result
|
||||||
of the evaluation of the thunk. If `v' is a delayed function
|
of the evaluation of the thunk. If `v' is a delayed function
|
||||||
application, call the function and overwrite `v' with the
|
application, call the function and overwrite `v' with the
|
||||||
result. Otherwise, this is a no-op. */
|
result. Otherwise, this is a no-op. */
|
||||||
void forceValue(Value & v);
|
inline void forceValue(Value & v);
|
||||||
|
|
||||||
/* Force a value, then recursively force list elements and
|
/* Force a value, then recursively force list elements and
|
||||||
attributes. */
|
attributes. */
|
||||||
|
@ -158,8 +157,8 @@ public:
|
||||||
/* Force `v', and then verify that it has the expected type. */
|
/* Force `v', and then verify that it has the expected type. */
|
||||||
int forceInt(Value & v);
|
int forceInt(Value & v);
|
||||||
bool forceBool(Value & v);
|
bool forceBool(Value & v);
|
||||||
void forceAttrs(Value & v);
|
inline void forceAttrs(Value & v);
|
||||||
void forceList(Value & v);
|
inline void forceList(Value & v);
|
||||||
void forceFunction(Value & v); // either lambda or primop
|
void forceFunction(Value & v); // either lambda or primop
|
||||||
string forceString(Value & v);
|
string forceString(Value & v);
|
||||||
string forceString(Value & v, PathSet & context);
|
string forceString(Value & v, PathSet & context);
|
||||||
|
@ -203,7 +202,7 @@ private:
|
||||||
void addPrimOp(const string & name,
|
void addPrimOp(const string & name,
|
||||||
unsigned int arity, PrimOpFun primOp);
|
unsigned int arity, PrimOpFun primOp);
|
||||||
|
|
||||||
Value * lookupVar(Env * env, const VarRef & var);
|
inline Value * lookupVar(Env * env, const VarRef & var);
|
||||||
|
|
||||||
friend class ExprVar;
|
friend class ExprVar;
|
||||||
friend class ExprAttrs;
|
friend class ExprAttrs;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "get-drvs.hh"
|
#include "get-drvs.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
#include "eval-inline.hh"
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "archive.hh"
|
#include "archive.hh"
|
||||||
#include "value-to-xml.hh"
|
#include "value-to-xml.hh"
|
||||||
#include "names.hh"
|
#include "names.hh"
|
||||||
|
#include "eval-inline.hh"
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
#include "eval.hh"
|
#include "eval.hh"
|
||||||
|
#include "eval-inline.hh"
|
||||||
#include "get-drvs.hh"
|
#include "get-drvs.hh"
|
||||||
#include "attr-path.hh"
|
#include "attr-path.hh"
|
||||||
#include "value-to-xml.hh"
|
#include "value-to-xml.hh"
|
||||||
|
|
Loading…
Reference in a new issue