Merge branch 'master' into bundler_drv

This commit is contained in:
tomberek 2022-02-03 02:39:18 -05:00 committed by GitHub
commit 12ff354d01
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
43 changed files with 437 additions and 313 deletions

View file

@ -16,6 +16,7 @@ LDFLAGS = @LDFLAGS@
LIBARCHIVE_LIBS = @LIBARCHIVE_LIBS@ LIBARCHIVE_LIBS = @LIBARCHIVE_LIBS@
LIBBROTLI_LIBS = @LIBBROTLI_LIBS@ LIBBROTLI_LIBS = @LIBBROTLI_LIBS@
LIBCURL_LIBS = @LIBCURL_LIBS@ LIBCURL_LIBS = @LIBCURL_LIBS@
LOWDOWN_LIBS = @LOWDOWN_LIBS@
OPENSSL_LIBS = @OPENSSL_LIBS@ OPENSSL_LIBS = @OPENSSL_LIBS@
LIBSECCOMP_LIBS = @LIBSECCOMP_LIBS@ LIBSECCOMP_LIBS = @LIBSECCOMP_LIBS@
PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_NAME = @PACKAGE_NAME@

View file

@ -272,7 +272,7 @@ AC_ARG_ENABLE(doc-gen, AS_HELP_STRING([--disable-doc-gen],[disable documentation
AC_SUBST(doc_generate) AC_SUBST(doc_generate)
# Look for lowdown library. # Look for lowdown library.
PKG_CHECK_MODULES([LOWDOWN], [lowdown >= 0.8.0], [CXXFLAGS="$LOWDOWN_CFLAGS $CXXFLAGS"]) PKG_CHECK_MODULES([LOWDOWN], [lowdown >= 0.9.0], [CXXFLAGS="$LOWDOWN_CFLAGS $CXXFLAGS"])
# Setuid installations. # Setuid installations.
AC_CHECK_FUNCS([setresuid setreuid lchown]) AC_CHECK_FUNCS([setresuid setreuid lchown])

View file

@ -8,7 +8,7 @@ libcmd_SOURCES := $(wildcard $(d)/*.cc)
libcmd_CXXFLAGS += -I src/libutil -I src/libstore -I src/libexpr -I src/libmain -I src/libfetchers libcmd_CXXFLAGS += -I src/libutil -I src/libstore -I src/libexpr -I src/libmain -I src/libfetchers
libcmd_LDFLAGS += -llowdown -pthread libcmd_LDFLAGS += $(LOWDOWN_LIBS) -pthread
libcmd_LIBS = libstore libutil libexpr libmain libfetchers libcmd_LIBS = libstore libutil libexpr libmain libfetchers

View file

@ -121,7 +121,7 @@ Pos findPackageFilename(EvalState & state, Value & v, std::string what)
std::string filename(pos, 0, colon); std::string filename(pos, 0, colon);
unsigned int lineno; unsigned int lineno;
try { try {
lineno = std::stoi(std::string(pos, colon + 1)); lineno = std::stoi(std::string(pos, colon + 1, string::npos));
} catch (std::invalid_argument & e) { } catch (std::invalid_argument & e) {
throw ParseError("cannot parse line number '%s'", pos); throw ParseError("cannot parse line number '%s'", pos);
} }

View file

@ -1,5 +1,6 @@
#include "eval.hh" #include "eval.hh"
#include "hash.hh" #include "hash.hh"
#include "types.hh"
#include "util.hh" #include "util.hh"
#include "store-api.hh" #include "store-api.hh"
#include "derivations.hh" #include "derivations.hh"
@ -1373,7 +1374,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
/* Nope, so show the first unexpected argument to the /* Nope, so show the first unexpected argument to the
user. */ user. */
for (auto & i : *args[0]->attrs) for (auto & i : *args[0]->attrs)
if (!lambda.formals->argNames.count(i.name)) if (!lambda.formals->has(i.name))
throwTypeError(pos, "%1% called with unexpected argument '%2%'", lambda, i.name); throwTypeError(pos, "%1% called with unexpected argument '%2%'", lambda, i.name);
abort(); // can't happen abort(); // can't happen
} }
@ -1694,7 +1695,7 @@ void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Po
void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
{ {
PathSet context; PathSet context;
std::vector<std::string> s; std::vector<BackedStringView> s;
size_t sSize = 0; size_t sSize = 0;
NixInt n = 0; NixInt n = 0;
NixFloat nf = 0; NixFloat nf = 0;
@ -1705,7 +1706,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
const auto str = [&] { const auto str = [&] {
std::string result; std::string result;
result.reserve(sSize); result.reserve(sSize);
for (const auto & part : s) result += part; for (const auto & part : s) result += *part;
return result; return result;
}; };
/* c_str() is not str().c_str() because we want to create a string /* c_str() is not str().c_str() because we want to create a string
@ -1715,15 +1716,18 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
char * result = allocString(sSize + 1); char * result = allocString(sSize + 1);
char * tmp = result; char * tmp = result;
for (const auto & part : s) { for (const auto & part : s) {
memcpy(tmp, part.c_str(), part.size()); memcpy(tmp, part->data(), part->size());
tmp += part.size(); tmp += part->size();
} }
*tmp = 0; *tmp = 0;
return result; return result;
}; };
Value values[es->size()];
Value * vTmpP = values;
for (auto & [i_pos, i] : *es) { for (auto & [i_pos, i] : *es) {
Value vTmp; Value & vTmp = *vTmpP++;
i->eval(state, env, vTmp); i->eval(state, env, vTmp);
/* If the first element is a path, then the result will also /* If the first element is a path, then the result will also
@ -1756,9 +1760,9 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
/* skip canonization of first path, which would only be not /* skip canonization of first path, which would only be not
canonized in the first place if it's coming from a ./${foo} type canonized in the first place if it's coming from a ./${foo} type
path */ path */
s.emplace_back( auto part = state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first);
state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first)); sSize += part->size();
sSize += s.back().size(); s.emplace_back(std::move(part));
} }
first = false; first = false;
@ -1857,7 +1861,7 @@ void EvalState::forceFunction(Value & v, const Pos & pos)
} }
string EvalState::forceString(Value & v, const Pos & pos) std::string_view EvalState::forceString(Value & v, const Pos & pos)
{ {
forceValue(v, pos); forceValue(v, pos);
if (v.type() != nString) { if (v.type() != nString) {
@ -1866,7 +1870,7 @@ string EvalState::forceString(Value & v, const Pos & pos)
else else
throwTypeError("value is %1% while a string was expected", v); throwTypeError("value is %1% while a string was expected", v);
} }
return string(v.string.s); return v.string.s;
} }
@ -1901,17 +1905,17 @@ std::vector<std::pair<Path, std::string>> Value::getContext()
} }
string EvalState::forceString(Value & v, PathSet & context, const Pos & pos) std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos & pos)
{ {
string s = forceString(v, pos); auto s = forceString(v, pos);
copyContext(v, context); copyContext(v, context);
return s; return s;
} }
string EvalState::forceStringNoCtx(Value & v, const Pos & pos) std::string_view EvalState::forceStringNoCtx(Value & v, const Pos & pos)
{ {
string s = forceString(v, pos); auto s = forceString(v, pos);
if (v.string.context) { if (v.string.context) {
if (pos) if (pos)
throwEvalError(pos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')", throwEvalError(pos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')",
@ -1942,34 +1946,35 @@ std::optional<string> EvalState::tryAttrsToString(const Pos & pos, Value & v,
if (i != v.attrs->end()) { if (i != v.attrs->end()) {
Value v1; Value v1;
callFunction(*i->value, v, v1, pos); callFunction(*i->value, v, v1, pos);
return coerceToString(pos, v1, context, coerceMore, copyToStore); return coerceToString(pos, v1, context, coerceMore, copyToStore).toOwned();
} }
return {}; return {};
} }
string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context, BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
bool coerceMore, bool copyToStore, bool canonicalizePath) bool coerceMore, bool copyToStore, bool canonicalizePath)
{ {
forceValue(v, pos); forceValue(v, pos);
string s;
if (v.type() == nString) { if (v.type() == nString) {
copyContext(v, context); copyContext(v, context);
return v.string.s; return std::string_view(v.string.s);
} }
if (v.type() == nPath) { if (v.type() == nPath) {
Path path(canonicalizePath ? canonPath(v.path) : v.path); BackedStringView path(PathView(v.path));
return copyToStore ? copyPathToStore(context, path) : path; if (canonicalizePath)
path = canonPath(*path);
if (copyToStore)
path = copyPathToStore(context, std::move(path).toOwned());
return path;
} }
if (v.type() == nAttrs) { if (v.type() == nAttrs) {
auto maybeString = tryAttrsToString(pos, v, context, coerceMore, copyToStore); auto maybeString = tryAttrsToString(pos, v, context, coerceMore, copyToStore);
if (maybeString) { if (maybeString)
return *maybeString; return std::move(*maybeString);
}
auto i = v.attrs->find(sOutPath); auto i = v.attrs->find(sOutPath);
if (i == v.attrs->end()) throwTypeError(pos, "cannot coerce a set to a string"); if (i == v.attrs->end()) throwTypeError(pos, "cannot coerce a set to a string");
return coerceToString(pos, *i->value, context, coerceMore, copyToStore); return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
@ -1991,14 +1996,13 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
if (v.isList()) { if (v.isList()) {
string result; string result;
for (auto [n, v2] : enumerate(v.listItems())) { for (auto [n, v2] : enumerate(v.listItems())) {
result += coerceToString(pos, *v2, result += *coerceToString(pos, *v2, context, coerceMore, copyToStore);
context, coerceMore, copyToStore);
if (n < v.listSize() - 1 if (n < v.listSize() - 1
/* !!! not quite correct */ /* !!! not quite correct */
&& (!v2->isList() || v2->listSize() != 0)) && (!v2->isList() || v2->listSize() != 0))
result += " "; result += " ";
} }
return result; return std::move(result);
} }
} }
@ -2032,7 +2036,7 @@ string EvalState::copyPathToStore(PathSet & context, const Path & path)
Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context) Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
{ {
string path = coerceToString(pos, v, context, false, false); string path = coerceToString(pos, v, context, false, false).toOwned();
if (path == "" || path[0] != '/') if (path == "" || path[0] != '/')
throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path); throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path);
return path; return path;

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "attr-set.hh" #include "attr-set.hh"
#include "types.hh"
#include "value.hh" #include "value.hh"
#include "nixexpr.hh" #include "nixexpr.hh"
#include "symbol-table.hh" #include "symbol-table.hh"
@ -201,8 +202,8 @@ public:
void resetFileCache(); void resetFileCache();
/* Look up a file in the search path. */ /* Look up a file in the search path. */
Path findFile(const string & path); Path findFile(const std::string_view path);
Path findFile(SearchPath & searchPath, const string & path, const Pos & pos = noPos); Path findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos = noPos);
/* If the specified search path element is a URI, download it. */ /* If the specified search path element is a URI, download it. */
std::pair<bool, std::string> resolveSearchPathElem(const SearchPathElem & elem); std::pair<bool, std::string> resolveSearchPathElem(const SearchPathElem & elem);
@ -236,9 +237,9 @@ public:
inline void forceList(Value & v); inline void forceList(Value & v);
inline void forceList(Value & v, const Pos & pos); inline void forceList(Value & v, const Pos & pos);
void forceFunction(Value & v, const Pos & pos); // either lambda or primop void forceFunction(Value & v, const Pos & pos); // either lambda or primop
string forceString(Value & v, const Pos & pos = noPos); std::string_view forceString(Value & v, const Pos & pos = noPos);
string forceString(Value & v, PathSet & context, const Pos & pos = noPos); std::string_view forceString(Value & v, PathSet & context, const Pos & pos = noPos);
string forceStringNoCtx(Value & v, const Pos & pos = noPos); std::string_view forceStringNoCtx(Value & v, const Pos & pos = noPos);
/* Return true iff the value `v' denotes a derivation (i.e. a /* Return true iff the value `v' denotes a derivation (i.e. a
set with attribute `type = "derivation"'). */ set with attribute `type = "derivation"'). */
@ -251,7 +252,7 @@ public:
string. If `coerceMore' is set, also converts nulls, integers, string. If `coerceMore' is set, also converts nulls, integers,
booleans and lists to a string. If `copyToStore' is set, booleans and lists to a string. If `copyToStore' is set,
referenced paths are copied to the Nix store as a side effect. */ referenced paths are copied to the Nix store as a side effect. */
string coerceToString(const Pos & pos, Value & v, PathSet & context, BackedStringView coerceToString(const Pos & pos, Value & v, PathSet & context,
bool coerceMore = false, bool copyToStore = true, bool coerceMore = false, bool copyToStore = true,
bool canonicalizePath = true); bool canonicalizePath = true);
@ -309,8 +310,8 @@ private:
friend struct ExprAttrs; friend struct ExprAttrs;
friend struct ExprLet; friend struct ExprLet;
Expr * parse(char * text, size_t length, FileOrigin origin, const Path & path, Expr * parse(char * text, size_t length, FileOrigin origin, const PathView path,
const Path & basePath, StaticEnv & staticEnv); const PathView basePath, StaticEnv & staticEnv);
public: public:

View file

@ -89,11 +89,11 @@ static void expectType(EvalState & state, ValueType type,
static std::map<FlakeId, FlakeInput> parseFlakeInputs( static std::map<FlakeId, FlakeInput> parseFlakeInputs(
EvalState & state, Value * value, const Pos & pos, EvalState & state, Value * value, const Pos & pos,
const std::optional<Path> & baseDir); const std::optional<Path> & baseDir, InputPath lockRootPath);
static FlakeInput parseFlakeInput(EvalState & state, static FlakeInput parseFlakeInput(EvalState & state,
const std::string & inputName, Value * value, const Pos & pos, const std::string & inputName, Value * value, const Pos & pos,
const std::optional<Path> & baseDir) const std::optional<Path> & baseDir, InputPath lockRootPath)
{ {
expectType(state, nAttrs, *value, pos); expectType(state, nAttrs, *value, pos);
@ -117,10 +117,12 @@ static FlakeInput parseFlakeInput(EvalState & state,
expectType(state, nBool, *attr.value, *attr.pos); expectType(state, nBool, *attr.value, *attr.pos);
input.isFlake = attr.value->boolean; input.isFlake = attr.value->boolean;
} else if (attr.name == sInputs) { } else if (attr.name == sInputs) {
input.overrides = parseFlakeInputs(state, attr.value, *attr.pos, baseDir); input.overrides = parseFlakeInputs(state, attr.value, *attr.pos, baseDir, lockRootPath);
} else if (attr.name == sFollows) { } else if (attr.name == sFollows) {
expectType(state, nString, *attr.value, *attr.pos); expectType(state, nString, *attr.value, *attr.pos);
input.follows = parseInputPath(attr.value->string.s); auto follows(parseInputPath(attr.value->string.s));
follows.insert(follows.begin(), lockRootPath.begin(), lockRootPath.end());
input.follows = follows;
} else { } else {
switch (attr.value->type()) { switch (attr.value->type()) {
case nString: case nString:
@ -166,7 +168,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
static std::map<FlakeId, FlakeInput> parseFlakeInputs( static std::map<FlakeId, FlakeInput> parseFlakeInputs(
EvalState & state, Value * value, const Pos & pos, EvalState & state, Value * value, const Pos & pos,
const std::optional<Path> & baseDir) const std::optional<Path> & baseDir, InputPath lockRootPath)
{ {
std::map<FlakeId, FlakeInput> inputs; std::map<FlakeId, FlakeInput> inputs;
@ -178,7 +180,8 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
inputAttr.name, inputAttr.name,
inputAttr.value, inputAttr.value,
*inputAttr.pos, *inputAttr.pos,
baseDir)); baseDir,
lockRootPath));
} }
return inputs; return inputs;
@ -188,7 +191,8 @@ static Flake getFlake(
EvalState & state, EvalState & state,
const FlakeRef & originalRef, const FlakeRef & originalRef,
bool allowLookup, bool allowLookup,
FlakeCache & flakeCache) FlakeCache & flakeCache,
InputPath lockRootPath)
{ {
auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree( auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree(
state, originalRef, allowLookup, flakeCache); state, originalRef, allowLookup, flakeCache);
@ -223,7 +227,7 @@ static Flake getFlake(
auto sInputs = state.symbols.create("inputs"); auto sInputs = state.symbols.create("inputs");
if (auto inputs = vInfo.attrs->get(sInputs)) if (auto inputs = vInfo.attrs->get(sInputs))
flake.inputs = parseFlakeInputs(state, inputs->value, *inputs->pos, flakeDir); flake.inputs = parseFlakeInputs(state, inputs->value, *inputs->pos, flakeDir, lockRootPath);
auto sOutputs = state.symbols.create("outputs"); auto sOutputs = state.symbols.create("outputs");
@ -250,10 +254,12 @@ static Flake getFlake(
for (auto & setting : *nixConfig->value->attrs) { for (auto & setting : *nixConfig->value->attrs) {
forceTrivialValue(state, *setting.value, *setting.pos); forceTrivialValue(state, *setting.value, *setting.pos);
if (setting.value->type() == nString) if (setting.value->type() == nString)
flake.config.settings.insert({setting.name, state.forceStringNoCtx(*setting.value, *setting.pos)}); flake.config.settings.insert({setting.name, string(state.forceStringNoCtx(*setting.value, *setting.pos))});
else if (setting.value->type() == nPath) { else if (setting.value->type() == nPath) {
PathSet emptyContext = {}; PathSet emptyContext = {};
flake.config.settings.insert({setting.name, state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true)}); flake.config.settings.emplace(
setting.name,
state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true) .toOwned());
} }
else if (setting.value->type() == nInt) else if (setting.value->type() == nInt)
flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos)}); flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos)});
@ -265,7 +271,7 @@ static Flake getFlake(
if (elem->type() != nString) if (elem->type() != nString)
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected", throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
setting.name, showType(*setting.value)); setting.name, showType(*setting.value));
ss.push_back(state.forceStringNoCtx(*elem, *setting.pos)); ss.emplace_back(state.forceStringNoCtx(*elem, *setting.pos));
} }
flake.config.settings.insert({setting.name, ss}); flake.config.settings.insert({setting.name, ss});
} }
@ -287,6 +293,11 @@ static Flake getFlake(
return flake; return flake;
} }
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup, FlakeCache & flakeCache)
{
return getFlake(state, originalRef, allowLookup, flakeCache, {});
}
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup) Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup)
{ {
FlakeCache flakeCache; FlakeCache flakeCache;
@ -332,22 +343,12 @@ LockedFlake lockFlake(
std::vector<FlakeRef> parents; std::vector<FlakeRef> parents;
struct LockParent {
/* The path to this parent. */
InputPath path;
/* Whether we are currently inside a top-level lockfile
(inputs absolute) or subordinate lockfile (inputs
relative). */
bool absolute;
};
std::function<void( std::function<void(
const FlakeInputs & flakeInputs, const FlakeInputs & flakeInputs,
std::shared_ptr<Node> node, std::shared_ptr<Node> node,
const InputPath & inputPathPrefix, const InputPath & inputPathPrefix,
std::shared_ptr<const Node> oldNode, std::shared_ptr<const Node> oldNode,
const LockParent & parent, const InputPath & lockRootPath,
const Path & parentPath, const Path & parentPath,
bool trustLock)> bool trustLock)>
computeLocks; computeLocks;
@ -357,7 +358,7 @@ LockedFlake lockFlake(
std::shared_ptr<Node> node, std::shared_ptr<Node> node,
const InputPath & inputPathPrefix, const InputPath & inputPathPrefix,
std::shared_ptr<const Node> oldNode, std::shared_ptr<const Node> oldNode,
const LockParent & parent, const InputPath & lockRootPath,
const Path & parentPath, const Path & parentPath,
bool trustLock) bool trustLock)
{ {
@ -402,17 +403,7 @@ LockedFlake lockFlake(
if (input.follows) { if (input.follows) {
InputPath target; InputPath target;
if (parent.absolute && !hasOverride) { target.insert(target.end(), input.follows->begin(), input.follows->end());
target = *input.follows;
} else {
if (hasOverride) {
target = inputPathPrefix;
target.pop_back();
} else
target = parent.path;
for (auto & i : *input.follows) target.push_back(i);
}
debug("input '%s' follows '%s'", inputPathS, printInputPath(target)); debug("input '%s' follows '%s'", inputPathS, printInputPath(target));
node->inputs.insert_or_assign(id, target); node->inputs.insert_or_assign(id, target);
@ -485,23 +476,25 @@ LockedFlake lockFlake(
break; break;
} }
} }
auto absoluteFollows(lockRootPath);
absoluteFollows.insert(absoluteFollows.end(), follows->begin(), follows->end());
fakeInputs.emplace(i.first, FlakeInput { fakeInputs.emplace(i.first, FlakeInput {
.follows = *follows, .follows = absoluteFollows,
}); });
} }
} }
} }
LockParent newParent { auto localPath(parentPath);
.path = inputPath, // If this input is a path, recurse it down.
.absolute = true // This allows us to resolve path inputs relative to the current flake.
}; if ((*input.ref).input.getType() == "path")
localPath = absPath(*input.ref->input.getSourcePath(), parentPath);
computeLocks( computeLocks(
mustRefetch mustRefetch
? getFlake(state, oldLock->lockedRef, false, flakeCache).inputs ? getFlake(state, oldLock->lockedRef, false, flakeCache, inputPath).inputs
: fakeInputs, : fakeInputs,
childNode, inputPath, oldLock, newParent, parentPath, !mustRefetch); childNode, inputPath, oldLock, lockRootPath, parentPath, !mustRefetch);
} else { } else {
/* We need to create a new lock file entry. So fetch /* We need to create a new lock file entry. So fetch
@ -520,7 +513,7 @@ LockedFlake lockFlake(
if (localRef.input.getType() == "path") if (localRef.input.getType() == "path")
localPath = absPath(*input.ref->input.getSourcePath(), parentPath); localPath = absPath(*input.ref->input.getSourcePath(), parentPath);
auto inputFlake = getFlake(state, localRef, useRegistries, flakeCache); auto inputFlake = getFlake(state, localRef, useRegistries, flakeCache, inputPath);
/* Note: in case of an --override-input, we use /* Note: in case of an --override-input, we use
the *original* ref (input2.ref) for the the *original* ref (input2.ref) for the
@ -541,13 +534,6 @@ LockedFlake lockFlake(
parents.push_back(*input.ref); parents.push_back(*input.ref);
Finally cleanup([&]() { parents.pop_back(); }); Finally cleanup([&]() { parents.pop_back(); });
// Follows paths from existing inputs in the top-level lockfile are absolute,
// whereas paths in subordinate lockfiles are relative to those lockfiles.
LockParent newParent {
.path = inputPath,
.absolute = oldLock ? true : false
};
/* Recursively process the inputs of this /* Recursively process the inputs of this
flake. Also, unless we already have this flake flake. Also, unless we already have this flake
in the top-level lock file, use this flake's in the top-level lock file, use this flake's
@ -558,7 +544,7 @@ LockedFlake lockFlake(
? std::dynamic_pointer_cast<const Node>(oldLock) ? std::dynamic_pointer_cast<const Node>(oldLock)
: LockFile::read( : LockFile::read(
inputFlake.sourceInfo->actualPath + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root, inputFlake.sourceInfo->actualPath + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root,
newParent, localPath, false); oldLock ? lockRootPath : inputPath, localPath, false);
} }
else { else {
@ -576,17 +562,12 @@ LockedFlake lockFlake(
} }
}; };
LockParent parent {
.path = {},
.absolute = true
};
// Bring in the current ref for relative path resolution if we have it // Bring in the current ref for relative path resolution if we have it
auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir, true); auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir, true);
computeLocks( computeLocks(
flake.inputs, newLockFile.root, {}, flake.inputs, newLockFile.root, {},
lockFlags.recreateLockFile ? nullptr : oldLockFile.root, parent, parentPath, false); lockFlags.recreateLockFile ? nullptr : oldLockFile.root, {}, parentPath, false);
for (auto & i : lockFlags.inputOverrides) for (auto & i : lockFlags.inputOverrides)
if (!overridesUsed.count(i.first)) if (!overridesUsed.count(i.first))
@ -726,7 +707,7 @@ static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Va
{ {
state.requireExperimentalFeatureOnEvaluation(Xp::Flakes, "builtins.getFlake", pos); state.requireExperimentalFeatureOnEvaluation(Xp::Flakes, "builtins.getFlake", pos);
auto flakeRefS = state.forceStringNoCtx(*args[0], pos); string flakeRefS(state.forceStringNoCtx(*args[0], pos));
auto flakeRef = parseFlakeRef(flakeRefS, {}, true); auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
if (evalSettings.pureEval && !flakeRef.input.isImmutable()) if (evalSettings.pureEval && !flakeRef.input.isImmutable())
throw Error("cannot call 'getFlake' on mutable flake reference '%s', at %s (use --impure to override)", flakeRefS, pos); throw Error("cannot call 'getFlake' on mutable flake reference '%s', at %s (use --impure to override)", flakeRefS, pos);

View file

@ -104,7 +104,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
/* For each output... */ /* For each output... */
for (auto elem : i->value->listItems()) { for (auto elem : i->value->listItems()) {
/* Evaluate the corresponding set. */ /* Evaluate the corresponding set. */
string name = state->forceStringNoCtx(*elem, *i->pos); string name(state->forceStringNoCtx(*elem, *i->pos));
Bindings::iterator out = attrs->find(state->symbols.create(name)); Bindings::iterator out = attrs->find(state->symbols.create(name));
if (out == attrs->end()) continue; // FIXME: throw error? if (out == attrs->end()) continue; // FIXME: throw error?
state->forceAttrs(*out->value); state->forceAttrs(*out->value);

View file

@ -163,7 +163,7 @@ public:
} }
}; };
void parseJSON(EvalState & state, const string & s_, Value & v) void parseJSON(EvalState & state, const std::string_view & s_, Value & v)
{ {
JSONSax parser(state, v); JSONSax parser(state, v);
bool res = json::sax_parse(s_, &parser); bool res = json::sax_parse(s_, &parser);

View file

@ -8,6 +8,6 @@ namespace nix {
MakeError(JSONParseError, EvalError); MakeError(JSONParseError, EvalError);
void parseJSON(EvalState & state, const string & s, Value & v); void parseJSON(EvalState & state, const std::string_view & s, Value & v);
} }

View file

@ -66,7 +66,7 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
// we make use of the fact that the parser receives a private copy of the input // we make use of the fact that the parser receives a private copy of the input
// string and can munge around in it. // string and can munge around in it.
static Expr * unescapeStr(SymbolTable & symbols, char * s, size_t length) static StringToken unescapeStr(SymbolTable & symbols, char * s, size_t length)
{ {
char * result = s; char * result = s;
char * t = s; char * t = s;
@ -89,7 +89,7 @@ static Expr * unescapeStr(SymbolTable & symbols, char * s, size_t length)
else *t = c; else *t = c;
t++; t++;
} }
return new ExprString(symbols.create({result, size_t(t - result)})); return {result, size_t(t - result)};
} }
@ -176,7 +176,7 @@ or { return OR_KW; }
/* It is impossible to match strings ending with '$' with one /* It is impossible to match strings ending with '$' with one
regex because trailing contexts are only valid at the end regex because trailing contexts are only valid at the end
of a rule. (A sane but undocumented limitation.) */ of a rule. (A sane but undocumented limitation.) */
yylval->e = unescapeStr(data->symbols, yytext, yyleng); yylval->str = unescapeStr(data->symbols, yytext, yyleng);
return STR; return STR;
} }
<STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; } <STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
@ -191,26 +191,26 @@ or { return OR_KW; }
\'\'(\ *\n)? { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; } \'\'(\ *\n)? { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; }
<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ { <IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
yylval->e = new ExprIndStr(yytext); yylval->str = {yytext, (size_t) yyleng, true};
return IND_STR; return IND_STR;
} }
<IND_STRING>\'\'\$ | <IND_STRING>\'\'\$ |
<IND_STRING>\$ { <IND_STRING>\$ {
yylval->e = new ExprIndStr("$"); yylval->str = {"$", 1};
return IND_STR; return IND_STR;
} }
<IND_STRING>\'\'\' { <IND_STRING>\'\'\' {
yylval->e = new ExprIndStr("''"); yylval->str = {"''", 2};
return IND_STR; return IND_STR;
} }
<IND_STRING>\'\'\\{ANY} { <IND_STRING>\'\'\\{ANY} {
yylval->e = unescapeStr(data->symbols, yytext + 2, yyleng - 2); yylval->str = unescapeStr(data->symbols, yytext + 2, yyleng - 2);
return IND_STR; return IND_STR;
} }
<IND_STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; } <IND_STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
<IND_STRING>\'\' { POP_STATE(); return IND_STRING_CLOSE; } <IND_STRING>\'\' { POP_STATE(); return IND_STRING_CLOSE; }
<IND_STRING>\' { <IND_STRING>\' {
yylval->e = new ExprIndStr("'"); yylval->str = {"'", 1};
return IND_STR; return IND_STR;
} }
@ -264,7 +264,7 @@ or { return OR_KW; }
PUSH_STATE(INPATH_SLASH); PUSH_STATE(INPATH_SLASH);
else else
PUSH_STATE(INPATH); PUSH_STATE(INPATH);
yylval->e = new ExprString(data->symbols.create(string(yytext))); yylval->str = {yytext, (size_t) yyleng};
return STR; return STR;
} }
<INPATH>{ANY} | <INPATH>{ANY} |

