aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr
diff options
context:
space:
mode:
Diffstat (limited to 'src/libexpr')
-rw-r--r--src/libexpr/common-eval-args.cc8
-rw-r--r--src/libexpr/eval.cc177
-rw-r--r--src/libexpr/eval.hh9
-rw-r--r--src/libexpr/function-trace.hh24
-rw-r--r--src/libexpr/get-drvs.cc2
-rw-r--r--src/libexpr/get-drvs.hh4
-rw-r--r--src/libexpr/json-to-value.cc4
-rw-r--r--src/libexpr/lexer.l41
-rw-r--r--src/libexpr/local.mk2
-rw-r--r--src/libexpr/nix-expr.pc.in2
-rw-r--r--src/libexpr/parser.y28
-rw-r--r--src/libexpr/primops.cc133
-rw-r--r--src/libexpr/primops/context.cc187
-rw-r--r--src/libexpr/primops/fetchGit.cc28
-rw-r--r--src/libexpr/primops/fetchMercurial.cc20
-rw-r--r--src/libexpr/primops/fromTOML.cc13
-rw-r--r--src/libexpr/symbol-table.hh7
-rw-r--r--src/libexpr/value.hh2
18 files changed, 506 insertions, 185 deletions
diff --git a/src/libexpr/common-eval-args.cc b/src/libexpr/common-eval-args.cc
index 3e0c78f28..13950ab8d 100644
--- a/src/libexpr/common-eval-args.cc
+++ b/src/libexpr/common-eval-args.cc
@@ -45,9 +45,11 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
Path lookupFileArg(EvalState & state, string s)
{
- if (isUri(s))
- return getDownloader()->downloadCached(state.store, s, true);
- else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {
+ if (isUri(s)) {
+ CachedDownloadRequest request(s);
+ request.unpack = true;
+ return getDownloader()->downloadCached(state.store, request).path;
+ } else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {
Path p = s.substr(1, s.size() - 2);
return state.findFile(p);
} else
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index f41905787..9f4b6b411 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -6,14 +6,17 @@
#include "globals.hh"
#include "eval-inline.hh"
#include "download.hh"
+#include "json.hh"
#include <algorithm>
+#include <chrono>
#include <cstring>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
+#include <iostream>
+#include <fstream>
-#include <sys/time.h>
#include <sys/resource.h>
#if HAVE_BOEHMGC
@@ -23,7 +26,6 @@
#endif
-
namespace nix {
@@ -128,6 +130,16 @@ std::ostream & operator << (std::ostream & str, const Value & v)
}
+const Value *getPrimOp(const Value &v) {
+ const Value * primOp = &v;
+ while (primOp->type == tPrimOpApp) {
+ primOp = primOp->primOpApp.left;
+ }
+ assert(primOp->type == tPrimOp);
+ return primOp;
+}
+
+
string showType(const Value & v)
{
switch (v.type) {
@@ -142,8 +154,10 @@ string showType(const Value & v)
case tApp: return "a function application";
case tLambda: return "a function";
case tBlackhole: return "a black hole";
- case tPrimOp: return "a built-in function";
- case tPrimOpApp: return "a partially applied built-in function";
+ case tPrimOp:
+ return fmt("the built-in function '%s'", string(v.primOp->name));
+ case tPrimOpApp:
+ return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name));
case tExternal: return v.external->showType();
case tFloat: return "a float";
}
@@ -755,6 +769,7 @@ void EvalState::evalFile(const Path & path_, Value & v)
void EvalState::resetFileCache()
{
fileEvalCache.clear();
+ fileParseCache.clear();
}
@@ -1079,9 +1094,13 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos)
}
}
-
void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & pos)
{
+ std::optional<FunctionCallTrace> trace;
+ if (evalSettings.traceFunctionCalls) {
+ trace.emplace(pos);
+ }
+
forceValue(fun, pos);
if (fun.type == tPrimOp || fun.type == tPrimOpApp) {
@@ -1723,12 +1742,9 @@ bool EvalState::eqValues(Value & v1, Value & v2)
}
}
-
void EvalState::printStats()
{
bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0";
- Verbosity v = showStats ? lvlInfo : lvlDebug;
- printMsg(v, "evaluation statistics:");
struct rusage buf;
getrusage(RUSAGE_SELF, &buf);
@@ -1739,62 +1755,107 @@ void EvalState::printStats()
uint64_t bValues = nrValues * sizeof(Value);
uint64_t bAttrsets = nrAttrsets * sizeof(Bindings) + nrAttrsInAttrsets * sizeof(Attr);
- printMsg(v, format(" time elapsed: %1%") % cpuTime);
- printMsg(v, format(" size of a value: %1%") % sizeof(Value));
- printMsg(v, format(" size of an attr: %1%") % sizeof(Attr));
- printMsg(v, format(" environments allocated count: %1%") % nrEnvs);
- printMsg(v, format(" environments allocated bytes: %1%") % bEnvs);
- printMsg(v, format(" list elements count: %1%") % nrListElems);
- printMsg(v, format(" list elements bytes: %1%") % bLists);
- printMsg(v, format(" list concatenations: %1%") % nrListConcats);
- printMsg(v, format(" values allocated count: %1%") % nrValues);
- printMsg(v, format(" values allocated bytes: %1%") % bValues);
- printMsg(v, format(" sets allocated: %1% (%2% bytes)") % nrAttrsets % bAttrsets);
- printMsg(v, format(" right-biased unions: %1%") % nrOpUpdates);
- printMsg(v, format(" values copied in right-biased unions: %1%") % nrOpUpdateValuesCopied);
- printMsg(v, format(" symbols in symbol table: %1%") % symbols.size());
- printMsg(v, format(" size of symbol table: %1%") % symbols.totalSize());
- printMsg(v, format(" number of thunks: %1%") % nrThunks);
- printMsg(v, format(" number of thunks avoided: %1%") % nrAvoided);
- printMsg(v, format(" number of attr lookups: %1%") % nrLookups);
- printMsg(v, format(" number of primop calls: %1%") % nrPrimOpCalls);
- printMsg(v, format(" number of function calls: %1%") % nrFunctionCalls);
- printMsg(v, format(" total allocations: %1% bytes") % (bEnvs + bLists + bValues + bAttrsets));
-
#if HAVE_BOEHMGC
GC_word heapSize, totalBytes;
GC_get_heap_usage_safe(&heapSize, 0, 0, 0, &totalBytes);
- printMsg(v, format(" current Boehm heap size: %1% bytes") % heapSize);
- printMsg(v, format(" total Boehm heap allocations: %1% bytes") % totalBytes);
+#endif
+ if (showStats) {
+ auto outPath = getEnv("NIX_SHOW_STATS_PATH","-");
+ std::fstream fs;
+ if (outPath != "-")
+ fs.open(outPath, std::fstream::out);
+ JSONObject topObj(outPath == "-" ? std::cerr : fs, true);
+ topObj.attr("cpuTime",cpuTime);
+ {
+ auto envs = topObj.object("envs");
+ envs.attr("number", nrEnvs);
+ envs.attr("elements", nrValuesInEnvs);
+ envs.attr("bytes", bEnvs);
+ }
+ {
+ auto lists = topObj.object("list");
+ lists.attr("elements", nrListElems);
+ lists.attr("bytes", bLists);
+ lists.attr("concats", nrListConcats);
+ }
+ {
+ auto values = topObj.object("values");
+ values.attr("number", nrValues);
+ values.attr("bytes", bValues);
+ }
+ {
+ auto syms = topObj.object("symbols");
+ syms.attr("number", symbols.size());
+ syms.attr("bytes", symbols.totalSize());
+ }
+ {
+ auto sets = topObj.object("sets");
+ sets.attr("number", nrAttrsets);
+ sets.attr("bytes", bAttrsets);
+ sets.attr("elements", nrAttrsInAttrsets);
+ }
+ {
+ auto sizes = topObj.object("sizes");
+ sizes.attr("Env", sizeof(Env));
+ sizes.attr("Value", sizeof(Value));
+ sizes.attr("Bindings", sizeof(Bindings));
+ sizes.attr("Attr", sizeof(Attr));
+ }
+ topObj.attr("nrOpUpdates", nrOpUpdates);
+ topObj.attr("nrOpUpdateValuesCopied", nrOpUpdateValuesCopied);
+ topObj.attr("nrThunks", nrThunks);
+ topObj.attr("nrAvoided", nrAvoided);
+ topObj.attr("nrLookups", nrLookups);
+ topObj.attr("nrPrimOpCalls", nrPrimOpCalls);
+ topObj.attr("nrFunctionCalls", nrFunctionCalls);
+#if HAVE_BOEHMGC
+ {
+ auto gc = topObj.object("gc");
+ gc.attr("heapSize", heapSize);
+ gc.attr("totalBytes", totalBytes);
+ }
#endif
- if (countCalls) {
- v = lvlInfo;
-
- printMsg(v, format("calls to %1% primops:") % primOpCalls.size());
- typedef std::multimap<size_t, Symbol> PrimOpCalls_;
- PrimOpCalls_ primOpCalls_;
- for (auto & i : primOpCalls)
- primOpCalls_.insert(std::pair<size_t, Symbol>(i.second, i.first));
- for (auto i = primOpCalls_.rbegin(); i != primOpCalls_.rend(); ++i)
- printMsg(v, format("%1$10d %2%") % i->first % i->second);
-
- printMsg(v, format("calls to %1% functions:") % functionCalls.size());
- typedef std::multimap<size_t, ExprLambda *> FunctionCalls_;
- FunctionCalls_ functionCalls_;
- for (auto & i : functionCalls)
- functionCalls_.insert(std::pair<size_t, ExprLambda *>(i.second, i.first));
- for (auto i = functionCalls_.rbegin(); i != functionCalls_.rend(); ++i)
- printMsg(v, format("%1$10d %2%") % i->first % i->second->showNamePos());
-
- printMsg(v, format("evaluations of %1% attributes:") % attrSelects.size());
- typedef std::multimap<size_t, Pos> AttrSelects_;
- AttrSelects_ attrSelects_;
- for (auto & i : attrSelects)
- attrSelects_.insert(std::pair<size_t, Pos>(i.second, i.first));
- for (auto i = attrSelects_.rbegin(); i != attrSelects_.rend(); ++i)
- printMsg(v, format("%1$10d %2%") % i->first % i->second);
+ if (countCalls) {
+ {
+ auto obj = topObj.object("primops");
+ for (auto & i : primOpCalls)
+ obj.attr(i.first, i.second);
+ }
+ {
+ auto list = topObj.list("functions");
+ for (auto & i : functionCalls) {
+ auto obj = list.object();
+ if (i.first->name.set())
+ obj.attr("name", (const string &) i.first->name);
+ else
+ obj.attr("name", nullptr);
+ if (i.first->pos) {
+ obj.attr("file", (const string &) i.first->pos.file);
+ obj.attr("line", i.first->pos.line);
+ obj.attr("column", i.first->pos.column);
+ }
+ obj.attr("count", i.second);
+ }
+ }
+ {
+ auto list = topObj.list("attributes");
+ for (auto & i : attrSelects) {
+ auto obj = list.object();
+ if (i.first) {
+ obj.attr("file", (const string &) i.first.file);
+ obj.attr("line", i.first.line);
+ obj.attr("column", i.first.column);
+ }
+ obj.attr("count", i.second);
+ }
+ }
+ }
+ if (getEnv("NIX_SHOW_SYMBOLS", "0") != "0") {
+ auto list = topObj.list("symbols");
+ symbols.dump([&](const std::string & s) { list.elem(s); });
+ }
}
}
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index d0f298e16..22472fd72 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -6,6 +6,7 @@
#include "symbol-table.hh"
#include "hash.hh"
#include "config.hh"
+#include "function-trace.hh"
#include <map>
#include <unordered_map>
@@ -81,7 +82,7 @@ public:
/* The allowed filesystem paths in restricted or pure evaluation
mode. */
- std::experimental::optional<PathSet> allowedPaths;
+ std::optional<PathSet> allowedPaths;
Value vEmptySet;
@@ -316,6 +317,9 @@ private:
/* Return a string representing the type of the value `v'. */
string showType(const Value & v);
+/* Decode a context string ‘!<name>!<path>’ into a pair <path,
+ name>. */
+std::pair<string, string> decodeContext(const string & s);
/* If `path' refers to a directory, then append "/default.nix". */
Path resolveExprPath(Path path);
@@ -346,6 +350,9 @@ struct EvalSettings : Config
Setting<Strings> allowedUris{this, {}, "allowed-uris",
"Prefixes of URIs that builtin functions such as fetchurl and fetchGit are allowed to fetch."};
+
+ Setting<bool> traceFunctionCalls{this, false, "trace-function-calls",
+ "Emit log messages for each function entry and exit at the 'vomit' log level (-vvvv)"};
};
extern EvalSettings evalSettings;
diff --git a/src/libexpr/function-trace.hh b/src/libexpr/function-trace.hh
new file mode 100644
index 000000000..8b0ec848d
--- /dev/null
+++ b/src/libexpr/function-trace.hh
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "eval.hh"
+#include <sys/time.h>
+
+namespace nix {
+
+struct FunctionCallTrace
+{
+ const Pos & pos;
+
+ FunctionCallTrace(const Pos & pos) : pos(pos) {
+ auto duration = std::chrono::high_resolution_clock::now().time_since_epoch();
+ auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
+ printMsg(lvlInfo, "function-trace entered %1% at %2%", pos, ns.count());
+ }
+
+ ~FunctionCallTrace() {
+ auto duration = std::chrono::high_resolution_clock::now().time_since_epoch();
+ auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
+ printMsg(lvlInfo, "function-trace exited %1% at %2%", pos, ns.count());
+ }
+};
+}
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index d38ed2df3..21a4d7917 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -295,7 +295,7 @@ static bool getDerivation(EvalState & state, Value & v,
}
-std::experimental::optional<DrvInfo> getDerivation(EvalState & state, Value & v,
+std::optional<DrvInfo> getDerivation(EvalState & state, Value & v,
bool ignoreAssertionFailures)
{
Done done;
diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh
index 4d9128e3f..d7860fc6a 100644
--- a/src/libexpr/get-drvs.hh
+++ b/src/libexpr/get-drvs.hh
@@ -44,7 +44,7 @@ public:
string queryDrvPath() const;
string queryOutPath() const;
string queryOutputName() const;
- /** Return the list of outputs. The "outputs to install" are determined by `mesa.outputsToInstall`. */
+ /** Return the list of outputs. The "outputs to install" are determined by `meta.outputsToInstall`. */
Outputs queryOutputs(bool onlyOutputsToInstall = false);
StringSet queryMetaNames();
@@ -78,7 +78,7 @@ typedef list<DrvInfo> DrvInfos;
/* If value `v' denotes a derivation, return a DrvInfo object
describing it. Otherwise return nothing. */
-std::experimental::optional<DrvInfo> getDerivation(EvalState & state,
+std::optional<DrvInfo> getDerivation(EvalState & state,
Value & v, bool ignoreAssertionFailures);
void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
diff --git a/src/libexpr/json-to-value.cc b/src/libexpr/json-to-value.cc
index 3f6017957..8bae986f9 100644
--- a/src/libexpr/json-to-value.cc
+++ b/src/libexpr/json-to-value.cc
@@ -111,9 +111,9 @@ static void parseJSON(EvalState & state, const char * & s, Value & v)
mkFloat(v, stod(tmp_number));
else
mkInt(v, stol(tmp_number));
- } catch (std::invalid_argument e) {
+ } catch (std::invalid_argument & e) {
throw JSONParseError("invalid JSON number");
- } catch (std::out_of_range e) {
+ } catch (std::out_of_range & e) {
throw JSONParseError("out-of-range JSON number");
}
}
diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l
index 29ca327c1..c34e5c383 100644
--- a/src/libexpr/lexer.l
+++ b/src/libexpr/lexer.l
@@ -6,12 +6,14 @@
%option nounput noyy_top_state
+%s DEFAULT
%x STRING
%x IND_STRING
-%x INSIDE_DOLLAR_CURLY
%{
+#include <boost/lexical_cast.hpp>
+
#include "nixexpr.hh"
#include "parser-tab.hh"
@@ -97,8 +99,6 @@ URI [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~
%%
-<INITIAL,INSIDE_DOLLAR_CURLY>{
-
if { return IF; }
then { return THEN; }
@@ -124,9 +124,11 @@ or { return OR_KW; }
{ID} { yylval->id = strdup(yytext); return ID; }
{INT} { errno = 0;
- yylval->n = strtol(yytext, 0, 10);
- if (errno != 0)
+ try {
+ yylval->n = boost::lexical_cast<int64_t>(yytext);
+ } catch (const boost::bad_lexical_cast &) {
throw ParseError(format("invalid integer '%1%'") % yytext);
+ }
return INT;
}
{FLOAT} { errno = 0;
@@ -136,17 +138,19 @@ or { return OR_KW; }
return FLOAT;
}
-\$\{ { PUSH_STATE(INSIDE_DOLLAR_CURLY); return DOLLAR_CURLY; }
-}
+\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
-\} { return '}'; }
-<INSIDE_DOLLAR_CURLY>\} { POP_STATE(); return '}'; }
-\{ { return '{'; }
-<INSIDE_DOLLAR_CURLY>\{ { PUSH_STATE(INSIDE_DOLLAR_CURLY); return '{'; }
+\} { /* State INITIAL only exists at the bottom of the stack and is
+ used as a marker. DEFAULT replaces it everywhere else.
+ Popping when in INITIAL state causes an empty stack exception,
+ so don't */
+ if (YYSTATE != INITIAL)
+ POP_STATE();
+ return '}';
+ }
+\{ { PUSH_STATE(DEFAULT); return '{'; }
-<INITIAL,INSIDE_DOLLAR_CURLY>\" {
- PUSH_STATE(STRING); return '"';
- }
+\" { PUSH_STATE(STRING); return '"'; }
<STRING>([^\$\"\\]|\$[^\{\"\\]|\\{ANY}|\$\\{ANY})*\$/\" |
<STRING>([^\$\"\\]|\$[^\{\"\\]|\\{ANY}|\$\\{ANY})+ {
/* It is impossible to match strings ending with '$' with one
@@ -155,7 +159,7 @@ or { return OR_KW; }
yylval->e = unescapeStr(data->symbols, yytext, yyleng);
return STR;
}
-<STRING>\$\{ { PUSH_STATE(INSIDE_DOLLAR_CURLY); return DOLLAR_CURLY; }
+<STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
<STRING>\" { POP_STATE(); return '"'; }
<STRING>\$|\\|\$\\ {
/* This can only occur when we reach EOF, otherwise the above
@@ -165,7 +169,7 @@ or { return OR_KW; }
return STR;
}
-<INITIAL,INSIDE_DOLLAR_CURLY>\'\'(\ *\n)? { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; }
+\'\'(\ *\n)? { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; }
<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
yylval->e = new ExprIndStr(yytext);
return IND_STR;
@@ -183,14 +187,13 @@ or { return OR_KW; }
yylval->e = unescapeStr(data->symbols, yytext + 2, yyleng - 2);
return IND_STR;
}
-<IND_STRING>\$\{ { PUSH_STATE(INSIDE_DOLLAR_CURLY); return DOLLAR_CURLY; }
+<IND_STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
<IND_STRING>\'\' { POP_STATE(); return IND_STRING_CLOSE; }
<IND_STRING>\' {
yylval->e = new ExprIndStr("'");
return IND_STR;
}
-<INITIAL,INSIDE_DOLLAR_CURLY>{
{PATH} { if (yytext[yyleng-1] == '/')
throw ParseError("path '%s' has a trailing slash", yytext);
@@ -215,7 +218,5 @@ or { return OR_KW; }
return (unsigned char) yytext[0];
}
-}
-
%%
diff --git a/src/libexpr/local.mk b/src/libexpr/local.mk
index daa3258f0..ccd5293e4 100644
--- a/src/libexpr/local.mk
+++ b/src/libexpr/local.mk
@@ -6,7 +6,7 @@ libexpr_DIR := $(d)
libexpr_SOURCES := $(wildcard $(d)/*.cc) $(wildcard $(d)/primops/*.cc) $(d)/lexer-tab.cc $(d)/parser-tab.cc
-libexpr_LIBS = libutil libstore libformat
+libexpr_LIBS = libutil libstore
libexpr_LDFLAGS =
ifneq ($(OS), FreeBSD)
diff --git a/src/libexpr/nix-expr.pc.in b/src/libexpr/nix-expr.pc.in
index 79f3e2f45..80f7a492b 100644
--- a/src/libexpr/nix-expr.pc.in
+++ b/src/libexpr/nix-expr.pc.in
@@ -7,4 +7,4 @@ Description: Nix Package Manager
Version: @PACKAGE_VERSION@
Requires: nix-store bdw-gc
Libs: -L${libdir} -lnixexpr
-Cflags: -I${includedir}/nix -std=c++14
+Cflags: -I${includedir}/nix -std=c++17
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index cbd576d7d..967c88d9b 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -1,7 +1,7 @@
%glr-parser
%pure-parser
%locations
-%error-verbose
+%define parse.error verbose
%defines
/* %no-lines */
%parse-param { void * scanner }
@@ -81,6 +81,8 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
AttrPath::iterator i;
// All attrpaths have at least one attr
assert(!attrPath.empty());
+ // Checking attrPath validity.
+ // ===========================
for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) {
if (i->symbol.set()) {
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
@@ -102,11 +104,29 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
attrs = nested;
}
}
+ // Expr insertion.
+ // ==========================
if (i->symbol.set()) {
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
if (j != attrs->attrs.end()) {
- dupAttr(attrPath, pos, j->second.pos);
+ // This attr path is already defined. However, if both
+ // e and the expr pointed by the attr path are two attribute sets,
+ // we want to merge them.
+ // Otherwise, throw an error.
+ auto ae = dynamic_cast<ExprAttrs *>(e);
+ auto jAttrs = dynamic_cast<ExprAttrs *>(j->second.e);
+ if (jAttrs && ae) {
+ for (auto & ad : ae->attrs) {
+ auto j2 = jAttrs->attrs.find(ad.first);
+ if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error.
+ dupAttr(ad.first, j2->second.pos, ad.second.pos);
+ jAttrs->attrs[ad.first] = ad.second;
+ }
+ } else {
+ dupAttr(attrPath, pos, j->second.pos);
+ }
} else {
+ // This attr path is not defined. Let's create it.
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(e, pos);
e->setName(i->symbol);
}
@@ -657,7 +677,9 @@ std::pair<bool, std::string> EvalState::resolveSearchPathElem(const SearchPathEl
if (isUri(elem.second)) {
try {
- res = { true, getDownloader()->downloadCached(store, elem.second, true) };
+ CachedDownloadRequest request(elem.second);
+ request.unpack = true;
+ res = { true, getDownloader()->downloadCached(store, request).path };
} catch (DownloadError & e) {
printError(format("warning: Nix search path entry '%1%' cannot be downloaded, ignoring") % elem.second);
res = { false, "" };
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 8ace6db4d..350dba474 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -315,6 +315,12 @@ static void prim_isBool(EvalState & state, const Pos & pos, Value * * args, Valu
mkBool(v, args[0]->type == tBool);
}
+/* Determine whether the argument is a path. */
+static void prim_isPath(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+ state.forceValue(*args[0]);
+ mkBool(v, args[0]->type == tPath);
+}
struct CompareValues
{
@@ -555,7 +561,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
PathSet context;
- std::experimental::optional<std::string> outputHash;
+ std::optional<std::string> outputHash;
std::string outputHashAlgo;
bool outputHashRecursive = false;
@@ -687,21 +693,12 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
}
}
- /* See prim_unsafeDiscardOutputDependency. */
- else if (path.at(0) == '~')
- drv.inputSrcs.insert(string(path, 1));
-
/* Handle derivation outputs of the form ‘!<name>!<path>’. */
else if (path.at(0) == '!') {
std::pair<string, string> ctx = decodeContext(path);
drv.inputDrvs[ctx.first].insert(ctx.second);
}
- /* Handle derivation contexts returned by
- ‘builtins.storePath’. */
- else if (isDerivation(path))
- drv.inputDrvs[path] = state.store->queryDerivationOutputNames(path);
-
/* Otherwise it's a source file. */
else
drv.inputSrcs.insert(path);
@@ -724,16 +721,14 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
if (outputs.size() != 1 || *(outputs.begin()) != "out")
throw Error(format("multiple outputs are not supported in fixed-output derivations, at %1%") % posDrvName);
- HashType ht = parseHashType(outputHashAlgo);
- if (ht == htUnknown)
- throw EvalError(format("unknown hash algorithm '%1%', at %2%") % outputHashAlgo % posDrvName);
+ HashType ht = outputHashAlgo.empty() ? htUnknown : parseHashType(outputHashAlgo);
Hash h(*outputHash, ht);
- outputHash = h.to_string(Base16, false);
- if (outputHashRecursive) outputHashAlgo = "r:" + outputHashAlgo;
Path outPath = state.store->makeFixedOutputPath(outputHashRecursive, h, drvName);
if (!jsonObject) drv.env["out"] = outPath;
- drv.outputs["out"] = DerivationOutput(outPath, outputHashAlgo, *outputHash);
+ drv.outputs["out"] = DerivationOutput(outPath,
+ (outputHashRecursive ? "r:" : "") + printHashType(h.type),
+ h.to_string(Base16, false));
}
else {
@@ -837,8 +832,14 @@ static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args,
{
PathSet context;
Path path = state.coerceToPath(pos, *args[0], context);
- if (!context.empty())
- throw EvalError(format("string '%1%' cannot refer to other paths, at %2%") % path % pos);
+ try {
+ state.realiseContext(context);
+ } catch (InvalidPathError & e) {
+ throw EvalError(format(
+ "cannot check the existence of '%1%', since path '%2%' is not valid, at %3%")
+ % path % e.path % pos);
+ }
+
try {
mkBool(v, pathExists(state.checkSourcePath(path)));
} catch (SysError & e) {
@@ -866,7 +867,7 @@ static void prim_baseNameOf(EvalState & state, const Pos & pos, Value * * args,
static void prim_dirOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
- Path dir = dirOf(state.coerceToPath(pos, *args[0], context));
+ Path dir = dirOf(state.coerceToString(pos, *args[0], context, false, false));
if (args[0]->type == tPath) mkPath(v, dir.c_str()); else mkString(v, dir, context);
}
@@ -928,6 +929,20 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
mkPath(v, state.checkSourcePath(state.findFile(searchPath, path, pos)).c_str());
}
+/* Return the cryptographic hash of a file in base-16. */
+static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+ string type = state.forceStringNoCtx(*args[0], pos);
+ HashType ht = parseHashType(type);
+ if (ht == htUnknown)
+ throw Error(format("unknown hash type '%1%', at %2%") % type % pos);
+
+ PathSet context; // discarded
+ Path p = state.coerceToPath(pos, *args[1], context);
+
+ mkString(v, hashFile(ht, state.checkSourcePath(p)).to_string(Base16, false), context);
+}
+
/* Read a directory (without . or ..) */
static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
@@ -1006,13 +1021,8 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu
PathSet refs;
for (auto path : context) {
- if (path.at(0) == '=') path = string(path, 1);
- if (isDerivation(path)) {
- /* See prim_unsafeDiscardOutputDependency. */
- if (path.at(0) != '~')
- throw EvalError(format("in 'toFile': the file '%1%' cannot refer to derivation outputs, at %2%") % name % pos);
- path = string(path, 1);
- }
+ if (path.at(0) != '/')
+ throw EvalError(format("in 'toFile': the file '%1%' cannot refer to derivation outputs, at %2%") % name % pos);
refs.insert(path);
}
@@ -1680,6 +1690,8 @@ static void prim_concatMap(EvalState & state, const Pos & pos, Value * * args, V
static void prim_add(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
+ state.forceValue(*args[0], pos);
+ state.forceValue(*args[1], pos);
if (args[0]->type == tFloat || args[1]->type == tFloat)
mkFloat(v, state.forceFloat(*args[0], pos) + state.forceFloat(*args[1], pos));
else
@@ -1689,6 +1701,8 @@ static void prim_add(EvalState & state, const Pos & pos, Value * * args, Value &
static void prim_sub(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
+ state.forceValue(*args[0], pos);
+ state.forceValue(*args[1], pos);
if (args[0]->type == tFloat || args[1]->type == tFloat)
mkFloat(v, state.forceFloat(*args[0], pos) - state.forceFloat(*args[1], pos));
else
@@ -1698,6 +1712,8 @@ static void prim_sub(EvalState & state, const Pos & pos, Value * * args, Value &
static void prim_mul(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
+ state.forceValue(*args[0], pos);
+ state.forceValue(*args[1], pos);
if (args[0]->type == tFloat || args[1]->type == tFloat)
mkFloat(v, state.forceFloat(*args[0], pos) * state.forceFloat(*args[1], pos));
else
@@ -1707,6 +1723,9 @@ static void prim_mul(EvalState & state, const Pos & pos, Value * * args, Value &
static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
+ state.forceValue(*args[0], pos);
+ state.forceValue(*args[1], pos);
+
NixFloat f2 = state.forceFloat(*args[1], pos);
if (f2 == 0) throw EvalError(format("division by zero, at %1%") % pos);
@@ -1787,41 +1806,6 @@ static void prim_stringLength(EvalState & state, const Pos & pos, Value * * args
}
-static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
-{
- PathSet context;
- string s = state.coerceToString(pos, *args[0], context);
- mkString(v, s, PathSet());
-}
-
-
-static void prim_hasContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
-{
- PathSet context;
- state.forceString(*args[0], context, pos);
- mkBool(v, !context.empty());
-}
-
-
-/* Sometimes we want to pass a derivation path (i.e. pkg.drvPath) to a
- builder without causing the derivation to be built (for instance,
- in the derivation that builds NARs in nix-push, when doing
- source-only deployment). This primop marks the string context so
- that builtins.derivation adds the path to drv.inputSrcs rather than
- drv.inputDrvs. */
-static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & pos, Value * * args, Value & v)
-{
- PathSet context;
- string s = state.coerceToString(pos, *args[0], context);
-
- PathSet context2;
- for (auto & p : context)
- context2.insert(p.at(0) == '=' ? "~" + string(p, 1) : p);
-
- mkString(v, s, context2);
-}
-
-
/* Return the cryptographic hash of a string in base-16. */
static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
@@ -2072,9 +2056,9 @@ static void prim_splitVersion(EvalState & state, const Pos & pos, Value * * args
void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
const string & who, bool unpack, const std::string & defaultName)
{
- string url;
- Hash expectedHash;
- string name = defaultName;
+ CachedDownloadRequest request("");
+ request.unpack = unpack;
+ request.name = defaultName;
state.forceValue(*args[0]);
@@ -2085,27 +2069,27 @@ void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
for (auto & attr : *args[0]->attrs) {
string n(attr.name);
if (n == "url")
- url = state.forceStringNoCtx(*attr.value, *attr.pos);
+ request.uri = state.forceStringNoCtx(*attr.value, *attr.pos);
else if (n == "sha256")
- expectedHash = Hash(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
+ request.expectedHash = Hash(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
else if (n == "name")
- name = state.forceStringNoCtx(*attr.value, *attr.pos);
+ request.name = state.forceStringNoCtx(*attr.value, *attr.pos);
else
throw EvalError(format("unsupported argument '%1%' to '%2%', at %3%") % attr.name % who % attr.pos);
}
- if (url.empty())
+ if (request.uri.empty())
throw EvalError(format("'url' argument required, at %1%") % pos);
} else
- url = state.forceStringNoCtx(*args[0], pos);
+ request.uri = state.forceStringNoCtx(*args[0], pos);
- state.checkURI(url);
+ state.checkURI(request.uri);
- if (evalSettings.pureEval && !expectedHash)
+ if (evalSettings.pureEval && !request.expectedHash)
throw Error("in pure evaluation mode, '%s' requires a 'sha256' argument", who);
- Path res = getDownloader()->downloadCached(state.store, url, unpack, name, expectedHash);
+ Path res = getDownloader()->downloadCached(state.store, request).path;
if (state.allowedPaths)
state.allowedPaths->insert(res);
@@ -2211,6 +2195,7 @@ void EvalState::createBaseEnv()
addPrimOp("__isInt", 1, prim_isInt);
addPrimOp("__isFloat", 1, prim_isFloat);
addPrimOp("__isBool", 1, prim_isBool);
+ addPrimOp("__isPath", 1, prim_isPath);
addPrimOp("__genericClosure", 1, prim_genericClosure);
addPrimOp("abort", 1, prim_abort);
addPrimOp("__addErrorContext", 2, prim_addErrorContext);
@@ -2237,6 +2222,7 @@ void EvalState::createBaseEnv()
addPrimOp("__readFile", 1, prim_readFile);
addPrimOp("__readDir", 1, prim_readDir);
addPrimOp("__findFile", 2, prim_findFile);
+ addPrimOp("__hashFile", 2, prim_hashFile);
// Creating files
addPrimOp("__toXML", 1, prim_toXML);
@@ -2292,9 +2278,6 @@ void EvalState::createBaseEnv()
addPrimOp("toString", 1, prim_toString);
addPrimOp("__substring", 3, prim_substring);
addPrimOp("__stringLength", 1, prim_stringLength);
- addPrimOp("__hasContext", 1, prim_hasContext);
- addPrimOp("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
- addPrimOp("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
addPrimOp("__hashString", 2, prim_hashString);
addPrimOp("__match", 2, prim_match);
addPrimOp("__split", 2, prim_split);
diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc
new file mode 100644
index 000000000..2d79739ea
--- /dev/null
+++ b/src/libexpr/primops/context.cc
@@ -0,0 +1,187 @@
+#include "primops.hh"
+#include "eval-inline.hh"
+#include "derivations.hh"
+
+namespace nix {
+
+static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+ PathSet context;
+ string s = state.coerceToString(pos, *args[0], context);
+ mkString(v, s, PathSet());
+}
+
+static RegisterPrimOp r1("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
+
+
+static void prim_hasContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+ PathSet context;
+ state.forceString(*args[0], context, pos);
+ mkBool(v, !context.empty());
+}
+
+static RegisterPrimOp r2("__hasContext", 1, prim_hasContext);
+
+
+/* Sometimes we want to pass a derivation path (i.e. pkg.drvPath) to a
+ builder without causing the derivation to be built (for instance,
+ in the derivation that builds NARs in nix-push, when doing
+ source-only deployment). This primop marks the string context so
+ that builtins.derivation adds the path to drv.inputSrcs rather than
+ drv.inputDrvs. */
+static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+ PathSet context;
+ string s = state.coerceToString(pos, *args[0], context);
+
+ PathSet context2;
+ for (auto & p : context)
+ context2.insert(p.at(0) == '=' ? string(p, 1) : p);
+
+ mkString(v, s, context2);
+}
+
+static RegisterPrimOp r3("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
+
+
+/* Extract the context of a string as a structured Nix value.
+
+ The context is represented as an attribute set whose keys are the
+ paths in the context set and whose values are attribute sets with
+ the following keys:
+ path: True if the relevant path is in the context as a plain store
+ path (i.e. the kind of context you get when interpolating
+ a Nix path (e.g. ./.) into a string). False if missing.
+ allOutputs: True if the relevant path is a derivation and it is
+ in the context as a drv file with all of its outputs
+ (i.e. the kind of context you get when referencing
+ .drvPath of some derivation). False if missing.
+ outputs: If a non-empty list, the relevant path is a derivation
+ and the provided outputs are referenced in the context
+ (i.e. the kind of context you get when referencing
+ .outPath of some derivation). Empty list if missing.
+ Note that for a given path any combination of the above attributes
+ may be present.
+*/
+static void prim_getContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+ struct ContextInfo {
+ bool path = false;
+ bool allOutputs = false;
+ Strings outputs;
+ };
+ PathSet context;
+ state.forceString(*args[0], context, pos);
+ auto contextInfos = std::map<Path, ContextInfo>();
+ for (const auto & p : context) {
+ Path drv;
+ string output;
+ const Path * path = &p;
+ if (p.at(0) == '=') {
+ drv = string(p, 1);
+ path = &drv;
+ } else if (p.at(0) == '!') {
+ std::pair<string, string> ctx = decodeContext(p);
+ drv = ctx.first;
+ output = ctx.second;
+ path = &drv;
+ }
+ auto isPath = drv.empty();
+ auto isAllOutputs = (!drv.empty()) && output.empty();
+
+ auto iter = contextInfos.find(*path);
+ if (iter == contextInfos.end()) {
+ contextInfos.emplace(*path, ContextInfo{isPath, isAllOutputs, output.empty() ? Strings{} : Strings{std::move(output)}});
+ } else {
+ if (isPath)
+ iter->second.path = true;
+ else if (isAllOutputs)
+ iter->second.allOutputs = true;
+ else
+ iter->second.outputs.emplace_back(std::move(output));
+ }
+ }
+
+ state.mkAttrs(v, contextInfos.size());
+
+ auto sPath = state.symbols.create("path");
+ auto sAllOutputs = state.symbols.create("allOutputs");
+ for (const auto & info : contextInfos) {
+ auto & infoVal = *state.allocAttr(v, state.symbols.create(info.first));
+ state.mkAttrs(infoVal, 3);
+ if (info.second.path)
+ mkBool(*state.allocAttr(infoVal, sPath), true);
+ if (info.second.allOutputs)
+ mkBool(*state.allocAttr(infoVal, sAllOutputs), true);
+ if (!info.second.outputs.empty()) {
+ auto & outputsVal = *state.allocAttr(infoVal, state.sOutputs);
+ state.mkList(outputsVal, info.second.outputs.size());
+ size_t i = 0;
+ for (const auto & output : info.second.outputs) {
+ mkString(*(outputsVal.listElems()[i++] = state.allocValue()), output);
+ }
+ }
+ infoVal.attrs->sort();
+ }
+ v.attrs->sort();
+}
+
+static RegisterPrimOp r4("__getContext", 1, prim_getContext);
+
+
+/* Append the given context to a given string.
+
+ See the commentary above unsafeGetContext for details of the
+ context representation.
+*/
+static void prim_appendContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+ PathSet context;
+ auto orig = state.forceString(*args[0], context, pos);
+
+ state.forceAttrs(*args[1], pos);
+
+ auto sPath = state.symbols.create("path");
+ auto sAllOutputs = state.symbols.create("allOutputs");
+ for (auto & i : *args[1]->attrs) {
+ if (!state.store->isStorePath(i.name))
+ throw EvalError("Context key '%s' is not a store path, at %s", i.name, i.pos);
+ if (!settings.readOnlyMode)
+ state.store->ensurePath(i.name);
+ state.forceAttrs(*i.value, *i.pos);
+ auto iter = i.value->attrs->find(sPath);
+ if (iter != i.value->attrs->end()) {
+ if (state.forceBool(*iter->value, *iter->pos))
+ context.insert(i.name);
+ }
+
+ iter = i.value->attrs->find(sAllOutputs);
+ if (iter != i.value->attrs->end()) {
+ if (state.forceBool(*iter->value, *iter->pos)) {
+ if (!isDerivation(i.name)) {
+ throw EvalError("Tried to add all-outputs context of %s, which is not a derivation, to a string, at %s", i.name, i.pos);
+ }
+ context.insert("=" + string(i.name));
+ }
+ }
+
+ iter = i.value->attrs->find(state.sOutputs);
+ if (iter != i.value->attrs->end()) {
+ state.forceList(*iter->value, *iter->pos);
+ if (iter->value->listSize() && !isDerivation(i.name)) {
+ throw EvalError("Tried to add derivation output context of %s, which is not a derivation, to a string, at %s", i.name, i.pos);
+ }
+ for (unsigned int n = 0; n < iter->value->listSize(); ++n) {
+ auto name = state.forceStringNoCtx(*iter->value->listElems()[n], *iter->pos);
+ context.insert("!" + name + "!" + string(i.name));
+ }
+ }
+ }
+
+ mkString(v, orig, context);
+}
+
+static RegisterPrimOp r5("__appendContext", 2, prim_appendContext);
+
+}
diff --git a/src/libexpr/primops/fetchGit.cc b/src/libexpr/primops/fetchGit.cc
index 7aa98e0bf..90f600284 100644
--- a/src/libexpr/primops/fetchGit.cc
+++ b/src/libexpr/primops/fetchGit.cc
@@ -3,6 +3,7 @@
#include "download.hh"
#include "store-api.hh"
#include "pathlocks.hh"
+#include "hash.hh"
#include <sys/time.h>
@@ -25,7 +26,7 @@ struct GitInfo
std::regex revRegex("^[0-9a-fA-F]{40}$");
GitInfo exportGit(ref<Store> store, const std::string & uri,
- std::experimental::optional<std::string> ref, std::string rev,
+ std::optional<std::string> ref, std::string rev,
const std::string & name)
{
if (evalSettings.pureEval && rev == "")
@@ -37,7 +38,7 @@ GitInfo exportGit(ref<Store> store, const std::string & uri,
try {
runProgram("git", true, { "-C", uri, "diff-index", "--quiet", "HEAD", "--" });
- } catch (ExecError e) {
+ } catch (ExecError & e) {
if (!WIFEXITED(e.status) || WEXITSTATUS(e.status) != 1) throw;
clean = false;
}
@@ -84,15 +85,20 @@ GitInfo exportGit(ref<Store> store, const std::string & uri,
if (rev != "" && !std::regex_match(rev, revRegex))
throw Error("invalid Git revision '%s'", rev);
- Path cacheDir = getCacheDir() + "/nix/git";
+ deletePath(getCacheDir() + "/nix/git");
+
+ Path cacheDir = getCacheDir() + "/nix/gitv2/" + hashString(htSHA256, uri).to_string(Base32, false);
if (!pathExists(cacheDir)) {
+ createDirs(dirOf(cacheDir));
runProgram("git", true, { "init", "--bare", cacheDir });
}
- std::string localRef = hashString(htSHA256, fmt("%s-%s", uri, *ref)).to_string(Base32, false);
-
- Path localRefFile = cacheDir + "/refs/heads/" + localRef;
+ Path localRefFile;
+ if (ref->compare(0, 5, "refs/") == 0)
+ localRefFile = cacheDir + "/" + *ref;
+ else
+ localRefFile = cacheDir + "/refs/heads/" + *ref;
bool doFetch;
time_t now = time(0);
@@ -114,7 +120,7 @@ GitInfo exportGit(ref<Store> store, const std::string & uri,
git fetch to update the local ref to the remote ref. */
struct stat st;
doFetch = stat(localRefFile.c_str(), &st) != 0 ||
- st.st_mtime + settings.tarballTtl <= now;
+ (uint64_t) st.st_mtime + settings.tarballTtl <= (uint64_t) now;
}
if (doFetch)
{
@@ -122,7 +128,7 @@ GitInfo exportGit(ref<Store> store, const std::string & uri,
// FIXME: git stderr messes up our progress indicator, so
// we're using --quiet for now. Should process its stderr.
- runProgram("git", true, { "-C", cacheDir, "fetch", "--quiet", "--force", "--", uri, *ref + ":" + localRef });
+ runProgram("git", true, { "-C", cacheDir, "fetch", "--quiet", "--force", "--", uri, fmt("%s:%s", *ref, *ref) });
struct timeval times[2];
times[0].tv_sec = now;
@@ -188,7 +194,7 @@ GitInfo exportGit(ref<Store> store, const std::string & uri,
static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
std::string url;
- std::experimental::optional<std::string> ref;
+ std::optional<std::string> ref;
std::string rev;
std::string name = "source";
PathSet context;
@@ -219,8 +225,6 @@ static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Va
} else
url = state.coerceToString(pos, *args[0], context, false, false);
- if (!isUri(url)) url = absPath(url);
-
// FIXME: git externals probably can be used to bypass the URI
// whitelist. Ah well.
state.checkURI(url);
@@ -235,7 +239,7 @@ static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Va
v.attrs->sort();
if (state.allowedPaths)
- state.allowedPaths->insert(gitInfo.storePath);
+ state.allowedPaths->insert(state.store->toRealPath(gitInfo.storePath));
}
static RegisterPrimOp r("fetchGit", 1, prim_fetchGit);
diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc
index 9d35f6d0d..a907d0e1c 100644
--- a/src/libexpr/primops/fetchMercurial.cc
+++ b/src/libexpr/primops/fetchMercurial.cc
@@ -80,7 +80,7 @@ HgInfo exportMercurial(ref<Store> store, const std::string & uri,
time_t now = time(0);
struct stat st;
if (stat(stampFile.c_str(), &st) != 0 ||
- st.st_mtime + settings.tarballTtl <= now)
+ (uint64_t) st.st_mtime + settings.tarballTtl <= (uint64_t) now)
{
/* Except that if this is a commit hash that we already have,
we don't have to pull again. */
@@ -93,7 +93,19 @@ HgInfo exportMercurial(ref<Store> store, const std::string & uri,
Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching Mercurial repository '%s'", uri));
if (pathExists(cacheDir)) {
- runProgram("hg", true, { "pull", "-R", cacheDir, "--", uri });
+ try {
+ runProgram("hg", true, { "pull", "-R", cacheDir, "--", uri });
+ }
+ catch (ExecError & e) {
+ string transJournal = cacheDir + "/.hg/store/journal";
+ /* hg throws "abandoned transaction" error only if this file exists */
+ if (pathExists(transJournal)) {
+ runProgram("hg", true, { "recover", "-R", cacheDir });
+ runProgram("hg", true, { "pull", "-R", cacheDir, "--", uri });
+ } else {
+ throw ExecError(e.status, fmt("'hg pull' %s", statusToString(e.status)));
+ }
+ }
} else {
createDirs(dirOf(cacheDir));
runProgram("hg", true, { "clone", "--noupdate", "--", uri, cacheDir });
@@ -184,8 +196,6 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
} else
url = state.coerceToString(pos, *args[0], context, false, false);
- if (!isUri(url)) url = absPath(url);
-
// FIXME: git externals probably can be used to bypass the URI
// whitelist. Ah well.
state.checkURI(url);
@@ -201,7 +211,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
v.attrs->sort();
if (state.allowedPaths)
- state.allowedPaths->insert(hgInfo.storePath);
+ state.allowedPaths->insert(state.store->toRealPath(hgInfo.storePath));
}
static RegisterPrimOp r("fetchMercurial", 1, prim_fetchMercurial);
diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc
index 4128de05d..a84e569e9 100644
--- a/src/libexpr/primops/fromTOML.cc
+++ b/src/libexpr/primops/fromTOML.cc
@@ -49,6 +49,19 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va
visit(*(v.listElems()[i] = state.allocValue()), t2->get()[i]);
}
+ // Handle cases like 'a = [[{ a = true }]]', which IMHO should be
+ // parsed as a array containing an array containing a table,
+ // but instead are parsed as an array containing a table array
+ // containing a table.
+ else if (auto t2 = t->as_table_array()) {
+ size_t size = t2->get().size();
+
+ state.mkList(v, size);
+
+ for (size_t j = 0; j < size; ++j)
+ visit(*(v.listElems()[j] = state.allocValue()), t2->get()[j]);
+ }
+
else if (t->is_value()) {
if (auto val = t->as<int64_t>())
mkInt(v, val->get());
diff --git a/src/libexpr/symbol-table.hh b/src/libexpr/symbol-table.hh
index 44929f7ee..91faea122 100644
--- a/src/libexpr/symbol-table.hh
+++ b/src/libexpr/symbol-table.hh
@@ -75,6 +75,13 @@ public:
}
size_t totalSize() const;
+
+ template<typename T>
+ void dump(T callback)
+ {
+ for (auto & s : symbols)
+ callback(s);
+ }
};
}
diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh
index 809772f7c..e1ec87d3b 100644
--- a/src/libexpr/value.hh
+++ b/src/libexpr/value.hh
@@ -43,7 +43,7 @@ class XMLWriter;
class JSONPlaceholder;
-typedef long NixInt;
+typedef int64_t NixInt;
typedef double NixFloat;
/* External values must descend from ExternalValueBase, so that