aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr
diff options
context:
space:
mode:
authortomberek <tomberek@users.noreply.github.com>2021-12-23 15:22:52 -0500
committerGitHub <noreply@github.com>2021-12-23 15:22:52 -0500
commitf4041893688914db9a3598ce5f6186b049a48678 (patch)
tree80c468f0bf29f26f6600b0bf90d34b4415f6968b /src/libexpr
parentb6cc0a704d8c1432e230ff65d4b74ea7114a730b (diff)
parentaf553b20902b8b8efbccab5f880879b09e95eb32 (diff)
Merge branch 'master' into flake_search
Diffstat (limited to 'src/libexpr')
-rw-r--r--src/libexpr/eval.cc25
-rw-r--r--src/libexpr/eval.hh8
-rw-r--r--src/libexpr/flake/config.cc8
-rw-r--r--src/libexpr/flake/flake.cc10
-rw-r--r--src/libexpr/flake/flakeref.cc83
-rw-r--r--src/libexpr/flake/flakeref.hh10
-rw-r--r--src/libexpr/nixexpr.cc4
-rw-r--r--src/libexpr/nixexpr.hh4
-rw-r--r--src/libexpr/parser.y38
-rw-r--r--src/libexpr/primops.cc127
-rw-r--r--src/libexpr/primops/fromTOML.cc111
11 files changed, 244 insertions, 184 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index b987e1888..a95726f5f 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -821,8 +821,23 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
Value * EvalState::allocValue()
{
+ /* We use the boehm batch allocator to speed up allocations of Values (of which there are many).
+ GC_malloc_many returns a linked list of objects of the given size, where the first word
+ of each object is also the pointer to the next object in the list. This also means that we
+ have to explicitly clear the first word of every object we take. */
+ if (!valueAllocCache) {
+ valueAllocCache = GC_malloc_many(sizeof(Value));
+ if (!valueAllocCache) throw std::bad_alloc();
+ }
+
+ /* GC_NEXT is a convenience macro for accessing the first word of an object.
+ Take the first list item, advance the list to the next item, and clear the next pointer. */
+ void * p = valueAllocCache;
+ GC_PTR_STORE_AND_DIRTY(&valueAllocCache, GC_NEXT(p));
+ GC_NEXT(p) = nullptr;
+
nrValues++;
- auto v = (Value *) allocBytes(sizeof(Value));
+ auto v = (Value *) p;
return v;
}
@@ -1656,7 +1671,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
bool first = !forceString;
ValueType firstType = nString;
- for (auto & i : *es) {
+ for (auto & [i_pos, i] : *es) {
Value vTmp;
i->eval(state, env, vTmp);
@@ -1677,19 +1692,19 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
nf = n;
nf += vTmp.fpoint;
} else
- throwEvalError(pos, "cannot add %1% to an integer", showType(vTmp));
+ throwEvalError(i_pos, "cannot add %1% to an integer", showType(vTmp));
} else if (firstType == nFloat) {
if (vTmp.type() == nInt) {
nf += vTmp.integer;
} else if (vTmp.type() == nFloat) {
nf += vTmp.fpoint;
} else
- throwEvalError(pos, "cannot add %1% to a float", showType(vTmp));
+ throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp));
} else
/* skip canonization of first path, which would only be not
canonized in the first place if it's coming from a ./${foo} type
path */
- s << state.coerceToString(pos, vTmp, context, false, firstType == nString, !first);
+ s << state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first);
first = false;
}
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 1aab8e166..d7ef7b88a 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -133,6 +133,9 @@ private:
/* Cache used by prim_match(). */
std::shared_ptr<RegexCache> regexCache;
+ /* Allocation cache for GC'd Value objects. */
+ void * valueAllocCache = nullptr;
+
public:
EvalState(
@@ -350,7 +353,10 @@ public:
/* Print statistics. */
void printStats();
- void realiseContext(const PathSet & context);
+ /* Realise the given context, and return a mapping from the placeholders
+ * used to construct the associated value to their final store path
+ */
+ [[nodiscard]] StringMap realiseContext(const PathSet & context);
private:
diff --git a/src/libexpr/flake/config.cc b/src/libexpr/flake/config.cc
index c03f4106c..7ecd61816 100644
--- a/src/libexpr/flake/config.cc
+++ b/src/libexpr/flake/config.cc
@@ -38,11 +38,11 @@ void ConfigFile::apply()
// FIXME: Move into libutil/config.cc.
std::string valueS;
- if (auto s = std::get_if<std::string>(&value))
+ if (auto* s = std::get_if<std::string>(&value))
valueS = *s;
- else if (auto n = std::get_if<int64_t>(&value))
- valueS = fmt("%d", n);
- else if (auto b = std::get_if<Explicit<bool>>(&value))
+ else if (auto* n = std::get_if<int64_t>(&value))
+ valueS = fmt("%d", *n);
+ else if (auto* b = std::get_if<Explicit<bool>>(&value))
valueS = b->t ? "true" : "false";
else if (auto ss = std::get_if<std::vector<std::string>>(&value))
valueS = concatStringsSep(" ", *ss); // FIXME: evil
diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc
index 33d253eee..b15878d5c 100644
--- a/src/libexpr/flake/flake.cc
+++ b/src/libexpr/flake/flake.cc
@@ -155,7 +155,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
if (!attrs.empty())
throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, pos);
if (url)
- input.ref = parseFlakeRef(*url, baseDir, true);
+ input.ref = parseFlakeRef(*url, baseDir, true, input.isFlake);
}
if (!input.follows && !input.ref)
@@ -194,8 +194,8 @@ static Flake getFlake(
state, originalRef, allowLookup, flakeCache);
// Guard against symlink attacks.
- auto flakeDir = canonPath(sourceInfo.actualPath + "/" + lockedRef.subdir);
- auto flakeFile = canonPath(flakeDir + "/flake.nix");
+ auto flakeDir = canonPath(sourceInfo.actualPath + "/" + lockedRef.subdir, true);
+ auto flakeFile = canonPath(flakeDir + "/flake.nix", true);
if (!isInDir(flakeFile, sourceInfo.actualPath))
throw Error("'flake.nix' file of flake '%s' escapes from '%s'",
lockedRef, state.store->printStorePath(sourceInfo.storePath));
@@ -254,7 +254,7 @@ static Flake getFlake(
else if (setting.value->type() == nInt)
flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos)});
else if (setting.value->type() == nBool)
- flake.config.settings.insert({setting.name, state.forceBool(*setting.value, *setting.pos)});
+ flake.config.settings.insert({setting.name, Explicit<bool> { state.forceBool(*setting.value, *setting.pos) }});
else if (setting.value->type() == nList) {
std::vector<std::string> ss;
for (auto elem : setting.value->listItems()) {
@@ -570,7 +570,7 @@ LockedFlake lockFlake(
};
// Bring in the current ref for relative path resolution if we have it
- auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir);
+ auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir, true);
computeLocks(
flake.inputs, newLockFile.root, {},
diff --git a/src/libexpr/flake/flakeref.cc b/src/libexpr/flake/flakeref.cc
index 074727f06..b3f01746e 100644
--- a/src/libexpr/flake/flakeref.cc
+++ b/src/libexpr/flake/flakeref.cc
@@ -48,9 +48,12 @@ FlakeRef FlakeRef::resolve(ref<Store> store) const
}
FlakeRef parseFlakeRef(
- const std::string & url, const std::optional<Path> & baseDir, bool allowMissing)
+ const std::string & url,
+ const std::optional<Path> & baseDir,
+ bool allowMissing,
+ bool isFlake)
{
- auto [flakeRef, fragment] = parseFlakeRefWithFragment(url, baseDir, allowMissing);
+ auto [flakeRef, fragment] = parseFlakeRefWithFragment(url, baseDir, allowMissing, isFlake);
if (fragment != "")
throw Error("unexpected fragment '%s' in flake reference '%s'", fragment, url);
return flakeRef;
@@ -67,7 +70,10 @@ std::optional<FlakeRef> maybeParseFlakeRef(
}
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
- const std::string & url, const std::optional<Path> & baseDir, bool allowMissing)
+ const std::string & url,
+ const std::optional<Path> & baseDir,
+ bool allowMissing,
+ bool isFlake)
{
using namespace fetchers;
@@ -112,10 +118,9 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
to 'baseDir'). If so, search upward to the root of the
repo (i.e. the directory containing .git). */
- path = absPath(path, baseDir, true);
+ path = absPath(path, baseDir);
- if (!S_ISDIR(lstat(path).st_mode))
- throw BadURL("path '%s' is not a flake (because it's not a directory)", path);
+ if (isFlake) {
if (!allowMissing && !pathExists(path + "/flake.nix")){
notice("path '%s' does not contain a 'flake.nix', searching up",path);
@@ -138,39 +143,45 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
if (!found)
throw BadURL("could not find a flake.nix file");
}
-
- auto flakeRoot = path;
- std::string subdir;
-
- while (flakeRoot != "/") {
- if (pathExists(flakeRoot + "/.git")) {
- auto base = std::string("git+file://") + flakeRoot;
-
- auto parsedURL = ParsedURL{
- .url = base, // FIXME
- .base = base,
- .scheme = "git+file",
- .authority = "",
- .path = flakeRoot,
- .query = decodeQuery(match[2]),
- };
-
- if (subdir != "") {
- if (parsedURL.query.count("dir"))
- throw Error("flake URL '%s' has an inconsistent 'dir' parameter", url);
- parsedURL.query.insert_or_assign("dir", subdir);
+ if (!S_ISDIR(lstat(path).st_mode))
+ throw BadURL("path '%s' is not a flake (because it's not a directory)", path);
+
+ if (!allowMissing && !pathExists(path + "/flake.nix"))
+ throw BadURL("path '%s' is not a flake (because it doesn't contain a 'flake.nix' file)", path);
+
+ auto flakeRoot = path;
+ std::string subdir;
+
+ while (flakeRoot != "/") {
+ if (pathExists(flakeRoot + "/.git")) {
+ auto base = std::string("git+file://") + flakeRoot;
+
+ auto parsedURL = ParsedURL{
+ .url = base, // FIXME
+ .base = base,
+ .scheme = "git+file",
+ .authority = "",
+ .path = flakeRoot,
+ .query = decodeQuery(match[2]),
+ };
+
+ if (subdir != "") {
+ if (parsedURL.query.count("dir"))
+ throw Error("flake URL '%s' has an inconsistent 'dir' parameter", url);
+ parsedURL.query.insert_or_assign("dir", subdir);
+ }
+
+ if (pathExists(flakeRoot + "/.git/shallow"))
+ parsedURL.query.insert_or_assign("shallow", "1");
+
+ return std::make_pair(
+ FlakeRef(Input::fromURL(parsedURL), get(parsedURL.query, "dir").value_or("")),
+ fragment);
}
- if (pathExists(flakeRoot + "/.git/shallow"))
- parsedURL.query.insert_or_assign("shallow", "1");
-
- return std::make_pair(
- FlakeRef(Input::fromURL(parsedURL), get(parsedURL.query, "dir").value_or("")),
- fragment);
+ subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir);
+ flakeRoot = dirOf(flakeRoot);
}
-
- subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir);
- flakeRoot = dirOf(flakeRoot);
}
} else {
diff --git a/src/libexpr/flake/flakeref.hh b/src/libexpr/flake/flakeref.hh
index 0292eb210..1fddfd9a0 100644
--- a/src/libexpr/flake/flakeref.hh
+++ b/src/libexpr/flake/flakeref.hh
@@ -62,13 +62,19 @@ struct FlakeRef
std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef);
FlakeRef parseFlakeRef(
- const std::string & url, const std::optional<Path> & baseDir = {}, bool allowMissing = false);
+ const std::string & url,
+ const std::optional<Path> & baseDir = {},
+ bool allowMissing = false,
+ bool isFlake = true);
std::optional<FlakeRef> maybeParseFlake(
const std::string & url, const std::optional<Path> & baseDir = {});
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
- const std::string & url, const std::optional<Path> & baseDir = {}, bool allowMissing = false);
+ const std::string & url,
+ const std::optional<Path> & baseDir = {},
+ bool allowMissing = false,
+ bool isFlake = true);
std::optional<std::pair<FlakeRef, std::string>> maybeParseFlakeRefWithFragment(
const std::string & url, const std::optional<Path> & baseDir = {});
diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc
index 57c2f6e44..a75357871 100644
--- a/src/libexpr/nixexpr.cc
+++ b/src/libexpr/nixexpr.cc
@@ -191,7 +191,7 @@ void ExprConcatStrings::show(std::ostream & str) const
str << "(";
for (auto & i : *es) {
if (first) first = false; else str << " + ";
- str << *i;
+ str << i.second;
}
str << ")";
}
@@ -439,7 +439,7 @@ void ExprOpNot::bindVars(const StaticEnv & env)
void ExprConcatStrings::bindVars(const StaticEnv & env)
{
for (auto & i : *es)
- i->bindVars(env);
+ i.second->bindVars(env);
}
void ExprPos::bindVars(const StaticEnv & env)
diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh
index 13256272c..c013f5deb 100644
--- a/src/libexpr/nixexpr.hh
+++ b/src/libexpr/nixexpr.hh
@@ -332,8 +332,8 @@ struct ExprConcatStrings : Expr
{
Pos pos;
bool forceString;
- vector<Expr *> * es;
- ExprConcatStrings(const Pos & pos, bool forceString, vector<Expr *> * es)
+ vector<std::pair<Pos, Expr *> > * es;
+ ExprConcatStrings(const Pos & pos, bool forceString, vector<std::pair<Pos, Expr *> > * es)
: pos(pos), forceString(forceString), es(es) { };
COMMON_METHODS
};
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index c1f4e72e0..f8aaea582 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -152,7 +152,7 @@ static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
}
-static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Expr *> & es)
+static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<std::pair<Pos, Expr *> > & es)
{
if (es.empty()) return new ExprString(symbols.create(""));
@@ -162,7 +162,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Ex
bool atStartOfLine = true; /* = seen only whitespace in the current line */
size_t minIndent = 1000000;
size_t curIndent = 0;
- for (auto & i : es) {
+ for (auto & [i_pos, i] : es) {
ExprIndStr * e = dynamic_cast<ExprIndStr *>(i);
if (!e) {
/* Anti-quotations end the current start-of-line whitespace. */
@@ -192,12 +192,12 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Ex
}
/* Strip spaces from each line. */
- vector<Expr *> * es2 = new vector<Expr *>;
+ vector<std::pair<Pos, Expr *> > * es2 = new vector<std::pair<Pos, Expr *> >;
atStartOfLine = true;
size_t curDropped = 0;
size_t n = es.size();
- for (vector<Expr *>::iterator i = es.begin(); i != es.end(); ++i, --n) {
- ExprIndStr * e = dynamic_cast<ExprIndStr *>(*i);
+ for (vector<std::pair<Pos, Expr *> >::iterator i = es.begin(); i != es.end(); ++i, --n) {
+ ExprIndStr * e = dynamic_cast<ExprIndStr *>(i->second);
if (!e) {
atStartOfLine = false;
curDropped = 0;
@@ -234,11 +234,11 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Ex
s2 = string(s2, 0, p + 1);
}
- es2->push_back(new ExprString(symbols.create(s2)));
+ es2->emplace_back(i->first, new ExprString(symbols.create(s2)));
}
/* If this is a single string, then don't do a concatenation. */
- return es2->size() == 1 && dynamic_cast<ExprString *>((*es2)[0]) ? (*es2)[0] : new ExprConcatStrings(pos, true, es2);
+ return es2->size() == 1 && dynamic_cast<ExprString *>((*es2)[0].second) ? (*es2)[0].second : new ExprConcatStrings(pos, true, es2);
}
@@ -277,7 +277,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
char * path;
char * uri;
std::vector<nix::AttrName> * attrNames;
- std::vector<nix::Expr *> * string_parts;
+ std::vector<std::pair<nix::Pos, nix::Expr *> > * string_parts;
}
%type <e> start expr expr_function expr_if expr_op
@@ -364,7 +364,7 @@ expr_op
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(CUR_POS, $1, $3); }
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); }
| expr_op '+' expr_op
- { $$ = new ExprConcatStrings(CUR_POS, false, new vector<Expr *>({$1, $3})); }
+ { $$ = new ExprConcatStrings(CUR_POS, false, new vector<std::pair<Pos, Expr *> >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
| expr_op '-' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
| expr_op '*' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__mul")), {$1, $3}); }
| expr_op '/' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__div")), {$1, $3}); }
@@ -410,7 +410,7 @@ expr_simple
}
| path_start PATH_END { $$ = $1; }
| path_start string_parts_interpolated PATH_END {
- $2->insert($2->begin(), $1);
+ $2->insert($2->begin(), {makeCurPos(@1, data), $1});
$$ = new ExprConcatStrings(CUR_POS, false, $2);
}
| SPATH {
@@ -448,13 +448,13 @@ string_parts
;
string_parts_interpolated
- : string_parts_interpolated STR { $$ = $1; $1->push_back($2); }
- | string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->push_back($3); }
- | DOLLAR_CURLY expr '}' { $$ = new vector<Expr *>; $$->push_back($2); }
+ : string_parts_interpolated STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); }
+ | string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
+ | DOLLAR_CURLY expr '}' { $$ = new vector<std::pair<Pos, Expr *> >; $$->emplace_back(makeCurPos(@1, data), $2); }
| STR DOLLAR_CURLY expr '}' {
- $$ = new vector<Expr *>;
- $$->push_back($1);
- $$->push_back($3);
+ $$ = new vector<std::pair<Pos, Expr *> >;
+ $$->emplace_back(makeCurPos(@1, data), $1);
+ $$->emplace_back(makeCurPos(@2, data), $3);
}
;
@@ -473,9 +473,9 @@ path_start
;
ind_string_parts
- : ind_string_parts IND_STR { $$ = $1; $1->push_back($2); }
- | ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->push_back($3); }
- | { $$ = new vector<Expr *>; }
+ : ind_string_parts IND_STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); }
+ | ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
+ | { $$ = new vector<std::pair<Pos, Expr *> >; }
;
binds
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 66af373d7..62c21c7c5 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -35,9 +35,10 @@ namespace nix {
InvalidPathError::InvalidPathError(const Path & path) :
EvalError("path '%s' is not valid", path), path(path) {}
-void EvalState::realiseContext(const PathSet & context)
+StringMap EvalState::realiseContext(const PathSet & context)
{
std::vector<DerivedPath::Built> drvs;
+ StringMap res;
for (auto & i : context) {
auto [ctxS, outputName] = decodeContext(i);
@@ -46,10 +47,12 @@ void EvalState::realiseContext(const PathSet & context)
throw InvalidPathError(store->printStorePath(ctx));
if (!outputName.empty() && ctx.isDerivation()) {
drvs.push_back({ctx, {outputName}});
+ } else {
+ res.insert_or_assign(ctxS, ctxS);
}
}
- if (drvs.empty()) return;
+ if (drvs.empty()) return {};
if (!evalSettings.enableImportFromDerivation)
throw Error(
@@ -61,19 +64,53 @@ void EvalState::realiseContext(const PathSet & context)
for (auto & d : drvs) buildReqs.emplace_back(DerivedPath { d });
store->buildPaths(buildReqs);
+ /* Get all the output paths corresponding to the placeholders we had */
+ for (auto & [drvPath, outputs] : drvs) {
+ auto outputPaths = store->queryDerivationOutputMap(drvPath);
+ for (auto & outputName : outputs) {
+ if (outputPaths.count(outputName) == 0)
+ throw Error("derivation '%s' does not have an output named '%s'",
+ store->printStorePath(drvPath), outputName);
+ res.insert_or_assign(
+ downstreamPlaceholder(*store, drvPath, outputName),
+ store->printStorePath(outputPaths.at(outputName))
+ );
+ }
+ }
+
/* Add the output of this derivations to the allowed
paths. */
if (allowedPaths) {
- for (auto & [drvPath, outputs] : drvs) {
- auto outputPaths = store->queryDerivationOutputMap(drvPath);
- for (auto & outputName : outputs) {
- if (outputPaths.count(outputName) == 0)
- throw Error("derivation '%s' does not have an output named '%s'",
- store->printStorePath(drvPath), outputName);
- allowPath(outputPaths.at(outputName));
- }
+ for (auto & [_placeholder, outputPath] : res) {
+ allowPath(outputPath);
}
}
+
+ return res;
+}
+
+struct RealisePathFlags {
+ // Whether to check whether the path is a valid absolute path
+ bool requireAbsolutePath = true;
+ // Whether to check that the path is allowed in pure eval mode
+ bool checkForPureEval = true;
+};
+
+static Path realisePath(EvalState & state, const Pos & pos, Value & v, const RealisePathFlags flags = {})
+{
+ PathSet context;
+
+ Path path = flags.requireAbsolutePath
+ ? state.coerceToPath(pos, v, context)
+ : state.coerceToString(pos, v, context, false, false);
+
+ StringMap rewrites = state.realiseContext(context);
+
+ auto realPath = state.toRealPath(rewriteStrings(path, rewrites), context);
+
+ return flags.checkForPureEval
+ ? state.checkSourcePath(realPath)
+ : realPath;
}
/* Add and attribute to the given attribute map from the output name to
@@ -109,11 +146,9 @@ static void mkOutputString(EvalState & state, Value & v,
argument. */
static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vScope, Value & v)
{
- PathSet context;
- Path path = state.coerceToPath(pos, vPath, context);
-
+ Path path;
try {
- state.realiseContext(context);
+ path = realisePath(state, pos, vPath);
} catch (InvalidPathError & e) {
throw EvalError({
.msg = hintfmt("cannot import '%1%', since path '%2%' is not valid", path, e.path),
@@ -124,8 +159,6 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
throw;
}
- Path realPath = state.checkSourcePath(state.toRealPath(path, context));
-
// FIXME
auto isValidDerivationInStore = [&]() -> std::optional<StorePath> {
if (!state.store->isStorePath(path))
@@ -177,7 +210,7 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
else {
if (!vScope)
- state.evalFile(realPath, v);
+ state.evalFile(path, v);
else {
state.forceAttrs(*vScope);
@@ -195,8 +228,8 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
// No need to call staticEnv.sort(), because
// args[0]->attrs is already sorted.
- printTalkative("evaluating file '%1%'", realPath);
- Expr * e = state.parseExprFromFile(resolveExprPath(realPath), staticEnv);
+ printTalkative("evaluating file '%1%'", path);
+ Expr * e = state.parseExprFromFile(resolveExprPath(path), staticEnv);
e->eval(state, *env, v);
}
@@ -281,22 +314,19 @@ extern "C" typedef void (*ValueInitializer)(EvalState & state, Value & v);
/* Load a ValueInitializer from a DSO and return whatever it initializes */
void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- PathSet context;
- Path path = state.coerceToPath(pos, *args[0], context);
-
+ Path path;
try {
- state.realiseContext(context);
+ path = realisePath(state, pos, *args[0]);
} catch (InvalidPathError & e) {
throw EvalError({
- .msg = hintfmt(
- "cannot import '%1%', since path '%2%' is not valid",
- path, e.path),
+ .msg = hintfmt("cannot import '%1%', since path '%2%' is not valid", path, e.path),
.errPos = pos
});
+ } catch (Error & e) {
+ e.addTrace(pos, "while importing '%s'", path);
+ throw;
}
- path = state.checkSourcePath(path);
-
string sym = state.forceStringNoCtx(*args[1], pos);
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
@@ -338,7 +368,7 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
for (unsigned int i = 1; i < args[0]->listSize(); ++i)
commandArgs.emplace_back(state.coerceToString(pos, *elems[i], context, false, false));
try {
- state.realiseContext(context);
+ auto _ = state.realiseContext(context); // FIXME: Handle CA derivations
} catch (InvalidPathError & e) {
throw EvalError({
.msg = hintfmt("cannot execute '%1%', since path '%2%' is not valid",
@@ -1349,10 +1379,14 @@ static RegisterPrimOp primop_storePath({
static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- PathSet context;
- Path path = state.coerceToPath(pos, *args[0], context);
+ Path path;
try {
- state.realiseContext(context);
+ // We don’t check the path right now, because we don’t want to throw if
+ // the path isn’t allowed, but just return false
+ // (and we can’t just catch the exception here because we still want to
+ // throw if something in the evaluation of `*args[0]` tries to access an
+ // unauthorized path)
+ path = realisePath(state, pos, *args[0], { .checkForPureEval = false });
} catch (InvalidPathError & e) {
throw EvalError({
.msg = hintfmt(
@@ -1426,17 +1460,16 @@ static RegisterPrimOp primop_dirOf({
/* Return the contents of a file as a string. */
static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- PathSet context;
- Path path = state.coerceToPath(pos, *args[0], context);
+ Path path;
try {
- state.realiseContext(context);
+ path = realisePath(state, pos, *args[0]);
} catch (InvalidPathError & e) {
throw EvalError({
.msg = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path),
.errPos = pos
});
}
- string s = readFile(state.checkSourcePath(state.toRealPath(path, context)));
+ string s = readFile(path);
if (s.find((char) 0) != string::npos)
throw Error("the contents of the file '%1%' cannot be represented as a Nix string", path);
mkString(v, s.c_str());
@@ -1475,11 +1508,10 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
pos
);
- PathSet context;
- string path = state.coerceToString(pos, *i->value, context, false, false);
+ Path path;
try {
- state.realiseContext(context);
+ path = realisePath(state, pos, *i->value, { .requireAbsolutePath = false });
} catch (InvalidPathError & e) {
throw EvalError({
.msg = hintfmt("cannot find '%1%', since path '%2%' is not valid", path, e.path),
@@ -1512,15 +1544,14 @@ static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Va
.errPos = pos
});
- PathSet context;
- Path path = state.coerceToPath(pos, *args[1], context);
+ Path path;
try {
- state.realiseContext(context);
+ path = realisePath(state, pos, *args[1]);
} catch (InvalidPathError & e) {
throw EvalError("cannot read '%s' since path '%s' is not valid, at %s", path, e.path, pos);
}
- mkString(v, hashFile(*ht, state.checkSourcePath(state.toRealPath(path, context))).to_string(Base16, false));
+ mkString(v, hashFile(*ht, path).to_string(Base16, false));
}
static RegisterPrimOp primop_hashFile({
@@ -1537,10 +1568,9 @@ static RegisterPrimOp primop_hashFile({
/* Read a directory (without . or ..) */
static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- PathSet ctx;
- Path path = state.coerceToPath(pos, *args[0], ctx);
+ Path path;
try {
- state.realiseContext(ctx);
+ path = realisePath(state, pos, *args[0]);
} catch (InvalidPathError & e) {
throw EvalError({
.msg = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path),
@@ -1548,7 +1578,7 @@ static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Val
});
}
- DirEntries entries = readDirectory(state.checkSourcePath(path));
+ DirEntries entries = readDirectory(path);
state.mkAttrs(v, entries.size());
for (auto & ent : entries) {
@@ -1875,7 +1905,8 @@ static void addPath(
try {
// FIXME: handle CA derivation outputs (where path needs to
// be rewritten to the actual output).
- state.realiseContext(context);
+ auto rewrites = state.realiseContext(context);
+ path = state.toRealPath(rewriteStrings(path, rewrites), context);
StorePathSet refs;
diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc
index 4c6682dfd..221c40db9 100644
--- a/src/libexpr/primops/fromTOML.cc
+++ b/src/libexpr/primops/fromTOML.cc
@@ -1,86 +1,77 @@
#include "primops.hh"
#include "eval-inline.hh"
-#include "../../cpptoml/cpptoml.h"
+#include "../../toml11/toml.hpp"
namespace nix {
-static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Value & val)
{
- using namespace cpptoml;
-
auto toml = state.forceStringNoCtx(*args[0], pos);
std::istringstream tomlStream(toml);
- std::function<void(Value &, std::shared_ptr<base>)> visit;
+ std::function<void(Value &, toml::value)> visit;
- visit = [&](Value & v, std::shared_ptr<base> t) {
+ visit = [&](Value & v, toml::value t) {
- if (auto t2 = t->as_table()) {
+ switch(t.type())
+ {
+ case toml::value_t::table:
+ {
+ auto table = toml::get<toml::table>(t);
- size_t size = 0;
- for (auto & i : *t2) { (void) i; size++; }
+ size_t size = 0;
+ for (auto & i : table) { (void) i; size++; }
- state.mkAttrs(v, size);
+ state.mkAttrs(v, size);
- for (auto & i : *t2) {
- auto & v2 = *state.allocAttr(v, state.symbols.create(i.first));
+ for(auto & elem: table) {
- if (auto i2 = i.second->as_table_array()) {
- size_t size2 = i2->get().size();
- state.mkList(v2, size2);
- for (size_t j = 0; j < size2; ++j)
- visit(*(v2.listElems()[j] = state.allocValue()), i2->get()[j]);
+ auto & v2 = *state.allocAttr(v, state.symbols.create(elem.first));
+ visit(v2, elem.second);
+ }
}
- else
- visit(v2, i.second);
- }
-
- v.attrs->sort();
- }
-
- else if (auto t2 = t->as_array()) {
- size_t size = t2->get().size();
-
- state.mkList(v, size);
-
- for (size_t i = 0; i < size; ++i)
- 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]);
- }
+ break;;
+ case toml::value_t::array:
+ {
+ auto array = toml::get<std::vector<toml::value>>(t);
+
+ size_t size = array.size();
+ state.mkList(v, size);
+ for (size_t i = 0; i < size; ++i)
+ visit(*(v.listElems()[i] = state.allocValue()), array[i]);
+ }
+ break;;
+ case toml::value_t::boolean:
+ mkBool(v, toml::get<bool>(t));
+ break;;
+ case toml::value_t::integer:
+ mkInt(v, toml::get<int64_t>(t));
+ break;;
+ case toml::value_t::floating:
+ mkFloat(v, toml::get<NixFloat>(t));
+ break;;
+ case toml::value_t::string:
+ mkString(v, toml::get<std::string>(t));
+ break;;
+ case toml::value_t::local_datetime:
+ case toml::value_t::offset_datetime:
+ case toml::value_t::local_date:
+ case toml::value_t::local_time:
+ // We fail since Nix doesn't have date and time types
+ throw std::runtime_error("Dates and times are not supported");
+ break;;
+ case toml::value_t::empty:
+ mkNull(v);
+ break;;
- else if (t->is_value()) {
- if (auto val = t->as<int64_t>())
- mkInt(v, val->get());
- else if (auto val = t->as<NixFloat>())
- mkFloat(v, val->get());
- else if (auto val = t->as<bool>())
- mkBool(v, val->get());
- else if (auto val = t->as<std::string>())
- mkString(v, val->get());
- else
- throw EvalError("unsupported value type in TOML");
}
-
- else abort();
};
try {
- visit(v, parser(tomlStream).parse());
- } catch (std::runtime_error & e) {
+ visit(val, toml::parse(tomlStream, "fromTOML" /* the "filename" */));
+ } catch (std::exception & e) { // TODO: toml::syntax_error
throw EvalError({
.msg = hintfmt("while parsing a TOML string: %s", e.what()),
.errPos = pos