nix-gh/src/libexpr/eval.hh

328 lines
9.6 KiB
C++
Raw Normal View History

#pragma once
#include "attr-set.hh"
#include "value.hh"
#include "nixexpr.hh"
#include "symbol-table.hh"
#include "hash.hh"
#include <map>
namespace nix {
class Store;
class EvalState;
enum RepairFlag : bool;
typedef void (* PrimOpFun) (EvalState & state, const Pos & pos, Value * * args, Value & v);
struct PrimOp
{
PrimOpFun fun;
size_t arity;
Symbol name;
PrimOp(PrimOpFun fun, size_t arity, Symbol name)
: fun(fun), arity(arity), name(name) { }
};
struct Env
{
Env * up;
unsigned short size; // used by valueSize
unsigned short prevWith:15; // nr of levels up to next `with' environment
unsigned short haveWithAttrs:1;
Value * values[0];
};
Value & mkString(Value & v, const string & s, const PathSet & context = PathSet());
2010-03-30 11:22:33 +02:00
void copyContext(const Value & v, PathSet & context);
2010-03-30 11:22:33 +02:00
/* Cache for calls to addToStore(); maps source paths to the store
paths. */
typedef std::map<Path, Path> SrcToStore;
std::ostream & operator << (std::ostream & str, const Value & v);
typedef std::pair<std::string, std::string> SearchPathElem;
typedef std::list<SearchPathElem> SearchPath;
/* Initialise the Boehm GC, if applicable. */
void initGC();
2013-09-02 16:29:15 +02:00
class EvalState
{
2010-03-31 17:38:03 +02:00
public:
SymbolTable symbols;
2013-10-28 07:34:44 +01:00
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
sFile, sLine, sColumn, sFunctor, sToString,
Add support for passing structured data to builders Previously, all derivation attributes had to be coerced into strings so that they could be passed via the environment. This is lossy (e.g. lists get flattened, necessitating configureFlags vs. configureFlagsArray, of which the latter cannot be specified as an attribute), doesn't support attribute sets at all, and has size limitations (necessitating hacks like passAsFile). This patch adds a new mode for passing attributes to builders, namely encoded as a JSON file ".attrs.json" in the current directory of the builder. This mode is activated via the special attribute __structuredAttrs = true; (The idea is that one day we can set this in stdenv.mkDerivation.) For example, stdenv.mkDerivation { __structuredAttrs = true; name = "foo"; buildInputs = [ pkgs.hello pkgs.cowsay ]; doCheck = true; hardening.format = false; } results in a ".attrs.json" file containing (sans the indentation): { "buildInputs": [], "builder": "/nix/store/ygl61ycpr2vjqrx775l1r2mw1g2rb754-bash-4.3-p48/bin/bash", "configureFlags": [ "--with-foo", "--with-bar=1 2" ], "doCheck": true, "hardening": { "format": false }, "name": "foo", "nativeBuildInputs": [ "/nix/store/10h6li26i7g6z3mdpvra09yyf10mmzdr-hello-2.10", "/nix/store/4jnvjin0r6wp6cv1hdm5jbkx3vinlcvk-cowsay-3.03" ], "propagatedBuildInputs": [], "propagatedNativeBuildInputs": [], "stdenv": "/nix/store/f3hw3p8armnzy6xhd4h8s7anfjrs15n2-stdenv", "system": "x86_64-linux" } "passAsFile" is ignored in this mode because it's not needed - large strings are included directly in the JSON representation. It is up to the builder to do something with the JSON representation. For example, in bash-based builders, lists/attrsets of string values could be mapped to bash (associative) arrays.
2017-01-25 16:42:07 +01:00
sRight, sWrong, sStructuredAttrs, sBuilder;
Symbol sDerivationNix;
/* If set, force copying files to the Nix store even if they
already exist there. */
RepairFlag repair;
/* If set, don't allow access to files outside of the Nix search
path or to environment variables. */
bool restricted;
Value vEmptySet;
const ref<Store> store;
2010-03-31 17:38:03 +02:00
private:
2013-09-02 16:29:15 +02:00
SrcToStore srcToStore;
/* A cache from path names to values. */
#if HAVE_BOEHMGC
typedef std::map<Path, Value, std::less<Path>, traceable_allocator<std::pair<const Path, Value>>> FileEvalCache;
#else
typedef std::map<Path, Value> FileEvalCache;
#endif
FileEvalCache fileEvalCache;
SearchPath searchPath;
std::map<std::string, std::pair<bool, std::string>> searchPathResolved;
2010-03-30 17:18:20 +02:00
public:
2013-09-02 16:29:15 +02:00
EvalState(const Strings & _searchPath, ref<Store> store);
~EvalState();
void addToSearchPath(const string & s);
SearchPath getSearchPath() { return searchPath; }
Path checkSourcePath(const Path & path);
void checkURI(const std::string & uri);
/* Parse a Nix expression from the specified file. */
Expr * parseExprFromFile(const Path & path);
Add primop ‘scopedImport’ ‘scopedImport’ works like ‘import’, except that it takes a set of attributes to be added to the lexical scope of the expression, essentially extending or overriding the builtin variables. For instance, the expression scopedImport { x = 1; } ./foo.nix where foo.nix contains ‘x’, will evaluate to 1. This has a few applications: * It allows getting rid of function argument specifications in package expressions. For instance, a package expression like: { stdenv, fetchurl, libfoo }: stdenv.mkDerivation { ... buildInputs = [ libfoo ]; } can now we written as just stdenv.mkDerivation { ... buildInputs = [ libfoo ]; } and imported in all-packages.nix as: bar = scopedImport pkgs ./bar.nix; So whereas we once had dependencies listed in three places (buildInputs, the function, and the call site), they now only need to appear in one place. * It allows overriding builtin functions. For instance, to trace all calls to ‘map’: let overrides = { map = f: xs: builtins.trace "map called!" (map f xs); # Ensure that our override gets propagated by calls to # import/scopedImport. import = fn: scopedImport overrides fn; scopedImport = attrs: fn: scopedImport (overrides // attrs) fn; # Also update ‘builtins’. builtins = builtins // overrides; }; in scopedImport overrides ./bla.nix * Similarly, it allows extending the set of builtin functions. For instance, during Nixpkgs/NixOS evaluation, the Nixpkgs library functions could be added to the default scope. There is a downside: calls to scopedImport are not memoized, unlike import. So importing a file multiple times leads to multiple parsings / evaluations. It would be possible to construct the AST only once, but that would require careful handling of variables/environments.
2014-05-26 13:46:11 +02:00
Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv);
/* Parse a Nix expression from the specified string. */
2013-09-02 18:34:04 +02:00
Expr * parseExprFromString(const string & s, const Path & basePath, StaticEnv & staticEnv);
Expr * parseExprFromString(const string & s, const Path & basePath);
2013-09-02 16:29:15 +02:00
Expr * parseStdin();
2010-03-30 11:22:33 +02:00
/* Evaluate an expression read from the given file to normal
form. */
void evalFile(const Path & path, Value & v);
2013-09-02 18:34:04 +02:00
void resetFileCache();
/* Look up a file in the search path. */
Path findFile(const string & path);
Path findFile(SearchPath & searchPath, const string & path, const Pos & pos = noPos);
/* If the specified search path element is a URI, download it. */
std::pair<bool, std::string> resolveSearchPathElem(const SearchPathElem & elem);
/* Evaluate an expression to normal form, storing the result in
value `v'. */
void eval(Expr * e, Value & v);
/* Evaluation the expression, then verify that it has the expected
type. */
inline bool evalBool(Env & env, Expr * e);
inline bool evalBool(Env & env, Expr * e, const Pos & pos);
inline void evalAttrs(Env & env, Expr * e, Value & v);
/* If `v' is a thunk, enter it and overwrite `v' with the result
2010-03-30 15:47:59 +02:00
of the evaluation of the thunk. If `v' is a delayed function
application, call the function and overwrite `v' with the
result. Otherwise, this is a no-op. */
inline void forceValue(Value & v, const Pos & pos = noPos);
/* Force a value, then recursively force list elements and
attributes. */
void forceValueDeep(Value & v);
/* Force `v', and then verify that it has the expected type. */
2014-04-04 18:58:15 +02:00
NixInt forceInt(Value & v, const Pos & pos);
NixFloat forceFloat(Value & v, const Pos & pos);
2016-08-29 17:56:35 +02:00
bool forceBool(Value & v, const Pos & pos);
inline void forceAttrs(Value & v);
2014-04-04 19:11:40 +02:00
inline void forceAttrs(Value & v, const Pos & pos);
inline void forceList(Value & v);
2014-04-04 19:05:36 +02:00
inline void forceList(Value & v, const Pos & pos);
void forceFunction(Value & v, const Pos & pos); // either lambda or primop
2014-04-04 21:14:11 +02:00
string forceString(Value & v, const Pos & pos = noPos);
2014-11-25 10:23:36 +01:00
string forceString(Value & v, PathSet & context, const Pos & pos = noPos);
2014-04-04 21:14:11 +02:00
string forceStringNoCtx(Value & v, const Pos & pos = noPos);
/* Return true iff the value `v' denotes a derivation (i.e. a
set with attribute `type = "derivation"'). */
bool isDerivation(Value & v);
2010-03-30 11:22:33 +02:00
/* String coercion. Converts strings, paths and derivations to a
string. If `coerceMore' is set, also converts nulls, integers,
booleans and lists to a string. If `copyToStore' is set,
2013-08-14 22:32:49 +02:00
referenced paths are copied to the Nix store as a side effect. */
string coerceToString(const Pos & pos, Value & v, PathSet & context,
2010-03-30 11:22:33 +02:00
bool coerceMore = false, bool copyToStore = true);
2013-11-19 00:03:11 +01:00
string copyPathToStore(PathSet & context, const Path & path);
2010-03-30 11:22:33 +02:00
/* Path coercion. Converts strings, paths and derivations to a
path. The result is guaranteed to be a canonicalised, absolute
path. Nothing is copied to the store. */
Path coerceToPath(const Pos & pos, Value & v, PathSet & context);
2010-03-30 11:22:33 +02:00
2013-09-02 18:34:04 +02:00
public:
/* The base environment, containing the builtin functions and
values. */
Env & baseEnv;
2010-04-15 00:59:39 +02:00
/* The same, but used during parsing to resolve variables. */
StaticEnv staticBaseEnv; // !!! should be private
private:
2013-09-02 16:29:15 +02:00
2015-07-23 23:14:07 +02:00
unsigned int baseEnvDispl = 0;
2013-09-02 18:34:04 +02:00
void createBaseEnv();
2013-09-02 16:29:15 +02:00
2010-03-30 16:39:27 +02:00
void addConstant(const string & name, Value & v);
void addPrimOp(const string & name,
unsigned int arity, PrimOpFun primOp);
public:
Value & getBuiltin(const string & name);
private:
2013-10-08 14:24:53 +02:00
inline Value * lookupVar(Env * env, const ExprVar & var, bool noEval);
2013-09-02 16:29:15 +02:00
2014-01-21 18:29:55 +01:00
friend struct ExprVar;
friend struct ExprAttrs;
friend struct ExprLet;
2013-09-02 18:34:04 +02:00
Expr * parse(const char * text, const Path & path,
const Path & basePath, StaticEnv & staticEnv);
public:
2013-09-02 16:29:15 +02:00
/* Do a deep equality test between two values. That is, list
elements and attributes are compared recursively. */
bool eqValues(Value & v1, Value & v2);
2015-09-07 01:03:23 +02:00
bool isFunctor(Value & fun);
void callFunction(Value & fun, Value & arg, Value & v, const Pos & pos);
void callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos);
/* Automatically call a function for which each argument has a
default value or has a binding in the `args' map. */
void autoCallFunction(Bindings & args, Value & fun, Value & res);
2013-09-02 16:29:15 +02:00
/* Allocation primitives. */
Value * allocValue();
Env & allocEnv(unsigned int size);
2010-03-30 16:39:27 +02:00
Value * allocAttr(Value & vAttrs, const Symbol & name);
Bindings * allocBindings(Bindings::size_t capacity);
2010-03-30 16:39:27 +02:00
void mkList(Value & v, unsigned int length);
void mkAttrs(Value & v, unsigned int capacity);
void mkThunk_(Value & v, Expr * expr);
void mkPos(Value & v, Pos * pos);
void concatLists(Value & v, unsigned int nrLists, Value * * lists, const Pos & pos);
2010-03-30 17:18:20 +02:00
/* Print statistics. */
void printStats();
void realiseContext(const PathSet & context);
private:
2012-08-13 05:41:48 +02:00
2015-07-23 23:14:07 +02:00
unsigned long nrEnvs = 0;
unsigned long nrValuesInEnvs = 0;
unsigned long nrValues = 0;
unsigned long nrListElems = 0;
unsigned long nrAttrsets = 0;
unsigned long nrAttrsInAttrsets = 0;
unsigned long nrOpUpdates = 0;
unsigned long nrOpUpdateValuesCopied = 0;
unsigned long nrListConcats = 0;
unsigned long nrPrimOpCalls = 0;
unsigned long nrFunctionCalls = 0;
unsigned long nrMemoiseHits = 0;
unsigned long nrMemoiseMisses = 0;
bool countCalls;
typedef std::map<Symbol, unsigned int> PrimOpCalls;
PrimOpCalls primOpCalls;
typedef std::map<ExprLambda *, unsigned int> FunctionCalls;
FunctionCalls functionCalls;
2013-11-07 18:04:36 +01:00
void incrFunctionCall(ExprLambda * fun);
typedef std::map<Pos, unsigned int> AttrSelects;
AttrSelects attrSelects;
2014-01-21 18:29:55 +01:00
friend struct ExprOpUpdate;
friend struct ExprOpConcatLists;
friend struct ExprSelect;
friend void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v);
friend void prim_memoise(EvalState & state, const Pos & pos, Value * * args, Value & v);
/* State for builtins.memoise. */
struct MemoArgComparator
{
EvalState & state;
MemoArgComparator(EvalState & state) : state(state) { }
bool operator()(Value * a, Value * b);
};
typedef std::map<Value *, Value, MemoArgComparator, traceable_allocator<std::pair<const Value *, Value>>> PerLambdaMemo;
typedef std::pair<Env *, ExprLambda *> LambdaKey;
// FIXME: use std::unordered_map
std::map<LambdaKey, PerLambdaMemo, std::less<LambdaKey>, traceable_allocator<std::pair<const LambdaKey, PerLambdaMemo>>> memos;
};
/* Return a string representing the type of the value `v'. */
2010-04-21 17:57:11 +02:00
string showType(const Value & v);
/* If `path' refers to a directory, then append "/default.nix". */
Path resolveExprPath(Path path);
struct InvalidPathError : EvalError
{
Path path;
InvalidPathError(const Path & path);
2014-10-20 18:15:50 +02:00
#ifdef EXCEPTION_NEEDS_THROW_SPEC
~InvalidPathError() throw () { };
#endif
};
}