From d7efd7639420f4c840cbfdfcbbb3c45292f3ac54 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 16 Oct 2006 15:55:34 +0000 Subject: [PATCH] * Big cleanup of the semantics of paths, strings, contexts, string concatenation and string coercion. This was a big mess (see e.g. NIX-67). Contexts are now folded into strings, so that they don't cause evaluation errors when they're not expected. The semantics of paths has been clarified (see nixexpr-ast.def). toString() and coerceToString() have been merged. Semantic change: paths are now copied to the store when they're in a concatenation (and in most other situations - that's the formalisation of the meaning of a path). So "foo " + ./bla evaluates to "foo /nix/store/hash...-bla", not "foo /path/to/current-dir/bla". This prevents accidental impurities, and is more consistent with the treatment of derivation outputs, e.g., `"foo " + bla' where `bla' is a derivation. (Here `bla' would be replaced by the output path of `bla'.) --- src/aterm-helper.pl | 1 + src/libexpr/eval.cc | 220 ++++++++++++++------------ src/libexpr/eval.hh | 23 ++- src/libexpr/expr-to-xml.cc | 22 +-- src/libexpr/expr-to-xml.hh | 2 +- src/libexpr/get-drvs.cc | 20 ++- src/libexpr/lexer.l | 2 +- src/libexpr/nixexpr-ast.def | 32 +++- src/libexpr/nixexpr.cc | 37 ++++- src/libexpr/nixexpr.hh | 13 ++ src/libexpr/parser.y | 4 +- src/libexpr/primops.cc | 308 +++++++++--------------------------- src/libstore/derivations.cc | 14 +- src/libutil/aterm.cc | 10 ++ src/libutil/aterm.hh | 1 + src/nix-env/main.cc | 16 +- src/nix-instantiate/main.cc | 2 +- 17 files changed, 331 insertions(+), 396 deletions(-) diff --git a/src/aterm-helper.pl b/src/aterm-helper.pl index 5b7b33691..9b2bde700 100755 --- a/src/aterm-helper.pl +++ b/src/aterm-helper.pl @@ -47,6 +47,7 @@ print HEADER "#endif\n\n\n"; print IMPL "namespace nix {\n"; while () { + s/\#.*//; next if (/^\s*$/); if (/^\s*(\w*)\s*\|([^\|]*)\|\s*(\w+)\s*\|\s*(\w+)?/) { diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 4f97c761e..2bb29f871 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -3,6 +3,7 @@ #include "hash.hh" #include "util.hh" #include "store.hh" +#include "derivations.hh" #include "nixexpr-ast.hh" @@ -157,23 +158,24 @@ static Expr updateAttrs(Expr e1, Expr e2) } -string evalString(EvalState & state, Expr e) +string evalString(EvalState & state, Expr e, PathSet & context) { e = evalExpr(state, e); - ATerm s; - if (!matchStr(e, s)) + string s; + if (!matchStr(e, s, context)) throw TypeError(format("value is %1% while a string was expected") % showType(e)); - return aterm2String(s); + return s; } -Path evalPath(EvalState & state, Expr e) +string evalStringNoCtx(EvalState & state, Expr e) { - e = evalExpr(state, e); - ATerm s; - if (!matchPath(e, s)) - throw TypeError(format("value is %1% while a path was expected") % showType(e)); - return aterm2String(s); + PathSet context; + string s = evalString(state, e, context); + if (!context.empty()) + throw EvalError(format("the string `%1%' is not allowed to refer to a store path (such as `%2%')") + % s % *(context.begin())); + return s; } @@ -206,76 +208,84 @@ 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) +static void flattenList(EvalState & state, Expr e, ATermList & result) +{ + ATermList es; + e = evalExpr(state, e); + if (matchList(e, es)) + for (ATermIterator i(es); i; ++i) + flattenList(state, *i, result); + else + result = ATinsert(result, e); +} + + +ATermList flattenList(EvalState & state, Expr e) +{ + ATermList result = ATempty; + flattenList(state, e, result); + return ATreverse(result); +} + + +string coerceToString(EvalState & state, Expr e, PathSet & context, + bool coerceMore, bool copyToStore) { - isPath = false; - e = evalExpr(state, e); - bool isWrapped = false; - ATermList es; - ATerm e2; - if (matchContext(e, es, e2)) { - isWrapped = true; - e = e2; - context = ATconcat(es, context); - } - - ATerm s; - if (matchStr(e, s)) return aterm2String(s); - - if (matchPath(e, s)) { - isPath = true; - Path path = aterm2String(s); - if (isInStore(path) && !isWrapped) { - context = ATinsert(context, makePath(toATerm(toStorePath(path)))); + string s; + + if (matchStr(e, s, context)) return s; + + ATerm s2; + if (matchPath(e, s2)) { + Path path(canonPath(aterm2String(s2))); + + if (!copyToStore) return path; + + if (isDerivation(path)) + throw EvalError(format("file names are not allowed to end in `%1%'") + % drvExtension); + + Path dstPath; + if (state.srcToStore[path] != "") + dstPath = state.srcToStore[path]; + else { + dstPath = addToStore(path); + state.srcToStore[path] = dstPath; + printMsg(lvlChatty, format("copied source `%1%' -> `%2%'") + % path % dstPath); } - return path; + + context.insert(dstPath); + return dstPath; } + + ATermList es; + if (matchAttrs(e, es)) + return coerceToString(state, makeSelect(e, toATerm("outPath")), + context, coerceMore, copyToStore); - if (matchAttrs(e, es)) { - ATermMap attrs(128); /* !!! */ - queryAllAttrs(e, attrs, false); + if (coerceMore) { - Expr a = attrs.get(toATerm("type")); - if (a && evalString(state, a) == "derivation") { - a = attrs.get(toATerm("outPath")); - if (!a) throw TypeError("output path missing from derivation"); - isPath = true; - context = ATinsert(context, e); - return evalPath(state, a); + /* Note that `false' is represented as an empty string for + shell scripting convenience, just like `null'. */ + if (e == eTrue) return "1"; + if (e == eFalse) return ""; + int n; + if (matchInt(e, n)) return int2String(n); + if (matchNull(e)) return ""; + + if (matchList(e, es)) { + string result; + es = flattenList(state, e); + bool first = true; + for (ATermIterator i(es); i; ++i) { + if (!first) result += " "; else first = false; + result += coerceToString(state, *i, + context, coerceMore, copyToStore); + } + return result; } } @@ -283,31 +293,41 @@ string coerceToStringWithContext(EvalState & state, } -/* Wrap an expression in a context if the context is not empty. */ -Expr wrapInContext(ATermList context, Expr e) +/* Common implementation of `+', ConcatStrings and `~'. */ +static ATerm concatStrings(EvalState & state, const ATermVector & args, + string separator = "") { - return context == ATempty ? e : makeContext(context, e); + PathSet context; + std::ostringstream s; + + /* 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, since + paths are copied when they are used in a derivation), and none + of the strings are allowed to have contexts. */ + ATerm dummy; + bool isPath = !args.empty() && matchPath(args.front(), dummy); + + for (ATermVector::const_iterator i = args.begin(); i != args.end(); ++i) { + if (i != args.begin()) s << separator; + s << coerceToString(state, *i, context, false, !isPath); + } + + if (isPath && !context.empty()) + throw EvalError(format("a string that refers to a store path cannot be appended to a path, in `%1%'") + % s.str()); + + return isPath + ? makePath(toATerm(s.str())) + : makeStr(s.str(), context); } -static ATerm concatStrings(EvalState & state, const ATermVector & args) +Path coerceToPath(EvalState & state, Expr e, PathSet & context) { - ATermList context = ATempty; - std::ostringstream s; - bool isPath = false; - - /* Note that if the first argument in the concatenation is a path, - then the result is also a path. */ - - 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; - } - - return wrapInContext(context, isPath - ? makePath(toATerm(canonPath(s.str()))) - : makeStr(toATerm(s.str()))); + string path = coerceToString(state, e, context, false, false); + if (path == "" || path[0] != '/') + throw EvalError(format("string `%1%' doesn't represent an absolute path") % path); + return path; } @@ -352,8 +372,7 @@ Expr evalExpr2(EvalState & state, Expr e) sym == symFunction1 || sym == symAttrs || sym == symList || - sym == symPrimOp || - sym == symContext) + sym == symPrimOp) return e; /* The `Closed' constructor is just a way to prevent substitutions @@ -371,6 +390,7 @@ Expr evalExpr2(EvalState & state, Expr e) ATermBlob fun; if (!matchPrimOpDef(primOp, arity, fun)) abort(); if (arity == 0) + /* !!! backtrace for primop call */ return ((PrimOp) ATgetBlobData(fun)) (state, ATermVector()); else return makePrimOp(arity, fun, ATempty); @@ -397,6 +417,7 @@ Expr evalExpr2(EvalState & state, Expr e) ATermVector args2(arity); for (ATermIterator i(args); i; ++i) args2[--arity] = *i; + /* !!! backtrace for primop call */ return ((PrimOp) ATgetBlobData((ATermBlob) fun)) (state, args2); } else @@ -550,11 +571,10 @@ Expr evalExpr2(EvalState & state, Expr e) if (matchSubPath(e, e1, e2)) { static bool haveWarned = false; warnOnce(haveWarned, "the subpath operator (~) is deprecated, use string concatenation (+) instead"); - ATermList context = ATempty; - bool dummy; - string s1 = coerceToStringWithContext(state, context, e1, dummy); - string s2 = coerceToStringWithContext(state, context, e2, dummy); - return wrapInContext(context, makePath(toATerm(canonPath(s1 + "/" + s2)))); + ATermVector args; + args.push_back(e1); + args.push_back(e2); + return concatStrings(state, args, "/"); } /* List concatenation. */ diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index a7f4e6943..323a82873 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -60,17 +60,26 @@ Expr strictEvalExpr(EvalState & state, Expr e, bool canonicalise = false); /* Specific results. */ -string evalString(EvalState & state, Expr e); -Path evalPath(EvalState & state, Expr e); +string evalString(EvalState & state, Expr e, PathSet & context); +string evalStringNoCtx(EvalState & state, Expr e); int evalInt(EvalState & state, Expr e); 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); +/* Flatten nested lists into a single list (or expand a singleton into + a list). */ +ATermList flattenList(EvalState & state, Expr e); + +/* String coercion. Converts strings, paths and derivations to a + string. If `coerceMore' is set, also converts nulls, integers, + booleans and lists to a string. */ +string coerceToString(EvalState & state, Expr e, PathSet & context, + bool coerceMore = false, bool copyToStore = true); + +/* Path coercion. Converts strings, paths and derivations to a path. + The result is guaranteed to be an canonicalised, absolute path. + Nothing is copied to the store. */ +Path coerceToPath(EvalState & state, Expr e, PathSet & context); /* Automatically call a function for which each argument has a default value or has a binding in the `args' map. Note: result is a call, diff --git a/src/libexpr/expr-to-xml.cc b/src/libexpr/expr-to-xml.cc index 195cbd7b8..f5b0e7477 100644 --- a/src/libexpr/expr-to-xml.cc +++ b/src/libexpr/expr-to-xml.cc @@ -15,26 +15,20 @@ static XMLAttrs singletonAttrs(const string & name, const string & value) } -static void printTermAsXML(Expr e, XMLWriter & doc, ATermList & context) +static void printTermAsXML(Expr e, XMLWriter & doc, PathSet & context) { XMLAttrs attrs; - ATerm s; + string s; + ATerm s2; int i; - Expr e2; ATermList as, es, formals; ATerm body, pos; - while (matchContext(e, es, e2)) { - e = e2; - for (ATermIterator i(es); i; ++i) - context = ATinsert(context, *i); - } + if (matchStr(e, s, context)) /* !!! show the context? */ + doc.writeEmptyElement("string", singletonAttrs("value", s)); - if (matchStr(e, s)) - doc.writeEmptyElement("string", singletonAttrs("value", aterm2String(s))); - - else if (matchPath(e, s)) - doc.writeEmptyElement("path", singletonAttrs("value", aterm2String(s))); + else if (matchPath(e, s2)) + doc.writeEmptyElement("path", singletonAttrs("value", aterm2String(s2))); else if (matchNull(e)) doc.writeEmptyElement("null"); @@ -90,7 +84,7 @@ static void printTermAsXML(Expr e, XMLWriter & doc, ATermList & context) } -void printTermAsXML(Expr e, std::ostream & out, ATermList & context) +void printTermAsXML(Expr e, std::ostream & out, PathSet & context) { XMLWriter doc(true, out); XMLOpenElement root(doc, "expr"); diff --git a/src/libexpr/expr-to-xml.hh b/src/libexpr/expr-to-xml.hh index eb628660d..36b8e4042 100644 --- a/src/libexpr/expr-to-xml.hh +++ b/src/libexpr/expr-to-xml.hh @@ -9,7 +9,7 @@ namespace nix { -void printTermAsXML(Expr e, std::ostream & out, ATermList & context); +void printTermAsXML(Expr e, std::ostream & out, PathSet & context); } diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 808e12ffd..acdc0c7b5 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -10,7 +10,8 @@ string DrvInfo::queryDrvPath(EvalState & state) const { if (drvPath == "") { Expr a = attrs->get(toATerm("drvPath")); - (string &) drvPath = a ? evalPath(state, a) : ""; + PathSet context; + (string &) drvPath = a ? coerceToPath(state, a, context) : ""; } return drvPath; } @@ -21,7 +22,8 @@ string DrvInfo::queryOutPath(EvalState & state) const if (outPath == "") { Expr a = attrs->get(toATerm("outPath")); if (!a) throw TypeError("output path missing"); - (string &) outPath = evalPath(state, a); + PathSet context; + (string &) outPath = coerceToPath(state, a, context); } return outPath; } @@ -38,9 +40,11 @@ MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const queryAllAttrs(evalExpr(state, a), attrs2); for (ATermMap::const_iterator i = attrs2.begin(); i != attrs2.end(); ++i) { - ATerm s = coerceToString(evalExpr(state, i->value)); - if (s) - meta[aterm2String(i->key)] = aterm2String(s); + Expr e = evalExpr(state, i->value); + string s; + PathSet context; + if (matchStr(e, s, context)) + meta[aterm2String(i->key)] = s; /* For future compatibility, ignore attribute values that are not strings. */ } @@ -74,7 +78,7 @@ static bool getDerivation(EvalState & state, Expr e, queryAllAttrs(e, *attrs, false); Expr a = attrs->get(toATerm("type")); - if (!a || evalString(state, a) != "derivation") return true; + if (!a || evalStringNoCtx(state, a) != "derivation") return true; /* Remove spurious duplicates (e.g., an attribute set like `rec { x = derivation {...}; y = x;}'. */ @@ -86,13 +90,13 @@ static bool getDerivation(EvalState & state, Expr e, a = attrs->get(toATerm("name")); /* !!! We really would like to have a decent back trace here. */ if (!a) throw TypeError("derivation name missing"); - drv.name = evalString(state, a); + drv.name = evalStringNoCtx(state, a); a = attrs->get(toATerm("system")); if (!a) drv.system = "unknown"; else - drv.system = evalString(state, a); + drv.system = evalStringNoCtx(state, a); drv.attrs = attrs; diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index df30a5ed0..9f0f0b335 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -64,7 +64,7 @@ static Expr unescapeStr(const char * s) } else t += c; } - return makeStr(toATerm(t)); + return makeStr(toATerm(t), ATempty); } diff --git a/src/libexpr/nixexpr-ast.def b/src/libexpr/nixexpr-ast.def index b797fcfc4..fe5fe71b0 100644 --- a/src/libexpr/nixexpr-ast.def +++ b/src/libexpr/nixexpr-ast.def @@ -24,8 +24,37 @@ Call | Expr Expr | Expr | Select | Expr string | Expr | Var | string | Expr | Int | int | Expr | -Str | string | Expr | + +# Strings in the evaluator carry a so-called `context' (the ATermList) +# which is a list of strings representing store paths. This is to +# allow users to write things like +# +# "--with-freetype2-library=" + freetype + "/lib" +# +# where `freetype' is a derivation (or a source to be copied to the +# store). If we just concatenated the strings without keeping track +# of the referenced store paths, then if the string is used as a +# derivation attribute, the derivation will not have the correct +# dependencies in its inputDrvs and inputSrcs. +# +# The semantics of the context is as follows: when a string with +# context C is used as a derivation attribute, then the derivations in +# C will be added to the inputDrvs of the derivation, and the other +# store paths in C will be added to the inputSrcs of the derivations. +# +# For canonicity, the store paths should be in sorted order. +Str | string ATermList | Expr | + +# A path is a reference to a file system object that is to be copied +# to the Nix store when used as a derivation attribute. When it is +# concatenated to a string (i.e., `str + path'), it is also copied and +# the resulting store path is concatenated to the string (with the +# store path in the context). If a string or path is concatenated to +# a path (i.e., `path + str' or `path + path'), the result is a new +# path (if the right-hand side is a string, the context must be +# empty). Path | string | Expr | + List | ATermList | Expr | BlackHole | | Expr | Undefined | | Expr | @@ -36,7 +65,6 @@ 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 diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index a36b45bc1..cb006d147 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -293,13 +293,35 @@ Expr makeBool(bool b) } +bool matchStr(Expr e, string & s, PathSet & context) +{ + ATermList l; + ATerm s_; + + if (!matchStr(e, s_, l)) return false; + + s = aterm2String(s_); + + for (ATermIterator i(l); i; ++i) + context.insert(aterm2String(*i)); + + return true; +} + + +Expr makeStr(const string & s, const PathSet & context) +{ + return makeStr(toATerm(s), toATermList(context)); +} + + string showType(Expr e) { ATerm t1, t2, t3; ATermList l1; ATermBlob b1; int i1; - if (matchStr(e, t1)) return "a string"; + if (matchStr(e, t1, l1)) return "a string"; if (matchPath(e, t1)) return "a path"; if (matchNull(e)) return "null"; if (matchInt(e, i1)) return "an integer"; @@ -309,18 +331,19 @@ string showType(Expr e) if (matchAttrs(e, l1)) return "an attribute set"; if (matchList(e, l1)) return "a list"; if (matchPrimOp(e, i1, b1, l1)) return "a partially applied built-in function"; - if (matchContext(e, l1, t1)) return "a context containing " + showType(t1); return "an unknown type"; } string showValue(Expr e) { - ATerm s; + PathSet context; + string s; + ATerm s2; int i; - if (matchStr(e, s)) { - string t = aterm2String(s), u; - for (string::iterator i = t.begin(); i != t.end(); ++i) + if (matchStr(e, s, context)) { + string u; + for (string::iterator i = s.begin(); i != s.end(); ++i) if (*i == '\"' || *i == '\\') u += "\\" + *i; else if (*i == '\n') u += "\\n"; else if (*i == '\r') u += "\\r"; @@ -328,7 +351,7 @@ string showValue(Expr e) else u += *i; return "\"" + u + "\""; } - if (matchPath(e, s)) return aterm2String(s); + if (matchPath(e, s2)) return aterm2String(s2); if (matchNull(e)) return "null"; if (matchInt(e, i)) return (format("%1%") % i).str(); if (e == eTrue) return "true"; diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 674321f92..2fdad73b7 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -71,6 +71,7 @@ struct TermFun }; ATerm bottomupRewrite(TermFun & f, ATerm e); + /* Query all attributes in an attribute set expression. The expression must be in normal form. */ void queryAllAttrs(Expr e, ATermMap & attrs, bool withPos = false); @@ -83,16 +84,28 @@ Expr queryAttr(Expr e, const string & name, ATerm & pos); /* Create an attribute set expression from an Attrs value. */ Expr makeAttrs(const ATermMap & attrs); + /* Perform a set of substitutions on an expression. */ Expr substitute(const Substitution & subs, Expr e); + /* Check whether all variables are defined in the given expression. Throw an exception if this isn't the case. */ void checkVarDefs(const ATermMap & def, Expr e); + /* Create an expression representing a boolean. */ Expr makeBool(bool b); + +/* Manipulation of Str() nodes. Note: matchStr() does not clear + context! */ +bool matchStr(Expr e, string & s, PathSet & context); + +Expr makeStr(const string & s, const PathSet & context = PathSet()); + + +/* Showing types, values. */ string showType(Expr e); string showValue(Expr e); diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index d13a1a133..cc403d437 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -195,12 +195,12 @@ expr_simple | INT { $$ = makeInt(ATgetInt((ATermInt) $1)); } | '"' string_parts '"' { /* For efficiency, and to simplify parse trees a bit. */ - if ($2 == ATempty) $$ = makeStr(toATerm("")); + if ($2 == ATempty) $$ = makeStr(toATerm(""), ATempty); else if (ATgetNext($2) == ATempty) $$ = ATgetFirst($2); else $$ = makeConcatStrings(ATreverse($2)); } | PATH { $$ = makePath(toATerm(absPath(aterm2String($1), data->basePath))); } - | URI { $$ = makeStr($1); } + | URI { $$ = makeStr($1, ATempty); } | '(' expr ')' { $$ = $2; } /* Let expressions `let {..., body = ...}' are just desugared into `(rec {..., body = ...}).body'. */ diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index f35ba737d..2fbfa538f 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -13,16 +13,6 @@ namespace nix { -static Expr unwrapContext(EvalState & state, Expr e, ATermList & context) -{ - context = ATempty; - e = evalExpr(state, e); - if (matchContext(e, context, e)) - e = evalExpr(state, e); - return e; -} - - static Expr primBuiltins(EvalState & state, const ATermVector & args) { /* Return an attribute set containing all primops. This allows @@ -51,34 +41,10 @@ static Expr primBuiltins(EvalState & state, const ATermVector & args) argument. */ static Expr primImport(EvalState & state, const ATermVector & args) { - ATermList es; - Path path; - ATermList context; /* don't care the context */ + PathSet context; + Path path = coerceToPath(state, args[0], context); - Expr arg = unwrapContext(state, args[0], context), arg2; - - if (matchPath(arg, arg2)) - path = aterm2String(arg2); - - else if (matchAttrs(arg, es)) { - Expr a = queryAttr(arg, "type"); - - /* If it is a derivation, we have to realise it and load the - Nix expression created at the derivation's output path. */ - if (a && evalString(state, a) == "derivation") { - a = queryAttr(arg, "drvPath"); - if (!a) throw EvalError("bad derivation in import"); - Path drvPath = evalPath(state, a); - - buildDerivations(singleton(drvPath)); - - a = queryAttr(arg, "outPath"); - if (!a) throw EvalError("bad derivation in import"); - path = evalPath(state, a); - } - } - - else throw TypeError(format("argument of `import' is %1% while a path or derivation is required") % showType(arg)); + /* !!! build the derivations in context */ return evalFile(state, path); } @@ -86,117 +52,11 @@ static Expr primImport(EvalState & state, const ATermVector & args) static Expr primPathExists(EvalState & state, const ATermVector & args) { - Expr arg = evalExpr(state, args[0]), arg2; - - if (!matchPath(arg, arg2)) - throw TypeError("`pathExists' requires a path as its argument"); - - return makeBool(pathExists(aterm2String(arg2))); -} - - -static void flattenList(EvalState & state, Expr e, ATermList & result) -{ - ATermList es; - e = evalExpr(state, e); - if (matchList(e, es)) - for (ATermIterator i(es); i; ++i) - flattenList(state, *i, result); - else - result = ATinsert(result, e); -} - - -ATermList flattenList(EvalState & state, Expr e) -{ - ATermList result = ATempty; - flattenList(state, e, result); - return ATreverse(result); -} - - -void toString(EvalState & state, Expr e, - ATermList & context, string & result) -{ - e = evalExpr(state, e); - - ATerm s; - ATermList es; - int n; - Expr e2; - - bool isWrapped = false; - while (matchContext(e, es, e2)) { - isWrapped = true; - e = e2; - for (ATermIterator i(es); i; ++i) - context = ATinsert(context, *i); - } - - /* Note that `false' is represented as an empty string for shell - scripting convenience, just like `null'. */ - - if (matchStr(e, s)) result += aterm2String(s); - else if (e == eTrue) result += "1"; - else if (e == eFalse) ; - else if (matchInt(e, n)) result += int2String(n); - else if (matchNull(e)) ; - - else if (matchAttrs(e, es)) { - Expr a = queryAttr(e, "type"); - - if (a && evalString(state, a) == "derivation") { - Expr a2 = queryAttr(e, "outPath"); - if (!a2) throw EvalError("output path missing"); - result += evalPath(state, a2); - context = ATinsert(context, e); - } - - else throw TypeError("cannot convert an attribute set to a string"); - } - - else if (matchPath(e, s)) { - Path path(canonPath(aterm2String(s))); - - if (isStorePath(path) || (isWrapped && isInStore(path))) { - result += path; - /* !!! smells hacky. Check whether this is the Right - Thing To Do. */ - if (!isWrapped) - context = ATinsert(context, makePath(toATerm(toStorePath(path)))); - } - - else { - if (isDerivation(path)) - throw EvalError(format("file names are not allowed to end in `%1%'") - % drvExtension); - - Path dstPath; - if (state.srcToStore[path] != "") - dstPath = state.srcToStore[path]; - else { - dstPath = addToStore(path); - state.srcToStore[path] = dstPath; - printMsg(lvlChatty, format("copied source `%1%' -> `%2%'") - % path % dstPath); - } - - result += dstPath; - context = ATinsert(context, makePath(toATerm(dstPath))); - } - } - - else if (matchList(e, es)) { - es = flattenList(state, e); - bool first = true; - for (ATermIterator i(es); i; ++i) { - if (!first) result += " "; else first = false; - toString(state, *i, context, result); - } - } - - else throw TypeError(format("cannot convert %1% to a string") % showType(e)); - + PathSet context; + Path path = coerceToPath(state, args[0], context); + if (!context.empty()) + throw EvalError(format("string `%1%' cannot refer to other paths") % path); + return makeBool(pathExists(path)); } @@ -275,11 +135,13 @@ static Expr primDerivationStrict(EvalState & state, const ATermVector & args) throw EvalError("required attribute `name' missing"); ATerm posDrvName; if (!matchAttrRHS(eDrvName, eDrvName, posDrvName)) abort(); - string drvName = evalString(state, eDrvName); + string drvName = evalStringNoCtx(state, eDrvName); /* Build the derivation expression by processing the attributes. */ Derivation drv; + PathSet context; + string outputHash; string outputHashAlgo; bool outputHashRecursive = false; @@ -294,8 +156,6 @@ static Expr primDerivationStrict(EvalState & state, const ATermVector & args) try { - ATermList context = ATempty; - /* The `args' attribute is special: it supplies the command-line arguments to the builder. */ if (key == "args") { @@ -307,8 +167,7 @@ static Expr primDerivationStrict(EvalState & state, const ATermVector & args) es = flattenList(state, value); } for (ATermIterator i(es); i; ++i) { - string s; - toString(state, *i, context, s); + string s = coerceToString(state, *i, context, true); drv.args.push_back(s); } } @@ -316,8 +175,7 @@ static Expr primDerivationStrict(EvalState & state, const ATermVector & args) /* All other attributes are passed to the builder through the environment. */ else { - string s; - toString(state, value, context, s); + string s = coerceToString(state, value, context, true); drv.env[key] = s; if (key == "builder") drv.builder = s; else if (key == "system") drv.platform = s; @@ -331,32 +189,6 @@ static Expr primDerivationStrict(EvalState & state, const ATermVector & args) } } - /* Everything in the context of the expression should be - added as dependencies of the resulting derivation. */ - - for (ATermIterator i(context); i; ++i) { - - ATerm s; - ATermList as; - - if (matchPath(*i, s)) { - assert(isStorePath(aterm2String(s))); - drv.inputSrcs.insert(aterm2String(s)); - } - - else if (matchAttrs(*i, as)) { - Expr a = queryAttr(*i, "type"); - assert(a && evalString(state, a) == "derivation"); - - Expr a2 = queryAttr(*i, "drvPath"); - if (!a2) throw EvalError("derivation path missing"); - - drv.inputDrvs[evalPath(state, a2)] = singleton("out"); - } - - else abort(); - } - } catch (Error & e) { e.addPrefix(format("while processing the derivation attribute `%1%' at %2%:\n") % key % showPos(pos)); @@ -367,6 +199,18 @@ static Expr primDerivationStrict(EvalState & state, const ATermVector & args) } + /* Everything in the context of the strings in the derivation + attributes should be added as dependencies of the resulting + derivation. */ + for (PathSet::iterator i = context.begin(); i != context.end(); ++i) { + debug(format("derivation uses `%1%'") % *i); + assert(isStorePath(*i)); + if (isDerivation(*i)) + drv.inputDrvs[*i] = singleton("out"); + else + drv.inputSrcs.insert(*i); + } + /* Do we have all required attributes? */ if (drv.builder == "") throw EvalError("required attribute `builder' missing"); @@ -434,9 +278,9 @@ static Expr primDerivationStrict(EvalState & state, const ATermVector & args) /* !!! assumes a single output */ ATermMap outAttrs(2); outAttrs.set(toATerm("outPath"), - makeAttrRHS(makePath(toATerm(outPath)), makeNoPos())); + makeAttrRHS(makeStr(outPath, singleton(drvPath)), makeNoPos())); outAttrs.set(toATerm("drvPath"), - makeAttrRHS(makePath(toATerm(drvPath)), makeNoPos())); + makeAttrRHS(makeStr(drvPath, singleton(drvPath)), makeNoPos())); return makeAttrs(outAttrs); } @@ -449,7 +293,7 @@ static Expr primDerivationLazy(EvalState & state, const ATermVector & args) queryAllAttrs(eAttrs, attrs, true); attrs.set(toATerm("type"), - makeAttrRHS(makeStr(toATerm("derivation")), makeNoPos())); + makeAttrRHS(makeStr("derivation"), makeNoPos())); Expr drvStrict = makeCall(makeVar(toATerm("derivation!")), eAttrs); @@ -466,7 +310,8 @@ static Expr primDerivationLazy(EvalState & state, const ATermVector & args) following the last slash. */ static Expr primBaseNameOf(EvalState & state, const ATermVector & args) { - return makeStr(toATerm(baseNameOf(evalString(state, args[0])))); + PathSet context; + return makeStr(baseNameOf(coerceToPath(state, args[0], context)), context); } @@ -474,39 +319,29 @@ static Expr primBaseNameOf(EvalState & state, const ATermVector & args) last slash. */ static Expr primDirOf(EvalState & state, const ATermVector & args) { - return makePath(toATerm(dirOf(evalPath(state, args[0])))); + PathSet context; + return makeStr(dirOf(coerceToPath(state, args[0], context)), context); } -ATerm coerceToString(Expr e) -{ - ATerm s; - if (matchStr(e, s) || matchPath(e, s)) - return s; - return 0; -} - - -/* Convert the argument (which can be a path or a uri) to a string. */ +/* Convert the argument to a string. */ static Expr primToString(EvalState & state, const ATermVector & args) { - ATermList context = ATempty; - bool dummy; - string s = coerceToStringWithContext(state, context, args[0], dummy); - return wrapInContext(context, makeStr(toATerm(s))); + PathSet context; + string s = coerceToString(state, args[0], context); + /* !!! do lists etc */ + return makeStr(s, context); } -/* Convert the argument to a path. */ +/* Convert the argument to a path. !!! obsolete? */ static Expr primToPath(EvalState & state, const ATermVector & args) { - Expr e = evalExpr(state, args[0]); - ATerm t = coerceToString(e); - if (!t) throw TypeError(format("cannot coerce %1% to a path in `toPath'") % showType(e)); - Path path = aterm2String(t); + PathSet context; + string path = evalString(state, args[0], context); if (path == "" || path[0] != '/') throw EvalError("string doesn't represent an absolute path in `toPath'"); - return makePath(toATerm(canonPath(path))); + return makeStr(canonPath(path), context); } @@ -516,9 +351,9 @@ static Expr primToPath(EvalState & state, const ATermVector & args) static Expr primToXML(EvalState & state, const ATermVector & args) { std::ostringstream out; - ATermList context = ATempty; + PathSet context; printTermAsXML(strictEvalExpr(state, args[0]), out, context); - return wrapInContext(context, makeStr(toATerm(out.str()))); + return makeStr(out.str(), context); } @@ -526,13 +361,14 @@ static Expr primToXML(EvalState & state, const ATermVector & args) as an input by derivations. */ static Expr primToFile(EvalState & state, const ATermVector & args) { - ATermList context; - string name = evalString(state, args[0]); - string contents = evalString(state, - unwrapContext(state, args[1], context)); + PathSet context; + string name = evalStringNoCtx(state, args[0]); + string contents = evalString(state, args[1], context); PathSet refs; +#if 0 + /* !!! */ for (ATermIterator i(context); i; ++i) { ATerm s; if (matchPath(*i, s)) { @@ -541,13 +377,15 @@ static Expr primToFile(EvalState & state, const ATermVector & args) } else throw EvalError("in `toFile': the file cannot contain references to derivation outputs"); } +#endif Path storePath = addTextToStore(name, contents, refs); - /* Note: we don't need to wrap the result in a context, since - `storePath' itself has references to the paths used in - args[1]. */ - return makePath(toATerm(storePath)); + /* Note: we don't need to add `context' to the context of the + result, since `storePath' itself has references to the paths + used in args[1]. */ + + return makeStr(storePath, singleton(storePath)); } @@ -652,7 +490,8 @@ static Expr primDependencyClosure(EvalState & state, const ATermVector & args) Path pivot; PathSet workSet; for (ATermIterator i(startSet2); i; ++i) { - Path p = evalPath(state, *i); + PathSet context; /* !!! what to do? */ + Path p = coerceToPath(state, *i, context); workSet.insert(p); pivot = dirOf(p); } @@ -663,7 +502,8 @@ static Expr primDependencyClosure(EvalState & state, const ATermVector & args) if (e) { ATermList list = evalList(state, e); for (ATermIterator i(list); i; ++i) { - Path p = evalPath(state, *i); + PathSet context; /* !!! what to do? */ + Path p = coerceToPath(state, *i, context); searchPath.insert(p); } } @@ -686,11 +526,11 @@ static Expr primDependencyClosure(EvalState & state, const ATermVector & args) /* Call the `scanner' function with `path' as argument. */ debug(format("finding dependencies in `%1%'") % path); - ATermList deps = evalList(state, makeCall(scanner, makePath(toATerm(path)))); + ATermList deps = evalList(state, makeCall(scanner, makeStr(path))); /* Try to find the dependencies relative to the `path'. */ for (ATermIterator i(deps); i; ++i) { - string s = evalString(state, *i); + string s = evalStringNoCtx(state, *i); Path dep = findDependency(dirOf(path), s); @@ -721,8 +561,8 @@ static Expr primDependencyClosure(EvalState & state, const ATermVector & args) /* Return a list of the dependencies we've just found. */ ATermList deps = ATempty; for (PathSet::iterator i = doneSet.begin(); i != doneSet.end(); ++i) { - deps = ATinsert(deps, makeStr(toATerm(relativise(pivot, *i)))); - deps = ATinsert(deps, makePath(toATerm(*i))); + deps = ATinsert(deps, makeStr(relativise(pivot, *i))); + deps = ATinsert(deps, makeStr(*i)); } debug(format("dependency list is `%1%'") % makeList(deps)); @@ -733,8 +573,9 @@ static Expr primDependencyClosure(EvalState & state, const ATermVector & args) static Expr primAbort(EvalState & state, const ATermVector & args) { + PathSet context; throw Abort(format("evaluation aborted with the following error message: `%1%'") % - evalString(state, args[0])); + evalString(state, args[0], context)); } @@ -762,8 +603,8 @@ static Expr primTail(EvalState & state, const ATermVector & args) /* Return an environment variable. Use with care. */ static Expr primGetEnv(EvalState & state, const ATermVector & args) { - string name = evalString(state, args[0]); - return makeStr(toATerm(getEnv(name))); + string name = evalStringNoCtx(state, args[0]); + return makeStr(getEnv(name)); } @@ -787,7 +628,7 @@ static Expr primMap(EvalState & state, const ATermVector & args) platforms. */ static Expr primCurrentSystem(EvalState & state, const ATermVector & args) { - return makeStr(toATerm(thisSystem)); + return makeStr(thisSystem); } @@ -800,7 +641,7 @@ static Expr primCurrentTime(EvalState & state, const ATermVector & args) /* Dynamic version of the `.' operator. */ static Expr primGetAttr(EvalState & state, const ATermVector & args) { - string attr = evalString(state, args[0]); + string attr = evalStringNoCtx(state, args[0]); return evalExpr(state, makeSelect(args[1], toATerm(attr))); } @@ -808,7 +649,7 @@ static Expr primGetAttr(EvalState & state, const ATermVector & args) /* Dynamic version of the `?' operator. */ static Expr primHasAttr(EvalState & state, const ATermVector & args) { - string attr = evalString(state, args[0]); + string attr = evalStringNoCtx(state, args[0]); return evalExpr(state, makeOpHasAttr(args[1], toATerm(attr))); } @@ -822,17 +663,18 @@ static Expr primRemoveAttrs(EvalState & state, const ATermVector & args) for (ATermIterator i(list); i; ++i) /* It's not an error for *i not to exist. */ - attrs.remove(toATerm(evalString(state, *i))); + attrs.remove(toATerm(evalStringNoCtx(state, *i))); return makeAttrs(attrs); } -static Expr primRelativise(EvalState & state, const ATermVector & args) +static Expr primRelativise(EvalState & state, const ATermVector & args) { - Path pivot = evalPath(state, args[0]); - Path path = evalPath(state, args[1]); - return makeStr(toATerm(relativise(pivot, path))); + PathSet context; /* !!! what to do? */ + Path pivot = coerceToPath(state, args[0], context); + Path path = coerceToPath(state, args[1], context); + return makeStr(relativise(pivot, path)); } diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index 61b3ea89c..aeab675b2 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -113,16 +113,6 @@ Derivation parseDerivation(ATerm t) } -static ATermList unparseStrings(const StringSet & paths) -{ - ATermList l = ATempty; - for (PathSet::const_reverse_iterator i = paths.rbegin(); - i != paths.rend(); ++i) - l = ATinsert(l, toATerm(*i)); - return l; -} - - ATerm unparseDerivation(const Derivation & drv) { ATermList outputs = ATempty; @@ -141,7 +131,7 @@ ATerm unparseDerivation(const Derivation & drv) inDrvs = ATinsert(inDrvs, makeDerivationInput( toATerm(i->first), - unparseStrings(i->second))); + toATermList(i->second))); ATermList args = ATempty; for (Strings::const_reverse_iterator i = drv.args.rbegin(); @@ -159,7 +149,7 @@ ATerm unparseDerivation(const Derivation & drv) return makeDerive( outputs, inDrvs, - unparseStrings(drv.inputSrcs), + toATermList(drv.inputSrcs), toATerm(drv.platform), toATerm(drv.builder), args, diff --git a/src/libutil/aterm.cc b/src/libutil/aterm.cc index bb6e33ce9..90a8e212e 100644 --- a/src/libutil/aterm.cc +++ b/src/libutil/aterm.cc @@ -41,3 +41,13 @@ ATerm nix::toATerm(const string & s) { return toATerm(s.c_str()); } + + +ATermList nix::toATermList(const StringSet & ss) +{ + ATermList l = ATempty; + for (StringSet::const_reverse_iterator i = ss.rbegin(); + i != ss.rend(); ++i) + l = ATinsert(l, toATerm(*i)); + return l; +} diff --git a/src/libutil/aterm.hh b/src/libutil/aterm.hh index 6a2415f0b..b1cbc3b6d 100644 --- a/src/libutil/aterm.hh +++ b/src/libutil/aterm.hh @@ -43,6 +43,7 @@ Error badTerm(const format & f, ATerm t); ATerm toATerm(const char * s); ATerm toATerm(const string & s); +ATermList toATermList(const StringSet & ss); } diff --git a/src/nix-env/main.cc b/src/nix-env/main.cc index 6a4038715..b55fc2246 100644 --- a/src/nix-env/main.cc +++ b/src/nix-env/main.cc @@ -167,18 +167,18 @@ static void createUserEnv(EvalState & state, const DrvInfos & elems, Path drvPath = keepDerivations ? i->queryDrvPath(state) : ""; ATerm t = makeAttrs(ATmakeList5( makeBind(toATerm("type"), - makeStr(toATerm("derivation")), makeNoPos()), + makeStr("derivation"), makeNoPos()), makeBind(toATerm("name"), - makeStr(toATerm(i->name)), makeNoPos()), + makeStr(i->name), makeNoPos()), makeBind(toATerm("system"), - makeStr(toATerm(i->system)), makeNoPos()), + makeStr(i->system), makeNoPos()), makeBind(toATerm("drvPath"), - makePath(toATerm(drvPath)), makeNoPos()), + makeStr(drvPath), makeNoPos()), makeBind(toATerm("outPath"), - makePath(toATerm(i->queryOutPath(state))), makeNoPos()) + makeStr(i->queryOutPath(state)), makeNoPos()) )); manifest = ATinsert(manifest, t); - inputs = ATinsert(inputs, makeStr(toATerm(i->queryOutPath(state)))); + inputs = ATinsert(inputs, makeStr(i->queryOutPath(state))); /* This is only necessary when installing store paths, e.g., `nix-env -i /nix/store/abcd...-foo'. */ @@ -196,11 +196,11 @@ static void createUserEnv(EvalState & state, const DrvInfos & elems, Expr topLevel = makeCall(envBuilder, makeAttrs(ATmakeList3( makeBind(toATerm("system"), - makeStr(toATerm(thisSystem)), makeNoPos()), + makeStr(thisSystem), makeNoPos()), makeBind(toATerm("derivations"), makeList(ATreverse(inputs)), makeNoPos()), makeBind(toATerm("manifest"), - makePath(toATerm(manifestFile)), makeNoPos()) + makeStr(manifestFile), makeNoPos()) ))); /* Instantiate it. */ diff --git a/src/nix-instantiate/main.cc b/src/nix-instantiate/main.cc index 93b7c03be..3eb6d3031 100644 --- a/src/nix-instantiate/main.cc +++ b/src/nix-instantiate/main.cc @@ -41,7 +41,7 @@ static bool indirectRoot = false; static void printResult(EvalState & state, Expr e, bool evalOnly, bool xmlOutput, const ATermMap & autoArgs) { - ATermList context; + PathSet context; if (evalOnly) if (xmlOutput)