View file

@ -110,20 +110,13 @@ struct ExprFloat : Expr
struct ExprString : Expr struct ExprString : Expr
{ {
Symbol s; string s;
Value v; Value v;
ExprString(const Symbol & s) : s(s) { v.mkString(s); }; ExprString(std::string s) : s(std::move(s)) { v.mkString(this->s.data()); };
COMMON_METHODS COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env); Value * maybeThunk(EvalState & state, Env & env);
}; };
/* Temporary class used during parsing of indented strings. */
struct ExprIndStr : Expr
{
string s;
ExprIndStr(const string & s) : s(s) { };
};
struct ExprPath : Expr struct ExprPath : Expr
{ {
string s; string s;
@ -223,10 +216,25 @@ struct Formal
struct Formals struct Formals
{ {
typedef std::list<Formal> Formals_; typedef std::vector<Formal> Formals_;
Formals_ formals; Formals_ formals;
std::set<Symbol> argNames; // used during parsing
bool ellipsis; bool ellipsis;
bool has(Symbol arg) const {
auto it = std::lower_bound(formals.begin(), formals.end(), arg,
[] (const Formal & f, const Symbol & sym) { return f.name < sym; });
return it != formals.end() && it->name == arg;
}
std::vector<Formal> lexicographicOrder() const
{
std::vector<Formal> result(formals.begin(), formals.end());
std::sort(result.begin(), result.end(),
[] (const Formal & a, const Formal & b) {
return std::string_view(a.name) < std::string_view(b.name);
});
return result;
}
}; };
struct ExprLambda : Expr struct ExprLambda : Expr
@ -239,11 +247,6 @@ struct ExprLambda : Expr
ExprLambda(const Pos & pos, const Symbol & arg, Formals * formals, Expr * body) ExprLambda(const Pos & pos, const Symbol & arg, Formals * formals, Expr * body)
: pos(pos), arg(arg), formals(formals), body(body) : pos(pos), arg(arg), formals(formals), body(body)
{ {
if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end())
throw ParseError({
.msg = hintfmt("duplicate formal function argument '%1%'", arg),
.errPos = pos
});
}; };
void setName(Symbol & name); void setName(Symbol & name);
string showNamePos() const; string showNamePos() const;

View file

@ -16,6 +16,8 @@
#ifndef BISON_HEADER #ifndef BISON_HEADER
#define BISON_HEADER #define BISON_HEADER
#include <variant>
#include "util.hh" #include "util.hh"
#include "nixexpr.hh" #include "nixexpr.hh"
@ -39,8 +41,22 @@ namespace nix {
{ }; { };
}; };
struct ParserFormals {
std::vector<Formal> formals;
bool ellipsis = false;
};
} }
// using C a struct allows us to avoid having to define the special
// members that using string_view here would implicitly delete.
struct StringToken {
const char * p;
size_t l;
bool hasIndentation;
operator std::string_view() const { return {p, l}; }
};
#define YY_DECL int yylex \ #define YY_DECL int yylex \
(YYSTYPE * yylval_param, YYLTYPE * yylloc_param, yyscan_t yyscanner, nix::ParseData * data) (YYSTYPE * yylval_param, YYLTYPE * yylloc_param, yyscan_t yyscanner, nix::ParseData * data)
@ -140,21 +156,46 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
} }
static void addFormal(const Pos & pos, Formals * formals, const Formal & formal) static Formals * toFormals(ParseData & data, ParserFormals * formals,
Pos pos = noPos, Symbol arg = {})
{ {
if (!formals->argNames.insert(formal.name).second) std::sort(formals->formals.begin(), formals->formals.end(),
[] (const auto & a, const auto & b) {
return std::tie(a.name, a.pos) < std::tie(b.name, b.pos);
});
std::optional<std::pair<Symbol, Pos>> duplicate;
for (size_t i = 0; i + 1 < formals->formals.size(); i++) {
if (formals->formals[i].name != formals->formals[i + 1].name)
continue;
std::pair thisDup{formals->formals[i].name, formals->formals[i + 1].pos};
duplicate = std::min(thisDup, duplicate.value_or(thisDup));
}
if (duplicate)
throw ParseError({ throw ParseError({
.msg = hintfmt("duplicate formal function argument '%1%'", .msg = hintfmt("duplicate formal function argument '%1%'", duplicate->first),
formal.name), .errPos = duplicate->second
});
Formals result;
result.ellipsis = formals->ellipsis;
result.formals = std::move(formals->formals);
if (arg.set() && result.has(arg))
throw ParseError({
.msg = hintfmt("duplicate formal function argument '%1%'", arg),
.errPos = pos .errPos = pos
}); });
formals->formals.push_front(formal);
delete formals;
return new Formals(std::move(result));
} }
static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<std::pair<Pos, Expr *> > & es) static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols,
vector<std::pair<Pos, std::variant<Expr *, StringToken> > > & es)
{ {
if (es.empty()) return new ExprString(symbols.create("")); if (es.empty()) return new ExprString("");
/* Figure out the minimum indentation. Note that by design /* Figure out the minimum indentation. Note that by design
whitespace-only final lines are not taken into account. (So whitespace-only final lines are not taken into account. (So
@ -163,20 +204,20 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<st
size_t minIndent = 1000000; size_t minIndent = 1000000;
size_t curIndent = 0; size_t curIndent = 0;
for (auto & [i_pos, i] : es) { for (auto & [i_pos, i] : es) {
ExprIndStr * e = dynamic_cast<ExprIndStr *>(i); auto * str = std::get_if<StringToken>(&i);
if (!e) { if (!str || !str->hasIndentation) {
/* Anti-quotations end the current start-of-line whitespace. */ /* Anti-quotations and escaped characters end the current start-of-line whitespace. */
if (atStartOfLine) { if (atStartOfLine) {
atStartOfLine = false; atStartOfLine = false;
if (curIndent < minIndent) minIndent = curIndent; if (curIndent < minIndent) minIndent = curIndent;
} }
continue; continue;
} }
for (size_t j = 0; j < e->s.size(); ++j) { for (size_t j = 0; j < str->l; ++j) {
if (atStartOfLine) { if (atStartOfLine) {
if (e->s[j] == ' ') if (str->p[j] == ' ')
curIndent++; curIndent++;
else if (e->s[j] == '\n') { else if (str->p[j] == '\n') {
/* Empty line, doesn't influence minimum /* Empty line, doesn't influence minimum
indentation. */ indentation. */
curIndent = 0; curIndent = 0;
@ -184,7 +225,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<st
atStartOfLine = false; atStartOfLine = false;
if (curIndent < minIndent) minIndent = curIndent; if (curIndent < minIndent) minIndent = curIndent;
} }
} else if (e->s[j] == '\n') { } else if (str->p[j] == '\n') {
atStartOfLine = true; atStartOfLine = true;
curIndent = 0; curIndent = 0;
} }
@ -196,33 +237,31 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<st
atStartOfLine = true; atStartOfLine = true;
size_t curDropped = 0; size_t curDropped = 0;
size_t n = es.size(); size_t n = es.size();
for (vector<std::pair<Pos, Expr *> >::iterator i = es.begin(); i != es.end(); ++i, --n) { auto i = es.begin();
ExprIndStr * e = dynamic_cast<ExprIndStr *>(i->second); const auto trimExpr = [&] (Expr * e) {
if (!e) { atStartOfLine = false;
atStartOfLine = false; curDropped = 0;
curDropped = 0; es2->emplace_back(i->first, e);
es2->push_back(*i); };
continue; const auto trimString = [&] (const StringToken & t) {
}
string s2; string s2;
for (size_t j = 0; j < e->s.size(); ++j) { for (size_t j = 0; j < t.l; ++j) {
if (atStartOfLine) { if (atStartOfLine) {
if (e->s[j] == ' ') { if (t.p[j] == ' ') {
if (curDropped++ >= minIndent) if (curDropped++ >= minIndent)
s2 += e->s[j]; s2 += t.p[j];
} }
else if (e->s[j] == '\n') { else if (t.p[j] == '\n') {
curDropped = 0; curDropped = 0;
s2 += e->s[j]; s2 += t.p[j];
} else { } else {
atStartOfLine = false; atStartOfLine = false;
curDropped = 0; curDropped = 0;
s2 += e->s[j]; s2 += t.p[j];
} }
} else { } else {
s2 += e->s[j]; s2 += t.p[j];
if (e->s[j] == '\n') atStartOfLine = true; if (t.p[j] == '\n') atStartOfLine = true;
} }
} }
@ -234,7 +273,10 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<st
s2 = string(s2, 0, p + 1); s2 = string(s2, 0, p + 1);
} }
es2->emplace_back(i->first, new ExprString(symbols.create(s2))); es2->emplace_back(i->first, new ExprString(s2));
};
for (; i != es.end(); ++i, --n) {
std::visit(overloaded { trimExpr, trimString }, i->second);
} }
/* If this is a single string, then don't do a concatenation. */ /* If this is a single string, then don't do a concatenation. */
@ -269,22 +311,17 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
nix::Expr * e; nix::Expr * e;
nix::ExprList * list; nix::ExprList * list;
nix::ExprAttrs * attrs; nix::ExprAttrs * attrs;
nix::Formals * formals; nix::ParserFormals * formals;
nix::Formal * formal; nix::Formal * formal;
nix::NixInt n; nix::NixInt n;
nix::NixFloat nf; nix::NixFloat nf;
// using C a struct allows us to avoid having to define the special
// members that using string_view here would implicitly delete.
struct StringToken {
const char * p;
size_t l;
operator std::string_view() const { return {p, l}; }
};
StringToken id; // !!! -> Symbol StringToken id; // !!! -> Symbol
StringToken path; StringToken path;
StringToken uri; StringToken uri;
StringToken str;
std::vector<nix::AttrName> * attrNames; std::vector<nix::AttrName> * attrNames;
std::vector<std::pair<nix::Pos, nix::Expr *> > * string_parts; std::vector<std::pair<nix::Pos, nix::Expr *> > * string_parts;
std::vector<std::pair<nix::Pos, std::variant<nix::Expr *, StringToken> > > * ind_string_parts;
} }
%type <e> start expr expr_function expr_if expr_op %type <e> start expr expr_function expr_if expr_op
@ -294,11 +331,12 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
%type <formals> formals %type <formals> formals
%type <formal> formal %type <formal> formal
%type <attrNames> attrs attrpath %type <attrNames> attrs attrpath
%type <string_parts> string_parts_interpolated ind_string_parts %type <string_parts> string_parts_interpolated
%type <ind_string_parts> ind_string_parts
%type <e> path_start string_parts string_attr %type <e> path_start string_parts string_attr
%type <id> attr %type <id> attr
%token <id> ID ATTRPATH %token <id> ID ATTRPATH
%token <e> STR IND_STR %token <str> STR IND_STR
%token <n> INT %token <n> INT
%token <nf> FLOAT %token <nf> FLOAT
%token <path> PATH HPATH SPATH PATH_END %token <path> PATH HPATH SPATH PATH_END
@ -331,11 +369,17 @@ expr_function
: ID ':' expr_function : ID ':' expr_function
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), 0, $3); } { $$ = new ExprLambda(CUR_POS, data->symbols.create($1), 0, $3); }
| '{' formals '}' ':' expr_function | '{' formals '}' ':' expr_function
{ $$ = new ExprLambda(CUR_POS, data->symbols.create(""), $2, $5); } { $$ = new ExprLambda(CUR_POS, data->symbols.create(""), toFormals(*data, $2), $5); }
| '{' formals '}' '@' ID ':' expr_function | '{' formals '}' '@' ID ':' expr_function
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($5), $2, $7); } {
Symbol arg = data->symbols.create($5);
$$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $2, CUR_POS, arg), $7);
}
| ID '@' '{' formals '}' ':' expr_function | ID '@' '{' formals '}' ':' expr_function
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), $4, $7); } {
Symbol arg = data->symbols.create($1);
$$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $4, CUR_POS, arg), $7);
}
| ASSERT expr ';' expr_function | ASSERT expr ';' expr_function
{ $$ = new ExprAssert(CUR_POS, $2, $4); } { $$ = new ExprAssert(CUR_POS, $2, $4); }
| WITH expr ';' expr_function | WITH expr ';' expr_function
@ -426,7 +470,7 @@ expr_simple
$$ = new ExprCall(CUR_POS, $$ = new ExprCall(CUR_POS,
new ExprVar(data->symbols.create("__findFile")), new ExprVar(data->symbols.create("__findFile")),
{new ExprVar(data->symbols.create("__nixPath")), {new ExprVar(data->symbols.create("__nixPath")),
new ExprString(data->symbols.create(path))}); new ExprString(path)});
} }
| URI { | URI {
static bool noURLLiterals = settings.isExperimentalFeatureEnabled(Xp::NoUrlLiterals); static bool noURLLiterals = settings.isExperimentalFeatureEnabled(Xp::NoUrlLiterals);
@ -435,7 +479,7 @@ expr_simple
.msg = hintfmt("URL literals are disabled"), .msg = hintfmt("URL literals are disabled"),
.errPos = CUR_POS .errPos = CUR_POS
}); });
$$ = new ExprString(data->symbols.create($1)); $$ = new ExprString(string($1));
} }
| '(' expr ')' { $$ = $2; } | '(' expr ')' { $$ = $2; }
/* Let expressions `let {..., body = ...}' are just desugared /* Let expressions `let {..., body = ...}' are just desugared
@ -450,18 +494,19 @@ expr_simple
; ;
string_parts string_parts
: STR : STR { $$ = new ExprString(string($1)); }
| string_parts_interpolated { $$ = new ExprConcatStrings(CUR_POS, true, $1); } | string_parts_interpolated { $$ = new ExprConcatStrings(CUR_POS, true, $1); }
| { $$ = new ExprString(data->symbols.create("")); } | { $$ = new ExprString(""); }
; ;
string_parts_interpolated string_parts_interpolated
: string_parts_interpolated STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); } : string_parts_interpolated STR
{ $$ = $1; $1->emplace_back(makeCurPos(@2, data), new ExprString(string($2))); }
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); } | 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); } | DOLLAR_CURLY expr '}' { $$ = new vector<std::pair<Pos, Expr *> >; $$->emplace_back(makeCurPos(@1, data), $2); }
| STR DOLLAR_CURLY expr '}' { | STR DOLLAR_CURLY expr '}' {
$$ = new vector<std::pair<Pos, Expr *> >; $$ = new vector<std::pair<Pos, Expr *> >;
$$->emplace_back(makeCurPos(@1, data), $1); $$->emplace_back(makeCurPos(@1, data), new ExprString(string($1)));
$$->emplace_back(makeCurPos(@2, data), $3); $$->emplace_back(makeCurPos(@2, data), $3);
} }
; ;
@ -483,7 +528,7 @@ path_start
ind_string_parts ind_string_parts
: ind_string_parts IND_STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); } : 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); } | ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
| { $$ = new vector<std::pair<Pos, Expr *> >; } | { $$ = new vector<std::pair<Pos, std::variant<Expr *, StringToken> > >; }
; ;
binds binds
@ -515,7 +560,7 @@ attrs
{ $$ = $1; { $$ = $1;
ExprString * str = dynamic_cast<ExprString *>($2); ExprString * str = dynamic_cast<ExprString *>($2);
if (str) { if (str) {
$$->push_back(AttrName(str->s)); $$->push_back(AttrName(data->symbols.create(str->s)));
delete str; delete str;
} else } else
throw ParseError({ throw ParseError({
@ -532,7 +577,7 @@ attrpath
{ $$ = $1; { $$ = $1;
ExprString * str = dynamic_cast<ExprString *>($3); ExprString * str = dynamic_cast<ExprString *>($3);
if (str) { if (str) {
$$->push_back(AttrName(str->s)); $$->push_back(AttrName(data->symbols.create(str->s)));
delete str; delete str;
} else } else
$$->push_back(AttrName($3)); $$->push_back(AttrName($3));
@ -542,7 +587,7 @@ attrpath
{ $$ = new vector<AttrName>; { $$ = new vector<AttrName>;
ExprString *str = dynamic_cast<ExprString *>($1); ExprString *str = dynamic_cast<ExprString *>($1);
if (str) { if (str) {
$$->push_back(AttrName(str->s)); $$->push_back(AttrName(data->symbols.create(str->s)));
delete str; delete str;
} else } else
$$->push_back(AttrName($1)); $$->push_back(AttrName($1));
@ -566,13 +611,13 @@ expr_list
formals formals
: formal ',' formals : formal ',' formals
{ $$ = $3; addFormal(CUR_POS, $$, *$1); } { $$ = $3; $$->formals.push_back(*$1); }
| formal | formal
{ $$ = new Formals; addFormal(CUR_POS, $$, *$1); $$->ellipsis = false; } { $$ = new ParserFormals; $$->formals.push_back(*$1); $$->ellipsis = false; }
| |
{ $$ = new Formals; $$->ellipsis = false; } { $$ = new ParserFormals; $$->ellipsis = false; }
| ELLIPSIS | ELLIPSIS
{ $$ = new Formals; $$->ellipsis = true; } { $$ = new ParserFormals; $$->ellipsis = true; }
; ;
formal formal
@ -598,7 +643,7 @@ namespace nix {
Expr * EvalState::parse(char * text, size_t length, FileOrigin origin, Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
const Path & path, const Path & basePath, StaticEnv & staticEnv) const PathView path, const PathView basePath, StaticEnv & staticEnv)
{ {
yyscan_t scanner; yyscan_t scanner;
ParseData data(*this); ParseData data(*this);
@ -709,24 +754,24 @@ void EvalState::addToSearchPath(const string & s)
} }
Path EvalState::findFile(const string & path) Path EvalState::findFile(const std::string_view path)
{ {
return findFile(searchPath, path); return findFile(searchPath, path);
} }
Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos & pos) Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos)
{ {
for (auto & i : searchPath) { for (auto & i : searchPath) {
std::string suffix; std::string suffix;
if (i.first.empty()) if (i.first.empty())
suffix = "/" + path; suffix = concatStrings("/", path);
else { else {
auto s = i.first.size(); auto s = i.first.size();
if (path.compare(0, s, i.first) != 0 || if (path.compare(0, s, i.first) != 0 ||
(path.size() > s && path[s] != '/')) (path.size() > s && path[s] != '/'))
continue; continue;
suffix = path.size() == s ? "" : "/" + string(path, s); suffix = path.size() == s ? "" : concatStrings("/", path.substr(s));
} }
auto r = resolveSearchPathElem(i); auto r = resolveSearchPathElem(i);
if (!r.first) continue; if (!r.first) continue;
@ -735,7 +780,7 @@ Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos
} }
if (hasPrefix(path, "nix/")) if (hasPrefix(path, "nix/"))
return corepkgsPrefix + path.substr(4); return concatStrings(corepkgsPrefix, path.substr(4));
throw ThrownError({ throw ThrownError({
.msg = hintfmt(evalSettings.pureEval .msg = hintfmt(evalSettings.pureEval

View file

@ -314,7 +314,7 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value
{ {
auto path = realisePath(state, pos, *args[0]); auto path = realisePath(state, pos, *args[0]);
string sym = state.forceStringNoCtx(*args[1], pos); string sym(state.forceStringNoCtx(*args[1], pos));
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL); void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
if (!handle) if (!handle)
@ -350,10 +350,11 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
}); });
} }
PathSet context; PathSet context;
auto program = state.coerceToString(pos, *elems[0], context, false, false); auto program = state.coerceToString(pos, *elems[0], context, false, false).toOwned();
Strings commandArgs; Strings commandArgs;
for (unsigned int i = 1; i < args[0]->listSize(); ++i) for (unsigned int i = 1; i < args[0]->listSize(); ++i) {
commandArgs.emplace_back(state.coerceToString(pos, *elems[i], context, false, false)); commandArgs.push_back(state.coerceToString(pos, *elems[i], context, false, false).toOwned());
}
try { try {
auto _ = state.realiseContext(context); // FIXME: Handle CA derivations auto _ = state.realiseContext(context); // FIXME: Handle CA derivations
} catch (InvalidPathError & e) { } catch (InvalidPathError & e) {
@ -706,7 +707,7 @@ static RegisterPrimOp primop_abort({
.fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v) .fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
string s = state.coerceToString(pos, *args[0], context); string s = state.coerceToString(pos, *args[0], context).toOwned();
throw Abort("evaluation aborted with the following error message: '%1%'", s); throw Abort("evaluation aborted with the following error message: '%1%'", s);
} }
}); });
@ -724,7 +725,7 @@ static RegisterPrimOp primop_throw({
.fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v) .fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
string s = state.coerceToString(pos, *args[0], context); string s = state.coerceToString(pos, *args[0], context).toOwned();
throw ThrownError(s); throw ThrownError(s);
} }
}); });
@ -736,7 +737,7 @@ static void prim_addErrorContext(EvalState & state, const Pos & pos, Value * * a
v = *args[1]; v = *args[1];
} catch (Error & e) { } catch (Error & e) {
PathSet context; PathSet context;
e.addTrace(std::nullopt, state.coerceToString(pos, *args[0], context)); e.addTrace(std::nullopt, state.coerceToString(pos, *args[0], context).toOwned());
throw; throw;
} }
} }
@ -825,7 +826,7 @@ static RegisterPrimOp primop_tryEval({
/* Return an environment variable. Use with care. */ /* Return an environment variable. Use with care. */
static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
string name = state.forceStringNoCtx(*args[0], pos); string name(state.forceStringNoCtx(*args[0], pos));
v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or("")); v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or(""));
} }
@ -975,7 +976,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
const string & key = i->name; const string & key = i->name;
vomit("processing attribute '%1%'", key); vomit("processing attribute '%1%'", key);
auto handleHashMode = [&](const std::string & s) { auto handleHashMode = [&](const std::string_view s) {
if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive; if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive;
else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat; else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat;
else else
@ -1030,7 +1031,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
else if (i->name == state.sArgs) { else if (i->name == state.sArgs) {
state.forceList(*i->value, pos); state.forceList(*i->value, pos);
for (auto elem : i->value->listItems()) { for (auto elem : i->value->listItems()) {
string s = state.coerceToString(posDrvName, *elem, context, true); string s = state.coerceToString(posDrvName, *elem, context, true).toOwned();
drv.args.push_back(s); drv.args.push_back(s);
} }
} }
@ -1066,7 +1067,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
} }
} else { } else {
auto s = state.coerceToString(*i->pos, *i->value, context, true); auto s = state.coerceToString(*i->pos, *i->value, context, true).toOwned();
drv.env.emplace(key, s); drv.env.emplace(key, s);
if (i->name == state.sBuilder) drv.builder = std::move(s); if (i->name == state.sBuilder) drv.builder = std::move(s);
else if (i->name == state.sSystem) drv.platform = std::move(s); else if (i->name == state.sSystem) drv.platform = std::move(s);
@ -1269,7 +1270,7 @@ static RegisterPrimOp primop_derivationStrict(RegisterPrimOp::Info {
substituted by the corresponding output path at build time. For substituted by the corresponding output path at build time. For
example, 'placeholder "out"' returns the string example, 'placeholder "out"' returns the string
/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9. At build /1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9. At build
time, any occurence of this string in an derivation attribute will time, any occurrence of this string in an derivation attribute will
be replaced with the concrete path in the Nix store of the output be replaced with the concrete path in the Nix store of the output
out. */ out. */
static void prim_placeholder(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_placeholder(EvalState & state, const Pos & pos, Value * * args, Value & v)
@ -1399,7 +1400,7 @@ static RegisterPrimOp primop_pathExists({
static void prim_baseNameOf(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_baseNameOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
v.mkString(baseNameOf(state.coerceToString(pos, *args[0], context, false, false)), context); v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context, false, false)), context);
} }
static RegisterPrimOp primop_baseNameOf({ static RegisterPrimOp primop_baseNameOf({
@ -1419,7 +1420,8 @@ static RegisterPrimOp primop_baseNameOf({
static void prim_dirOf(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_dirOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
Path dir = dirOf(state.coerceToString(pos, *args[0], context, false, false)); auto path = state.coerceToString(pos, *args[0], context, false, false);
auto dir = dirOf(*path);
if (args[0]->type() == nPath) v.mkPath(dir); else v.mkString(dir, context); if (args[0]->type() == nPath) v.mkPath(dir); else v.mkString(dir, context);
} }
@ -1486,7 +1488,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
); );
PathSet context; PathSet context;
string path = state.coerceToString(pos, *i->value, context, false, false); string path = state.coerceToString(pos, *i->value, context, false, false).toOwned();
try { try {
auto rewrites = state.realiseContext(context); auto rewrites = state.realiseContext(context);
@ -1502,7 +1504,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
searchPath.emplace_back(prefix, path); searchPath.emplace_back(prefix, path);
} }
string path = state.forceStringNoCtx(*args[1], pos); auto path = state.forceStringNoCtx(*args[1], pos);
v.mkPath(state.checkSourcePath(state.findFile(searchPath, path, pos))); v.mkPath(state.checkSourcePath(state.findFile(searchPath, path, pos)));
} }
@ -1516,7 +1518,7 @@ static RegisterPrimOp primop_findFile(RegisterPrimOp::Info {
/* Return the cryptographic hash of a file in base-16. */ /* Return the cryptographic hash of a file in base-16. */
static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
string type = state.forceStringNoCtx(*args[0], pos); auto type = state.forceStringNoCtx(*args[0], pos);
std::optional<HashType> ht = parseHashType(type); std::optional<HashType> ht = parseHashType(type);
if (!ht) if (!ht)
throw Error({ throw Error({
@ -1723,7 +1725,7 @@ static RegisterPrimOp primop_toJSON({
/* Parse a JSON string to a value. */ /* Parse a JSON string to a value. */
static void prim_fromJSON(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_fromJSON(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
string s = state.forceStringNoCtx(*args[0], pos); auto s = state.forceStringNoCtx(*args[0], pos);
try { try {
parseJSON(state, s, v); parseJSON(state, s, v);
} catch (JSONParseError &e) { } catch (JSONParseError &e) {
@ -1752,8 +1754,8 @@ static RegisterPrimOp primop_fromJSON({
static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
string name = state.forceStringNoCtx(*args[0], pos); string name(state.forceStringNoCtx(*args[0], pos));
string contents = state.forceString(*args[1], context, pos); string contents(state.forceString(*args[1], context, pos));
StorePathSet refs; StorePathSet refs;
@ -2153,7 +2155,7 @@ static RegisterPrimOp primop_attrValues({
/* Dynamic version of the `.' operator. */ /* Dynamic version of the `.' operator. */
void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v) void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
string attr = state.forceStringNoCtx(*args[0], pos); auto attr = state.forceStringNoCtx(*args[0], pos);
state.forceAttrs(*args[1], pos); state.forceAttrs(*args[1], pos);
Bindings::iterator i = getAttr( Bindings::iterator i = getAttr(
state, state,
@ -2183,7 +2185,7 @@ static RegisterPrimOp primop_getAttr({
/* Return position information of the specified attribute. */ /* Return position information of the specified attribute. */
static void prim_unsafeGetAttrPos(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_unsafeGetAttrPos(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
string attr = state.forceStringNoCtx(*args[0], pos); auto attr = state.forceStringNoCtx(*args[0], pos);
state.forceAttrs(*args[1], pos); state.forceAttrs(*args[1], pos);
Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr)); Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr));
if (i == args[1]->attrs->end()) if (i == args[1]->attrs->end())
@ -2201,7 +2203,7 @@ static RegisterPrimOp primop_unsafeGetAttrPos(RegisterPrimOp::Info {
/* Dynamic version of the `?' operator. */ /* Dynamic version of the `?' operator. */
static void prim_hasAttr(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_hasAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
string attr = state.forceStringNoCtx(*args[0], pos); auto attr = state.forceStringNoCtx(*args[0], pos);
state.forceAttrs(*args[1], pos); state.forceAttrs(*args[1], pos);
v.mkBool(args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end()); v.mkBool(args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end());
} }
@ -2279,7 +2281,7 @@ static RegisterPrimOp primop_removeAttrs({
/* Builds a set from a list specifying (name, value) pairs. To be /* Builds a set from a list specifying (name, value) pairs. To be
precise, a list [{name = "name1"; value = value1;} ... {name = precise, a list [{name = "name1"; value = value1;} ... {name =
"nameN"; value = valueN;}] is transformed to {name1 = value1; "nameN"; value = valueN;}] is transformed to {name1 = value1;
... nameN = valueN;}. In case of duplicate occurences of the same ... nameN = valueN;}. In case of duplicate occurrences of the same
name, the first takes precedence. */ name, the first takes precedence. */
static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
@ -2300,7 +2302,7 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
pos pos
); );
string name = state.forceStringNoCtx(*j->value, *j->pos); auto name = state.forceStringNoCtx(*j->value, *j->pos);
Symbol sym = state.symbols.create(name); Symbol sym = state.symbols.create(name);
if (seen.insert(sym).second) { if (seen.insert(sym).second) {
@ -3032,7 +3034,7 @@ static void prim_groupBy(EvalState & state, const Pos & pos, Value * * args, Val
for (auto vElem : args[1]->listItems()) { for (auto vElem : args[1]->listItems()) {
Value res; Value res;
state.callFunction(*args[0], *vElem, res, pos); state.callFunction(*args[0], *vElem, res, pos);
string name = state.forceStringNoCtx(res, pos); auto name = state.forceStringNoCtx(res, pos);
Symbol sym = state.symbols.create(name); Symbol sym = state.symbols.create(name);
auto vector = attrs.try_emplace(sym, ValueVector()).first; auto vector = attrs.try_emplace(sym, ValueVector()).first;
vector->second.push_back(vElem); vector->second.push_back(vElem);
@ -3288,8 +3290,8 @@ static RegisterPrimOp primop_lessThan({
static void prim_toString(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_toString(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
string s = state.coerceToString(pos, *args[0], context, true, false); auto s = state.coerceToString(pos, *args[0], context, true, false);
v.mkString(s, context); v.mkString(*s, context);
} }
static RegisterPrimOp primop_toString({ static RegisterPrimOp primop_toString({
@ -3325,7 +3327,7 @@ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, V
int start = state.forceInt(*args[0], pos); int start = state.forceInt(*args[0], pos);
int len = state.forceInt(*args[1], pos); int len = state.forceInt(*args[1], pos);
PathSet context; PathSet context;
string s = state.coerceToString(pos, *args[2], context); auto s = state.coerceToString(pos, *args[2], context);
if (start < 0) if (start < 0)
throw EvalError({ throw EvalError({
@ -3333,7 +3335,7 @@ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, V
.errPos = pos .errPos = pos
}); });
v.mkString((unsigned int) start >= s.size() ? "" : string(s, start, len), context); v.mkString((unsigned int) start >= s->size() ? "" : s->substr(start, len), context);
} }
static RegisterPrimOp primop_substring({ static RegisterPrimOp primop_substring({
@ -3359,8 +3361,8 @@ static RegisterPrimOp primop_substring({
static void prim_stringLength(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_stringLength(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
string s = state.coerceToString(pos, *args[0], context); auto s = state.coerceToString(pos, *args[0], context);
v.mkInt(s.size()); v.mkInt(s->size());
} }
static RegisterPrimOp primop_stringLength({ static RegisterPrimOp primop_stringLength({
@ -3376,7 +3378,7 @@ static RegisterPrimOp primop_stringLength({
/* Return the cryptographic hash of a string in base-16. */ /* Return the cryptographic hash of a string in base-16. */
static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
string type = state.forceStringNoCtx(*args[0], pos); auto type = state.forceStringNoCtx(*args[0], pos);
std::optional<HashType> ht = parseHashType(type); std::optional<HashType> ht = parseHashType(type);
if (!ht) if (!ht)
throw Error({ throw Error({
@ -3385,7 +3387,7 @@ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args,
}); });
PathSet context; // discarded PathSet context; // discarded
string s = state.forceString(*args[1], context, pos); auto s = state.forceString(*args[1], context, pos);
v.mkString(hashString(*ht, s).to_string(Base16, false)); v.mkString(hashString(*ht, s).to_string(Base16, false));
} }
@ -3403,7 +3405,18 @@ static RegisterPrimOp primop_hashString({
struct RegexCache struct RegexCache
{ {
std::unordered_map<std::string, std::regex> cache; // TODO use C++20 transparent comparison when available
std::unordered_map<std::string_view, std::regex> cache;
std::list<std::string> keys;
std::regex get(std::string_view re)
{
auto it = cache.find(re);
if (it != cache.end())
return it->second;
keys.emplace_back(re);
return cache.emplace(keys.back(), std::regex(keys.back(), std::regex::extended)).first->second;
}
}; };
std::shared_ptr<RegexCache> makeRegexCache() std::shared_ptr<RegexCache> makeRegexCache()
@ -3417,15 +3430,13 @@ void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v)
try { try {
auto regex = state.regexCache->cache.find(re); auto regex = state.regexCache->get(re);
if (regex == state.regexCache->cache.end())
regex = state.regexCache->cache.emplace(re, std::regex(re, std::regex::extended)).first;
PathSet context; PathSet context;
const std::string str = state.forceString(*args[1], context, pos); const auto str = state.forceString(*args[1], context, pos);
std::smatch match; std::cmatch match;
if (!std::regex_match(str, match, regex->second)) { if (!std::regex_match(str.begin(), str.end(), match, regex)) {
v.mkNull(); v.mkNull();
return; return;
} }
@ -3500,15 +3511,13 @@ void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v)
try { try {
auto regex = state.regexCache->cache.find(re); auto regex = state.regexCache->get(re);
if (regex == state.regexCache->cache.end())
regex = state.regexCache->cache.emplace(re, std::regex(re, std::regex::extended)).first;
PathSet context; PathSet context;
const std::string str = state.forceString(*args[1], context, pos); const auto str = state.forceString(*args[1], context, pos);
auto begin = std::sregex_iterator(str.begin(), str.end(), regex->second); auto begin = std::cregex_iterator(str.begin(), str.end(), regex);
auto end = std::sregex_iterator(); auto end = std::cregex_iterator();
// Any matches results are surrounded by non-matching results. // Any matches results are surrounded by non-matching results.
const size_t len = std::distance(begin, end); const size_t len = std::distance(begin, end);
@ -3520,9 +3529,9 @@ void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v)
return; return;
} }
for (std::sregex_iterator i = begin; i != end; ++i) { for (auto i = begin; i != end; ++i) {
assert(idx <= 2 * len + 1 - 3); assert(idx <= 2 * len + 1 - 3);
std::smatch match = *i; auto match = *i;
// Add a string for non-matched characters. // Add a string for non-matched characters.
(v.listElems()[idx++] = state.allocValue())->mkString(match.prefix().str()); (v.listElems()[idx++] = state.allocValue())->mkString(match.prefix().str());
@ -3613,7 +3622,7 @@ static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * *
for (auto elem : args[1]->listItems()) { for (auto elem : args[1]->listItems()) {
if (first) first = false; else res += sep; if (first) first = false; else res += sep;
res += state.coerceToString(pos, *elem, context); res += *state.coerceToString(pos, *elem, context);
} }
v.mkString(res, context); v.mkString(res, context);
@ -3643,14 +3652,14 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar
vector<string> from; vector<string> from;
from.reserve(args[0]->listSize()); from.reserve(args[0]->listSize());
for (auto elem : args[0]->listItems()) for (auto elem : args[0]->listItems())
from.push_back(state.forceString(*elem, pos)); from.emplace_back(state.forceString(*elem, pos));
vector<std::pair<string, PathSet>> to; vector<std::pair<string, PathSet>> to;
to.reserve(args[1]->listSize()); to.reserve(args[1]->listSize());
for (auto elem : args[1]->listItems()) { for (auto elem : args[1]->listItems()) {
PathSet ctx; PathSet ctx;
auto s = state.forceString(*elem, ctx, pos); auto s = state.forceString(*elem, ctx, pos);
to.push_back(std::make_pair(std::move(s), std::move(ctx))); to.emplace_back(s, std::move(ctx));
} }
PathSet context; PathSet context;
@ -3712,7 +3721,7 @@ static RegisterPrimOp primop_replaceStrings({
static void prim_parseDrvName(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_parseDrvName(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
string name = state.forceStringNoCtx(*args[0], pos); auto name = state.forceStringNoCtx(*args[0], pos);
DrvName parsed(name); DrvName parsed(name);
auto attrs = state.buildBindings(2); auto attrs = state.buildBindings(2);
attrs.alloc(state.sName).mkString(parsed.name); attrs.alloc(state.sName).mkString(parsed.name);
@ -3736,8 +3745,8 @@ static RegisterPrimOp primop_parseDrvName({
static void prim_compareVersions(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_compareVersions(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
string version1 = state.forceStringNoCtx(*args[0], pos); auto version1 = state.forceStringNoCtx(*args[0], pos);
string version2 = state.forceStringNoCtx(*args[1], pos); auto version2 = state.forceStringNoCtx(*args[1], pos);
v.mkInt(compareVersions(version1, version2)); v.mkInt(compareVersions(version1, version2));
} }
@ -3756,14 +3765,14 @@ static RegisterPrimOp primop_compareVersions({
static void prim_splitVersion(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_splitVersion(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
string version = state.forceStringNoCtx(*args[0], pos); auto version = state.forceStringNoCtx(*args[0], pos);
auto iter = version.cbegin(); auto iter = version.cbegin();
Strings components; Strings components;
while (iter != version.cend()) { while (iter != version.cend()) {
auto component = nextComponent(iter, version.cend()); auto component = nextComponent(iter, version.cend());
if (component.empty()) if (component.empty())
break; break;
components.emplace_back(std::move(component)); components.emplace_back(component);
} }
state.mkList(v, components.size()); state.mkList(v, components.size());
for (const auto & [n, component] : enumerate(components)) for (const auto & [n, component] : enumerate(components))

View file

@ -7,7 +7,8 @@ namespace nix {
static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
v.mkString(state.coerceToString(pos, *args[0], context)); auto s = state.coerceToString(pos, *args[0], context);
v.mkString(*s);
} }
static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext); static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
@ -32,13 +33,13 @@ static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext);
static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
string s = state.coerceToString(pos, *args[0], context); auto s = state.coerceToString(pos, *args[0], context);
PathSet context2; PathSet context2;
for (auto & p : context) for (auto & p : context)
context2.insert(p.at(0) == '=' ? string(p, 1) : p); context2.insert(p.at(0) == '=' ? string(p, 1) : p);
v.mkString(s, context2); v.mkString(*s, context2);
} }
static RegisterPrimOp primop_unsafeDiscardOutputDependency("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency); static RegisterPrimOp primop_unsafeDiscardOutputDependency("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
@ -180,7 +181,7 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
} }
for (auto elem : iter->value->listItems()) { for (auto elem : iter->value->listItems()) {
auto name = state.forceStringNoCtx(*elem, *iter->pos); auto name = state.forceStringNoCtx(*elem, *iter->pos);
context.insert("!" + name + "!" + string(i.name)); context.insert(concatStrings("!", name, "!", i.name));
} }
} }
} }

View file

@ -12,7 +12,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
std::string url; std::string url;
std::optional<Hash> rev; std::optional<Hash> rev;
std::optional<std::string> ref; std::optional<std::string> ref;
std::string name = "source"; std::string_view name = "source";
PathSet context; PathSet context;
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
@ -22,14 +22,14 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos);
for (auto & attr : *args[0]->attrs) { for (auto & attr : *args[0]->attrs) {
string n(attr.name); std::string_view n(attr.name);
if (n == "url") if (n == "url")
url = state.coerceToString(*attr.pos, *attr.value, context, false, false); url = state.coerceToString(*attr.pos, *attr.value, context, false, false).toOwned();
else if (n == "rev") { else if (n == "rev") {
// Ugly: unlike fetchGit, here the "rev" attribute can // Ugly: unlike fetchGit, here the "rev" attribute can
// be both a revision or a branch/tag name. // be both a revision or a branch/tag name.
auto value = state.forceStringNoCtx(*attr.value, *attr.pos); auto value = state.forceStringNoCtx(*attr.value, *attr.pos);
if (std::regex_match(value, revRegex)) if (std::regex_match(value.begin(), value.end(), revRegex))
rev = Hash::parseAny(value, htSHA1); rev = Hash::parseAny(value, htSHA1);
else else
ref = value; ref = value;
@ -50,7 +50,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
}); });
} else } else
url = state.coerceToString(pos, *args[0], context, false, false); url = state.coerceToString(pos, *args[0], context, false, false).toOwned();
// FIXME: git externals probably can be used to bypass the URI // FIXME: git externals probably can be used to bypass the URI
// whitelist. Ah well. // whitelist. Ah well.
@ -62,7 +62,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
fetchers::Attrs attrs; fetchers::Attrs attrs;
attrs.insert_or_assign("type", "hg"); attrs.insert_or_assign("type", "hg");
attrs.insert_or_assign("url", url.find("://") != std::string::npos ? url : "file://" + url); attrs.insert_or_assign("url", url.find("://") != std::string::npos ? url : "file://" + url);
attrs.insert_or_assign("name", name); attrs.insert_or_assign("name", string(name));
if (ref) attrs.insert_or_assign("ref", *ref); if (ref) attrs.insert_or_assign("ref", *ref);
if (rev) attrs.insert_or_assign("rev", rev->gitRev()); if (rev) attrs.insert_or_assign("rev", rev->gitRev());
auto input = fetchers::Input::fromAttrs(std::move(attrs)); auto input = fetchers::Input::fromAttrs(std::move(attrs));

View file

@ -125,7 +125,7 @@ static void fetchTree(
if (attr.name == state.sType) continue; if (attr.name == state.sType) continue;
state.forceValue(*attr.value, *attr.pos); state.forceValue(*attr.value, *attr.pos);
if (attr.value->type() == nPath || attr.value->type() == nString) { if (attr.value->type() == nPath || attr.value->type() == nString) {
auto s = state.coerceToString(*attr.pos, *attr.value, context, false, false); auto s = state.coerceToString(*attr.pos, *attr.value, context, false, false).toOwned();
attrs.emplace(attr.name, attrs.emplace(attr.name,
attr.name == "url" attr.name == "url"
? type == "git" ? type == "git"
@ -151,7 +151,7 @@ static void fetchTree(
input = fetchers::Input::fromAttrs(std::move(attrs)); input = fetchers::Input::fromAttrs(std::move(attrs));
} else { } else {
auto url = state.coerceToString(pos, *args[0], context, false, false); auto url = state.coerceToString(pos, *args[0], context, false, false).toOwned();
if (type == "git") { if (type == "git") {
fetchers::Attrs attrs; fetchers::Attrs attrs;

View file

@ -9,7 +9,7 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va
{ {
auto toml = state.forceStringNoCtx(*args[0], pos); auto toml = state.forceStringNoCtx(*args[0], pos);
std::istringstream tomlStream(toml); std::istringstream tomlStream(string{toml});
std::function<void(Value &, toml::value)> visit; std::function<void(Value &, toml::value)> visit;

View file

@ -142,7 +142,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg; if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg;
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1"; if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
XMLOpenElement _(doc, "attrspat", attrs); XMLOpenElement _(doc, "attrspat", attrs);
for (auto & i : v.lambda.fun->formals->formals) for (auto & i : v.lambda.fun->formals->lexicographicOrder())
doc.writeEmptyElement("attr", singletonAttrs("name", i.name)); doc.writeEmptyElement("attr", singletonAttrs("name", i.name));
} else } else
doc.writeEmptyElement("varpat", singletonAttrs("name", v.lambda.fun->arg)); doc.writeEmptyElement("varpat", singletonAttrs("name", v.lambda.fun->arg));

View file

@ -699,10 +699,10 @@ void writeDerivation(Sink & out, const Store & store, const BasicDerivation & dr
} }
std::string hashPlaceholder(const std::string & outputName) std::string hashPlaceholder(const std::string_view outputName)
{ {
// FIXME: memoize? // FIXME: memoize?
return "/" + hashString(htSHA256, "nix-output:" + outputName).to_string(Base32, false); return "/" + hashString(htSHA256, concatStrings("nix-output:", outputName)).to_string(Base32, false);
} }
std::string downstreamPlaceholder(const Store & store, const StorePath & drvPath, std::string_view outputName) std::string downstreamPlaceholder(const Store & store, const StorePath & drvPath, std::string_view outputName)

View file

@ -236,7 +236,7 @@ void writeDerivation(Sink & out, const Store & store, const BasicDerivation & dr
It is used as a placeholder to allow derivations to refer to their It is used as a placeholder to allow derivations to refer to their
own outputs without needing to use the hash of a derivation in own outputs without needing to use the hash of a derivation in
itself, making the hash near-impossible to calculate. */ itself, making the hash near-impossible to calculate. */
std::string hashPlaceholder(const std::string & outputName); std::string hashPlaceholder(const std::string_view outputName);
/* This creates an opaque and almost certainly unique string /* This creates an opaque and almost certainly unique string
deterministically from a derivation path and output name. deterministically from a derivation path and output name.

View file

@ -970,7 +970,7 @@ public:
Setting<std::string> commitLockFileSummary{ Setting<std::string> commitLockFileSummary{
this, "", "commit-lockfile-summary", this, "", "commit-lockfile-summary",
R"( R"(
The commit summary to use when commiting changed flake lock files. If The commit summary to use when committing changed flake lock files. If
empty, the summary is generated based on the action performed. empty, the summary is generated based on the action performed.
)"}; )"};
}; };

View file

@ -56,8 +56,8 @@ bool DrvName::matches(const DrvName & n)
} }
string nextComponent(string::const_iterator & p, std::string_view nextComponent(std::string_view::const_iterator & p,
const string::const_iterator end) const std::string_view::const_iterator end)
{ {
/* Skip any dots and dashes (component separators). */ /* Skip any dots and dashes (component separators). */
while (p != end && (*p == '.' || *p == '-')) ++p; while (p != end && (*p == '.' || *p == '-')) ++p;
@ -67,18 +67,18 @@ string nextComponent(string::const_iterator & p,
/* If the first character is a digit, consume the longest sequence /* If the first character is a digit, consume the longest sequence
of digits. Otherwise, consume the longest sequence of of digits. Otherwise, consume the longest sequence of
non-digit, non-separator characters. */ non-digit, non-separator characters. */
string s; auto s = p;
if (isdigit(*p)) if (isdigit(*p))
while (p != end && isdigit(*p)) s += *p++; while (p != end && isdigit(*p)) p++;
else else
while (p != end && (!isdigit(*p) && *p != '.' && *p != '-')) while (p != end && (!isdigit(*p) && *p != '.' && *p != '-'))
s += *p++; p++;
return s; return {s, size_t(p - s)};
} }
static bool componentsLT(const string & c1, const string & c2) static bool componentsLT(const std::string_view c1, const std::string_view c2)
{ {
auto n1 = string2Int<int>(c1); auto n1 = string2Int<int>(c1);
auto n2 = string2Int<int>(c2); auto n2 = string2Int<int>(c2);
@ -94,14 +94,14 @@ static bool componentsLT(const string & c1, const string & c2)
} }
int compareVersions(const string & v1, const string & v2) int compareVersions(const std::string_view v1, const std::string_view v2)
{ {
string::const_iterator p1 = v1.begin(); auto p1 = v1.begin();
string::const_iterator p2 = v2.begin(); auto p2 = v2.begin();
while (p1 != v1.end() || p2 != v2.end()) { while (p1 != v1.end() || p2 != v2.end()) {
string c1 = nextComponent(p1, v1.end()); auto c1 = nextComponent(p1, v1.end());
string c2 = nextComponent(p2, v2.end()); auto c2 = nextComponent(p2, v2.end());
if (componentsLT(c1, c2)) return -1; if (componentsLT(c1, c2)) return -1;
else if (componentsLT(c2, c1)) return 1; else if (componentsLT(c2, c1)) return 1;
} }

View file

@ -27,9 +27,9 @@ private:
typedef list<DrvName> DrvNames; typedef list<DrvName> DrvNames;
string nextComponent(string::const_iterator & p, std::string_view nextComponent(std::string_view::const_iterator & p,
const string::const_iterator end); const std::string_view::const_iterator end);
int compareVersions(const string & v1, const string & v2); int compareVersions(const std::string_view v1, const std::string_view v2);
DrvNames drvNamesFromArgs(const Strings & opArgs); DrvNames drvNamesFromArgs(const Strings & opArgs);
} }

View file

@ -26,7 +26,7 @@ static void makeWritable(const Path & path)
struct MakeReadOnly struct MakeReadOnly
{ {
Path path; Path path;
MakeReadOnly(const Path & path) : path(path) { } MakeReadOnly(const PathView path) : path(path) { }
~MakeReadOnly() ~MakeReadOnly()
{ {
try { try {
@ -205,12 +205,13 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
/* Make the containing directory writable, but only if it's not /* Make the containing directory writable, but only if it's not
the store itself (we don't want or need to mess with its the store itself (we don't want or need to mess with its
permissions). */ permissions). */
bool mustToggle = dirOf(path) != realStoreDir.get(); const Path dirOfPath(dirOf(path));
if (mustToggle) makeWritable(dirOf(path)); bool mustToggle = dirOfPath != realStoreDir.get();
if (mustToggle) makeWritable(dirOfPath);
/* When we're done, make the directory read-only again and reset /* When we're done, make the directory read-only again and reset
its timestamp back to 0. */ its timestamp back to 0. */
MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : ""); MakeReadOnly makeReadOnly(mustToggle ? dirOfPath : "");
Path tempLink = (format("%1%/.tmp-link-%2%-%3%") Path tempLink = (format("%1%/.tmp-link-%2%-%3%")
% realStoreDir % getpid() % random()).str(); % realStoreDir % getpid() % random()).str();

View file

@ -170,7 +170,7 @@ std::string writeStructuredAttrsShell(const nlohmann::json & json)
auto handleSimpleType = [](const nlohmann::json & value) -> std::optional<std::string> { auto handleSimpleType = [](const nlohmann::json & value) -> std::optional<std::string> {
if (value.is_string()) if (value.is_string())
return shellEscape(value); return shellEscape(value.get<std::string_view>());
if (value.is_number()) { if (value.is_number()) {
auto f = value.get<float>(); auto f = value.get<float>();

View file

@ -259,7 +259,7 @@ Hash::Hash(std::string_view rest, HashType type, bool isSRI)
throw BadHash("hash '%s' has wrong length for hash type '%s'", rest, printHashType(this->type)); throw BadHash("hash '%s' has wrong length for hash type '%s'", rest, printHashType(this->type));
} }
Hash newHashAllowEmpty(std::string hashStr, std::optional<HashType> ht) Hash newHashAllowEmpty(std::string_view hashStr, std::optional<HashType> ht)
{ {
if (hashStr.empty()) { if (hashStr.empty()) {
if (!ht) if (!ht)

View file

@ -107,7 +107,7 @@ public:
}; };
/* Helper that defaults empty hashes to the 0 hash. */ /* Helper that defaults empty hashes to the 0 hash. */
Hash newHashAllowEmpty(std::string hashStr, std::optional<HashType> ht); Hash newHashAllowEmpty(std::string_view hashStr, std::optional<HashType> ht);
/* Print a hash in base-16 if it's MD5, or base-32 otherwise. */ /* Print a hash in base-16 if it's MD5, or base-32 otherwise. */
string printHash16or32(const Hash & hash); string printHash16or32(const Hash & hash);

View file

@ -6,6 +6,7 @@
#include <set> #include <set>
#include <string> #include <string>
#include <map> #include <map>
#include <variant>
#include <vector> #include <vector>
namespace nix { namespace nix {
@ -47,4 +48,63 @@ struct Explicit {
} }
}; };
/* This wants to be a little bit like rust's Cow type.
Some parts of the evaluator benefit greatly from being able to reuse
existing allocations for strings, but have to be able to also use
newly allocated storage for values.
We do not define implicit conversions, even with ref qualifiers,
since those can easily become ambiguous to the reader and can degrade
into copying behaviour we want to avoid. */
class BackedStringView {
private:
std::variant<std::string, std::string_view> data;
/* Needed to introduce a temporary since operator-> must return
a pointer. Without this we'd need to store the view object
even when we already own a string. */
class Ptr {
private:
std::string_view view;
public:
Ptr(std::string_view view): view(view) {}
const std::string_view * operator->() const { return &view; }
};
public:
BackedStringView(std::string && s): data(std::move(s)) {}
BackedStringView(std::string_view sv): data(sv) {}
template<size_t N>
BackedStringView(const char (& lit)[N]): data(std::string_view(lit)) {}
BackedStringView(const BackedStringView &) = delete;
BackedStringView & operator=(const BackedStringView &) = delete;
/* We only want move operations defined since the sole purpose of
this type is to avoid copies. */
BackedStringView(BackedStringView && other) = default;
BackedStringView & operator=(BackedStringView && other) = default;
bool isOwned() const
{
return std::holds_alternative<std::string>(data);
}
std::string toOwned() &&
{
return isOwned()
? std::move(std::get<std::string>(data))
: std::string(std::get<std::string_view>(data));
}
std::string_view operator*() const
{
return isOwned()
? std::get<std::string>(data)
: std::get<std::string_view>(data);
}
Ptr operator->() const { return Ptr(**this); }
};
} }

View file

@ -81,7 +81,7 @@ void replaceEnv(std::map<std::string, std::string> newEnv)
} }
Path absPath(Path path, std::optional<Path> dir, bool resolveSymlinks) Path absPath(Path path, std::optional<PathView> dir, bool resolveSymlinks)
{ {
if (path[0] != '/') { if (path[0] != '/') {
if (!dir) { if (!dir) {
@ -95,12 +95,12 @@ Path absPath(Path path, std::optional<Path> dir, bool resolveSymlinks)
if (!getcwd(buf, sizeof(buf))) if (!getcwd(buf, sizeof(buf)))
#endif #endif
throw SysError("cannot get cwd"); throw SysError("cannot get cwd");
dir = buf; path = concatStrings(buf, "/", path);
#ifdef __GNU__ #ifdef __GNU__
free(buf); free(buf);
#endif #endif
} } else
path = *dir + "/" + path; path = concatStrings(*dir, "/", path);
} }
return canonPath(path, resolveSymlinks); return canonPath(path, resolveSymlinks);
} }
@ -147,7 +147,7 @@ Path canonPath(PathView path, bool resolveSymlinks)
path = {}; path = {};
} else { } else {
s += path.substr(0, slash); s += path.substr(0, slash);
path = path.substr(slash + 1); path = path.substr(slash);
} }
/* If s points to a symlink, resolve it and continue from there */ /* If s points to a symlink, resolve it and continue from there */
@ -172,7 +172,7 @@ Path canonPath(PathView path, bool resolveSymlinks)
} }
Path dirOf(const Path & path) Path dirOf(const PathView path)
{ {
Path::size_type pos = path.rfind('/'); Path::size_type pos = path.rfind('/');
if (pos == string::npos) if (pos == string::npos)
@ -1344,9 +1344,11 @@ std::string toLower(const std::string & s)
} }
std::string shellEscape(const std::string & s) std::string shellEscape(const std::string_view s)
{ {
std::string r = "'"; std::string r;
r.reserve(s.size() + 2);
r += "'";
for (auto & i : s) for (auto & i : s)
if (i == '\'') r += "'\\''"; else r += i; if (i == '\'') r += "'\\''"; else r += i;
r += '\''; r += '\'';
@ -1751,7 +1753,7 @@ void bind(int fd, const std::string & path)
if (path.size() + 1 >= sizeof(addr.sun_path)) { if (path.size() + 1 >= sizeof(addr.sun_path)) {
Pid pid = startProcess([&]() { Pid pid = startProcess([&]() {
auto dir = dirOf(path); Path dir = dirOf(path);
if (chdir(dir.c_str()) == -1) if (chdir(dir.c_str()) == -1)
throw SysError("chdir to '%s' failed", dir); throw SysError("chdir to '%s' failed", dir);
std::string base(baseNameOf(path)); std::string base(baseNameOf(path));
@ -1780,7 +1782,7 @@ void connect(int fd, const std::string & path)
if (path.size() + 1 >= sizeof(addr.sun_path)) { if (path.size() + 1 >= sizeof(addr.sun_path)) {
Pid pid = startProcess([&]() { Pid pid = startProcess([&]() {
auto dir = dirOf(path); Path dir = dirOf(path);
if (chdir(dir.c_str()) == -1) if (chdir(dir.c_str()) == -1)
throw SysError("chdir to '%s' failed", dir); throw SysError("chdir to '%s' failed", dir);
std::string base(baseNameOf(path)); std::string base(baseNameOf(path));

View file

@ -49,7 +49,7 @@ void clearEnv();
specified directory, or the current directory otherwise. The path specified directory, or the current directory otherwise. The path
is also canonicalised. */ is also canonicalised. */
Path absPath(Path path, Path absPath(Path path,
std::optional<Path> dir = {}, std::optional<PathView> dir = {},
bool resolveSymlinks = false); bool resolveSymlinks = false);
/* Canonicalise a path by removing all `.' or `..' components and /* Canonicalise a path by removing all `.' or `..' components and
@ -62,7 +62,7 @@ Path canonPath(PathView path, bool resolveSymlinks = false);
everything before the final `/'. If the path is the root or an everything before the final `/'. If the path is the root or an
immediate child thereof (e.g., `/foo'), this means `/' immediate child thereof (e.g., `/foo'), this means `/'
is returned.*/ is returned.*/
Path dirOf(const Path & path); Path dirOf(const PathView path);
/* Return the base name of the given canonical path, i.e., everything /* Return the base name of the given canonical path, i.e., everything
following the final `/' (trailing slashes are removed). */ following the final `/' (trailing slashes are removed). */
@ -148,6 +148,9 @@ Path getDataDir();
/* Create a directory and all its parents, if necessary. Returns the /* Create a directory and all its parents, if necessary. Returns the
list of created directories, in order of creation. */ list of created directories, in order of creation. */
Paths createDirs(const Path & path); Paths createDirs(const Path & path);
inline Paths createDirs(PathView path) {
return createDirs(Path(path));
}
/* Create a symlink. */ /* Create a symlink. */
void createSymlink(const Path & target, const Path & link, void createSymlink(const Path & target, const Path & link,
@ -187,6 +190,7 @@ public:
void cancel(); void cancel();
void reset(const Path & p, bool recursive = true); void reset(const Path & p, bool recursive = true);
operator Path() const { return path; } operator Path() const { return path; }
operator PathView() const { return path; }
}; };
@ -491,7 +495,7 @@ std::string toLower(const std::string & s);
/* Escape a string as a shell word. */ /* Escape a string as a shell word. */
std::string shellEscape(const std::string & s); std::string shellEscape(const std::string_view s);
/* Exception handling in destructors: print an error message, then /* Exception handling in destructors: print an error message, then

View file

@ -472,9 +472,11 @@ struct CmdDevelop : Common, MixEnvironment
else { else {
script = "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc;\n" + script; script = "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc;\n" + script;
if (developSettings.bashPrompt != "") if (developSettings.bashPrompt != "")
script += fmt("[ -n \"$PS1\" ] && PS1=%s;\n", shellEscape(developSettings.bashPrompt)); script += fmt("[ -n \"$PS1\" ] && PS1=%s;\n",
shellEscape(developSettings.bashPrompt.get()));
if (developSettings.bashPromptSuffix != "") if (developSettings.bashPromptSuffix != "")
script += fmt("[ -n \"$PS1\" ] && PS1+=%s;\n", shellEscape(developSettings.bashPromptSuffix)); script += fmt("[ -n \"$PS1\" ] && PS1+=%s;\n",
shellEscape(developSettings.bashPromptSuffix.get()));
} }
writeFull(rcFileFd.get(), script); writeFull(rcFileFd.get(), script);

View file

@ -55,7 +55,7 @@ R""(
# nix develop /tmp/my-build-env # nix develop /tmp/my-build-env
``` ```
* Replace all occurences of the store path corresponding to * Replace all occurrences of the store path corresponding to
`glibc.dev` with a writable directory: `glibc.dev` with a writable directory:
```console ```console

View file

@ -107,7 +107,7 @@ struct CmdEval : MixJSON, InstallableCommand
else if (raw) { else if (raw) {
stopProgressBar(); stopProgressBar();
std::cout << state->coerceToString(noPos, *v, context); std::cout << *state->coerceToString(noPos, *v, context);
} }
else if (json) { else if (json) {

View file

@ -292,6 +292,12 @@ The following attributes are supported in `flake.nix`:
value (e.g. `packages.x86_64-linux` must be an attribute set of value (e.g. `packages.x86_64-linux` must be an attribute set of
derivations built for the `x86_64-linux` platform). derivations built for the `x86_64-linux` platform).
* `nixConfig`: a set of `nix.conf` options to be set when evaluating any
part of a flake. In the interests of security, only a small set of
whitelisted options (currently `bash-prompt`, `bash-prompt-suffix`,
and `flake-registry`) are allowed to be set without confirmation so long as
`accept-flake-config` is not set in the global configuration.
## Flake inputs ## Flake inputs
The attribute `inputs` specifies the dependencies of a flake, as an The attribute `inputs` specifies the dependencies of a flake, as an

View file

@ -76,7 +76,7 @@ the Nix store. Here are the recognised types of installables:
Note that the search will only include files indexed by git. In particular, files Note that the search will only include files indexed by git. In particular, files
which are matched by `.gitignore` or have never been `git add`-ed will not be which are matched by `.gitignore` or have never been `git add`-ed will not be
available in the flake. If this is undesireable, specify `path:<directory>` explicitly; available in the flake. If this is undesirable, specify `path:<directory>` explicitly;
For example, if `/foo/bar` is a git repository with the following structure: For example, if `/foo/bar` is a git repository with the following structure:
``` ```

View file

@ -38,7 +38,7 @@ string resolveMirrorUrl(EvalState & state, string url)
if (mirrorList->value->listSize() < 1) if (mirrorList->value->listSize() < 1)
throw Error("mirror URL '%s' did not expand to anything", url); throw Error("mirror URL '%s' did not expand to anything", url);
auto mirror = state.forceString(*mirrorList->value->listElems()[0]); string mirror(state.forceString(*mirrorList->value->listElems()[0]));
return mirror + (hasSuffix(mirror, "/") ? "" : "/") + string(s, p + 1); return mirror + (hasSuffix(mirror, "/") ? "" : "/") + string(s, p + 1);
} }

View file

@ -1,7 +1,7 @@
R"MdBoundary( R"MdBoundary(
# Description # Description
Display some informations about the given realisation Display some information about the given realisation
# Examples # Examples

View file

@ -463,7 +463,7 @@ bool NixRepl::processLine(string line)
if (v.type() == nPath || v.type() == nString) { if (v.type() == nPath || v.type() == nString) {
PathSet context; PathSet context;
auto filename = state->coerceToString(noPos, v, context); auto filename = state->coerceToString(noPos, v, context);
pos.file = state->symbols.create(filename); pos.file = state->symbols.create(*filename);
} else if (v.isLambda()) { } else if (v.isLambda()) {
pos = v.lambda.fun->pos; pos = v.lambda.fun->pos;
} else { } else {

View file

@ -107,7 +107,7 @@ Path resolveSymlink(const Path & path)
auto target = readLink(path); auto target = readLink(path);
return hasPrefix(target, "/") return hasPrefix(target, "/")
? target ? target
: dirOf(path) + "/" + target; : concatStrings(dirOf(path), "/", target);
} }
std::set<string> resolveTree(const Path & path, PathSet & deps) std::set<string> resolveTree(const Path & path, PathSet & deps)

View file

@ -50,6 +50,6 @@ with import ./config.nix;
fetchurl = import <nix/fetchurl.nix> { fetchurl = import <nix/fetchurl.nix> {
url = "file://" + toString ./lang/eval-okay-xml.exp.xml; url = "file://" + toString ./lang/eval-okay-xml.exp.xml;
sha256 = "0kg4sla7ihm8ijr8cb3117fhl99zrc2bwy1jrngsfmkh8bav4m0v"; sha256 = "sha256-behBlX+DQK/Pjvkuc8Tx68Jwi4E5v86wDq+ZLaHyhQE=";
}; };
} }

View file

@ -730,6 +730,7 @@ cat > $flakeFollowsB/flake.nix <<EOF
description = "Flake B"; description = "Flake B";
inputs = { inputs = {
foobar.url = "path:$flakeFollowsA/flakeE"; foobar.url = "path:$flakeFollowsA/flakeE";
goodoo.follows = "C/goodoo";
C = { C = {
url = "path:./flakeC"; url = "path:./flakeC";
inputs.foobar.follows = "foobar"; inputs.foobar.follows = "foobar";
@ -744,6 +745,7 @@ cat > $flakeFollowsC/flake.nix <<EOF
description = "Flake C"; description = "Flake C";
inputs = { inputs = {
foobar.url = "path:$flakeFollowsA/flakeE"; foobar.url = "path:$flakeFollowsA/flakeE";
goodoo.follows = "foobar";
}; };
outputs = { ... }: {}; outputs = { ... }: {};
} }
@ -759,7 +761,7 @@ EOF
cat > $flakeFollowsE/flake.nix <<EOF cat > $flakeFollowsE/flake.nix <<EOF
{ {
description = "Flake D"; description = "Flake E";
inputs = {}; inputs = {};
outputs = { ... }: {}; outputs = { ... }: {};
} }
@ -768,6 +770,8 @@ EOF
git -C $flakeFollowsA add flake.nix flakeB/flake.nix \ git -C $flakeFollowsA add flake.nix flakeB/flake.nix \
flakeB/flakeC/flake.nix flakeD/flake.nix flakeE/flake.nix flakeB/flakeC/flake.nix flakeD/flake.nix flakeE/flake.nix
nix flake metadata $flakeFollowsA
nix flake update $flakeFollowsA nix flake update $flakeFollowsA
oldLock="$(cat "$flakeFollowsA/flake.lock")" oldLock="$(cat "$flakeFollowsA/flake.lock")"

View file

@ -31,9 +31,9 @@
<attr name="f"> <attr name="f">
<function> <function>
<attrspat> <attrspat>
<attr name="z" />
<attr name="x" /> <attr name="x" />
<attr name="y" /> <attr name="y" />
<attr name="z" />
</attrspat> </attrspat>
</function> </function>
</attr> </attr>