mirror of
https://github.com/NixOS/nix.git
synced 2025-11-28 21:21:00 +01:00
Merge remote-tracking branch 'origin/master' into gdennis/git-filter-config
This commit is contained in:
commit
9bba7ac3e8
887 changed files with 34455 additions and 28681 deletions
|
|
@ -1,4 +1,5 @@
|
|||
project('nix-external-api-docs',
|
||||
project(
|
||||
'nix-external-api-docs',
|
||||
version : files('.version'),
|
||||
meson_version : '>= 1.1',
|
||||
license : 'LGPL-2.1-or-later',
|
||||
|
|
@ -10,7 +11,7 @@ doxygen_cfg = configure_file(
|
|||
input : 'doxygen.cfg.in',
|
||||
output : 'doxygen.cfg',
|
||||
configuration : {
|
||||
'PROJECT_NUMBER': meson.project_version(),
|
||||
'PROJECT_NUMBER' : meson.project_version(),
|
||||
'OUTPUT_DIRECTORY' : meson.current_build_dir(),
|
||||
'src' : fs.parent(fs.parent(meson.project_source_root())),
|
||||
},
|
||||
|
|
@ -20,7 +21,7 @@ doxygen = find_program('doxygen', native : true, required : true)
|
|||
|
||||
custom_target(
|
||||
'external-api-docs',
|
||||
command : [ doxygen , doxygen_cfg ],
|
||||
command : [ doxygen, doxygen_cfg ],
|
||||
input : [
|
||||
doxygen_cfg,
|
||||
],
|
||||
|
|
|
|||
|
|
@ -57,9 +57,7 @@ INPUT = \
|
|||
@src@/libutil/args \
|
||||
@src@/libutil-tests \
|
||||
@src@/libutil-test-support/tests \
|
||||
@src@/nix \
|
||||
@src@/nix-env \
|
||||
@src@/nix-store
|
||||
@src@/nix
|
||||
|
||||
# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
|
||||
# in the source code. If set to NO, only conditional compilation will be
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
project('nix-internal-api-docs',
|
||||
project(
|
||||
'nix-internal-api-docs',
|
||||
version : files('.version'),
|
||||
meson_version : '>= 1.1',
|
||||
license : 'LGPL-2.1-or-later',
|
||||
|
|
@ -10,7 +11,7 @@ doxygen_cfg = configure_file(
|
|||
input : 'doxygen.cfg.in',
|
||||
output : 'doxygen.cfg',
|
||||
configuration : {
|
||||
'PROJECT_NUMBER': meson.project_version(),
|
||||
'PROJECT_NUMBER' : meson.project_version(),
|
||||
'OUTPUT_DIRECTORY' : meson.current_build_dir(),
|
||||
'BUILD_ROOT' : meson.build_root(),
|
||||
'src' : fs.parent(fs.parent(meson.project_source_root())) / 'src',
|
||||
|
|
@ -21,7 +22,7 @@ doxygen = find_program('doxygen', native : true, required : true)
|
|||
|
||||
custom_target(
|
||||
'internal-api-docs',
|
||||
command : [ doxygen , doxygen_cfg ],
|
||||
command : [ doxygen, doxygen_cfg ],
|
||||
input : [
|
||||
doxygen_cfg,
|
||||
],
|
||||
|
|
|
|||
|
|
@ -10,23 +10,13 @@
|
|||
namespace nix {
|
||||
|
||||
// Custom implementation to avoid `ref` ptr equality
|
||||
GENERATE_CMP_EXT(
|
||||
,
|
||||
std::strong_ordering,
|
||||
SingleBuiltPathBuilt,
|
||||
*me->drvPath,
|
||||
me->output);
|
||||
GENERATE_CMP_EXT(, std::strong_ordering, SingleBuiltPathBuilt, *me->drvPath, me->output);
|
||||
|
||||
// Custom implementation to avoid `ref` ptr equality
|
||||
|
||||
// TODO no `GENERATE_CMP_EXT` because no `std::set::operator<=>` on
|
||||
// Darwin, per header.
|
||||
GENERATE_EQUAL(
|
||||
,
|
||||
BuiltPathBuilt ::,
|
||||
BuiltPathBuilt,
|
||||
*me->drvPath,
|
||||
me->outputs);
|
||||
GENERATE_EQUAL(, BuiltPathBuilt ::, BuiltPathBuilt, *me->drvPath, me->outputs);
|
||||
|
||||
StorePath SingleBuiltPath::outPath() const
|
||||
{
|
||||
|
|
@ -34,8 +24,8 @@ StorePath SingleBuiltPath::outPath() const
|
|||
overloaded{
|
||||
[](const SingleBuiltPath::Opaque & p) { return p.path; },
|
||||
[](const SingleBuiltPath::Built & b) { return b.output.second; },
|
||||
}, raw()
|
||||
);
|
||||
},
|
||||
raw());
|
||||
}
|
||||
|
||||
StorePathSet BuiltPath::outPaths() const
|
||||
|
|
@ -49,13 +39,13 @@ StorePathSet BuiltPath::outPaths() const
|
|||
res.insert(path);
|
||||
return res;
|
||||
},
|
||||
}, raw()
|
||||
);
|
||||
},
|
||||
raw());
|
||||
}
|
||||
|
||||
SingleDerivedPath::Built SingleBuiltPath::Built::discardOutputPath() const
|
||||
{
|
||||
return SingleDerivedPath::Built {
|
||||
return SingleDerivedPath::Built{
|
||||
.drvPath = make_ref<SingleDerivedPath>(drvPath->discardOutputPath()),
|
||||
.output = output.first,
|
||||
};
|
||||
|
|
@ -65,14 +55,10 @@ SingleDerivedPath SingleBuiltPath::discardOutputPath() const
|
|||
{
|
||||
return std::visit(
|
||||
overloaded{
|
||||
[](const SingleBuiltPath::Opaque & p) -> SingleDerivedPath {
|
||||
return p;
|
||||
},
|
||||
[](const SingleBuiltPath::Built & b) -> SingleDerivedPath {
|
||||
return b.discardOutputPath();
|
||||
},
|
||||
}, raw()
|
||||
);
|
||||
[](const SingleBuiltPath::Opaque & p) -> SingleDerivedPath { return p; },
|
||||
[](const SingleBuiltPath::Built & b) -> SingleDerivedPath { return b.discardOutputPath(); },
|
||||
},
|
||||
raw());
|
||||
}
|
||||
|
||||
nlohmann::json BuiltPath::Built::toJSON(const StoreDirConfig & store) const
|
||||
|
|
@ -97,16 +83,12 @@ nlohmann::json SingleBuiltPath::Built::toJSON(const StoreDirConfig & store) cons
|
|||
|
||||
nlohmann::json SingleBuiltPath::toJSON(const StoreDirConfig & store) const
|
||||
{
|
||||
return std::visit([&](const auto & buildable) {
|
||||
return buildable.toJSON(store);
|
||||
}, raw());
|
||||
return std::visit([&](const auto & buildable) { return buildable.toJSON(store); }, raw());
|
||||
}
|
||||
|
||||
nlohmann::json BuiltPath::toJSON(const StoreDirConfig & store) const
|
||||
{
|
||||
return std::visit([&](const auto & buildable) {
|
||||
return buildable.toJSON(store);
|
||||
}, raw());
|
||||
return std::visit([&](const auto & buildable) { return buildable.toJSON(store); }, raw());
|
||||
}
|
||||
|
||||
RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const
|
||||
|
|
@ -116,20 +98,18 @@ RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const
|
|||
overloaded{
|
||||
[&](const BuiltPath::Opaque & p) { res.insert(p.path); },
|
||||
[&](const BuiltPath::Built & p) {
|
||||
auto drvHashes =
|
||||
staticOutputHashes(store, store.readDerivation(p.drvPath->outPath()));
|
||||
for (auto& [outputName, outputPath] : p.outputs) {
|
||||
if (experimentalFeatureSettings.isEnabled(
|
||||
Xp::CaDerivations)) {
|
||||
auto drvHashes = staticOutputHashes(store, store.readDerivation(p.drvPath->outPath()));
|
||||
for (auto & [outputName, outputPath] : p.outputs) {
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
||||
auto drvOutput = get(drvHashes, outputName);
|
||||
if (!drvOutput)
|
||||
throw Error(
|
||||
"the derivation '%s' has unrealised output '%s' (derived-path.cc/toRealisedPaths)",
|
||||
store.printStorePath(p.drvPath->outPath()), outputName);
|
||||
auto thisRealisation = store.queryRealisation(
|
||||
DrvOutput{*drvOutput, outputName});
|
||||
assert(thisRealisation); // We’ve built it, so we must
|
||||
// have the realisation
|
||||
store.printStorePath(p.drvPath->outPath()),
|
||||
outputName);
|
||||
auto thisRealisation = store.queryRealisation(DrvOutput{*drvOutput, outputName});
|
||||
assert(thisRealisation); // We’ve built it, so we must
|
||||
// have the realisation
|
||||
res.insert(*thisRealisation);
|
||||
} else {
|
||||
res.insert(outputPath);
|
||||
|
|
@ -141,4 +121,4 @@ RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const
|
|||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -8,4 +8,4 @@ void InstallableValueCommand::run(ref<Store> store, ref<Installable> installable
|
|||
run(store, installableValue);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -402,4 +402,4 @@ void MixOutLinkBase::createOutLinksMaybe(const std::vector<BuiltPathWithResult>
|
|||
createOutLinks(outLink, toBuiltPaths(buildables), *store2);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -15,15 +15,15 @@
|
|||
#include "nix/fetchers/fetch-to-store.hh"
|
||||
#include "nix/cmd/compatibility-settings.hh"
|
||||
#include "nix/expr/eval-settings.hh"
|
||||
#include "nix/store/globals.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
fetchers::Settings fetchSettings;
|
||||
|
||||
static GlobalConfig::Register rFetchSettings(&fetchSettings);
|
||||
|
||||
EvalSettings evalSettings {
|
||||
EvalSettings evalSettings{
|
||||
settings.readOnlyMode,
|
||||
{
|
||||
{
|
||||
|
|
@ -31,7 +31,7 @@ EvalSettings evalSettings {
|
|||
[](EvalState & state, std::string_view rest) {
|
||||
experimentalFeatureSettings.require(Xp::Flakes);
|
||||
// FIXME `parseFlakeRef` should take a `std::string_view`.
|
||||
auto flakeRef = parseFlakeRef(fetchSettings, std::string { rest }, {}, true, false);
|
||||
auto flakeRef = parseFlakeRef(fetchSettings, std::string{rest}, {}, true, false);
|
||||
debug("fetching flake search path element '%s''", rest);
|
||||
auto [accessor, lockedRef] = flakeRef.resolve(state.store).lazyFetch(state.store);
|
||||
auto storePath = nix::fetchToStore(
|
||||
|
|
@ -49,17 +49,14 @@ EvalSettings evalSettings {
|
|||
|
||||
static GlobalConfig::Register rEvalSettings(&evalSettings);
|
||||
|
||||
|
||||
flake::Settings flakeSettings;
|
||||
|
||||
static GlobalConfig::Register rFlakeSettings(&flakeSettings);
|
||||
|
||||
|
||||
CompatibilitySettings compatibilitySettings {};
|
||||
CompatibilitySettings compatibilitySettings{};
|
||||
|
||||
static GlobalConfig::Register rCompatibilitySettings(&compatibilitySettings);
|
||||
|
||||
|
||||
MixEvalArgs::MixEvalArgs()
|
||||
{
|
||||
addFlag({
|
||||
|
|
@ -67,7 +64,9 @@ MixEvalArgs::MixEvalArgs()
|
|||
.description = "Pass the value *expr* as the argument *name* to Nix functions.",
|
||||
.category = category,
|
||||
.labels = {"name", "expr"},
|
||||
.handler = {[&](std::string name, std::string expr) { autoArgs.insert_or_assign(name, AutoArg{AutoArgExpr{expr}}); }},
|
||||
.handler = {[&](std::string name, std::string expr) {
|
||||
autoArgs.insert_or_assign(name, AutoArg{AutoArgExpr{expr}});
|
||||
}},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
|
|
@ -75,7 +74,9 @@ MixEvalArgs::MixEvalArgs()
|
|||
.description = "Pass the string *string* as the argument *name* to Nix functions.",
|
||||
.category = category,
|
||||
.labels = {"name", "string"},
|
||||
.handler = {[&](std::string name, std::string s) { autoArgs.insert_or_assign(name, AutoArg{AutoArgString{s}}); }},
|
||||
.handler = {[&](std::string name, std::string s) {
|
||||
autoArgs.insert_or_assign(name, AutoArg{AutoArgString{s}});
|
||||
}},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
|
|
@ -83,7 +84,9 @@ MixEvalArgs::MixEvalArgs()
|
|||
.description = "Pass the contents of file *path* as the argument *name* to Nix functions.",
|
||||
.category = category,
|
||||
.labels = {"name", "path"},
|
||||
.handler = {[&](std::string name, std::string path) { autoArgs.insert_or_assign(name, AutoArg{AutoArgFile{path}}); }},
|
||||
.handler = {[&](std::string name, std::string path) {
|
||||
autoArgs.insert_or_assign(name, AutoArg{AutoArgFile{path}});
|
||||
}},
|
||||
.completer = completePath,
|
||||
});
|
||||
|
||||
|
|
@ -107,18 +110,14 @@ MixEvalArgs::MixEvalArgs()
|
|||
)",
|
||||
.category = category,
|
||||
.labels = {"path"},
|
||||
.handler = {[&](std::string s) {
|
||||
lookupPath.elements.emplace_back(LookupPath::Elem::parse(s));
|
||||
}},
|
||||
.handler = {[&](std::string s) { lookupPath.elements.emplace_back(LookupPath::Elem::parse(s)); }},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "impure",
|
||||
.description = "Allow access to mutable paths and repositories.",
|
||||
.category = category,
|
||||
.handler = {[&]() {
|
||||
evalSettings.pureEval = false;
|
||||
}},
|
||||
.handler = {[&]() { evalSettings.pureEval = false; }},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
|
|
@ -130,7 +129,8 @@ MixEvalArgs::MixEvalArgs()
|
|||
auto from = parseFlakeRef(fetchSettings, _from, std::filesystem::current_path().string());
|
||||
auto to = parseFlakeRef(fetchSettings, _to, std::filesystem::current_path().string());
|
||||
fetchers::Attrs extraAttrs;
|
||||
if (to.subdir != "") extraAttrs["dir"] = to.subdir;
|
||||
if (to.subdir != "")
|
||||
extraAttrs["dir"] = to.subdir;
|
||||
fetchers::overrideRegistry(from.input, to.input, extraAttrs);
|
||||
}},
|
||||
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
|
||||
|
|
@ -141,7 +141,7 @@ MixEvalArgs::MixEvalArgs()
|
|||
addFlag({
|
||||
.longName = "eval-store",
|
||||
.description =
|
||||
R"(
|
||||
R"(
|
||||
The [URL of the Nix store](@docroot@/store/types/index.md#store-url-format)
|
||||
to use for evaluation, i.e. to store derivations (`.drv` files) and inputs referenced by them.
|
||||
)",
|
||||
|
|
@ -156,20 +156,21 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
|
|||
auto res = state.buildBindings(autoArgs.size());
|
||||
for (auto & [name, arg] : autoArgs) {
|
||||
auto v = state.allocValue();
|
||||
std::visit(overloaded {
|
||||
[&](const AutoArgExpr & arg) {
|
||||
state.mkThunk_(*v, state.parseExprFromString(arg.expr, compatibilitySettings.nixShellShebangArgumentsRelativeToScript ? state.rootPath(absPath(getCommandBaseDir())) : state.rootPath(".")));
|
||||
},
|
||||
[&](const AutoArgString & arg) {
|
||||
v->mkString(arg.s);
|
||||
},
|
||||
[&](const AutoArgFile & arg) {
|
||||
v->mkString(readFile(arg.path.string()));
|
||||
},
|
||||
[&](const AutoArgStdin & arg) {
|
||||
v->mkString(readFile(STDIN_FILENO));
|
||||
}
|
||||
}, arg);
|
||||
std::visit(
|
||||
overloaded{
|
||||
[&](const AutoArgExpr & arg) {
|
||||
state.mkThunk_(
|
||||
*v,
|
||||
state.parseExprFromString(
|
||||
arg.expr,
|
||||
compatibilitySettings.nixShellShebangArgumentsRelativeToScript
|
||||
? state.rootPath(absPath(getCommandBaseDir()))
|
||||
: state.rootPath(".")));
|
||||
},
|
||||
[&](const AutoArgString & arg) { v->mkString(arg.s); },
|
||||
[&](const AutoArgFile & arg) { v->mkString(readFile(arg.path.string())); },
|
||||
[&](const AutoArgStdin & arg) { v->mkString(readFile(STDIN_FILENO)); }},
|
||||
arg);
|
||||
res.insert(state.symbols.create(name), v);
|
||||
}
|
||||
return res.finish();
|
||||
|
|
@ -178,15 +179,8 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
|
|||
SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * baseDir)
|
||||
{
|
||||
if (EvalSettings::isPseudoUrl(s)) {
|
||||
auto accessor = fetchers::downloadTarball(
|
||||
state.store,
|
||||
state.fetchSettings,
|
||||
EvalSettings::resolvePseudoUrl(s));
|
||||
auto storePath = fetchToStore(
|
||||
state.fetchSettings,
|
||||
*state.store,
|
||||
SourcePath(accessor),
|
||||
FetchMode::Copy);
|
||||
auto accessor = fetchers::downloadTarball(state.store, state.fetchSettings, EvalSettings::resolvePseudoUrl(s));
|
||||
auto storePath = fetchToStore(state.fetchSettings, *state.store, SourcePath(accessor), FetchMode::Copy);
|
||||
return state.storePath(storePath);
|
||||
}
|
||||
|
||||
|
|
@ -195,11 +189,7 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * bas
|
|||
auto flakeRef = parseFlakeRef(fetchSettings, std::string(s.substr(6)), {}, true, false);
|
||||
auto [accessor, lockedRef] = flakeRef.resolve(state.store).lazyFetch(state.store);
|
||||
auto storePath = nix::fetchToStore(
|
||||
state.fetchSettings,
|
||||
*state.store,
|
||||
SourcePath(accessor),
|
||||
FetchMode::Copy,
|
||||
lockedRef.input.getName());
|
||||
state.fetchSettings, *state.store, SourcePath(accessor), FetchMode::Copy, lockedRef.input.getName());
|
||||
state.allowPath(storePath);
|
||||
return state.storePath(storePath);
|
||||
}
|
||||
|
|
@ -213,4 +203,4 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * bas
|
|||
return state.rootPath(baseDir ? absPath(s, *baseDir) : absPath(s));
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -11,14 +11,12 @@ Strings editorFor(const SourcePath & file, uint32_t line)
|
|||
throw Error("cannot open '%s' in an editor because it has no physical path", file);
|
||||
auto editor = getEnv("EDITOR").value_or("cat");
|
||||
auto args = tokenizeString<Strings>(editor);
|
||||
if (line > 0 && (
|
||||
editor.find("emacs") != std::string::npos ||
|
||||
editor.find("nano") != std::string::npos ||
|
||||
editor.find("vim") != std::string::npos ||
|
||||
editor.find("kak") != std::string::npos))
|
||||
if (line > 0
|
||||
&& (editor.find("emacs") != std::string::npos || editor.find("nano") != std::string::npos
|
||||
|| editor.find("vim") != std::string::npos || editor.find("kak") != std::string::npos))
|
||||
args.push_back(fmt("+%d", line));
|
||||
args.push_back(path->string());
|
||||
return args;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ namespace nix {
|
|||
|
||||
struct SingleBuiltPath;
|
||||
|
||||
struct SingleBuiltPathBuilt {
|
||||
struct SingleBuiltPathBuilt
|
||||
{
|
||||
ref<SingleBuiltPath> drvPath;
|
||||
std::pair<std::string, StorePath> output;
|
||||
|
||||
|
|
@ -18,26 +19,25 @@ struct SingleBuiltPathBuilt {
|
|||
static SingleBuiltPathBuilt parse(const StoreDirConfig & store, std::string_view, std::string_view);
|
||||
nlohmann::json toJSON(const StoreDirConfig & store) const;
|
||||
|
||||
bool operator ==(const SingleBuiltPathBuilt &) const noexcept;
|
||||
std::strong_ordering operator <=>(const SingleBuiltPathBuilt &) const noexcept;
|
||||
bool operator==(const SingleBuiltPathBuilt &) const noexcept;
|
||||
std::strong_ordering operator<=>(const SingleBuiltPathBuilt &) const noexcept;
|
||||
};
|
||||
|
||||
using _SingleBuiltPathRaw = std::variant<
|
||||
DerivedPathOpaque,
|
||||
SingleBuiltPathBuilt
|
||||
>;
|
||||
using _SingleBuiltPathRaw = std::variant<DerivedPathOpaque, SingleBuiltPathBuilt>;
|
||||
|
||||
struct SingleBuiltPath : _SingleBuiltPathRaw {
|
||||
struct SingleBuiltPath : _SingleBuiltPathRaw
|
||||
{
|
||||
using Raw = _SingleBuiltPathRaw;
|
||||
using Raw::Raw;
|
||||
|
||||
using Opaque = DerivedPathOpaque;
|
||||
using Built = SingleBuiltPathBuilt;
|
||||
|
||||
bool operator == (const SingleBuiltPath &) const = default;
|
||||
auto operator <=> (const SingleBuiltPath &) const = default;
|
||||
bool operator==(const SingleBuiltPath &) const = default;
|
||||
auto operator<=>(const SingleBuiltPath &) const = default;
|
||||
|
||||
inline const Raw & raw() const {
|
||||
inline const Raw & raw() const
|
||||
{
|
||||
return static_cast<const Raw &>(*this);
|
||||
}
|
||||
|
||||
|
|
@ -51,7 +51,7 @@ struct SingleBuiltPath : _SingleBuiltPathRaw {
|
|||
|
||||
static inline ref<SingleBuiltPath> staticDrv(StorePath drvPath)
|
||||
{
|
||||
return make_ref<SingleBuiltPath>(SingleBuiltPath::Opaque { drvPath });
|
||||
return make_ref<SingleBuiltPath>(SingleBuiltPath::Opaque{drvPath});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -59,40 +59,41 @@ static inline ref<SingleBuiltPath> staticDrv(StorePath drvPath)
|
|||
*
|
||||
* See 'BuiltPath' for more an explanation.
|
||||
*/
|
||||
struct BuiltPathBuilt {
|
||||
struct BuiltPathBuilt
|
||||
{
|
||||
ref<SingleBuiltPath> drvPath;
|
||||
std::map<std::string, StorePath> outputs;
|
||||
|
||||
bool operator == (const BuiltPathBuilt &) const noexcept;
|
||||
bool operator==(const BuiltPathBuilt &) const noexcept;
|
||||
// TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet.
|
||||
//std::strong_ordering operator <=> (const BuiltPathBuilt &) const noexcept;
|
||||
// std::strong_ordering operator <=> (const BuiltPathBuilt &) const noexcept;
|
||||
|
||||
std::string to_string(const StoreDirConfig & store) const;
|
||||
static BuiltPathBuilt parse(const StoreDirConfig & store, std::string_view, std::string_view);
|
||||
nlohmann::json toJSON(const StoreDirConfig & store) const;
|
||||
};
|
||||
|
||||
using _BuiltPathRaw = std::variant<
|
||||
DerivedPath::Opaque,
|
||||
BuiltPathBuilt
|
||||
>;
|
||||
using _BuiltPathRaw = std::variant<DerivedPath::Opaque, BuiltPathBuilt>;
|
||||
|
||||
/**
|
||||
* A built path. Similar to a DerivedPath, but enriched with the corresponding
|
||||
* output path(s).
|
||||
*/
|
||||
struct BuiltPath : _BuiltPathRaw {
|
||||
struct BuiltPath : _BuiltPathRaw
|
||||
{
|
||||
using Raw = _BuiltPathRaw;
|
||||
using Raw::Raw;
|
||||
|
||||
using Opaque = DerivedPathOpaque;
|
||||
using Built = BuiltPathBuilt;
|
||||
|
||||
bool operator == (const BuiltPath &) const = default;
|
||||
// TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet.
|
||||
//auto operator <=> (const BuiltPath &) const = default;
|
||||
bool operator==(const BuiltPath &) const = default;
|
||||
|
||||
inline const Raw & raw() const {
|
||||
// TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet.
|
||||
// auto operator <=> (const BuiltPath &) const = default;
|
||||
|
||||
inline const Raw & raw() const
|
||||
{
|
||||
return static_cast<const Raw &>(*this);
|
||||
}
|
||||
|
||||
|
|
@ -104,4 +105,4 @@ struct BuiltPath : _BuiltPathRaw {
|
|||
|
||||
typedef std::vector<BuiltPath> BuiltPaths;
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -20,4 +20,4 @@ struct InstallableValueCommand : InstallableCommand
|
|||
void run(ref<Store> store, ref<Installable> installable) override;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -13,13 +13,17 @@ namespace nix {
|
|||
|
||||
class Store;
|
||||
|
||||
namespace fetchers { struct Settings; }
|
||||
namespace fetchers {
|
||||
struct Settings;
|
||||
}
|
||||
|
||||
class EvalState;
|
||||
struct CompatibilitySettings;
|
||||
class Bindings;
|
||||
|
||||
namespace flake { struct Settings; }
|
||||
namespace flake {
|
||||
struct Settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo Get rid of global settings variables
|
||||
|
|
@ -54,10 +58,23 @@ struct MixEvalArgs : virtual Args, virtual MixRepair
|
|||
std::optional<std::string> evalStoreUrl;
|
||||
|
||||
private:
|
||||
struct AutoArgExpr { std::string expr; };
|
||||
struct AutoArgString { std::string s; };
|
||||
struct AutoArgFile { std::filesystem::path path; };
|
||||
struct AutoArgStdin { };
|
||||
struct AutoArgExpr
|
||||
{
|
||||
std::string expr;
|
||||
};
|
||||
|
||||
struct AutoArgString
|
||||
{
|
||||
std::string s;
|
||||
};
|
||||
|
||||
struct AutoArgFile
|
||||
{
|
||||
std::filesystem::path path;
|
||||
};
|
||||
|
||||
struct AutoArgStdin
|
||||
{};
|
||||
|
||||
using AutoArg = std::variant<AutoArgExpr, AutoArgString, AutoArgFile, AutoArgStdin>;
|
||||
|
||||
|
|
@ -65,8 +82,8 @@ private:
|
|||
};
|
||||
|
||||
/**
|
||||
* @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory)
|
||||
* @param baseDir Optional [base directory](https://nix.dev/manual/nix/development/glossary#gloss-base-directory)
|
||||
*/
|
||||
SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * baseDir = nullptr);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -33,4 +33,4 @@ struct CompatibilitySettings : public Config
|
|||
)"};
|
||||
};
|
||||
|
||||
};
|
||||
}; // namespace nix
|
||||
|
|
|
|||
|
|
@ -12,4 +12,4 @@ namespace nix {
|
|||
*/
|
||||
Strings editorFor(const SourcePath & file, uint32_t line);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -39,7 +39,10 @@ class InstallableAttrPath : public InstallableValue
|
|||
const std::string & attrPath,
|
||||
ExtendedOutputsSpec extendedOutputsSpec);
|
||||
|
||||
std::string what() const override { return attrPath; };
|
||||
std::string what() const override
|
||||
{
|
||||
return attrPath;
|
||||
};
|
||||
|
||||
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
|
||||
|
||||
|
|
@ -55,4 +58,4 @@ public:
|
|||
ExtendedOutputsSpec extendedOutputsSpec);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -11,8 +11,10 @@ struct InstallableDerivedPath : Installable
|
|||
DerivedPath derivedPath;
|
||||
|
||||
InstallableDerivedPath(ref<Store> store, DerivedPath && derivedPath)
|
||||
: store(store), derivedPath(std::move(derivedPath))
|
||||
{ }
|
||||
: store(store)
|
||||
, derivedPath(std::move(derivedPath))
|
||||
{
|
||||
}
|
||||
|
||||
std::string what() const override;
|
||||
|
||||
|
|
@ -20,10 +22,8 @@ struct InstallableDerivedPath : Installable
|
|||
|
||||
std::optional<StorePath> getStorePath() override;
|
||||
|
||||
static InstallableDerivedPath parse(
|
||||
ref<Store> store,
|
||||
std::string_view prefix,
|
||||
ExtendedOutputsSpec extendedOutputsSpec);
|
||||
static InstallableDerivedPath
|
||||
parse(ref<Store> store, std::string_view prefix, ExtendedOutputsSpec extendedOutputsSpec);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ struct ExtraPathInfoFlake : ExtraPathInfoValue
|
|||
/**
|
||||
* Extra struct to get around C++ designated initializer limitations
|
||||
*/
|
||||
struct Flake {
|
||||
struct Flake
|
||||
{
|
||||
FlakeRef originalRef;
|
||||
FlakeRef lockedRef;
|
||||
};
|
||||
|
|
@ -26,8 +27,10 @@ struct ExtraPathInfoFlake : ExtraPathInfoValue
|
|||
Flake flake;
|
||||
|
||||
ExtraPathInfoFlake(Value && v, Flake && f)
|
||||
: ExtraPathInfoValue(std::move(v)), flake(std::move(f))
|
||||
{ }
|
||||
: ExtraPathInfoValue(std::move(v))
|
||||
, flake(std::move(f))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct InstallableFlake : InstallableValue
|
||||
|
|
@ -49,7 +52,10 @@ struct InstallableFlake : InstallableValue
|
|||
Strings prefixes,
|
||||
const flake::LockFlags & lockFlags);
|
||||
|
||||
std::string what() const override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }
|
||||
std::string what() const override
|
||||
{
|
||||
return flakeRef.to_string() + "#" + *attrPaths.begin();
|
||||
}
|
||||
|
||||
std::vector<std::string> getActualAttrPaths();
|
||||
|
||||
|
|
@ -61,8 +67,7 @@ struct InstallableFlake : InstallableValue
|
|||
* Get a cursor to every attrpath in getActualAttrPaths() that
|
||||
* exists. However if none exists, throw an exception.
|
||||
*/
|
||||
std::vector<ref<eval_cache::AttrCursor>>
|
||||
getCursors(EvalState & state) override;
|
||||
std::vector<ref<eval_cache::AttrCursor>> getCursors(EvalState & state) override;
|
||||
|
||||
std::shared_ptr<flake::LockedFlake> getLockedFlake() const;
|
||||
|
||||
|
|
@ -79,11 +84,9 @@ struct InstallableFlake : InstallableValue
|
|||
*/
|
||||
static inline FlakeRef defaultNixpkgsFlakeRef()
|
||||
{
|
||||
return FlakeRef::fromAttrs(fetchSettings, {{"type","indirect"}, {"id", "nixpkgs"}});
|
||||
return FlakeRef::fromAttrs(fetchSettings, {{"type", "indirect"}, {"id", "nixpkgs"}});
|
||||
}
|
||||
|
||||
ref<eval_cache::EvalCache> openEvalCache(
|
||||
EvalState & state,
|
||||
std::shared_ptr<flake::LockedFlake> lockedFlake);
|
||||
ref<eval_cache::EvalCache> openEvalCache(EvalState & state, std::shared_ptr<flake::LockedFlake> lockedFlake);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ namespace nix {
|
|||
struct PackageInfo;
|
||||
struct SourceExprCommand;
|
||||
|
||||
namespace eval_cache { class EvalCache; class AttrCursor; }
|
||||
namespace eval_cache {
|
||||
class EvalCache;
|
||||
class AttrCursor;
|
||||
} // namespace eval_cache
|
||||
|
||||
struct App
|
||||
{
|
||||
|
|
@ -37,7 +40,8 @@ struct ExtraPathInfoValue : ExtraPathInfo
|
|||
/**
|
||||
* Extra struct to get around C++ designated initializer limitations
|
||||
*/
|
||||
struct Value {
|
||||
struct Value
|
||||
{
|
||||
/**
|
||||
* An optional priority for use with "build envs". See Package
|
||||
*/
|
||||
|
|
@ -61,7 +65,8 @@ struct ExtraPathInfoValue : ExtraPathInfo
|
|||
|
||||
ExtraPathInfoValue(Value && v)
|
||||
: value(std::move(v))
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~ExtraPathInfoValue() = default;
|
||||
};
|
||||
|
|
@ -74,9 +79,12 @@ struct InstallableValue : Installable
|
|||
{
|
||||
ref<EvalState> state;
|
||||
|
||||
InstallableValue(ref<EvalState> state) : state(state) {}
|
||||
InstallableValue(ref<EvalState> state)
|
||||
: state(state)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~InstallableValue() { }
|
||||
virtual ~InstallableValue() {}
|
||||
|
||||
virtual std::pair<Value *, PosIdx> toValue(EvalState & state) = 0;
|
||||
|
||||
|
|
@ -85,15 +93,13 @@ struct InstallableValue : Installable
|
|||
* However if none exists, throw exception instead of returning
|
||||
* empty vector.
|
||||
*/
|
||||
virtual std::vector<ref<eval_cache::AttrCursor>>
|
||||
getCursors(EvalState & state);
|
||||
virtual std::vector<ref<eval_cache::AttrCursor>> getCursors(EvalState & state);
|
||||
|
||||
/**
|
||||
* Get the first and most preferred cursor this Installable could
|
||||
* refer to, or throw an exception if none exists.
|
||||
*/
|
||||
virtual ref<eval_cache::AttrCursor>
|
||||
getCursor(EvalState & state);
|
||||
virtual ref<eval_cache::AttrCursor> getCursor(EvalState & state);
|
||||
|
||||
UnresolvedApp toApp(EvalState & state);
|
||||
|
||||
|
|
@ -116,7 +122,8 @@ protected:
|
|||
* @result A derived path (with empty info, for now) if the value
|
||||
* matched the above criteria.
|
||||
*/
|
||||
std::optional<DerivedPathWithInfo> trySinglePathToDerivedPaths(Value & v, const PosIdx pos, std::string_view errorCtx);
|
||||
std::optional<DerivedPathWithInfo>
|
||||
trySinglePathToDerivedPaths(Value & v, const PosIdx pos, std::string_view errorCtx);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ typedef std::vector<ref<Installable>> Installables;
|
|||
*/
|
||||
struct Installable
|
||||
{
|
||||
virtual ~Installable() { }
|
||||
virtual ~Installable() {}
|
||||
|
||||
/**
|
||||
* What Installable is this?
|
||||
|
|
@ -168,37 +168,19 @@ struct Installable
|
|||
BuildMode bMode = bmNormal);
|
||||
|
||||
static std::set<StorePath> toStorePathSet(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
Realise mode,
|
||||
OperateOn operateOn,
|
||||
const Installables & installables);
|
||||
ref<Store> evalStore, ref<Store> store, Realise mode, OperateOn operateOn, const Installables & installables);
|
||||
|
||||
static std::vector<StorePath> toStorePaths(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
Realise mode,
|
||||
OperateOn operateOn,
|
||||
const Installables & installables);
|
||||
ref<Store> evalStore, ref<Store> store, Realise mode, OperateOn operateOn, const Installables & installables);
|
||||
|
||||
static StorePath toStorePath(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
Realise mode,
|
||||
OperateOn operateOn,
|
||||
ref<Installable> installable);
|
||||
ref<Store> evalStore, ref<Store> store, Realise mode, OperateOn operateOn, ref<Installable> installable);
|
||||
|
||||
static std::set<StorePath> toDerivations(
|
||||
ref<Store> store,
|
||||
const Installables & installables,
|
||||
bool useDeriver = false);
|
||||
static std::set<StorePath>
|
||||
toDerivations(ref<Store> store, const Installables & installables, bool useDeriver = false);
|
||||
|
||||
static BuiltPaths toBuiltPaths(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
Realise mode,
|
||||
OperateOn operateOn,
|
||||
const Installables & installables);
|
||||
ref<Store> evalStore, ref<Store> store, Realise mode, OperateOn operateOn, const Installables & installables);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -7,13 +7,14 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
typedef std::function<void(int, char * *)> MainFunction;
|
||||
typedef std::function<void(int, char **)> MainFunction;
|
||||
|
||||
struct RegisterLegacyCommand
|
||||
{
|
||||
typedef std::map<std::string, MainFunction> Commands;
|
||||
|
||||
static Commands & commands() {
|
||||
static Commands & commands()
|
||||
{
|
||||
static Commands commands;
|
||||
return commands;
|
||||
}
|
||||
|
|
@ -24,4 +25,4 @@ struct RegisterLegacyCommand
|
|||
}
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -14,4 +14,4 @@ namespace nix {
|
|||
*/
|
||||
std::string renderMarkdownToTerminal(std::string_view markdown);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Public headers directory
|
||||
|
||||
include_dirs = [include_directories('../..')]
|
||||
include_dirs = [ include_directories('../..') ]
|
||||
|
||||
headers = files(
|
||||
'built-path.hh',
|
||||
|
|
|
|||
|
|
@ -4,18 +4,22 @@
|
|||
namespace nix::flag {
|
||||
|
||||
Args::Flag hashAlgo(std::string && longName, HashAlgorithm * ha);
|
||||
|
||||
static inline Args::Flag hashAlgo(HashAlgorithm * ha)
|
||||
{
|
||||
return hashAlgo("hash-algo", ha);
|
||||
}
|
||||
|
||||
Args::Flag hashAlgoOpt(std::string && longName, std::optional<HashAlgorithm> * oha);
|
||||
Args::Flag hashFormatWithDefault(std::string && longName, HashFormat * hf);
|
||||
Args::Flag hashFormatOpt(std::string && longName, std::optional<HashFormat> * ohf);
|
||||
|
||||
static inline Args::Flag hashAlgoOpt(std::optional<HashAlgorithm> * oha)
|
||||
{
|
||||
return hashAlgoOpt("hash-algo", oha);
|
||||
}
|
||||
|
||||
Args::Flag fileIngestionMethod(FileIngestionMethod * method);
|
||||
Args::Flag contentAddressMethod(ContentAddressMethod * method);
|
||||
|
||||
}
|
||||
} // namespace nix::flag
|
||||
|
|
|
|||
|
|
@ -19,4 +19,4 @@ extern const StringSet networkProxyVariables;
|
|||
*/
|
||||
bool haveNetworkProxyConnection();
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -11,10 +11,11 @@ namespace nix {
|
|||
namespace detail {
|
||||
/** Provides the completion hooks for the repl, without exposing its complete
|
||||
* internals. */
|
||||
struct ReplCompleterMixin {
|
||||
struct ReplCompleterMixin
|
||||
{
|
||||
virtual StringSet completePrefix(const std::string & prefix) = 0;
|
||||
};
|
||||
};
|
||||
}; // namespace detail
|
||||
|
||||
enum class ReplPromptType {
|
||||
ReplPrompt,
|
||||
|
|
@ -29,7 +30,7 @@ public:
|
|||
virtual Guard init(detail::ReplCompleterMixin * repl) = 0;
|
||||
/** Returns a boolean of whether the interacter got EOF */
|
||||
virtual bool getLine(std::string & input, ReplPromptType promptType) = 0;
|
||||
virtual ~ReplInteracter(){};
|
||||
virtual ~ReplInteracter() {};
|
||||
};
|
||||
|
||||
class ReadlineLikeInteracter : public virtual ReplInteracter
|
||||
|
|
@ -40,9 +41,10 @@ public:
|
|||
: historyFile(historyFile)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Guard init(detail::ReplCompleterMixin * repl) override;
|
||||
virtual bool getLine(std::string & input, ReplPromptType promptType) override;
|
||||
virtual ~ReadlineLikeInteracter() override;
|
||||
};
|
||||
|
||||
};
|
||||
}; // namespace nix
|
||||
|
|
|
|||
|
|
@ -12,12 +12,12 @@ struct AbstractNixRepl
|
|||
|
||||
AbstractNixRepl(ref<EvalState> state)
|
||||
: state(state)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~AbstractNixRepl()
|
||||
{ }
|
||||
virtual ~AbstractNixRepl() {}
|
||||
|
||||
typedef std::vector<std::pair<Value*,std::string>> AnnotatedValues;
|
||||
typedef std::vector<std::pair<Value *, std::string>> AnnotatedValues;
|
||||
|
||||
using RunNix = void(Path program, const Strings & args, const std::optional<std::string> & input);
|
||||
|
||||
|
|
@ -33,13 +33,11 @@ struct AbstractNixRepl
|
|||
std::function<AnnotatedValues()> getValues,
|
||||
RunNix * runNix = nullptr);
|
||||
|
||||
static ReplExitStatus runSimple(
|
||||
ref<EvalState> evalState,
|
||||
const ValMap & extraEnv);
|
||||
static ReplExitStatus runSimple(ref<EvalState> evalState, const ValMap & extraEnv);
|
||||
|
||||
virtual void initEnv() = 0;
|
||||
|
||||
virtual ReplExitStatus mainLoop() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ InstallableAttrPath::InstallableAttrPath(
|
|||
, v(allocRootValue(v))
|
||||
, attrPath(attrPath)
|
||||
, extendedOutputsSpec(std::move(extendedOutputsSpec))
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
std::pair<Value *, PosIdx> InstallableAttrPath::toValue(EvalState & state)
|
||||
{
|
||||
|
|
@ -48,12 +49,9 @@ DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths()
|
|||
{
|
||||
auto [v, pos] = toValue(*state);
|
||||
|
||||
if (std::optional derivedPathWithInfo = trySinglePathToDerivedPaths(
|
||||
*v,
|
||||
pos,
|
||||
fmt("while evaluating the attribute '%s'", attrPath)))
|
||||
{
|
||||
return { *derivedPathWithInfo };
|
||||
if (std::optional derivedPathWithInfo =
|
||||
trySinglePathToDerivedPaths(*v, pos, fmt("while evaluating the attribute '%s'", attrPath))) {
|
||||
return {*derivedPathWithInfo};
|
||||
}
|
||||
|
||||
Bindings & autoArgs = *cmd.getAutoArgs(*state);
|
||||
|
|
@ -70,19 +68,19 @@ DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths()
|
|||
if (!drvPath)
|
||||
throw Error("'%s' is not a derivation", what());
|
||||
|
||||
auto newOutputs = std::visit(overloaded {
|
||||
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
|
||||
StringSet outputsToInstall;
|
||||
for (auto & output : packageInfo.queryOutputs(false, true))
|
||||
outputsToInstall.insert(output.first);
|
||||
if (outputsToInstall.empty())
|
||||
outputsToInstall.insert("out");
|
||||
return OutputsSpec::Names { std::move(outputsToInstall) };
|
||||
auto newOutputs = std::visit(
|
||||
overloaded{
|
||||
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
|
||||
StringSet outputsToInstall;
|
||||
for (auto & output : packageInfo.queryOutputs(false, true))
|
||||
outputsToInstall.insert(output.first);
|
||||
if (outputsToInstall.empty())
|
||||
outputsToInstall.insert("out");
|
||||
return OutputsSpec::Names{std::move(outputsToInstall)};
|
||||
},
|
||||
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec { return e; },
|
||||
},
|
||||
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec {
|
||||
return e;
|
||||
},
|
||||
}, extendedOutputsSpec.raw);
|
||||
extendedOutputsSpec.raw);
|
||||
|
||||
auto [iter, didInsert] = byDrvPath.emplace(*drvPath, newOutputs);
|
||||
|
||||
|
|
@ -93,11 +91,12 @@ DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths()
|
|||
DerivedPathsWithInfo res;
|
||||
for (auto & [drvPath, outputs] : byDrvPath)
|
||||
res.push_back({
|
||||
.path = DerivedPath::Built {
|
||||
.drvPath = makeConstantStorePathRef(drvPath),
|
||||
.outputs = outputs,
|
||||
},
|
||||
.info = make_ref<ExtraPathInfoValue>(ExtraPathInfoValue::Value {
|
||||
.path =
|
||||
DerivedPath::Built{
|
||||
.drvPath = makeConstantStorePathRef(drvPath),
|
||||
.outputs = outputs,
|
||||
},
|
||||
.info = make_ref<ExtraPathInfoValue>(ExtraPathInfoValue::Value{
|
||||
.extendedOutputsSpec = outputs,
|
||||
/* FIXME: reconsider backwards compatibility above
|
||||
so we can fill in this info. */
|
||||
|
|
@ -115,10 +114,12 @@ InstallableAttrPath InstallableAttrPath::parse(
|
|||
ExtendedOutputsSpec extendedOutputsSpec)
|
||||
{
|
||||
return {
|
||||
state, cmd, v,
|
||||
prefix == "." ? "" : std::string { prefix },
|
||||
state,
|
||||
cmd,
|
||||
v,
|
||||
prefix == "." ? "" : std::string{prefix},
|
||||
std::move(extendedOutputsSpec),
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -21,35 +21,35 @@ std::optional<StorePath> InstallableDerivedPath::getStorePath()
|
|||
return derivedPath.getBaseStorePath();
|
||||
}
|
||||
|
||||
InstallableDerivedPath InstallableDerivedPath::parse(
|
||||
ref<Store> store,
|
||||
std::string_view prefix,
|
||||
ExtendedOutputsSpec extendedOutputsSpec)
|
||||
InstallableDerivedPath
|
||||
InstallableDerivedPath::parse(ref<Store> store, std::string_view prefix, ExtendedOutputsSpec extendedOutputsSpec)
|
||||
{
|
||||
auto derivedPath = std::visit(overloaded {
|
||||
// If the user did not use ^, we treat the output more
|
||||
// liberally: we accept a symlink chain or an actual
|
||||
// store path.
|
||||
[&](const ExtendedOutputsSpec::Default &) -> DerivedPath {
|
||||
auto storePath = store->followLinksToStorePath(prefix);
|
||||
return DerivedPath::Opaque {
|
||||
.path = std::move(storePath),
|
||||
};
|
||||
auto derivedPath = std::visit(
|
||||
overloaded{
|
||||
// If the user did not use ^, we treat the output more
|
||||
// liberally: we accept a symlink chain or an actual
|
||||
// store path.
|
||||
[&](const ExtendedOutputsSpec::Default &) -> DerivedPath {
|
||||
auto storePath = store->followLinksToStorePath(prefix);
|
||||
return DerivedPath::Opaque{
|
||||
.path = std::move(storePath),
|
||||
};
|
||||
},
|
||||
// If the user did use ^, we just do exactly what is written.
|
||||
[&](const ExtendedOutputsSpec::Explicit & outputSpec) -> DerivedPath {
|
||||
auto drv = make_ref<SingleDerivedPath>(SingleDerivedPath::parse(*store, prefix));
|
||||
drvRequireExperiment(*drv);
|
||||
return DerivedPath::Built{
|
||||
.drvPath = std::move(drv),
|
||||
.outputs = outputSpec,
|
||||
};
|
||||
},
|
||||
},
|
||||
// If the user did use ^, we just do exactly what is written.
|
||||
[&](const ExtendedOutputsSpec::Explicit & outputSpec) -> DerivedPath {
|
||||
auto drv = make_ref<SingleDerivedPath>(SingleDerivedPath::parse(*store, prefix));
|
||||
drvRequireExperiment(*drv);
|
||||
return DerivedPath::Built {
|
||||
.drvPath = std::move(drv),
|
||||
.outputs = outputSpec,
|
||||
};
|
||||
},
|
||||
}, extendedOutputsSpec.raw);
|
||||
return InstallableDerivedPath {
|
||||
extendedOutputsSpec.raw);
|
||||
return InstallableDerivedPath{
|
||||
store,
|
||||
std::move(derivedPath),
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ namespace nix {
|
|||
std::vector<std::string> InstallableFlake::getActualAttrPaths()
|
||||
{
|
||||
std::vector<std::string> res;
|
||||
if (attrPaths.size() == 1 && attrPaths.front().starts_with(".")){
|
||||
attrPaths.front().erase(0,1);
|
||||
if (attrPaths.size() == 1 && attrPaths.front().starts_with(".")) {
|
||||
attrPaths.front().erase(0, 1);
|
||||
res.push_back(attrPaths.front());
|
||||
return res;
|
||||
}
|
||||
|
|
@ -47,8 +47,11 @@ static std::string showAttrPaths(const std::vector<std::string> & paths)
|
|||
{
|
||||
std::string s;
|
||||
for (const auto & [n, i] : enumerate(paths)) {
|
||||
if (n > 0) s += n + 1 == paths.size() ? " or " : ", ";
|
||||
s += '\''; s += i; s += '\'';
|
||||
if (n > 0)
|
||||
s += n + 1 == paths.size() ? " or " : ", ";
|
||||
s += '\'';
|
||||
s += i;
|
||||
s += '\'';
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
|
@ -62,12 +65,12 @@ InstallableFlake::InstallableFlake(
|
|||
Strings attrPaths,
|
||||
Strings prefixes,
|
||||
const flake::LockFlags & lockFlags)
|
||||
: InstallableValue(state),
|
||||
flakeRef(flakeRef),
|
||||
attrPaths(fragment == "" ? attrPaths : Strings{(std::string) fragment}),
|
||||
prefixes(fragment == "" ? Strings{} : prefixes),
|
||||
extendedOutputsSpec(std::move(extendedOutputsSpec)),
|
||||
lockFlags(lockFlags)
|
||||
: InstallableValue(state)
|
||||
, flakeRef(flakeRef)
|
||||
, attrPaths(fragment == "" ? attrPaths : Strings{(std::string) fragment})
|
||||
, prefixes(fragment == "" ? Strings{} : prefixes)
|
||||
, extendedOutputsSpec(std::move(extendedOutputsSpec))
|
||||
, lockFlags(lockFlags)
|
||||
{
|
||||
if (cmd && cmd->getAutoArgs(*state)->size())
|
||||
throw UsageError("'--arg' and '--argstr' are incompatible with flakes");
|
||||
|
|
@ -87,18 +90,14 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
|
|||
auto v = attr->forceValue();
|
||||
|
||||
if (std::optional derivedPathWithInfo = trySinglePathToDerivedPaths(
|
||||
v,
|
||||
noPos,
|
||||
fmt("while evaluating the flake output attribute '%s'", attrPath)))
|
||||
{
|
||||
return { *derivedPathWithInfo };
|
||||
v, noPos, fmt("while evaluating the flake output attribute '%s'", attrPath))) {
|
||||
return {*derivedPathWithInfo};
|
||||
} else {
|
||||
throw Error(
|
||||
"expected flake output attribute '%s' to be a derivation or path but found %s: %s",
|
||||
attrPath,
|
||||
showType(v),
|
||||
ValuePrinter(*this->state, v, errorPrintOptions)
|
||||
);
|
||||
ValuePrinter(*this->state, v, errorPrintOptions));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -106,46 +105,47 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
|
|||
|
||||
std::optional<NixInt::Inner> priority;
|
||||
|
||||
if (attr->maybeGetAttr(state->sOutputSpecified)) {
|
||||
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
|
||||
if (attr->maybeGetAttr(state->s.outputSpecified)) {
|
||||
} else if (auto aMeta = attr->maybeGetAttr(state->s.meta)) {
|
||||
if (auto aPriority = aMeta->maybeGetAttr("priority"))
|
||||
priority = aPriority->getInt().value;
|
||||
}
|
||||
|
||||
return {{
|
||||
.path = DerivedPath::Built {
|
||||
.drvPath = makeConstantStorePathRef(std::move(drvPath)),
|
||||
.outputs = std::visit(overloaded {
|
||||
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
|
||||
StringSet outputsToInstall;
|
||||
if (auto aOutputSpecified = attr->maybeGetAttr(state->sOutputSpecified)) {
|
||||
if (aOutputSpecified->getBool()) {
|
||||
if (auto aOutputName = attr->maybeGetAttr("outputName"))
|
||||
outputsToInstall = { aOutputName->getString() };
|
||||
}
|
||||
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
|
||||
if (auto aOutputsToInstall = aMeta->maybeGetAttr("outputsToInstall"))
|
||||
for (auto & s : aOutputsToInstall->getListOfStrings())
|
||||
outputsToInstall.insert(s);
|
||||
}
|
||||
.path =
|
||||
DerivedPath::Built{
|
||||
.drvPath = makeConstantStorePathRef(std::move(drvPath)),
|
||||
.outputs = std::visit(
|
||||
overloaded{
|
||||
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
|
||||
StringSet outputsToInstall;
|
||||
if (auto aOutputSpecified = attr->maybeGetAttr(state->s.outputSpecified)) {
|
||||
if (aOutputSpecified->getBool()) {
|
||||
if (auto aOutputName = attr->maybeGetAttr("outputName"))
|
||||
outputsToInstall = {aOutputName->getString()};
|
||||
}
|
||||
} else if (auto aMeta = attr->maybeGetAttr(state->s.meta)) {
|
||||
if (auto aOutputsToInstall = aMeta->maybeGetAttr("outputsToInstall"))
|
||||
for (auto & s : aOutputsToInstall->getListOfStrings())
|
||||
outputsToInstall.insert(s);
|
||||
}
|
||||
|
||||
if (outputsToInstall.empty())
|
||||
outputsToInstall.insert("out");
|
||||
if (outputsToInstall.empty())
|
||||
outputsToInstall.insert("out");
|
||||
|
||||
return OutputsSpec::Names { std::move(outputsToInstall) };
|
||||
},
|
||||
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec {
|
||||
return e;
|
||||
},
|
||||
}, extendedOutputsSpec.raw),
|
||||
},
|
||||
return OutputsSpec::Names{std::move(outputsToInstall)};
|
||||
},
|
||||
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec { return e; },
|
||||
},
|
||||
extendedOutputsSpec.raw),
|
||||
},
|
||||
.info = make_ref<ExtraPathInfoFlake>(
|
||||
ExtraPathInfoValue::Value {
|
||||
ExtraPathInfoValue::Value{
|
||||
.priority = priority,
|
||||
.attrPath = attrPath,
|
||||
.extendedOutputsSpec = extendedOutputsSpec,
|
||||
},
|
||||
ExtraPathInfoFlake::Flake {
|
||||
ExtraPathInfoFlake::Flake{
|
||||
.originalRef = flakeRef,
|
||||
.lockedRef = getLockedFlake()->flake.lockedRef,
|
||||
}),
|
||||
|
|
@ -157,8 +157,7 @@ std::pair<Value *, PosIdx> InstallableFlake::toValue(EvalState & state)
|
|||
return {&getCursor(state)->forceValue(), noPos};
|
||||
}
|
||||
|
||||
std::vector<ref<eval_cache::AttrCursor>>
|
||||
InstallableFlake::getCursors(EvalState & state)
|
||||
std::vector<ref<eval_cache::AttrCursor>> InstallableFlake::getCursors(EvalState & state)
|
||||
{
|
||||
auto evalCache = openEvalCache(state, getLockedFlake());
|
||||
|
||||
|
|
@ -181,11 +180,7 @@ InstallableFlake::getCursors(EvalState & state)
|
|||
}
|
||||
|
||||
if (res.size() == 0)
|
||||
throw Error(
|
||||
suggestions,
|
||||
"flake '%s' does not provide attribute %s",
|
||||
flakeRef,
|
||||
showAttrPaths(attrPaths));
|
||||
throw Error(suggestions, "flake '%s' does not provide attribute %s", flakeRef, showAttrPaths(attrPaths));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
@ -196,8 +191,8 @@ std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
|
|||
flake::LockFlags lockFlagsApplyConfig = lockFlags;
|
||||
// FIXME why this side effect?
|
||||
lockFlagsApplyConfig.applyNixConfig = true;
|
||||
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(
|
||||
flakeSettings, *state, flakeRef, lockFlagsApplyConfig));
|
||||
_lockedFlake =
|
||||
std::make_shared<flake::LockedFlake>(lockFlake(flakeSettings, *state, flakeRef, lockFlagsApplyConfig));
|
||||
}
|
||||
return _lockedFlake;
|
||||
}
|
||||
|
|
@ -216,4 +211,4 @@ FlakeRef InstallableFlake::nixpkgsFlakeRef() const
|
|||
return defaultNixpkgsFlakeRef();
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -4,17 +4,14 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
std::vector<ref<eval_cache::AttrCursor>>
|
||||
InstallableValue::getCursors(EvalState & state)
|
||||
std::vector<ref<eval_cache::AttrCursor>> InstallableValue::getCursors(EvalState & state)
|
||||
{
|
||||
auto evalCache =
|
||||
std::make_shared<nix::eval_cache::EvalCache>(std::nullopt, state,
|
||||
[&]() { return toValue(state).first; });
|
||||
std::make_shared<nix::eval_cache::EvalCache>(std::nullopt, state, [&]() { return toValue(state).first; });
|
||||
return {evalCache->getRoot()};
|
||||
}
|
||||
|
||||
ref<eval_cache::AttrCursor>
|
||||
InstallableValue::getCursor(EvalState & state)
|
||||
ref<eval_cache::AttrCursor> InstallableValue::getCursor(EvalState & state)
|
||||
{
|
||||
/* Although getCursors should return at least one element, in case it doesn't,
|
||||
bound check to avoid an undefined behavior for vector[0] */
|
||||
|
|
@ -39,30 +36,32 @@ ref<InstallableValue> InstallableValue::require(ref<Installable> installable)
|
|||
auto castedInstallable = installable.dynamic_pointer_cast<InstallableValue>();
|
||||
if (!castedInstallable)
|
||||
throw nonValueInstallable(*installable);
|
||||
return ref { castedInstallable };
|
||||
return ref{castedInstallable};
|
||||
}
|
||||
|
||||
std::optional<DerivedPathWithInfo> InstallableValue::trySinglePathToDerivedPaths(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||
std::optional<DerivedPathWithInfo>
|
||||
InstallableValue::trySinglePathToDerivedPaths(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||
{
|
||||
if (v.type() == nPath) {
|
||||
auto storePath = fetchToStore(state->fetchSettings, *state->store, v.path(), FetchMode::Copy);
|
||||
return {{
|
||||
.path = DerivedPath::Opaque {
|
||||
.path = std::move(storePath),
|
||||
},
|
||||
.path =
|
||||
DerivedPath::Opaque{
|
||||
.path = std::move(storePath),
|
||||
},
|
||||
.info = make_ref<ExtraPathInfo>(),
|
||||
}};
|
||||
}
|
||||
|
||||
else if (v.type() == nString) {
|
||||
return {{
|
||||
.path = DerivedPath::fromSingle(
|
||||
state->coerceToSingleDerivedPath(pos, v, errorCtx)),
|
||||
.path = DerivedPath::fromSingle(state->coerceToSingleDerivedPath(pos, v, errorCtx)),
|
||||
.info = make_ref<ExtraPathInfo>(),
|
||||
}};
|
||||
}
|
||||
|
||||
else return std::nullopt;
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -61,7 +61,8 @@ MixFlakeOptions::MixFlakeOptions()
|
|||
.category = category,
|
||||
.handler = {[&]() {
|
||||
lockFlags.recreateLockFile = true;
|
||||
warn("'--recreate-lock-file' is deprecated and will be removed in a future version; use 'nix flake update' instead.");
|
||||
warn(
|
||||
"'--recreate-lock-file' is deprecated and will be removed in a future version; use 'nix flake update' instead.");
|
||||
}},
|
||||
});
|
||||
|
||||
|
|
@ -158,9 +159,7 @@ MixFlakeOptions::MixFlakeOptions()
|
|||
.description = "Write the given lock file instead of `flake.lock` within the top-level flake.",
|
||||
.category = category,
|
||||
.labels = {"flake-lock-path"},
|
||||
.handler = {[&](std::string lockFilePath) {
|
||||
lockFlags.outputLockFilePath = lockFilePath;
|
||||
}},
|
||||
.handler = {[&](std::string lockFilePath) { lockFlags.outputLockFilePath = lockFilePath; }},
|
||||
.completer = completePath,
|
||||
});
|
||||
|
||||
|
|
@ -175,14 +174,20 @@ MixFlakeOptions::MixFlakeOptions()
|
|||
flakeSettings,
|
||||
*evalState,
|
||||
parseFlakeRef(fetchSettings, flakeRef, absPath(getCommandBaseDir())),
|
||||
{ .writeLockFile = false });
|
||||
{.writeLockFile = false});
|
||||
for (auto & [inputName, input] : flake.lockFile.root->inputs) {
|
||||
auto input2 = flake.lockFile.findInput({inputName}); // resolve 'follows' nodes
|
||||
if (auto input3 = std::dynamic_pointer_cast<const flake::LockedNode>(input2)) {
|
||||
fetchers::Attrs extraAttrs;
|
||||
|
||||
if (!input3->lockedRef.subdir.empty()) {
|
||||
extraAttrs["dir"] = input3->lockedRef.subdir;
|
||||
}
|
||||
|
||||
overrideRegistry(
|
||||
fetchers::Input::fromAttrs(fetchSettings, {{"type","indirect"}, {"id", inputName}}),
|
||||
fetchers::Input::fromAttrs(fetchSettings, {{"type", "indirect"}, {"id", inputName}}),
|
||||
input3->lockedRef.input,
|
||||
{});
|
||||
extraAttrs);
|
||||
}
|
||||
}
|
||||
}},
|
||||
|
|
@ -209,7 +214,8 @@ SourceExprCommand::SourceExprCommand()
|
|||
|
||||
addFlag({
|
||||
.longName = "expr",
|
||||
.description = "Interpret [*installables*](@docroot@/command-ref/new-cli/nix.md#installables) as attribute paths relative to the Nix expression *expr*.",
|
||||
.description =
|
||||
"Interpret [*installables*](@docroot@/command-ref/new-cli/nix.md#installables) as attribute paths relative to the Nix expression *expr*.",
|
||||
.category = installablesCategory,
|
||||
.labels = {"expr"},
|
||||
.handler = {&expr},
|
||||
|
|
@ -220,32 +226,26 @@ MixReadOnlyOption::MixReadOnlyOption()
|
|||
{
|
||||
addFlag({
|
||||
.longName = "read-only",
|
||||
.description =
|
||||
"Do not instantiate each evaluated derivation. "
|
||||
"This improves performance, but can cause errors when accessing "
|
||||
"store paths of derivations during evaluation.",
|
||||
.description = "Do not instantiate each evaluated derivation. "
|
||||
"This improves performance, but can cause errors when accessing "
|
||||
"store paths of derivations during evaluation.",
|
||||
.handler = {&settings.readOnlyMode, true},
|
||||
});
|
||||
}
|
||||
|
||||
Strings SourceExprCommand::getDefaultFlakeAttrPaths()
|
||||
{
|
||||
return {
|
||||
"packages." + settings.thisSystem.get() + ".default",
|
||||
"defaultPackage." + settings.thisSystem.get()
|
||||
};
|
||||
return {"packages." + settings.thisSystem.get() + ".default", "defaultPackage." + settings.thisSystem.get()};
|
||||
}
|
||||
|
||||
Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes()
|
||||
{
|
||||
return {
|
||||
// As a convenience, look for the attribute in
|
||||
// 'outputs.packages'.
|
||||
"packages." + settings.thisSystem.get() + ".",
|
||||
// As a temporary hack until Nixpkgs is properly converted
|
||||
// to provide a clean 'packages' set, look in 'legacyPackages'.
|
||||
"legacyPackages." + settings.thisSystem.get() + "."
|
||||
};
|
||||
return {// As a convenience, look for the attribute in
|
||||
// 'outputs.packages'.
|
||||
"packages." + settings.thisSystem.get() + ".",
|
||||
// As a temporary hack until Nixpkgs is properly converted
|
||||
// to provide a clean 'packages' set, look in 'legacyPackages'.
|
||||
"legacyPackages." + settings.thisSystem.get() + "."};
|
||||
}
|
||||
|
||||
Args::CompleterClosure SourceExprCommand::getCompleteInstallable()
|
||||
|
|
@ -263,10 +263,7 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s
|
|||
|
||||
evalSettings.pureEval = false;
|
||||
auto state = getEvalState();
|
||||
auto e =
|
||||
state->parseExprFromFile(
|
||||
resolveExprPath(
|
||||
lookupFileArg(*state, *file)));
|
||||
auto e = state->parseExprFromFile(resolveExprPath(lookupFileArg(*state, *file)));
|
||||
|
||||
Value root;
|
||||
state->eval(e, root);
|
||||
|
|
@ -285,7 +282,7 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s
|
|||
}
|
||||
|
||||
auto [v, pos] = findAlongAttrPath(*state, prefix_, *autoArgs, root);
|
||||
Value &v1(*v);
|
||||
Value & v1(*v);
|
||||
state->forceValue(v1, pos);
|
||||
Value v2;
|
||||
state->autoCallFunction(*autoArgs, v1, v2);
|
||||
|
|
@ -310,7 +307,7 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s
|
|||
getDefaultFlakeAttrPaths(),
|
||||
prefix);
|
||||
}
|
||||
} catch (EvalError&) {
|
||||
} catch (EvalError &) {
|
||||
// Don't want eval errors to mess-up with the completion engine, so let's just swallow them
|
||||
}
|
||||
}
|
||||
|
|
@ -334,22 +331,23 @@ void completeFlakeRefWithFragment(
|
|||
|
||||
auto fragment = prefix.substr(hash + 1);
|
||||
std::string prefixRoot = "";
|
||||
if (fragment.starts_with(".")){
|
||||
if (fragment.starts_with(".")) {
|
||||
fragment = fragment.substr(1);
|
||||
prefixRoot = ".";
|
||||
}
|
||||
auto flakeRefS = std::string(prefix.substr(0, hash));
|
||||
|
||||
// TODO: ideally this would use the command base directory instead of assuming ".".
|
||||
auto flakeRef = parseFlakeRef(fetchSettings, expandTilde(flakeRefS), std::filesystem::current_path().string());
|
||||
auto flakeRef =
|
||||
parseFlakeRef(fetchSettings, expandTilde(flakeRefS), std::filesystem::current_path().string());
|
||||
|
||||
auto evalCache = openEvalCache(*evalState,
|
||||
std::make_shared<flake::LockedFlake>(lockFlake(
|
||||
flakeSettings, *evalState, flakeRef, lockFlags)));
|
||||
auto evalCache = openEvalCache(
|
||||
*evalState,
|
||||
std::make_shared<flake::LockedFlake>(lockFlake(flakeSettings, *evalState, flakeRef, lockFlags)));
|
||||
|
||||
auto root = evalCache->getRoot();
|
||||
|
||||
if (prefixRoot == "."){
|
||||
if (prefixRoot == ".") {
|
||||
attrPathPrefixes.clear();
|
||||
}
|
||||
/* Complete 'fragment' relative to all the
|
||||
|
|
@ -369,7 +367,8 @@ void completeFlakeRefWithFragment(
|
|||
}
|
||||
|
||||
auto attr = root->findAlongAttrPath(attrPath);
|
||||
if (!attr) continue;
|
||||
if (!attr)
|
||||
continue;
|
||||
|
||||
for (auto & attr2 : (*attr)->getAttrs()) {
|
||||
if (hasPrefix(evalState->symbols[attr2], lastAttr)) {
|
||||
|
|
@ -377,7 +376,9 @@ void completeFlakeRefWithFragment(
|
|||
/* Strip the attrpath prefix. */
|
||||
attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size());
|
||||
// FIXME: handle names with dots
|
||||
completions.add(flakeRefS + "#" + prefixRoot + concatStringsSep(".", evalState->symbols.resolve(attrPath2)));
|
||||
completions.add(
|
||||
flakeRefS + "#" + prefixRoot
|
||||
+ concatStringsSep(".", evalState->symbols.resolve(attrPath2)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -387,7 +388,8 @@ void completeFlakeRefWithFragment(
|
|||
if (fragment.empty()) {
|
||||
for (auto & attrPath : defaultFlakeAttrPaths) {
|
||||
auto attr = root->findAlongAttrPath(parseAttrPath(*evalState, attrPath));
|
||||
if (!attr) continue;
|
||||
if (!attr)
|
||||
continue;
|
||||
completions.add(flakeRefS + "#" + prefixRoot);
|
||||
}
|
||||
}
|
||||
|
|
@ -427,14 +429,12 @@ DerivedPathWithInfo Installable::toDerivedPath()
|
|||
{
|
||||
auto buildables = toDerivedPaths();
|
||||
if (buildables.size() != 1)
|
||||
throw Error("installable '%s' evaluates to %d derivations, where only one is expected", what(), buildables.size());
|
||||
throw Error(
|
||||
"installable '%s' evaluates to %d derivations, where only one is expected", what(), buildables.size());
|
||||
return std::move(buildables[0]);
|
||||
}
|
||||
|
||||
static StorePath getDeriver(
|
||||
ref<Store> store,
|
||||
const Installable & i,
|
||||
const StorePath & drvPath)
|
||||
static StorePath getDeriver(ref<Store> store, const Installable & i, const StorePath & drvPath)
|
||||
{
|
||||
auto derivers = store->queryValidDerivers(drvPath);
|
||||
if (derivers.empty())
|
||||
|
|
@ -443,35 +443,35 @@ static StorePath getDeriver(
|
|||
return *derivers.begin();
|
||||
}
|
||||
|
||||
ref<eval_cache::EvalCache> openEvalCache(
|
||||
EvalState & state,
|
||||
std::shared_ptr<flake::LockedFlake> lockedFlake)
|
||||
ref<eval_cache::EvalCache> openEvalCache(EvalState & state, std::shared_ptr<flake::LockedFlake> lockedFlake)
|
||||
{
|
||||
auto fingerprint = evalSettings.useEvalCache && evalSettings.pureEval
|
||||
? lockedFlake->getFingerprint(state.store, state.fetchSettings)
|
||||
: std::nullopt;
|
||||
auto rootLoader = [&state, lockedFlake]()
|
||||
{
|
||||
/* For testing whether the evaluation cache is
|
||||
complete. */
|
||||
if (getEnv("NIX_ALLOW_EVAL").value_or("1") == "0")
|
||||
throw Error("not everything is cached, but evaluation is not allowed");
|
||||
? lockedFlake->getFingerprint(state.store, state.fetchSettings)
|
||||
: std::nullopt;
|
||||
auto rootLoader = [&state, lockedFlake]() {
|
||||
/* For testing whether the evaluation cache is
|
||||
complete. */
|
||||
if (getEnv("NIX_ALLOW_EVAL").value_or("1") == "0")
|
||||
throw Error("not everything is cached, but evaluation is not allowed");
|
||||
|
||||
auto vFlake = state.allocValue();
|
||||
flake::callFlake(state, *lockedFlake, *vFlake);
|
||||
auto vFlake = state.allocValue();
|
||||
flake::callFlake(state, *lockedFlake, *vFlake);
|
||||
|
||||
state.forceAttrs(*vFlake, noPos, "while parsing cached flake data");
|
||||
state.forceAttrs(*vFlake, noPos, "while parsing cached flake data");
|
||||
|
||||
auto aOutputs = vFlake->attrs()->get(state.symbols.create("outputs"));
|
||||
assert(aOutputs);
|
||||
auto aOutputs = vFlake->attrs()->get(state.symbols.create("outputs"));
|
||||
assert(aOutputs);
|
||||
|
||||
return aOutputs->value;
|
||||
};
|
||||
return aOutputs->value;
|
||||
};
|
||||
|
||||
if (fingerprint) {
|
||||
auto search = state.evalCaches.find(fingerprint.value());
|
||||
if (search == state.evalCaches.end()) {
|
||||
search = state.evalCaches.emplace(fingerprint.value(), make_ref<nix::eval_cache::EvalCache>(fingerprint, state, rootLoader)).first;
|
||||
search =
|
||||
state.evalCaches
|
||||
.emplace(fingerprint.value(), make_ref<nix::eval_cache::EvalCache>(fingerprint, state, rootLoader))
|
||||
.first;
|
||||
}
|
||||
return search->second;
|
||||
} else {
|
||||
|
|
@ -479,8 +479,7 @@ ref<eval_cache::EvalCache> openEvalCache(
|
|||
}
|
||||
}
|
||||
|
||||
Installables SourceExprCommand::parseInstallables(
|
||||
ref<Store> store, std::vector<std::string> ss)
|
||||
Installables SourceExprCommand::parseInstallables(ref<Store> store, std::vector<std::string> ss)
|
||||
{
|
||||
Installables result;
|
||||
|
||||
|
|
@ -501,12 +500,10 @@ Installables SourceExprCommand::parseInstallables(
|
|||
if (file == "-") {
|
||||
auto e = state->parseStdin();
|
||||
state->eval(e, *vFile);
|
||||
}
|
||||
else if (file) {
|
||||
} else if (file) {
|
||||
auto dir = absPath(getCommandBaseDir());
|
||||
state->evalFile(lookupFileArg(*state, *file, &dir), *vFile);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
Path dir = absPath(getCommandBaseDir());
|
||||
auto e = state->parseExprFromString(*expr, state->rootPath(dir));
|
||||
state->eval(e, *vFile);
|
||||
|
|
@ -515,9 +512,8 @@ Installables SourceExprCommand::parseInstallables(
|
|||
for (auto & s : ss) {
|
||||
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(s);
|
||||
result.push_back(
|
||||
make_ref<InstallableAttrPath>(
|
||||
InstallableAttrPath::parse(
|
||||
state, *this, vFile, std::move(prefix), std::move(extendedOutputsSpec))));
|
||||
make_ref<InstallableAttrPath>(InstallableAttrPath::parse(
|
||||
state, *this, vFile, std::move(prefix), std::move(extendedOutputsSpec))));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
|
@ -532,8 +528,9 @@ Installables SourceExprCommand::parseInstallables(
|
|||
|
||||
if (prefix.find('/') != std::string::npos) {
|
||||
try {
|
||||
result.push_back(make_ref<InstallableDerivedPath>(
|
||||
InstallableDerivedPath::parse(store, prefix, extendedOutputsSpec.raw)));
|
||||
result.push_back(
|
||||
make_ref<InstallableDerivedPath>(
|
||||
InstallableDerivedPath::parse(store, prefix, extendedOutputsSpec.raw)));
|
||||
continue;
|
||||
} catch (BadStorePath &) {
|
||||
} catch (...) {
|
||||
|
|
@ -543,9 +540,10 @@ Installables SourceExprCommand::parseInstallables(
|
|||
}
|
||||
|
||||
try {
|
||||
auto [flakeRef, fragment] = parseFlakeRefWithFragment(
|
||||
fetchSettings, std::string { prefix }, absPath(getCommandBaseDir()));
|
||||
result.push_back(make_ref<InstallableFlake>(
|
||||
auto [flakeRef, fragment] =
|
||||
parseFlakeRefWithFragment(fetchSettings, std::string{prefix}, absPath(getCommandBaseDir()));
|
||||
result.push_back(
|
||||
make_ref<InstallableFlake>(
|
||||
this,
|
||||
getEvalState(),
|
||||
std::move(flakeRef),
|
||||
|
|
@ -566,8 +564,7 @@ Installables SourceExprCommand::parseInstallables(
|
|||
return result;
|
||||
}
|
||||
|
||||
ref<Installable> SourceExprCommand::parseInstallable(
|
||||
ref<Store> store, const std::string & installable)
|
||||
ref<Installable> SourceExprCommand::parseInstallable(ref<Store> store, const std::string & installable)
|
||||
{
|
||||
auto installables = parseInstallables(store, {installable});
|
||||
assert(installables.size() == 1);
|
||||
|
|
@ -578,20 +575,18 @@ static SingleBuiltPath getBuiltPath(ref<Store> evalStore, ref<Store> store, cons
|
|||
{
|
||||
return std::visit(
|
||||
overloaded{
|
||||
[&](const SingleDerivedPath::Opaque & bo) -> SingleBuiltPath {
|
||||
return SingleBuiltPath::Opaque { bo.path };
|
||||
},
|
||||
[&](const SingleDerivedPath::Opaque & bo) -> SingleBuiltPath { return SingleBuiltPath::Opaque{bo.path}; },
|
||||
[&](const SingleDerivedPath::Built & bfd) -> SingleBuiltPath {
|
||||
auto drvPath = getBuiltPath(evalStore, store, *bfd.drvPath);
|
||||
// Resolving this instead of `bfd` will yield the same result, but avoid duplicative work.
|
||||
SingleDerivedPath::Built truncatedBfd {
|
||||
SingleDerivedPath::Built truncatedBfd{
|
||||
.drvPath = makeConstantStorePathRef(drvPath.outPath()),
|
||||
.output = bfd.output,
|
||||
};
|
||||
auto outputPath = resolveDerivedPath(*store, truncatedBfd, &*evalStore);
|
||||
return SingleBuiltPath::Built {
|
||||
return SingleBuiltPath::Built{
|
||||
.drvPath = make_ref<SingleBuiltPath>(std::move(drvPath)),
|
||||
.output = { bfd.output, outputPath },
|
||||
.output = {bfd.output, outputPath},
|
||||
};
|
||||
},
|
||||
},
|
||||
|
|
@ -599,11 +594,7 @@ static SingleBuiltPath getBuiltPath(ref<Store> evalStore, ref<Store> store, cons
|
|||
}
|
||||
|
||||
std::vector<BuiltPathWithResult> Installable::build(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
Realise mode,
|
||||
const Installables & installables,
|
||||
BuildMode bMode)
|
||||
ref<Store> evalStore, ref<Store> store, Realise mode, const Installables & installables, BuildMode bMode)
|
||||
{
|
||||
std::vector<BuiltPathWithResult> res;
|
||||
for (auto & [_, builtPathWithResult] : build2(evalStore, store, mode, installables, bMode))
|
||||
|
|
@ -611,9 +602,7 @@ std::vector<BuiltPathWithResult> Installable::build(
|
|||
return res;
|
||||
}
|
||||
|
||||
static void throwBuildErrors(
|
||||
std::vector<KeyedBuildResult> & buildResults,
|
||||
const Store & store)
|
||||
static void throwBuildErrors(std::vector<KeyedBuildResult> & buildResults, const Store & store)
|
||||
{
|
||||
std::vector<KeyedBuildResult> failed;
|
||||
for (auto & buildResult : buildResults) {
|
||||
|
|
@ -630,10 +619,11 @@ static void throwBuildErrors(
|
|||
StringSet failedPaths;
|
||||
for (; failedResult != failed.end(); failedResult++) {
|
||||
if (!failedResult->errorMsg.empty()) {
|
||||
logError(ErrorInfo{
|
||||
.level = lvlError,
|
||||
.msg = failedResult->errorMsg,
|
||||
});
|
||||
logError(
|
||||
ErrorInfo{
|
||||
.level = lvlError,
|
||||
.msg = failedResult->errorMsg,
|
||||
});
|
||||
}
|
||||
failedPaths.insert(failedResult->path.to_string(store));
|
||||
}
|
||||
|
|
@ -643,11 +633,7 @@ static void throwBuildErrors(
|
|||
}
|
||||
|
||||
std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> Installable::build2(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
Realise mode,
|
||||
const Installables & installables,
|
||||
BuildMode bMode)
|
||||
ref<Store> evalStore, ref<Store> store, Realise mode, const Installables & installables, BuildMode bMode)
|
||||
{
|
||||
if (mode == Realise::Nothing)
|
||||
settings.readOnlyMode = true;
|
||||
|
|
@ -678,22 +664,25 @@ std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> Installable::build
|
|||
|
||||
for (auto & path : pathsToBuild) {
|
||||
for (auto & aux : backmap[path]) {
|
||||
std::visit(overloaded {
|
||||
[&](const DerivedPath::Built & bfd) {
|
||||
auto outputs = resolveDerivedPath(*store, bfd, &*evalStore);
|
||||
res.push_back({aux.installable, {
|
||||
.path = BuiltPath::Built {
|
||||
.drvPath = make_ref<SingleBuiltPath>(getBuiltPath(evalStore, store, *bfd.drvPath)),
|
||||
.outputs = outputs,
|
||||
},
|
||||
.info = aux.info}});
|
||||
std::visit(
|
||||
overloaded{
|
||||
[&](const DerivedPath::Built & bfd) {
|
||||
auto outputs = resolveDerivedPath(*store, bfd, &*evalStore);
|
||||
res.push_back(
|
||||
{aux.installable,
|
||||
{.path =
|
||||
BuiltPath::Built{
|
||||
.drvPath =
|
||||
make_ref<SingleBuiltPath>(getBuiltPath(evalStore, store, *bfd.drvPath)),
|
||||
.outputs = outputs,
|
||||
},
|
||||
.info = aux.info}});
|
||||
},
|
||||
[&](const DerivedPath::Opaque & bo) {
|
||||
res.push_back({aux.installable, {.path = BuiltPath::Opaque{bo.path}, .info = aux.info}});
|
||||
},
|
||||
},
|
||||
[&](const DerivedPath::Opaque & bo) {
|
||||
res.push_back({aux.installable, {
|
||||
.path = BuiltPath::Opaque { bo.path },
|
||||
.info = aux.info}});
|
||||
},
|
||||
}, path.raw());
|
||||
path.raw());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -707,26 +696,30 @@ std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> Installable::build
|
|||
throwBuildErrors(buildResults, *store);
|
||||
for (auto & buildResult : buildResults) {
|
||||
for (auto & aux : backmap[buildResult.path]) {
|
||||
std::visit(overloaded {
|
||||
[&](const DerivedPath::Built & bfd) {
|
||||
std::map<std::string, StorePath> outputs;
|
||||
for (auto & [outputName, realisation] : buildResult.builtOutputs)
|
||||
outputs.emplace(outputName, realisation.outPath);
|
||||
res.push_back({aux.installable, {
|
||||
.path = BuiltPath::Built {
|
||||
.drvPath = make_ref<SingleBuiltPath>(getBuiltPath(evalStore, store, *bfd.drvPath)),
|
||||
.outputs = outputs,
|
||||
},
|
||||
.info = aux.info,
|
||||
.result = buildResult}});
|
||||
std::visit(
|
||||
overloaded{
|
||||
[&](const DerivedPath::Built & bfd) {
|
||||
std::map<std::string, StorePath> outputs;
|
||||
for (auto & [outputName, realisation] : buildResult.builtOutputs)
|
||||
outputs.emplace(outputName, realisation.outPath);
|
||||
res.push_back(
|
||||
{aux.installable,
|
||||
{.path =
|
||||
BuiltPath::Built{
|
||||
.drvPath =
|
||||
make_ref<SingleBuiltPath>(getBuiltPath(evalStore, store, *bfd.drvPath)),
|
||||
.outputs = outputs,
|
||||
},
|
||||
.info = aux.info,
|
||||
.result = buildResult}});
|
||||
},
|
||||
[&](const DerivedPath::Opaque & bo) {
|
||||
res.push_back(
|
||||
{aux.installable,
|
||||
{.path = BuiltPath::Opaque{bo.path}, .info = aux.info, .result = buildResult}});
|
||||
},
|
||||
},
|
||||
[&](const DerivedPath::Opaque & bo) {
|
||||
res.push_back({aux.installable, {
|
||||
.path = BuiltPath::Opaque { bo.path },
|
||||
.info = aux.info,
|
||||
.result = buildResult}});
|
||||
},
|
||||
}, buildResult.path.raw());
|
||||
buildResult.path.raw());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -741,11 +734,7 @@ std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> Installable::build
|
|||
}
|
||||
|
||||
BuiltPaths Installable::toBuiltPaths(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
Realise mode,
|
||||
OperateOn operateOn,
|
||||
const Installables & installables)
|
||||
ref<Store> evalStore, ref<Store> store, Realise mode, OperateOn operateOn, const Installables & installables)
|
||||
{
|
||||
if (operateOn == OperateOn::Output) {
|
||||
BuiltPaths res;
|
||||
|
|
@ -764,10 +753,7 @@ BuiltPaths Installable::toBuiltPaths(
|
|||
}
|
||||
|
||||
StorePathSet Installable::toStorePathSet(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
Realise mode, OperateOn operateOn,
|
||||
const Installables & installables)
|
||||
ref<Store> evalStore, ref<Store> store, Realise mode, OperateOn operateOn, const Installables & installables)
|
||||
{
|
||||
StorePathSet outPaths;
|
||||
for (auto & path : toBuiltPaths(evalStore, store, mode, operateOn, installables)) {
|
||||
|
|
@ -778,10 +764,7 @@ StorePathSet Installable::toStorePathSet(
|
|||
}
|
||||
|
||||
StorePaths Installable::toStorePaths(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
Realise mode, OperateOn operateOn,
|
||||
const Installables & installables)
|
||||
ref<Store> evalStore, ref<Store> store, Realise mode, OperateOn operateOn, const Installables & installables)
|
||||
{
|
||||
StorePaths outPaths;
|
||||
for (auto & path : toBuiltPaths(evalStore, store, mode, operateOn, installables)) {
|
||||
|
|
@ -792,10 +775,7 @@ StorePaths Installable::toStorePaths(
|
|||
}
|
||||
|
||||
StorePath Installable::toStorePath(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
Realise mode, OperateOn operateOn,
|
||||
ref<Installable> installable)
|
||||
ref<Store> evalStore, ref<Store> store, Realise mode, OperateOn operateOn, ref<Installable> installable)
|
||||
{
|
||||
auto paths = toStorePathSet(evalStore, store, mode, operateOn, {installable});
|
||||
|
||||
|
|
@ -805,28 +785,23 @@ StorePath Installable::toStorePath(
|
|||
return *paths.begin();
|
||||
}
|
||||
|
||||
StorePathSet Installable::toDerivations(
|
||||
ref<Store> store,
|
||||
const Installables & installables,
|
||||
bool useDeriver)
|
||||
StorePathSet Installable::toDerivations(ref<Store> store, const Installables & installables, bool useDeriver)
|
||||
{
|
||||
StorePathSet drvPaths;
|
||||
|
||||
for (const auto & i : installables)
|
||||
for (const auto & b : i->toDerivedPaths())
|
||||
std::visit(overloaded {
|
||||
[&](const DerivedPath::Opaque & bo) {
|
||||
drvPaths.insert(
|
||||
bo.path.isDerivation()
|
||||
? bo.path
|
||||
: useDeriver
|
||||
? getDeriver(store, *i, bo.path)
|
||||
: throw Error("argument '%s' did not evaluate to a derivation", i->what()));
|
||||
std::visit(
|
||||
overloaded{
|
||||
[&](const DerivedPath::Opaque & bo) {
|
||||
drvPaths.insert(
|
||||
bo.path.isDerivation() ? bo.path
|
||||
: useDeriver ? getDeriver(store, *i, bo.path)
|
||||
: throw Error("argument '%s' did not evaluate to a derivation", i->what()));
|
||||
},
|
||||
[&](const DerivedPath::Built & bfd) { drvPaths.insert(resolveDerivedPath(*store, *bfd.drvPath)); },
|
||||
},
|
||||
[&](const DerivedPath::Built & bfd) {
|
||||
drvPaths.insert(resolveDerivedPath(*store, *bfd.drvPath));
|
||||
},
|
||||
}, b.path.raw());
|
||||
b.path.raw());
|
||||
|
||||
return drvPaths;
|
||||
}
|
||||
|
|
@ -861,10 +836,7 @@ std::vector<FlakeRef> RawInstallablesCommand::getFlakeRefsForCompletion()
|
|||
std::vector<FlakeRef> res;
|
||||
res.reserve(rawInstallables.size());
|
||||
for (const auto & i : rawInstallables)
|
||||
res.push_back(parseFlakeRefWithFragment(
|
||||
fetchSettings,
|
||||
expandTilde(i),
|
||||
absPath(getCommandBaseDir())).first);
|
||||
res.push_back(parseFlakeRefWithFragment(fetchSettings, expandTilde(i), absPath(getCommandBaseDir())).first);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
@ -883,12 +855,7 @@ void RawInstallablesCommand::run(ref<Store> store)
|
|||
|
||||
std::vector<FlakeRef> InstallableCommand::getFlakeRefsForCompletion()
|
||||
{
|
||||
return {
|
||||
parseFlakeRefWithFragment(
|
||||
fetchSettings,
|
||||
expandTilde(_installable),
|
||||
absPath(getCommandBaseDir())).first
|
||||
};
|
||||
return {parseFlakeRefWithFragment(fetchSettings, expandTilde(_installable), absPath(getCommandBaseDir())).first};
|
||||
}
|
||||
|
||||
void InstallablesCommand::run(ref<Store> store, std::vector<std::string> && rawInstallables)
|
||||
|
|
@ -928,4 +895,4 @@ BuiltPaths toBuiltPaths(const std::vector<BuiltPathWithResult> & builtPathsWithR
|
|||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -18,29 +18,36 @@ static std::string doRenderMarkdownToTerminal(std::string_view markdown)
|
|||
{
|
||||
int windowWidth = getWindowSize().second;
|
||||
|
||||
#if HAVE_LOWDOWN_1_4
|
||||
struct lowdown_opts_term opts_term {
|
||||
# if HAVE_LOWDOWN_1_4
|
||||
struct lowdown_opts_term opts_term{
|
||||
.cols = (size_t) std::max(windowWidth - 5, 60),
|
||||
.hmargin = 0,
|
||||
.vmargin = 0,
|
||||
};
|
||||
#endif
|
||||
struct lowdown_opts opts
|
||||
{
|
||||
# endif
|
||||
struct lowdown_opts opts{
|
||||
.type = LOWDOWN_TERM,
|
||||
#if HAVE_LOWDOWN_1_4
|
||||
# if HAVE_LOWDOWN_1_4
|
||||
.term = opts_term,
|
||||
#endif
|
||||
# endif
|
||||
.maxdepth = 20,
|
||||
#if !HAVE_LOWDOWN_1_4
|
||||
# if !HAVE_LOWDOWN_1_4
|
||||
.cols = (size_t) std::max(windowWidth - 5, 60),
|
||||
.hmargin = 0,
|
||||
.vmargin = 0,
|
||||
#endif
|
||||
# endif
|
||||
.feat = LOWDOWN_COMMONMARK | LOWDOWN_FENCED | LOWDOWN_DEFLIST | LOWDOWN_TABLES,
|
||||
.oflags = LOWDOWN_TERM_NOLINK,
|
||||
.oflags =
|
||||
# if HAVE_LOWDOWN_1_4
|
||||
LOWDOWN_TERM_NORELLINK // To render full links while skipping relative ones
|
||||
# else
|
||||
LOWDOWN_TERM_NOLINK
|
||||
# endif
|
||||
};
|
||||
|
||||
if (!isTTY())
|
||||
opts.oflags |= LOWDOWN_TERM_NOANSI;
|
||||
|
||||
auto doc = lowdown_doc_new(&opts);
|
||||
if (!doc)
|
||||
throw Error("cannot allocate Markdown document");
|
||||
|
|
@ -66,7 +73,7 @@ static std::string doRenderMarkdownToTerminal(std::string_view markdown)
|
|||
if (!rndr_res)
|
||||
throw Error("allocation error while rendering Markdown");
|
||||
|
||||
return filterANSIEscapes(std::string(buf->data, buf->size), !isTTY());
|
||||
return std::string(buf->data, buf->size);
|
||||
}
|
||||
|
||||
std::string renderMarkdownToTerminal(std::string_view markdown)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
project('nix-cmd', 'cpp',
|
||||
project(
|
||||
'nix-cmd',
|
||||
'cpp',
|
||||
version : files('.version'),
|
||||
default_options : [
|
||||
'cpp_std=c++2a',
|
||||
'cpp_std=c++23',
|
||||
# TODO(Qyriad): increase the warning level
|
||||
'warning_level=1',
|
||||
'errorlogs=true', # Please print logs for tests that fail
|
||||
|
|
@ -16,8 +18,7 @@ subdir('nix-meson-build-support/deps-lists')
|
|||
|
||||
configdata = configuration_data()
|
||||
|
||||
deps_private_maybe_subproject = [
|
||||
]
|
||||
deps_private_maybe_subproject = []
|
||||
deps_public_maybe_subproject = [
|
||||
dependency('nix-util'),
|
||||
dependency('nix-store'),
|
||||
|
|
@ -31,11 +32,18 @@ subdir('nix-meson-build-support/subprojects')
|
|||
nlohmann_json = dependency('nlohmann_json', version : '>= 3.9')
|
||||
deps_public += nlohmann_json
|
||||
|
||||
lowdown = dependency('lowdown', version : '>= 0.9.0', required : get_option('markdown'))
|
||||
lowdown = dependency(
|
||||
'lowdown',
|
||||
version : '>= 0.9.0',
|
||||
required : get_option('markdown'),
|
||||
)
|
||||
deps_private += lowdown
|
||||
configdata.set('HAVE_LOWDOWN', lowdown.found().to_int())
|
||||
# The API changed slightly around terminal initialization.
|
||||
configdata.set('HAVE_LOWDOWN_1_4', lowdown.version().version_compare('>= 1.4.0').to_int())
|
||||
configdata.set(
|
||||
'HAVE_LOWDOWN_1_4',
|
||||
lowdown.version().version_compare('>= 1.4.0').to_int(),
|
||||
)
|
||||
|
||||
readline_flavor = get_option('readline-flavor')
|
||||
if readline_flavor == 'editline'
|
||||
|
|
@ -50,7 +58,7 @@ endif
|
|||
configdata.set(
|
||||
'USE_READLINE',
|
||||
(readline_flavor == 'readline').to_int(),
|
||||
description: 'Use readline instead of editline',
|
||||
description : 'Use readline instead of editline',
|
||||
)
|
||||
|
||||
config_priv_h = configure_file(
|
||||
|
|
@ -89,9 +97,10 @@ this_library = library(
|
|||
config_priv_h,
|
||||
dependencies : deps_public + deps_private + deps_other,
|
||||
include_directories : include_dirs,
|
||||
link_args: linker_export_flags,
|
||||
link_args : linker_export_flags,
|
||||
prelink : true, # For C++ static initializers
|
||||
install : true,
|
||||
cpp_pch : do_pch ? [ 'pch/precompiled-headers.hh' ] : [],
|
||||
)
|
||||
|
||||
install_headers(headers, subdir : 'nix/cmd', preserve_path : true)
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@
|
|||
|
||||
option(
|
||||
'markdown',
|
||||
type: 'feature',
|
||||
description: 'Enable Markdown rendering in the Nix binary (requires lowdown)',
|
||||
type : 'feature',
|
||||
description : 'Enable Markdown rendering in the Nix binary (requires lowdown)',
|
||||
)
|
||||
|
||||
option(
|
||||
'readline-flavor',
|
||||
type : 'combo',
|
||||
choices : ['editline', 'readline'],
|
||||
choices : [ 'editline', 'readline' ],
|
||||
value : 'editline',
|
||||
description : 'Which library to use for nice line editing with the Nix language REPL',
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#include "nix/cmd/misc-store-flags.hh"
|
||||
|
||||
namespace nix::flag
|
||||
{
|
||||
namespace nix::flag {
|
||||
|
||||
static void hashFormatCompleter(AddCompletions & completions, size_t index, std::string_view prefix)
|
||||
{
|
||||
|
|
@ -15,27 +14,23 @@ static void hashFormatCompleter(AddCompletions & completions, size_t index, std:
|
|||
Args::Flag hashFormatWithDefault(std::string && longName, HashFormat * hf)
|
||||
{
|
||||
assert(*hf == nix::HashFormat::SRI);
|
||||
return Args::Flag {
|
||||
.longName = std::move(longName),
|
||||
.description = "Hash format (`base16`, `nix32`, `base64`, `sri`). Default: `sri`.",
|
||||
.labels = {"hash-format"},
|
||||
.handler = {[hf](std::string s) {
|
||||
*hf = parseHashFormat(s);
|
||||
}},
|
||||
.completer = hashFormatCompleter,
|
||||
return Args::Flag{
|
||||
.longName = std::move(longName),
|
||||
.description = "Hash format (`base16`, `nix32`, `base64`, `sri`). Default: `sri`.",
|
||||
.labels = {"hash-format"},
|
||||
.handler = {[hf](std::string s) { *hf = parseHashFormat(s); }},
|
||||
.completer = hashFormatCompleter,
|
||||
};
|
||||
}
|
||||
|
||||
Args::Flag hashFormatOpt(std::string && longName, std::optional<HashFormat> * ohf)
|
||||
{
|
||||
return Args::Flag {
|
||||
.longName = std::move(longName),
|
||||
.description = "Hash format (`base16`, `nix32`, `base64`, `sri`).",
|
||||
.labels = {"hash-format"},
|
||||
.handler = {[ohf](std::string s) {
|
||||
*ohf = std::optional<HashFormat>{parseHashFormat(s)};
|
||||
}},
|
||||
.completer = hashFormatCompleter,
|
||||
return Args::Flag{
|
||||
.longName = std::move(longName),
|
||||
.description = "Hash format (`base16`, `nix32`, `base64`, `sri`).",
|
||||
.labels = {"hash-format"},
|
||||
.handler = {[ohf](std::string s) { *ohf = std::optional<HashFormat>{parseHashFormat(s)}; }},
|
||||
.completer = hashFormatCompleter,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -48,34 +43,31 @@ static void hashAlgoCompleter(AddCompletions & completions, size_t index, std::s
|
|||
|
||||
Args::Flag hashAlgo(std::string && longName, HashAlgorithm * ha)
|
||||
{
|
||||
return Args::Flag {
|
||||
.longName = std::move(longName),
|
||||
.description = "Hash algorithm (`blake3`, `md5`, `sha1`, `sha256`, or `sha512`).",
|
||||
.labels = {"hash-algo"},
|
||||
.handler = {[ha](std::string s) {
|
||||
*ha = parseHashAlgo(s);
|
||||
}},
|
||||
.completer = hashAlgoCompleter,
|
||||
return Args::Flag{
|
||||
.longName = std::move(longName),
|
||||
.description = "Hash algorithm (`blake3`, `md5`, `sha1`, `sha256`, or `sha512`).",
|
||||
.labels = {"hash-algo"},
|
||||
.handler = {[ha](std::string s) { *ha = parseHashAlgo(s); }},
|
||||
.completer = hashAlgoCompleter,
|
||||
};
|
||||
}
|
||||
|
||||
Args::Flag hashAlgoOpt(std::string && longName, std::optional<HashAlgorithm> * oha)
|
||||
{
|
||||
return Args::Flag {
|
||||
.longName = std::move(longName),
|
||||
.description = "Hash algorithm (`blake3`, `md5`, `sha1`, `sha256`, or `sha512`). Can be omitted for SRI hashes.",
|
||||
.labels = {"hash-algo"},
|
||||
.handler = {[oha](std::string s) {
|
||||
*oha = std::optional<HashAlgorithm>{parseHashAlgo(s)};
|
||||
}},
|
||||
.completer = hashAlgoCompleter,
|
||||
return Args::Flag{
|
||||
.longName = std::move(longName),
|
||||
.description =
|
||||
"Hash algorithm (`blake3`, `md5`, `sha1`, `sha256`, or `sha512`). Can be omitted for SRI hashes.",
|
||||
.labels = {"hash-algo"},
|
||||
.handler = {[oha](std::string s) { *oha = std::optional<HashAlgorithm>{parseHashAlgo(s)}; }},
|
||||
.completer = hashAlgoCompleter,
|
||||
};
|
||||
}
|
||||
|
||||
Args::Flag fileIngestionMethod(FileIngestionMethod * method)
|
||||
{
|
||||
return Args::Flag {
|
||||
.longName = "mode",
|
||||
return Args::Flag{
|
||||
.longName = "mode",
|
||||
// FIXME indentation carefully made for context, this is messed up.
|
||||
.description = R"(
|
||||
How to compute the hash of the input.
|
||||
|
|
@ -92,16 +84,14 @@ Args::Flag fileIngestionMethod(FileIngestionMethod * method)
|
|||
it to the hash function.
|
||||
)",
|
||||
.labels = {"file-ingestion-method"},
|
||||
.handler = {[method](std::string s) {
|
||||
*method = parseFileIngestionMethod(s);
|
||||
}},
|
||||
.handler = {[method](std::string s) { *method = parseFileIngestionMethod(s); }},
|
||||
};
|
||||
}
|
||||
|
||||
Args::Flag contentAddressMethod(ContentAddressMethod * method)
|
||||
{
|
||||
return Args::Flag {
|
||||
.longName = "mode",
|
||||
return Args::Flag{
|
||||
.longName = "mode",
|
||||
// FIXME indentation carefully made for context, this is messed up.
|
||||
.description = R"(
|
||||
How to compute the content-address of the store object.
|
||||
|
|
@ -126,10 +116,8 @@ Args::Flag contentAddressMethod(ContentAddressMethod * method)
|
|||
for regular usage prefer `nar` and `flat`.
|
||||
)",
|
||||
.labels = {"content-address-method"},
|
||||
.handler = {[method](std::string s) {
|
||||
*method = ContentAddressMethod::parse(s);
|
||||
}},
|
||||
.handler = {[method](std::string s) { *method = ContentAddressMethod::parse(s); }},
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix::flag
|
||||
|
|
|
|||
|
|
@ -47,4 +47,4 @@ bool haveNetworkProxyConnection()
|
|||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -53,7 +53,8 @@ mkMesonLibrary (finalAttrs: {
|
|||
|
||||
buildInputs = [
|
||||
({ inherit editline readline; }.${readlineFlavor})
|
||||
] ++ lib.optional enableMarkdown lowdown;
|
||||
]
|
||||
++ lib.optional enableMarkdown lowdown;
|
||||
|
||||
propagatedBuildInputs = [
|
||||
nix-util
|
||||
|
|
|
|||
4
src/libcmd/pch/precompiled-headers.hh
Normal file
4
src/libcmd/pch/precompiled-headers.hh
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
#include "nix/cmd/installables.hh"
|
||||
#include "nix/expr/eval.hh"
|
||||
#include "nix/util/util.hh"
|
||||
#include "nix/flake/flake.hh"
|
||||
|
|
@ -5,8 +5,8 @@
|
|||
#include <signal.h>
|
||||
|
||||
#if USE_READLINE
|
||||
#include <readline/history.h>
|
||||
#include <readline/readline.h>
|
||||
# include <readline/history.h>
|
||||
# include <readline/readline.h>
|
||||
#else
|
||||
// editline < 1.15.2 don't wrap their API for C++ usage
|
||||
// (added in https://github.com/troglobit/editline/commit/91398ceb3427b730995357e9d120539fb9bb7461).
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
// For compatibility with these versions, we wrap the API here
|
||||
// (wrapping multiple times on newer versions is no problem).
|
||||
extern "C" {
|
||||
#include <editline.h>
|
||||
# include <editline.h>
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ void sigintHandler(int signo)
|
|||
{
|
||||
g_signal_received = signo;
|
||||
}
|
||||
};
|
||||
}; // namespace
|
||||
|
||||
static detail::ReplCompleterMixin * curRepl; // ugly
|
||||
|
||||
|
|
@ -185,8 +185,7 @@ bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptT
|
|||
// editline doesn't echo the input to the output when non-interactive, unlike readline
|
||||
// this results in a different behavior when running tests. The echoing is
|
||||
// quite useful for reading the test output, so we add it here.
|
||||
if (auto e = getEnv("_NIX_TEST_REPL_ECHO"); s && e && *e == "1")
|
||||
{
|
||||
if (auto e = getEnv("_NIX_TEST_REPL_ECHO"); s && e && *e == "1") {
|
||||
#if !USE_READLINE
|
||||
// This is probably not right for multi-line input, but we don't use that
|
||||
// in the characterisation tests, so it's fine.
|
||||
|
|
@ -207,4 +206,4 @@ ReadlineLikeInteracter::~ReadlineLikeInteracter()
|
|||
write_history(historyFile.c_str());
|
||||
}
|
||||
|
||||
};
|
||||
}; // namespace nix
|
||||
|
|
|
|||
|
|
@ -54,10 +54,7 @@ enum class ProcessLineResult {
|
|||
PromptAgain,
|
||||
};
|
||||
|
||||
struct NixRepl
|
||||
: AbstractNixRepl
|
||||
, detail::ReplCompleterMixin
|
||||
, gc
|
||||
struct NixRepl : AbstractNixRepl, detail::ReplCompleterMixin, gc
|
||||
{
|
||||
size_t debugTraceIndex;
|
||||
|
||||
|
|
@ -80,8 +77,12 @@ struct NixRepl
|
|||
|
||||
std::unique_ptr<ReplInteracter> interacter;
|
||||
|
||||
NixRepl(const LookupPath & lookupPath, nix::ref<Store> store,ref<EvalState> state,
|
||||
std::function<AnnotatedValues()> getValues, RunNix * runNix);
|
||||
NixRepl(
|
||||
const LookupPath & lookupPath,
|
||||
nix::ref<Store> store,
|
||||
ref<EvalState> state,
|
||||
std::function<AnnotatedValues()> getValues,
|
||||
RunNix * runNix);
|
||||
virtual ~NixRepl() = default;
|
||||
|
||||
ReplExitStatus mainLoop() override;
|
||||
|
|
@ -103,20 +104,22 @@ struct NixRepl
|
|||
void evalString(std::string s, Value & v);
|
||||
void loadDebugTraceEnv(DebugTrace & dt);
|
||||
|
||||
void printValue(std::ostream & str,
|
||||
Value & v,
|
||||
unsigned int maxDepth = std::numeric_limits<unsigned int>::max())
|
||||
void printValue(std::ostream & str, Value & v, unsigned int maxDepth = std::numeric_limits<unsigned int>::max())
|
||||
{
|
||||
// Hide the progress bar during printing because it might interfere
|
||||
auto suspension = logger->suspend();
|
||||
::nix::printValue(*state, str, v, PrintOptions {
|
||||
.ansiColors = true,
|
||||
.force = true,
|
||||
.derivationPaths = true,
|
||||
.maxDepth = maxDepth,
|
||||
.prettyIndent = 2,
|
||||
.errors = ErrorPrintBehavior::ThrowTopLevel,
|
||||
});
|
||||
::nix::printValue(
|
||||
*state,
|
||||
str,
|
||||
v,
|
||||
PrintOptions{
|
||||
.ansiColors = true,
|
||||
.force = true,
|
||||
.derivationPaths = true,
|
||||
.maxDepth = maxDepth,
|
||||
.prettyIndent = 2,
|
||||
.errors = ErrorPrintBehavior::ThrowTopLevel,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -124,13 +127,17 @@ std::string removeWhitespace(std::string s)
|
|||
{
|
||||
s = chomp(s);
|
||||
size_t n = s.find_first_not_of(" \n\r\t");
|
||||
if (n != std::string::npos) s = std::string(s, n);
|
||||
if (n != std::string::npos)
|
||||
s = std::string(s, n);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
NixRepl::NixRepl(const LookupPath & lookupPath, nix::ref<Store> store, ref<EvalState> state,
|
||||
std::function<NixRepl::AnnotatedValues()> getValues, RunNix * runNix)
|
||||
NixRepl::NixRepl(
|
||||
const LookupPath & lookupPath,
|
||||
nix::ref<Store> store,
|
||||
ref<EvalState> state,
|
||||
std::function<NixRepl::AnnotatedValues()> getValues,
|
||||
RunNix * runNix)
|
||||
: AbstractNixRepl(state)
|
||||
, debugTraceIndex(0)
|
||||
, getValues(getValues)
|
||||
|
|
@ -188,7 +195,8 @@ ReplExitStatus NixRepl::mainLoop()
|
|||
auto suspension = logger->suspend();
|
||||
// When continuing input from previous lines, don't print a prompt, just align to the same
|
||||
// number of chars as the prompt.
|
||||
if (!interacter->getLine(input, input.empty() ? ReplPromptType::ReplPrompt : ReplPromptType::ContinuationPrompt)) {
|
||||
if (!interacter->getLine(
|
||||
input, input.empty() ? ReplPromptType::ReplPrompt : ReplPromptType::ContinuationPrompt)) {
|
||||
// Ctrl-D should exit the debugger.
|
||||
state->debugStop = false;
|
||||
logger->cout("");
|
||||
|
|
@ -200,14 +208,14 @@ ReplExitStatus NixRepl::mainLoop()
|
|||
}
|
||||
try {
|
||||
switch (processLine(input)) {
|
||||
case ProcessLineResult::Quit:
|
||||
return ReplExitStatus::QuitAll;
|
||||
case ProcessLineResult::Continue:
|
||||
return ReplExitStatus::Continue;
|
||||
case ProcessLineResult::PromptAgain:
|
||||
break;
|
||||
default:
|
||||
unreachable();
|
||||
case ProcessLineResult::Quit:
|
||||
return ReplExitStatus::QuitAll;
|
||||
case ProcessLineResult::Continue:
|
||||
return ReplExitStatus::Continue;
|
||||
case ProcessLineResult::PromptAgain:
|
||||
break;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
} catch (IncompleteReplExpr &) {
|
||||
continue;
|
||||
|
|
@ -256,7 +264,8 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
|
|||
/* This is a variable name; look it up in the current scope. */
|
||||
StringSet::iterator i = varNames.lower_bound(cur);
|
||||
while (i != varNames.end()) {
|
||||
if (i->substr(0, cur.size()) != cur) break;
|
||||
if (i->substr(0, cur.size()) != cur)
|
||||
break;
|
||||
completions.insert(prev + *i);
|
||||
i++;
|
||||
}
|
||||
|
|
@ -275,11 +284,15 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
|
|||
Expr * e = parseString(expr);
|
||||
Value v;
|
||||
e->eval(*state, *env, v);
|
||||
state->forceAttrs(v, noPos, "while evaluating an attrset for the purpose of completion (this error should not be displayed; file an issue?)");
|
||||
state->forceAttrs(
|
||||
v,
|
||||
noPos,
|
||||
"while evaluating an attrset for the purpose of completion (this error should not be displayed; file an issue?)");
|
||||
|
||||
for (auto & i : *v.attrs()) {
|
||||
std::string_view name = state->symbols[i.name];
|
||||
if (name.substr(0, cur2.size()) != cur2) continue;
|
||||
if (name.substr(0, cur2.size()) != cur2)
|
||||
continue;
|
||||
completions.insert(concatStrings(prev, expr, ".", name));
|
||||
}
|
||||
|
||||
|
|
@ -297,24 +310,23 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
|
|||
return completions;
|
||||
}
|
||||
|
||||
|
||||
// FIXME: DRY and match or use the parser
|
||||
static bool isVarName(std::string_view s)
|
||||
{
|
||||
if (s.size() == 0) return false;
|
||||
if (s.size() == 0)
|
||||
return false;
|
||||
char c = s[0];
|
||||
if ((c >= '0' && c <= '9') || c == '-' || c == '\'') return false;
|
||||
if ((c >= '0' && c <= '9') || c == '-' || c == '\'')
|
||||
return false;
|
||||
for (auto & i : s)
|
||||
if (!((i >= 'a' && i <= 'z') ||
|
||||
(i >= 'A' && i <= 'Z') ||
|
||||
(i >= '0' && i <= '9') ||
|
||||
i == '_' || i == '-' || i == '\''))
|
||||
if (!((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || (i >= '0' && i <= '9') || i == '_' || i == '-'
|
||||
|| i == '\''))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
StorePath NixRepl::getDerivationPath(Value & v) {
|
||||
StorePath NixRepl::getDerivationPath(Value & v)
|
||||
{
|
||||
auto packageInfo = getDerivation(*state, v, false);
|
||||
if (!packageInfo)
|
||||
throw Error("expression does not evaluate to a derivation, so I can't build it");
|
||||
|
|
@ -353,53 +365,50 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
|||
if (line[0] == ':') {
|
||||
size_t p = line.find_first_of(" \n\r\t");
|
||||
command = line.substr(0, p);
|
||||
if (p != std::string::npos) arg = removeWhitespace(line.substr(p));
|
||||
if (p != std::string::npos)
|
||||
arg = removeWhitespace(line.substr(p));
|
||||
} else {
|
||||
arg = line;
|
||||
}
|
||||
|
||||
if (command == ":?" || command == ":help") {
|
||||
// FIXME: convert to Markdown, include in the 'nix repl' manpage.
|
||||
std::cout
|
||||
<< "The following commands are available:\n"
|
||||
<< "\n"
|
||||
<< " <expr> Evaluate and print expression\n"
|
||||
<< " <x> = <expr> Bind expression to variable\n"
|
||||
<< " :a, :add <expr> Add attributes from resulting set to scope\n"
|
||||
<< " :b <expr> Build a derivation\n"
|
||||
<< " :bl <expr> Build a derivation, creating GC roots in the\n"
|
||||
<< " working directory\n"
|
||||
<< " :e, :edit <expr> Open package or function in $EDITOR\n"
|
||||
<< " :i <expr> Build derivation, then install result into\n"
|
||||
<< " current profile\n"
|
||||
<< " :l, :load <path> Load Nix expression and add it to scope\n"
|
||||
<< " :lf, :load-flake <ref> Load Nix flake and add it to scope\n"
|
||||
<< " :ll, :last-loaded Show most recently loaded variables added to scope\n"
|
||||
<< " :p, :print <expr> Evaluate and print expression recursively\n"
|
||||
<< " Strings are printed directly, without escaping.\n"
|
||||
<< " :q, :quit Exit nix-repl\n"
|
||||
<< " :r, :reload Reload all files\n"
|
||||
<< " :sh <expr> Build dependencies of derivation, then start\n"
|
||||
<< " nix-shell\n"
|
||||
<< " :t <expr> Describe result of evaluation\n"
|
||||
<< " :u <expr> Build derivation, then start nix-shell\n"
|
||||
<< " :doc <expr> Show documentation of a builtin function\n"
|
||||
<< " :log <expr> Show logs for a derivation\n"
|
||||
<< " :te, :trace-enable [bool] Enable, disable or toggle showing traces for\n"
|
||||
<< " errors\n"
|
||||
<< " :?, :help Brings up this help menu\n"
|
||||
;
|
||||
std::cout << "The following commands are available:\n"
|
||||
<< "\n"
|
||||
<< " <expr> Evaluate and print expression\n"
|
||||
<< " <x> = <expr> Bind expression to variable\n"
|
||||
<< " :a, :add <expr> Add attributes from resulting set to scope\n"
|
||||
<< " :b <expr> Build a derivation\n"
|
||||
<< " :bl <expr> Build a derivation, creating GC roots in the\n"
|
||||
<< " working directory\n"
|
||||
<< " :e, :edit <expr> Open package or function in $EDITOR\n"
|
||||
<< " :i <expr> Build derivation, then install result into\n"
|
||||
<< " current profile\n"
|
||||
<< " :l, :load <path> Load Nix expression and add it to scope\n"
|
||||
<< " :lf, :load-flake <ref> Load Nix flake and add it to scope\n"
|
||||
<< " :ll, :last-loaded Show most recently loaded variables added to scope\n"
|
||||
<< " :p, :print <expr> Evaluate and print expression recursively\n"
|
||||
<< " Strings are printed directly, without escaping.\n"
|
||||
<< " :q, :quit Exit nix-repl\n"
|
||||
<< " :r, :reload Reload all files\n"
|
||||
<< " :sh <expr> Build dependencies of derivation, then start\n"
|
||||
<< " nix-shell\n"
|
||||
<< " :t <expr> Describe result of evaluation\n"
|
||||
<< " :u <expr> Build derivation, then start nix-shell\n"
|
||||
<< " :doc <expr> Show documentation of a builtin function\n"
|
||||
<< " :log <expr> Show logs for a derivation\n"
|
||||
<< " :te, :trace-enable [bool] Enable, disable or toggle showing traces for\n"
|
||||
<< " errors\n"
|
||||
<< " :?, :help Brings up this help menu\n";
|
||||
if (state->debugRepl) {
|
||||
std::cout
|
||||
<< "\n"
|
||||
<< " Debug mode commands\n"
|
||||
<< " :env Show env stack\n"
|
||||
<< " :bt, :backtrace Show trace stack\n"
|
||||
<< " :st Show current trace\n"
|
||||
<< " :st <idx> Change to another trace in the stack\n"
|
||||
<< " :c, :continue Go until end of program, exception, or builtins.break\n"
|
||||
<< " :s, :step Go one step\n"
|
||||
;
|
||||
std::cout << "\n"
|
||||
<< " Debug mode commands\n"
|
||||
<< " :env Show env stack\n"
|
||||
<< " :bt, :backtrace Show trace stack\n"
|
||||
<< " :st Show current trace\n"
|
||||
<< " :st <idx> Change to another trace in the stack\n"
|
||||
<< " :c, :continue Go until end of program, exception, or builtins.break\n"
|
||||
<< " :s, :step Go one step\n";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -424,17 +433,18 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
|||
try {
|
||||
// change the DebugTrace index.
|
||||
debugTraceIndex = stoi(arg);
|
||||
} catch (...) { }
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
for (const auto & [idx, i] : enumerate(state->debugTraces)) {
|
||||
if (idx == debugTraceIndex) {
|
||||
std::cout << "\n" << ANSI_BLUE << idx << ANSI_NORMAL << ": ";
|
||||
showDebugTrace(std::cout, state->positions, i);
|
||||
std::cout << std::endl;
|
||||
printEnvBindings(*state, i.expr, i.env);
|
||||
loadDebugTraceEnv(i);
|
||||
break;
|
||||
}
|
||||
if (idx == debugTraceIndex) {
|
||||
std::cout << "\n" << ANSI_BLUE << idx << ANSI_NORMAL << ": ";
|
||||
showDebugTrace(std::cout, state->positions, i);
|
||||
std::cout << std::endl;
|
||||
printEnvBindings(*state, i.expr, i.env);
|
||||
loadDebugTraceEnv(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -478,7 +488,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
|||
Value v;
|
||||
evalString(arg, v);
|
||||
|
||||
const auto [path, line] = [&] () -> std::pair<SourcePath, uint32_t> {
|
||||
const auto [path, line] = [&]() -> std::pair<SourcePath, uint32_t> {
|
||||
if (v.type() == nPath || v.type() == nString) {
|
||||
NixStringContext context;
|
||||
auto path = state->coerceToPath(noPos, v, context, "while evaluating the filename to edit");
|
||||
|
|
@ -502,7 +512,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
|||
|
||||
// runProgram redirects stdout to a StringSink,
|
||||
// using runProgram2 to allow editors to display their UI
|
||||
runProgram2(RunOptions { .program = editor, .lookupPath = true, .args = args , .isInteractive = true });
|
||||
runProgram2(RunOptions{.program = editor, .lookupPath = true, .args = args, .isInteractive = true});
|
||||
|
||||
// Reload right after exiting the editor
|
||||
state->resetFileCache();
|
||||
|
|
@ -533,9 +543,9 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
|||
|
||||
if (command == ":b" || command == ":bl") {
|
||||
state->store->buildPaths({
|
||||
DerivedPath::Built {
|
||||
DerivedPath::Built{
|
||||
.drvPath = makeConstantStorePathRef(drvPath),
|
||||
.outputs = OutputsSpec::All { },
|
||||
.outputs = OutputsSpec::All{},
|
||||
},
|
||||
});
|
||||
auto drv = state->store->readDerivation(drvPath);
|
||||
|
|
@ -554,9 +564,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
|||
runNix("nix-env", {"-i", drvPathRaw});
|
||||
} else if (command == ":log") {
|
||||
settings.readOnlyMode = true;
|
||||
Finally roModeReset([&]() {
|
||||
settings.readOnlyMode = false;
|
||||
});
|
||||
Finally roModeReset([&]() { settings.readOnlyMode = false; });
|
||||
auto subs = getDefaultSubstituters();
|
||||
|
||||
subs.push_front(state->store);
|
||||
|
|
@ -566,20 +574,22 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
|||
for (auto & sub : subs) {
|
||||
auto * logSubP = dynamic_cast<LogStore *>(&*sub);
|
||||
if (!logSubP) {
|
||||
printInfo("Skipped '%s' which does not support retrieving build logs", sub->getUri());
|
||||
printInfo(
|
||||
"Skipped '%s' which does not support retrieving build logs", sub->config.getHumanReadableURI());
|
||||
continue;
|
||||
}
|
||||
auto & logSub = *logSubP;
|
||||
|
||||
auto log = logSub.getBuildLog(drvPath);
|
||||
if (log) {
|
||||
printInfo("got build log for '%s' from '%s'", drvPathRaw, logSub.getUri());
|
||||
printInfo("got build log for '%s' from '%s'", drvPathRaw, logSub.config.getHumanReadableURI());
|
||||
logger->writeToStdout(*log);
|
||||
foundLog = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundLog) throw Error("build log of '%s' is not available", drvPathRaw);
|
||||
if (!foundLog)
|
||||
throw Error("build log of '%s' is not available", drvPathRaw);
|
||||
} else {
|
||||
runNix("nix-shell", {drvPathRaw});
|
||||
}
|
||||
|
|
@ -642,9 +652,8 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
|||
for (auto & arg : args)
|
||||
arg = "*" + arg + "*";
|
||||
|
||||
markdown +=
|
||||
"**Synopsis:** `builtins." + (std::string) (*doc->name) + "` "
|
||||
+ concatStringsSep(" ", args) + "\n\n";
|
||||
markdown += "**Synopsis:** `builtins." + (std::string) (*doc->name) + "` " + concatStringsSep(" ", args)
|
||||
+ "\n\n";
|
||||
}
|
||||
|
||||
markdown += stripIndentation(doc->doc);
|
||||
|
|
@ -685,11 +694,8 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
|||
else {
|
||||
size_t p = line.find('=');
|
||||
std::string name;
|
||||
if (p != std::string::npos &&
|
||||
p < line.size() &&
|
||||
line[p + 1] != '=' &&
|
||||
isVarName(name = removeWhitespace(line.substr(0, p))))
|
||||
{
|
||||
if (p != std::string::npos && p < line.size() && line[p + 1] != '='
|
||||
&& isVarName(name = removeWhitespace(line.substr(0, p)))) {
|
||||
Expr * e = parseString(line.substr(p + 1));
|
||||
Value & v(*state->allocValue());
|
||||
v.mkThunk(env, e);
|
||||
|
|
@ -737,9 +743,13 @@ void NixRepl::loadFlake(const std::string & flakeRefS)
|
|||
|
||||
Value v;
|
||||
|
||||
flake::callFlake(*state,
|
||||
flake::lockFlake(flakeSettings, *state, flakeRef,
|
||||
flake::LockFlags {
|
||||
flake::callFlake(
|
||||
*state,
|
||||
flake::lockFlake(
|
||||
flakeSettings,
|
||||
*state,
|
||||
flakeRef,
|
||||
flake::LockFlags{
|
||||
.updateLockFile = false,
|
||||
.useRegistries = !evalSettings.pureEval,
|
||||
.allowUnlocked = !evalSettings.pureEval,
|
||||
|
|
@ -748,7 +758,6 @@ void NixRepl::loadFlake(const std::string & flakeRefS)
|
|||
addAttrsToScope(v);
|
||||
}
|
||||
|
||||
|
||||
void NixRepl::initEnv()
|
||||
{
|
||||
env = &state->allocEnv(envSize);
|
||||
|
|
@ -771,7 +780,6 @@ void NixRepl::showLastLoaded()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void NixRepl::reloadFilesAndFlakes()
|
||||
{
|
||||
initEnv();
|
||||
|
|
@ -780,7 +788,6 @@ void NixRepl::reloadFilesAndFlakes()
|
|||
loadFlakes();
|
||||
}
|
||||
|
||||
|
||||
void NixRepl::loadFiles()
|
||||
{
|
||||
Strings old = loadedFiles;
|
||||
|
|
@ -797,7 +804,6 @@ void NixRepl::loadFiles()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void NixRepl::loadFlakes()
|
||||
{
|
||||
Strings old = loadedFlakes;
|
||||
|
|
@ -809,10 +815,12 @@ void NixRepl::loadFlakes()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void NixRepl::addAttrsToScope(Value & attrs)
|
||||
{
|
||||
state->forceAttrs(attrs, [&]() { return attrs.determinePos(noPos); }, "while evaluating an attribute set to be merged in the global scope");
|
||||
state->forceAttrs(
|
||||
attrs,
|
||||
[&]() { return attrs.determinePos(noPos); },
|
||||
"while evaluating an attribute set to be merged in the global scope");
|
||||
if (displ + attrs.attrs()->size() >= envSize)
|
||||
throw Error("environment full; cannot add more variables");
|
||||
|
||||
|
|
@ -847,7 +855,6 @@ void NixRepl::addAttrsToScope(Value & attrs)
|
|||
notice("... and %1% more; view with :ll", attrs.attrs()->size() - max_print);
|
||||
}
|
||||
|
||||
|
||||
void NixRepl::addVarToScope(const Symbol name, Value & v)
|
||||
{
|
||||
if (displ >= envSize)
|
||||
|
|
@ -860,13 +867,11 @@ void NixRepl::addVarToScope(const Symbol name, Value & v)
|
|||
varNames.emplace(state->symbols[name]);
|
||||
}
|
||||
|
||||
|
||||
Expr * NixRepl::parseString(std::string s)
|
||||
{
|
||||
return state->parseExprFromString(std::move(s), state->rootPath("."), staticEnv);
|
||||
}
|
||||
|
||||
|
||||
void NixRepl::evalString(std::string s, Value & v)
|
||||
{
|
||||
Expr * e;
|
||||
|
|
@ -884,46 +889,40 @@ void NixRepl::evalString(std::string s, Value & v)
|
|||
state->forceValue(v, v.determinePos(noPos));
|
||||
}
|
||||
|
||||
|
||||
void NixRepl::runNix(Path program, const Strings & args, const std::optional<std::string> & input)
|
||||
{
|
||||
if (runNixPtr)
|
||||
(*runNixPtr)(program, args, input);
|
||||
else
|
||||
throw Error("Cannot run '%s' because no method of calling the Nix CLI was provided. This is a configuration problem pertaining to how this program was built. See Nix 2.25 release notes", program);
|
||||
throw Error(
|
||||
"Cannot run '%s' because no method of calling the Nix CLI was provided. This is a configuration problem pertaining to how this program was built. See Nix 2.25 release notes",
|
||||
program);
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<AbstractNixRepl> AbstractNixRepl::create(
|
||||
const LookupPath & lookupPath, nix::ref<Store> store, ref<EvalState> state,
|
||||
std::function<AnnotatedValues()> getValues, RunNix * runNix)
|
||||
const LookupPath & lookupPath,
|
||||
nix::ref<Store> store,
|
||||
ref<EvalState> state,
|
||||
std::function<AnnotatedValues()> getValues,
|
||||
RunNix * runNix)
|
||||
{
|
||||
return std::make_unique<NixRepl>(
|
||||
lookupPath,
|
||||
std::move(store),
|
||||
state,
|
||||
getValues,
|
||||
runNix
|
||||
);
|
||||
return std::make_unique<NixRepl>(lookupPath, std::move(store), state, getValues, runNix);
|
||||
}
|
||||
|
||||
|
||||
ReplExitStatus AbstractNixRepl::runSimple(
|
||||
ref<EvalState> evalState,
|
||||
const ValMap & extraEnv)
|
||||
ReplExitStatus AbstractNixRepl::runSimple(ref<EvalState> evalState, const ValMap & extraEnv)
|
||||
{
|
||||
auto getValues = [&]()->NixRepl::AnnotatedValues{
|
||||
auto getValues = [&]() -> NixRepl::AnnotatedValues {
|
||||
NixRepl::AnnotatedValues values;
|
||||
return values;
|
||||
};
|
||||
LookupPath lookupPath = {};
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDelete)
|
||||
auto repl = std::make_unique<NixRepl>(
|
||||
lookupPath,
|
||||
openStore(),
|
||||
evalState,
|
||||
getValues,
|
||||
/*runNix=*/nullptr
|
||||
);
|
||||
lookupPath,
|
||||
openStore(),
|
||||
evalState,
|
||||
getValues,
|
||||
/*runNix=*/nullptr);
|
||||
|
||||
repl->initEnv();
|
||||
|
||||
|
|
@ -934,4 +933,4 @@ ReplExitStatus AbstractNixRepl::runSimple(
|
|||
return repl->mainLoop();
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
project('nix-expr-c', 'cpp',
|
||||
project(
|
||||
'nix-expr-c',
|
||||
'cpp',
|
||||
version : files('.version'),
|
||||
default_options : [
|
||||
'cpp_std=c++2a',
|
||||
'cpp_std=c++23',
|
||||
# TODO(Qyriad): increase the warning level
|
||||
'warning_level=1',
|
||||
'errorlogs=true', # Please print logs for tests that fail
|
||||
|
|
@ -33,7 +35,7 @@ sources = files(
|
|||
'nix_api_value.cc',
|
||||
)
|
||||
|
||||
include_dirs = [include_directories('.')]
|
||||
include_dirs = [ include_directories('.') ]
|
||||
|
||||
headers = files(
|
||||
'nix_api_expr.h',
|
||||
|
|
@ -50,7 +52,7 @@ this_library = library(
|
|||
sources,
|
||||
dependencies : deps_public + deps_private + deps_other,
|
||||
include_directories : include_dirs,
|
||||
link_args: linker_export_flags,
|
||||
link_args : linker_export_flags,
|
||||
prelink : true, # For C++ static initializers
|
||||
install : true,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
#include "nix_api_util_internal.h"
|
||||
|
||||
#if NIX_USE_BOEHMGC
|
||||
# include <mutex>
|
||||
# include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
|
@ -31,17 +31,17 @@
|
|||
* @param init Function that takes a T* and returns the initializer for T
|
||||
* @return Pointer to allocated and initialized object
|
||||
*/
|
||||
template <typename T, typename F>
|
||||
template<typename T, typename F>
|
||||
static T * unsafe_new_with_self(F && init)
|
||||
{
|
||||
// Allocate
|
||||
void * p = ::operator new(
|
||||
sizeof(T),
|
||||
static_cast<std::align_val_t>(alignof(T)));
|
||||
void * p = ::operator new(sizeof(T), static_cast<std::align_val_t>(alignof(T)));
|
||||
// Initialize with placement new
|
||||
return new (p) T(init(static_cast<T *>(p)));
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
nix_err nix_libexpr_init(nix_c_context * context)
|
||||
{
|
||||
if (context)
|
||||
|
|
@ -86,12 +86,13 @@ nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, n
|
|||
NIXC_CATCH_ERRS
|
||||
}
|
||||
|
||||
nix_err nix_value_call_multi(nix_c_context * context, EvalState * state, nix_value * fn, size_t nargs, nix_value ** args, nix_value * value)
|
||||
nix_err nix_value_call_multi(
|
||||
nix_c_context * context, EvalState * state, nix_value * fn, size_t nargs, nix_value ** args, nix_value * value)
|
||||
{
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
try {
|
||||
state->state.callFunction(fn->value, {(nix::Value * *) args, nargs}, value->value, nix::noPos);
|
||||
state->state.callFunction(fn->value, {(nix::Value **) args, nargs}, value->value, nix::noPos);
|
||||
state->state.forceValue(value->value, nix::noPos);
|
||||
}
|
||||
NIXC_CATCH_ERRS
|
||||
|
|
@ -152,7 +153,8 @@ nix_err nix_eval_state_builder_load(nix_c_context * context, nix_eval_state_buil
|
|||
NIXC_CATCH_ERRS
|
||||
}
|
||||
|
||||
nix_err nix_eval_state_builder_set_lookup_path(nix_c_context * context, nix_eval_state_builder * builder, const char ** lookupPath_c)
|
||||
nix_err nix_eval_state_builder_set_lookup_path(
|
||||
nix_c_context * context, nix_eval_state_builder * builder, const char ** lookupPath_c)
|
||||
{
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
|
|
@ -175,11 +177,7 @@ EvalState * nix_eval_state_build(nix_c_context * context, nix_eval_state_builder
|
|||
return EvalState{
|
||||
.fetchSettings = std::move(builder->fetchSettings),
|
||||
.settings = std::move(builder->settings),
|
||||
.state = nix::EvalState(
|
||||
builder->lookupPath,
|
||||
builder->store,
|
||||
self->fetchSettings,
|
||||
self->settings),
|
||||
.state = nix::EvalState(builder->lookupPath, builder->store, self->fetchSettings, self->settings),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
@ -195,11 +193,10 @@ EvalState * nix_state_create(nix_c_context * context, const char ** lookupPath_c
|
|||
if (nix_eval_state_builder_load(context, builder) != NIX_OK)
|
||||
return nullptr;
|
||||
|
||||
if (nix_eval_state_builder_set_lookup_path(context, builder, lookupPath_c)
|
||||
!= NIX_OK)
|
||||
if (nix_eval_state_builder_set_lookup_path(context, builder, lookupPath_c) != NIX_OK)
|
||||
return nullptr;
|
||||
|
||||
auto *state = nix_eval_state_build(context, builder);
|
||||
auto * state = nix_eval_state_build(context, builder);
|
||||
nix_eval_state_builder_free(builder);
|
||||
return state;
|
||||
}
|
||||
|
|
@ -210,28 +207,20 @@ void nix_state_free(EvalState * state)
|
|||
}
|
||||
|
||||
#if NIX_USE_BOEHMGC
|
||||
std::unordered_map<
|
||||
boost::concurrent_flat_map<
|
||||
const void *,
|
||||
unsigned int,
|
||||
std::hash<const void *>,
|
||||
std::equal_to<const void *>,
|
||||
traceable_allocator<std::pair<const void * const, unsigned int>>>
|
||||
nix_refcounts;
|
||||
|
||||
std::mutex nix_refcount_lock;
|
||||
nix_refcounts{};
|
||||
|
||||
nix_err nix_gc_incref(nix_c_context * context, const void * p)
|
||||
{
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
try {
|
||||
std::scoped_lock lock(nix_refcount_lock);
|
||||
auto f = nix_refcounts.find(p);
|
||||
if (f != nix_refcounts.end()) {
|
||||
f->second++;
|
||||
} else {
|
||||
nix_refcounts[p] = 1;
|
||||
}
|
||||
nix_refcounts.insert_or_visit({p, 1}, [](auto & kv) { kv.second++; });
|
||||
}
|
||||
NIXC_CATCH_ERRS
|
||||
}
|
||||
|
|
@ -242,12 +231,12 @@ nix_err nix_gc_decref(nix_c_context * context, const void * p)
|
|||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
try {
|
||||
std::scoped_lock lock(nix_refcount_lock);
|
||||
auto f = nix_refcounts.find(p);
|
||||
if (f != nix_refcounts.end()) {
|
||||
if (--f->second == 0)
|
||||
nix_refcounts.erase(f);
|
||||
} else
|
||||
bool fail = true;
|
||||
nix_refcounts.erase_if(p, [&](auto & kv) {
|
||||
fail = false;
|
||||
return !--kv.second;
|
||||
});
|
||||
if (fail)
|
||||
throw std::runtime_error("nix_gc_decref: object was not referenced");
|
||||
}
|
||||
NIXC_CATCH_ERRS
|
||||
|
|
@ -265,20 +254,23 @@ nix_err nix_gc_incref(nix_c_context * context, const void *)
|
|||
context->last_err_code = NIX_OK;
|
||||
return NIX_OK;
|
||||
}
|
||||
|
||||
nix_err nix_gc_decref(nix_c_context * context, const void *)
|
||||
{
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
return NIX_OK;
|
||||
}
|
||||
|
||||
void nix_gc_now() {}
|
||||
#endif
|
||||
|
||||
nix_err nix_value_incref(nix_c_context * context, nix_value *x)
|
||||
nix_err nix_value_incref(nix_c_context * context, nix_value * x)
|
||||
{
|
||||
return nix_gc_incref(context, (const void *) x);
|
||||
}
|
||||
nix_err nix_value_decref(nix_c_context * context, nix_value *x)
|
||||
|
||||
nix_err nix_value_decref(nix_c_context * context, nix_value * x)
|
||||
{
|
||||
return nix_gc_decref(context, (const void *) x);
|
||||
}
|
||||
|
|
@ -289,3 +281,5 @@ void nix_gc_register_finalizer(void * obj, void * cd, void (*finalizer)(void * o
|
|||
GC_REGISTER_FINALIZER(obj, finalizer, cd, 0, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@
|
|||
#include "nix_api_value.h"
|
||||
#include "nix/expr/search-path.hh"
|
||||
|
||||
extern "C" {
|
||||
|
||||
struct nix_eval_state_builder
|
||||
{
|
||||
nix::ref<nix::Store> store;
|
||||
|
|
@ -61,4 +63,6 @@ struct nix_realised_string
|
|||
std::vector<StorePath> storePaths;
|
||||
};
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#endif // NIX_API_EXPR_INTERNAL_H
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
extern "C" {
|
||||
|
||||
void nix_set_string_return(nix_string_return * str, const char * c)
|
||||
{
|
||||
str->str = c;
|
||||
|
|
@ -40,6 +42,8 @@ nix_err nix_external_add_string_context(nix_c_context * context, nix_string_cont
|
|||
NIXC_CATCH_ERRS
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
class NixCExternalValue : public nix::ExternalValueBase
|
||||
{
|
||||
NixCExternalValueDesc & desc;
|
||||
|
|
@ -48,11 +52,13 @@ class NixCExternalValue : public nix::ExternalValueBase
|
|||
public:
|
||||
NixCExternalValue(NixCExternalValueDesc & desc, void * v)
|
||||
: desc(desc)
|
||||
, v(v){};
|
||||
, v(v) {};
|
||||
|
||||
void * get_ptr()
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print out the value
|
||||
*/
|
||||
|
|
@ -155,13 +161,21 @@ public:
|
|||
}
|
||||
nix_string_context ctx{context};
|
||||
desc.printValueAsXML(
|
||||
v, (EvalState *) &state, strict, location, &doc, &ctx, &drvsSeen,
|
||||
v,
|
||||
(EvalState *) &state,
|
||||
strict,
|
||||
location,
|
||||
&doc,
|
||||
&ctx,
|
||||
&drvsSeen,
|
||||
*reinterpret_cast<const uint32_t *>(&pos));
|
||||
}
|
||||
|
||||
virtual ~NixCExternalValue() override{};
|
||||
virtual ~NixCExternalValue() override {};
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
ExternalValue * nix_create_external_value(nix_c_context * context, NixCExternalValueDesc * desc, void * v)
|
||||
{
|
||||
if (context)
|
||||
|
|
@ -190,3 +204,5 @@ void * nix_get_external_value_content(nix_c_context * context, ExternalValue * b
|
|||
}
|
||||
NIXC_CATCH_ERRS_NULL
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
|
|
|||
|
|
@ -111,6 +111,8 @@ static void nix_c_primop_wrapper(
|
|||
v = vTmp;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
PrimOp * nix_alloc_primop(
|
||||
nix_c_context * context,
|
||||
PrimOpFun fun,
|
||||
|
|
@ -592,7 +594,7 @@ nix_err nix_bindings_builder_insert(nix_c_context * context, BindingsBuilder * b
|
|||
context->last_err_code = NIX_OK;
|
||||
try {
|
||||
auto & v = check_value_not_null(value);
|
||||
nix::Symbol s = bb->builder.state.symbols.create(name);
|
||||
nix::Symbol s = bb->builder.state.get().symbols.create(name);
|
||||
bb->builder.insert(s, &v);
|
||||
}
|
||||
NIXC_CATCH_ERRS
|
||||
|
|
@ -651,3 +653,5 @@ const StorePath * nix_realised_string_get_store_path(nix_realised_string * s, si
|
|||
{
|
||||
return &s->storePaths[i];
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
|
|
|||
|
|
@ -16,141 +16,159 @@
|
|||
#include "nix/store/tests/libstore.hh"
|
||||
|
||||
namespace nix {
|
||||
class LibExprTest : public LibStoreTest {
|
||||
public:
|
||||
static void SetUpTestSuite() {
|
||||
LibStoreTest::SetUpTestSuite();
|
||||
initGC();
|
||||
}
|
||||
|
||||
protected:
|
||||
LibExprTest()
|
||||
: LibStoreTest()
|
||||
, state({}, store, fetchSettings, evalSettings, nullptr)
|
||||
{
|
||||
evalSettings.nixPath = {};
|
||||
}
|
||||
Value eval(std::string input, bool forceValue = true) {
|
||||
Value v;
|
||||
Expr * e = state.parseExprFromString(input, state.rootPath(CanonPath::root));
|
||||
assert(e);
|
||||
state.eval(e, v);
|
||||
if (forceValue)
|
||||
state.forceValue(v, noPos);
|
||||
return v;
|
||||
}
|
||||
|
||||
Value * maybeThunk(std::string input, bool forceValue = true) {
|
||||
Expr * e = state.parseExprFromString(input, state.rootPath(CanonPath::root));
|
||||
assert(e);
|
||||
return e->maybeThunk(state, state.baseEnv);
|
||||
}
|
||||
|
||||
Symbol createSymbol(const char * value) {
|
||||
return state.symbols.create(value);
|
||||
}
|
||||
|
||||
bool readOnlyMode = true;
|
||||
fetchers::Settings fetchSettings{};
|
||||
EvalSettings evalSettings{readOnlyMode};
|
||||
EvalState state;
|
||||
};
|
||||
|
||||
MATCHER(IsListType, "") {
|
||||
return arg != nList;
|
||||
class LibExprTest : public LibStoreTest
|
||||
{
|
||||
public:
|
||||
static void SetUpTestSuite()
|
||||
{
|
||||
LibStoreTest::SetUpTestSuite();
|
||||
initGC();
|
||||
}
|
||||
|
||||
MATCHER(IsList, "") {
|
||||
return arg.type() == nList;
|
||||
protected:
|
||||
LibExprTest()
|
||||
: LibStoreTest()
|
||||
, state({}, store, fetchSettings, evalSettings, nullptr)
|
||||
{
|
||||
evalSettings.nixPath = {};
|
||||
}
|
||||
|
||||
MATCHER(IsString, "") {
|
||||
return arg.type() == nString;
|
||||
Value eval(std::string input, bool forceValue = true)
|
||||
{
|
||||
Value v;
|
||||
Expr * e = state.parseExprFromString(input, state.rootPath(CanonPath::root));
|
||||
assert(e);
|
||||
state.eval(e, v);
|
||||
if (forceValue)
|
||||
state.forceValue(v, noPos);
|
||||
return v;
|
||||
}
|
||||
|
||||
MATCHER(IsNull, "") {
|
||||
return arg.type() == nNull;
|
||||
Value * maybeThunk(std::string input, bool forceValue = true)
|
||||
{
|
||||
Expr * e = state.parseExprFromString(input, state.rootPath(CanonPath::root));
|
||||
assert(e);
|
||||
return e->maybeThunk(state, state.baseEnv);
|
||||
}
|
||||
|
||||
MATCHER(IsThunk, "") {
|
||||
return arg.type() == nThunk;
|
||||
Symbol createSymbol(const char * value)
|
||||
{
|
||||
return state.symbols.create(value);
|
||||
}
|
||||
|
||||
MATCHER(IsAttrs, "") {
|
||||
return arg.type() == nAttrs;
|
||||
}
|
||||
bool readOnlyMode = true;
|
||||
fetchers::Settings fetchSettings{};
|
||||
EvalSettings evalSettings{readOnlyMode};
|
||||
EvalState state;
|
||||
};
|
||||
|
||||
MATCHER_P(IsStringEq, s, fmt("The string is equal to \"%1%\"", s)) {
|
||||
if (arg.type() != nString) {
|
||||
MATCHER(IsListType, "")
|
||||
{
|
||||
return arg != nList;
|
||||
}
|
||||
|
||||
MATCHER(IsList, "")
|
||||
{
|
||||
return arg.type() == nList;
|
||||
}
|
||||
|
||||
MATCHER(IsString, "")
|
||||
{
|
||||
return arg.type() == nString;
|
||||
}
|
||||
|
||||
MATCHER(IsNull, "")
|
||||
{
|
||||
return arg.type() == nNull;
|
||||
}
|
||||
|
||||
MATCHER(IsThunk, "")
|
||||
{
|
||||
return arg.type() == nThunk;
|
||||
}
|
||||
|
||||
MATCHER(IsAttrs, "")
|
||||
{
|
||||
return arg.type() == nAttrs;
|
||||
}
|
||||
|
||||
MATCHER_P(IsStringEq, s, fmt("The string is equal to \"%1%\"", s))
|
||||
{
|
||||
if (arg.type() != nString) {
|
||||
return false;
|
||||
}
|
||||
return std::string_view(arg.c_str()) == s;
|
||||
}
|
||||
|
||||
MATCHER_P(IsIntEq, v, fmt("The string is equal to \"%1%\"", v))
|
||||
{
|
||||
if (arg.type() != nInt) {
|
||||
return false;
|
||||
}
|
||||
return arg.integer().value == v;
|
||||
}
|
||||
|
||||
MATCHER_P(IsFloatEq, v, fmt("The float is equal to \"%1%\"", v))
|
||||
{
|
||||
if (arg.type() != nFloat) {
|
||||
return false;
|
||||
}
|
||||
return arg.fpoint() == v;
|
||||
}
|
||||
|
||||
MATCHER(IsTrue, "")
|
||||
{
|
||||
if (arg.type() != nBool) {
|
||||
return false;
|
||||
}
|
||||
return arg.boolean() == true;
|
||||
}
|
||||
|
||||
MATCHER(IsFalse, "")
|
||||
{
|
||||
if (arg.type() != nBool) {
|
||||
return false;
|
||||
}
|
||||
return arg.boolean() == false;
|
||||
}
|
||||
|
||||
MATCHER_P(IsPathEq, p, fmt("Is a path equal to \"%1%\"", p))
|
||||
{
|
||||
if (arg.type() != nPath) {
|
||||
*result_listener << "Expected a path got " << arg.type();
|
||||
return false;
|
||||
} else {
|
||||
auto path = arg.path();
|
||||
if (path.path != CanonPath(p)) {
|
||||
*result_listener << "Expected a path that equals \"" << p << "\" but got: " << path.path;
|
||||
return false;
|
||||
}
|
||||
return std::string_view(arg.c_str()) == s;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
MATCHER_P(IsIntEq, v, fmt("The string is equal to \"%1%\"", v)) {
|
||||
if (arg.type() != nInt) {
|
||||
return false;
|
||||
}
|
||||
return arg.integer().value == v;
|
||||
MATCHER_P(IsListOfSize, n, fmt("Is a list of size [%1%]", n))
|
||||
{
|
||||
if (arg.type() != nList) {
|
||||
*result_listener << "Expected list got " << arg.type();
|
||||
return false;
|
||||
} else if (arg.listSize() != (size_t) n) {
|
||||
*result_listener << "Expected as list of size " << n << " got " << arg.listSize();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
MATCHER_P(IsFloatEq, v, fmt("The float is equal to \"%1%\"", v)) {
|
||||
if (arg.type() != nFloat) {
|
||||
return false;
|
||||
}
|
||||
return arg.fpoint() == v;
|
||||
MATCHER_P(IsAttrsOfSize, n, fmt("Is a set of size [%1%]", n))
|
||||
{
|
||||
if (arg.type() != nAttrs) {
|
||||
*result_listener << "Expected set got " << arg.type();
|
||||
return false;
|
||||
} else if (arg.attrs()->size() != (size_t) n) {
|
||||
*result_listener << "Expected a set with " << n << " attributes but got " << arg.attrs()->size();
|
||||
return false;
|
||||
}
|
||||
|
||||
MATCHER(IsTrue, "") {
|
||||
if (arg.type() != nBool) {
|
||||
return false;
|
||||
}
|
||||
return arg.boolean() == true;
|
||||
}
|
||||
|
||||
MATCHER(IsFalse, "") {
|
||||
if (arg.type() != nBool) {
|
||||
return false;
|
||||
}
|
||||
return arg.boolean() == false;
|
||||
}
|
||||
|
||||
MATCHER_P(IsPathEq, p, fmt("Is a path equal to \"%1%\"", p)) {
|
||||
if (arg.type() != nPath) {
|
||||
*result_listener << "Expected a path got " << arg.type();
|
||||
return false;
|
||||
} else {
|
||||
auto path = arg.path();
|
||||
if (path.path != CanonPath(p)) {
|
||||
*result_listener << "Expected a path that equals \"" << p << "\" but got: " << path.path;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
MATCHER_P(IsListOfSize, n, fmt("Is a list of size [%1%]", n)) {
|
||||
if (arg.type() != nList) {
|
||||
*result_listener << "Expected list got " << arg.type();
|
||||
return false;
|
||||
} else if (arg.listSize() != (size_t)n) {
|
||||
*result_listener << "Expected as list of size " << n << " got " << arg.listSize();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
MATCHER_P(IsAttrsOfSize, n, fmt("Is a set of size [%1%]", n)) {
|
||||
if (arg.type() != nAttrs) {
|
||||
*result_listener << "Expected set got " << arg.type();
|
||||
return false;
|
||||
} else if (arg.attrs()->size() != (size_t) n) {
|
||||
*result_listener << "Expected a set with " << n << " attributes but got " << arg.attrs()->size();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} /* namespace nix */
|
||||
|
|
|
|||
|
|
@ -6,5 +6,4 @@ headers = files(
|
|||
'libexpr.hh',
|
||||
'nix_api_expr.hh',
|
||||
'value/context.hh',
|
||||
# hack for trailing newline
|
||||
)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ protected:
|
|||
state = nix_state_create(nullptr, nullptr, store);
|
||||
value = nix_alloc_value(nullptr, state);
|
||||
}
|
||||
|
||||
~nix_api_expr_test()
|
||||
{
|
||||
nix_gc_decref(nullptr, value);
|
||||
|
|
@ -28,4 +29,4 @@ protected:
|
|||
nix_value * value;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nixC
|
||||
|
|
|
|||
|
|
@ -9,23 +9,27 @@ namespace rc {
|
|||
using namespace nix;
|
||||
|
||||
template<>
|
||||
struct Arbitrary<NixStringContextElem::Opaque> {
|
||||
struct Arbitrary<NixStringContextElem::Opaque>
|
||||
{
|
||||
static Gen<NixStringContextElem::Opaque> arbitrary();
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Arbitrary<NixStringContextElem::Built> {
|
||||
struct Arbitrary<NixStringContextElem::Built>
|
||||
{
|
||||
static Gen<NixStringContextElem::Built> arbitrary();
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Arbitrary<NixStringContextElem::DrvDeep> {
|
||||
struct Arbitrary<NixStringContextElem::DrvDeep>
|
||||
{
|
||||
static Gen<NixStringContextElem::DrvDeep> arbitrary();
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Arbitrary<NixStringContextElem> {
|
||||
struct Arbitrary<NixStringContextElem>
|
||||
{
|
||||
static Gen<NixStringContextElem> arbitrary();
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace rc
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
project('nix-expr-test-support', 'cpp',
|
||||
project(
|
||||
'nix-expr-test-support',
|
||||
'cpp',
|
||||
version : files('.version'),
|
||||
default_options : [
|
||||
'cpp_std=c++2a',
|
||||
'cpp_std=c++23',
|
||||
# TODO(Qyriad): increase the warning level
|
||||
'warning_level=1',
|
||||
'errorlogs=true', # Please print logs for tests that fail
|
||||
|
|
@ -14,8 +16,7 @@ cxx = meson.get_compiler('cpp')
|
|||
|
||||
subdir('nix-meson-build-support/deps-lists')
|
||||
|
||||
deps_private_maybe_subproject = [
|
||||
]
|
||||
deps_private_maybe_subproject = []
|
||||
deps_public_maybe_subproject = [
|
||||
dependency('nix-util'),
|
||||
dependency('nix-util-test-support'),
|
||||
|
|
@ -47,7 +48,7 @@ this_library = library(
|
|||
include_directories : include_dirs,
|
||||
# TODO: Remove `-lrapidcheck` when https://github.com/emil-e/rapidcheck/pull/326
|
||||
# is available. See also ../libutil/build.meson
|
||||
link_args: linker_export_flags + ['-lrapidcheck'],
|
||||
link_args : linker_export_flags + [ '-lrapidcheck' ],
|
||||
prelink : true, # For C++ static initializers
|
||||
install : true,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#include <exception> // Needed by rapidcheck on Darwin
|
||||
#include <rapidcheck.h>
|
||||
|
||||
#include "nix/store/tests/path.hh"
|
||||
|
|
@ -36,4 +37,4 @@ Gen<NixStringContextElem> Arbitrary<NixStringContextElem>::arbitrary()
|
|||
});
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace rc
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include <nlohmann/json.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
#include <exception> // Needed by rapidcheck on Darwin
|
||||
#include <rapidcheck/gtest.h>
|
||||
|
||||
#include "nix/store/tests/derived-path.hh"
|
||||
|
|
@ -8,36 +9,30 @@
|
|||
namespace nix {
|
||||
|
||||
// Testing of trivial expressions
|
||||
class DerivedPathExpressionTest : public LibExprTest {};
|
||||
class DerivedPathExpressionTest : public LibExprTest
|
||||
{};
|
||||
|
||||
// FIXME: `RC_GTEST_FIXTURE_PROP` isn't calling `SetUpTestSuite` because it is
|
||||
// no a real fixture.
|
||||
//
|
||||
// See https://github.com/emil-e/rapidcheck/blob/master/doc/gtest.md#rc_gtest_fixture_propfixture-name-args
|
||||
TEST_F(DerivedPathExpressionTest, force_init)
|
||||
{
|
||||
}
|
||||
TEST_F(DerivedPathExpressionTest, force_init) {}
|
||||
|
||||
#ifndef COVERAGE
|
||||
|
||||
RC_GTEST_FIXTURE_PROP(
|
||||
DerivedPathExpressionTest,
|
||||
prop_opaque_path_round_trip,
|
||||
(const SingleDerivedPath::Opaque & o))
|
||||
RC_GTEST_FIXTURE_PROP(DerivedPathExpressionTest, prop_opaque_path_round_trip, (const SingleDerivedPath::Opaque & o))
|
||||
{
|
||||
auto * v = state.allocValue();
|
||||
state.mkStorePathString(o.path, *v);
|
||||
auto d = state.coerceToSingleDerivedPath(noPos, *v, "");
|
||||
RC_ASSERT(SingleDerivedPath { o } == d);
|
||||
RC_ASSERT(SingleDerivedPath{o} == d);
|
||||
}
|
||||
|
||||
// TODO use DerivedPath::Built for parameter once it supports a single output
|
||||
// path only.
|
||||
|
||||
RC_GTEST_FIXTURE_PROP(
|
||||
DerivedPathExpressionTest,
|
||||
prop_derived_path_built_placeholder_round_trip,
|
||||
(const SingleDerivedPath::Built & b))
|
||||
DerivedPathExpressionTest, prop_derived_path_built_placeholder_round_trip, (const SingleDerivedPath::Built & b))
|
||||
{
|
||||
/**
|
||||
* We set these in tests rather than the regular globals so we don't have
|
||||
|
|
@ -49,7 +44,7 @@ RC_GTEST_FIXTURE_PROP(
|
|||
auto * v = state.allocValue();
|
||||
state.mkOutputString(*v, b, std::nullopt, mockXpSettings);
|
||||
auto [d, _] = state.coerceToSingleDerivedPathUnchecked(noPos, *v, "", mockXpSettings);
|
||||
RC_ASSERT(SingleDerivedPath { b } == d);
|
||||
RC_ASSERT(SingleDerivedPath{b} == d);
|
||||
}
|
||||
|
||||
RC_GTEST_FIXTURE_PROP(
|
||||
|
|
@ -63,7 +58,7 @@ RC_GTEST_FIXTURE_PROP(
|
|||
auto * v = state.allocValue();
|
||||
state.mkOutputString(*v, b, outPath, mockXpSettings);
|
||||
auto [d, _] = state.coerceToSingleDerivedPathUnchecked(noPos, *v, "", mockXpSettings);
|
||||
RC_ASSERT(SingleDerivedPath { b } == d);
|
||||
RC_ASSERT(SingleDerivedPath{b} == d);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -6,7 +6,8 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
TEST(nix_isAllowedURI, http_example_com) {
|
||||
TEST(nix_isAllowedURI, http_example_com)
|
||||
{
|
||||
Strings allowed;
|
||||
allowed.push_back("http://example.com");
|
||||
|
||||
|
|
@ -20,7 +21,8 @@ TEST(nix_isAllowedURI, http_example_com) {
|
|||
ASSERT_FALSE(isAllowedURI("http://example.org/foo", allowed));
|
||||
}
|
||||
|
||||
TEST(nix_isAllowedURI, http_example_com_foo) {
|
||||
TEST(nix_isAllowedURI, http_example_com_foo)
|
||||
{
|
||||
Strings allowed;
|
||||
allowed.push_back("http://example.com/foo");
|
||||
|
||||
|
|
@ -34,7 +36,8 @@ TEST(nix_isAllowedURI, http_example_com_foo) {
|
|||
// ASSERT_TRUE(isAllowedURI("http://example.com/foo?ok=1", allowed));
|
||||
}
|
||||
|
||||
TEST(nix_isAllowedURI, http) {
|
||||
TEST(nix_isAllowedURI, http)
|
||||
{
|
||||
Strings allowed;
|
||||
allowed.push_back("http://");
|
||||
|
||||
|
|
@ -48,7 +51,8 @@ TEST(nix_isAllowedURI, http) {
|
|||
ASSERT_FALSE(isAllowedURI("http:foo", allowed));
|
||||
}
|
||||
|
||||
TEST(nix_isAllowedURI, https) {
|
||||
TEST(nix_isAllowedURI, https)
|
||||
{
|
||||
Strings allowed;
|
||||
allowed.push_back("https://");
|
||||
|
||||
|
|
@ -58,7 +62,8 @@ TEST(nix_isAllowedURI, https) {
|
|||
ASSERT_FALSE(isAllowedURI("http://example.com/https:", allowed));
|
||||
}
|
||||
|
||||
TEST(nix_isAllowedURI, absolute_path) {
|
||||
TEST(nix_isAllowedURI, absolute_path)
|
||||
{
|
||||
Strings allowed;
|
||||
allowed.push_back("/var/evil"); // bad idea
|
||||
|
||||
|
|
@ -76,7 +81,8 @@ TEST(nix_isAllowedURI, absolute_path) {
|
|||
ASSERT_FALSE(isAllowedURI("http://example.com//var/evil/foo", allowed));
|
||||
}
|
||||
|
||||
TEST(nix_isAllowedURI, file_url) {
|
||||
TEST(nix_isAllowedURI, file_url)
|
||||
{
|
||||
Strings allowed;
|
||||
allowed.push_back("file:///var/evil"); // bad idea
|
||||
|
||||
|
|
@ -103,7 +109,8 @@ TEST(nix_isAllowedURI, file_url) {
|
|||
ASSERT_FALSE(isAllowedURI("file://", allowed));
|
||||
}
|
||||
|
||||
TEST(nix_isAllowedURI, github_all) {
|
||||
TEST(nix_isAllowedURI, github_all)
|
||||
{
|
||||
Strings allowed;
|
||||
allowed.push_back("github:");
|
||||
ASSERT_TRUE(isAllowedURI("github:", allowed));
|
||||
|
|
@ -117,7 +124,8 @@ TEST(nix_isAllowedURI, github_all) {
|
|||
ASSERT_FALSE(isAllowedURI("github", allowed));
|
||||
}
|
||||
|
||||
TEST(nix_isAllowedURI, github_org) {
|
||||
TEST(nix_isAllowedURI, github_org)
|
||||
{
|
||||
Strings allowed;
|
||||
allowed.push_back("github:foo");
|
||||
ASSERT_FALSE(isAllowedURI("github:", allowed));
|
||||
|
|
@ -130,7 +138,8 @@ TEST(nix_isAllowedURI, github_org) {
|
|||
ASSERT_FALSE(isAllowedURI("file:///github:foo/bar/archive/master.tar.gz", allowed));
|
||||
}
|
||||
|
||||
TEST(nix_isAllowedURI, non_scheme_colon) {
|
||||
TEST(nix_isAllowedURI, non_scheme_colon)
|
||||
{
|
||||
Strings allowed;
|
||||
allowed.push_back("https://foo/bar:");
|
||||
ASSERT_TRUE(isAllowedURI("https://foo/bar:", allowed));
|
||||
|
|
@ -138,16 +147,19 @@ TEST(nix_isAllowedURI, non_scheme_colon) {
|
|||
ASSERT_FALSE(isAllowedURI("https://foo/bar:baz", allowed));
|
||||
}
|
||||
|
||||
class EvalStateTest : public LibExprTest {};
|
||||
class EvalStateTest : public LibExprTest
|
||||
{};
|
||||
|
||||
TEST_F(EvalStateTest, getBuiltins_ok) {
|
||||
TEST_F(EvalStateTest, getBuiltins_ok)
|
||||
{
|
||||
auto evaled = maybeThunk("builtins");
|
||||
auto & builtins = state.getBuiltins();
|
||||
ASSERT_TRUE(builtins.type() == nAttrs);
|
||||
ASSERT_EQ(evaled, &builtins);
|
||||
}
|
||||
|
||||
TEST_F(EvalStateTest, getBuiltin_ok) {
|
||||
TEST_F(EvalStateTest, getBuiltin_ok)
|
||||
{
|
||||
auto & builtin = state.getBuiltin("toString");
|
||||
ASSERT_TRUE(builtin.type() == nFunction);
|
||||
// FIXME
|
||||
|
|
@ -157,7 +169,8 @@ TEST_F(EvalStateTest, getBuiltin_ok) {
|
|||
ASSERT_EQ(state.forceBool(builtin2, noPos, "in unit test"), true);
|
||||
}
|
||||
|
||||
TEST_F(EvalStateTest, getBuiltin_fail) {
|
||||
TEST_F(EvalStateTest, getBuiltin_fail)
|
||||
{
|
||||
ASSERT_THROW(state.getBuiltin("nonexistent"), EvalError);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,65 +4,75 @@
|
|||
namespace nix {
|
||||
// Testing the conversion to JSON
|
||||
|
||||
class JSONValueTest : public LibExprTest {
|
||||
protected:
|
||||
std::string getJSONValue(Value& value) {
|
||||
std::stringstream ss;
|
||||
NixStringContext ps;
|
||||
printValueAsJSON(state, true, value, noPos, ss, ps);
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(JSONValueTest, null) {
|
||||
Value v;
|
||||
v.mkNull();
|
||||
ASSERT_EQ(getJSONValue(v), "null");
|
||||
class JSONValueTest : public LibExprTest
|
||||
{
|
||||
protected:
|
||||
std::string getJSONValue(Value & value)
|
||||
{
|
||||
std::stringstream ss;
|
||||
NixStringContext ps;
|
||||
printValueAsJSON(state, true, value, noPos, ss, ps);
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(JSONValueTest, BoolFalse) {
|
||||
Value v;
|
||||
v.mkBool(false);
|
||||
ASSERT_EQ(getJSONValue(v),"false");
|
||||
}
|
||||
TEST_F(JSONValueTest, null)
|
||||
{
|
||||
Value v;
|
||||
v.mkNull();
|
||||
ASSERT_EQ(getJSONValue(v), "null");
|
||||
}
|
||||
|
||||
TEST_F(JSONValueTest, BoolTrue) {
|
||||
Value v;
|
||||
v.mkBool(true);
|
||||
ASSERT_EQ(getJSONValue(v), "true");
|
||||
}
|
||||
TEST_F(JSONValueTest, BoolFalse)
|
||||
{
|
||||
Value v;
|
||||
v.mkBool(false);
|
||||
ASSERT_EQ(getJSONValue(v), "false");
|
||||
}
|
||||
|
||||
TEST_F(JSONValueTest, IntPositive) {
|
||||
Value v;
|
||||
v.mkInt(100);
|
||||
ASSERT_EQ(getJSONValue(v), "100");
|
||||
}
|
||||
TEST_F(JSONValueTest, BoolTrue)
|
||||
{
|
||||
Value v;
|
||||
v.mkBool(true);
|
||||
ASSERT_EQ(getJSONValue(v), "true");
|
||||
}
|
||||
|
||||
TEST_F(JSONValueTest, IntNegative) {
|
||||
Value v;
|
||||
v.mkInt(-100);
|
||||
ASSERT_EQ(getJSONValue(v), "-100");
|
||||
}
|
||||
TEST_F(JSONValueTest, IntPositive)
|
||||
{
|
||||
Value v;
|
||||
v.mkInt(100);
|
||||
ASSERT_EQ(getJSONValue(v), "100");
|
||||
}
|
||||
|
||||
TEST_F(JSONValueTest, String) {
|
||||
Value v;
|
||||
v.mkString("test");
|
||||
ASSERT_EQ(getJSONValue(v), "\"test\"");
|
||||
}
|
||||
TEST_F(JSONValueTest, IntNegative)
|
||||
{
|
||||
Value v;
|
||||
v.mkInt(-100);
|
||||
ASSERT_EQ(getJSONValue(v), "-100");
|
||||
}
|
||||
|
||||
TEST_F(JSONValueTest, StringQuotes) {
|
||||
Value v;
|
||||
TEST_F(JSONValueTest, String)
|
||||
{
|
||||
Value v;
|
||||
v.mkStringNoCopy("test");
|
||||
ASSERT_EQ(getJSONValue(v), "\"test\"");
|
||||
}
|
||||
|
||||
v.mkString("test\"");
|
||||
ASSERT_EQ(getJSONValue(v), "\"test\\\"\"");
|
||||
}
|
||||
TEST_F(JSONValueTest, StringQuotes)
|
||||
{
|
||||
Value v;
|
||||
|
||||
// The dummy store doesn't support writing files. Fails with this exception message:
|
||||
// C++ exception with description "error: operation 'addToStoreFromDump' is
|
||||
// not supported by store 'dummy'" thrown in the test body.
|
||||
TEST_F(JSONValueTest, DISABLED_Path) {
|
||||
Value v;
|
||||
v.mkPath(state.rootPath(CanonPath("/test")));
|
||||
ASSERT_EQ(getJSONValue(v), "\"/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x\"");
|
||||
}
|
||||
v.mkStringNoCopy("test\"");
|
||||
ASSERT_EQ(getJSONValue(v), "\"test\\\"\"");
|
||||
}
|
||||
|
||||
// The dummy store doesn't support writing files. Fails with this exception message:
|
||||
// C++ exception with description "error: operation 'addToStoreFromDump' is
|
||||
// not supported by store 'dummy'" thrown in the test body.
|
||||
TEST_F(JSONValueTest, DISABLED_Path)
|
||||
{
|
||||
Value v;
|
||||
v.mkPath(state.rootPath(CanonPath("/test")));
|
||||
ASSERT_EQ(getJSONValue(v), "\"/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x\"");
|
||||
}
|
||||
} /* namespace nix */
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@
|
|||
|
||||
using namespace nix;
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
if (argc > 1 && std::string_view(argv[1]) == "__build-remote") {
|
||||
printError("test-build-remote: not supported in libexpr unit tests");
|
||||
return 1;
|
||||
|
|
@ -14,25 +15,26 @@ int main (int argc, char **argv) {
|
|||
// Disable build hook. We won't be testing remote builds in these unit tests. If we do, fix the above build hook.
|
||||
settings.buildHook = {};
|
||||
|
||||
#ifdef __linux__ // should match the conditional around sandboxBuildDir declaration.
|
||||
#ifdef __linux__ // should match the conditional around sandboxBuildDir declaration.
|
||||
|
||||
// When building and testing nix within the host's Nix sandbox, our store dir will be located in the host's sandboxBuildDir, e.g.:
|
||||
// Host
|
||||
// When building and testing nix within the host's Nix sandbox, our store dir will be located in the host's
|
||||
// sandboxBuildDir, e.g.: Host
|
||||
// storeDir = /nix/store
|
||||
// sandboxBuildDir = /build
|
||||
// This process
|
||||
// storeDir = /build/foo/bar/store
|
||||
// sandboxBuildDir = /build
|
||||
// However, we have a rule that the store dir must not be inside the storeDir, so we need to pick a different sandboxBuildDir.
|
||||
// However, we have a rule that the store dir must not be inside the storeDir, so we need to pick a different
|
||||
// sandboxBuildDir.
|
||||
settings.sandboxBuildDir = "/test-build-dir-instead-of-usual-build-dir";
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#ifdef __APPLE__
|
||||
// Avoid this error, when already running in a sandbox:
|
||||
// sandbox-exec: sandbox_apply: Operation not permitted
|
||||
settings.sandboxMode = smDisabled;
|
||||
setEnv("_NIX_TEST_NO_SANDBOX", "1");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// For pipe operator tests in trivial.cc
|
||||
experimentalFeatureSettings.set("experimental-features", "pipe-operators");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
project('nix-expr-tests', 'cpp',
|
||||
project(
|
||||
'nix-expr-tests',
|
||||
'cpp',
|
||||
version : files('.version'),
|
||||
default_options : [
|
||||
'cpp_std=c++2a',
|
||||
'cpp_std=c++23',
|
||||
# TODO(Qyriad): increase the warning level
|
||||
'warning_level=1',
|
||||
'errorlogs=true', # Please print logs for tests that fail
|
||||
|
|
@ -19,8 +21,7 @@ deps_private_maybe_subproject = [
|
|||
dependency('nix-expr-c'),
|
||||
dependency('nix-expr-test-support'),
|
||||
]
|
||||
deps_public_maybe_subproject = [
|
||||
]
|
||||
deps_public_maybe_subproject = []
|
||||
subdir('nix-meson-build-support/subprojects')
|
||||
|
||||
subdir('nix-meson-build-support/export-all-symbols')
|
||||
|
|
@ -54,6 +55,7 @@ sources = files(
|
|||
'nix_api_expr.cc',
|
||||
'nix_api_external.cc',
|
||||
'nix_api_value.cc',
|
||||
'nix_api_value_internal.cc',
|
||||
'primops.cc',
|
||||
'search-path.cc',
|
||||
'trivial.cc',
|
||||
|
|
@ -62,7 +64,7 @@ sources = files(
|
|||
'value/value.cc',
|
||||
)
|
||||
|
||||
include_dirs = [include_directories('.')]
|
||||
include_dirs = [ include_directories('.') ]
|
||||
|
||||
|
||||
this_exe = executable(
|
||||
|
|
@ -72,15 +74,16 @@ this_exe = executable(
|
|||
dependencies : deps_private_subproject + deps_private + deps_other,
|
||||
include_directories : include_dirs,
|
||||
# TODO: -lrapidcheck, see ../libutil-support/build.meson
|
||||
link_args: linker_export_flags + ['-lrapidcheck'],
|
||||
link_args : linker_export_flags + [ '-lrapidcheck' ],
|
||||
install : true,
|
||||
cpp_pch : do_pch ? [ 'pch/precompiled-headers.hh' ] : [],
|
||||
)
|
||||
|
||||
test(
|
||||
meson.project_name(),
|
||||
this_exe,
|
||||
env : {
|
||||
'_NIX_TEST_UNIT_DATA': meson.current_source_dir() / 'data',
|
||||
'_NIX_TEST_UNIT_DATA' : meson.current_source_dir() / 'data',
|
||||
},
|
||||
protocol : 'gtest',
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
#include "nix_api_store.h"
|
||||
#include "nix_api_store_internal.h"
|
||||
#include "nix_api_util.h"
|
||||
#include "nix_api_util_internal.h"
|
||||
#include "nix_api_expr.h"
|
||||
#include "nix_api_value.h"
|
||||
|
||||
|
|
@ -151,8 +149,8 @@ TEST_F(nix_api_expr_test, nix_expr_realise_context_bad_value)
|
|||
assert_ctx_ok();
|
||||
auto r = nix_string_realise(ctx, state, value, false);
|
||||
ASSERT_EQ(nullptr, r);
|
||||
ASSERT_EQ(ctx->last_err_code, NIX_ERR_NIX_ERROR);
|
||||
ASSERT_THAT(ctx->last_err, testing::Optional(testing::HasSubstr("cannot coerce")));
|
||||
ASSERT_EQ(nix_err_code(ctx), NIX_ERR_NIX_ERROR);
|
||||
ASSERT_THAT(nix_err_msg(nullptr, ctx, nullptr), testing::HasSubstr("cannot coerce"));
|
||||
}
|
||||
|
||||
TEST_F(nix_api_expr_test, nix_expr_realise_context_bad_build)
|
||||
|
|
@ -168,8 +166,8 @@ TEST_F(nix_api_expr_test, nix_expr_realise_context_bad_build)
|
|||
assert_ctx_ok();
|
||||
auto r = nix_string_realise(ctx, state, value, false);
|
||||
ASSERT_EQ(nullptr, r);
|
||||
ASSERT_EQ(ctx->last_err_code, NIX_ERR_NIX_ERROR);
|
||||
ASSERT_THAT(ctx->last_err, testing::Optional(testing::HasSubstr("failed with exit code 1")));
|
||||
ASSERT_EQ(nix_err_code(ctx), NIX_ERR_NIX_ERROR);
|
||||
ASSERT_THAT(nix_err_msg(nullptr, ctx, nullptr), testing::HasSubstr("failed with exit code 1"));
|
||||
}
|
||||
|
||||
TEST_F(nix_api_expr_test, nix_expr_realise_context)
|
||||
|
|
@ -381,12 +379,11 @@ TEST_F(nix_api_expr_test, nix_expr_primop_bad_no_return)
|
|||
nix_value * result = nix_alloc_value(ctx, state);
|
||||
assert_ctx_ok();
|
||||
nix_value_call(ctx, state, primopValue, three, result);
|
||||
ASSERT_EQ(ctx->last_err_code, NIX_ERR_NIX_ERROR);
|
||||
ASSERT_EQ(nix_err_code(ctx), NIX_ERR_NIX_ERROR);
|
||||
ASSERT_THAT(
|
||||
ctx->last_err,
|
||||
testing::Optional(
|
||||
testing::HasSubstr("Implementation error in custom function: return value was not initialized")));
|
||||
ASSERT_THAT(ctx->last_err, testing::Optional(testing::HasSubstr("badNoReturn")));
|
||||
nix_err_msg(nullptr, ctx, nullptr),
|
||||
testing::HasSubstr("Implementation error in custom function: return value was not initialized"));
|
||||
ASSERT_THAT(nix_err_msg(nullptr, ctx, nullptr), testing::HasSubstr("badNoReturn"));
|
||||
}
|
||||
|
||||
static void primop_bad_return_thunk(
|
||||
|
|
@ -394,6 +391,7 @@ static void primop_bad_return_thunk(
|
|||
{
|
||||
nix_init_apply(context, ret, args[0], args[1]);
|
||||
}
|
||||
|
||||
TEST_F(nix_api_expr_test, nix_expr_primop_bad_return_thunk)
|
||||
{
|
||||
PrimOp * primop =
|
||||
|
|
@ -418,12 +416,11 @@ TEST_F(nix_api_expr_test, nix_expr_primop_bad_return_thunk)
|
|||
assert_ctx_ok();
|
||||
NIX_VALUE_CALL(ctx, state, result, primopValue, toString, four);
|
||||
|
||||
ASSERT_EQ(ctx->last_err_code, NIX_ERR_NIX_ERROR);
|
||||
ASSERT_EQ(nix_err_code(ctx), NIX_ERR_NIX_ERROR);
|
||||
ASSERT_THAT(
|
||||
ctx->last_err,
|
||||
testing::Optional(
|
||||
testing::HasSubstr("Implementation error in custom function: return value must not be a thunk")));
|
||||
ASSERT_THAT(ctx->last_err, testing::Optional(testing::HasSubstr("badReturnThunk")));
|
||||
nix_err_msg(nullptr, ctx, nullptr),
|
||||
testing::HasSubstr("Implementation error in custom function: return value must not be a thunk"));
|
||||
ASSERT_THAT(nix_err_msg(nullptr, ctx, nullptr), testing::HasSubstr("badReturnThunk"));
|
||||
}
|
||||
|
||||
TEST_F(nix_api_expr_test, nix_value_call_multi_no_args)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
#include "nix_api_store.h"
|
||||
#include "nix_api_store_internal.h"
|
||||
#include "nix_api_util.h"
|
||||
#include "nix_api_util_internal.h"
|
||||
#include "nix_api_expr.h"
|
||||
#include "nix_api_expr_internal.h"
|
||||
#include "nix_api_value.h"
|
||||
#include "nix_api_external.h"
|
||||
|
||||
|
|
@ -27,6 +24,7 @@ public:
|
|||
|
||||
private:
|
||||
int _x;
|
||||
|
||||
static void print_function(void * self, nix_printer * printer) {}
|
||||
|
||||
static void show_type_function(void * self, nix_string_return * res) {}
|
||||
|
|
@ -38,7 +36,7 @@ private:
|
|||
std::string type_string = "nix-external<MyExternalValueDesc( ";
|
||||
type_string += std::to_string(obj->_x);
|
||||
type_string += " )>";
|
||||
res->str = &*type_string.begin();
|
||||
nix_set_string_return(res, &*type_string.begin());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -68,4 +66,4 @@ TEST_F(nix_api_expr_test, nix_expr_eval_external)
|
|||
nix_state_free(stateFn);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nixC
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
#include "nix_api_store.h"
|
||||
#include "nix_api_store_internal.h"
|
||||
#include "nix_api_util.h"
|
||||
#include "nix_api_util_internal.h"
|
||||
#include "nix_api_expr.h"
|
||||
#include "nix_api_value.h"
|
||||
#include "nix_api_expr_internal.h"
|
||||
|
||||
#include "nix/expr/tests/nix_api_expr.hh"
|
||||
#include "nix/util/tests/string_callback.hh"
|
||||
|
|
@ -16,14 +13,6 @@
|
|||
|
||||
namespace nixC {
|
||||
|
||||
TEST_F(nix_api_expr_test, as_nix_value_ptr)
|
||||
{
|
||||
// nix_alloc_value casts nix::Value to nix_value
|
||||
// It should be obvious from the decl that that works, but if it doesn't,
|
||||
// the whole implementation would be utterly broken.
|
||||
ASSERT_EQ(sizeof(nix::Value), sizeof(nix_value));
|
||||
}
|
||||
|
||||
TEST_F(nix_api_expr_test, nix_value_get_int_invalid)
|
||||
{
|
||||
ASSERT_EQ(0, nix_get_int(ctx, nullptr));
|
||||
|
|
@ -120,6 +109,7 @@ TEST_F(nix_api_expr_test, nix_value_set_get_path_invalid)
|
|||
ASSERT_EQ(nullptr, nix_get_path_string(ctx, value));
|
||||
assert_ctx_err();
|
||||
}
|
||||
|
||||
TEST_F(nix_api_expr_test, nix_value_set_get_path)
|
||||
{
|
||||
const char * p = "/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname";
|
||||
|
|
@ -319,8 +309,10 @@ TEST_F(nix_api_expr_test, nix_value_init_apply_error)
|
|||
|
||||
// Evaluate it
|
||||
nix_value_force(ctx, state, v);
|
||||
ASSERT_EQ(ctx->last_err_code, NIX_ERR_NIX_ERROR);
|
||||
ASSERT_THAT(ctx->last_err.value(), testing::HasSubstr("attempt to call something which is not a function but"));
|
||||
ASSERT_EQ(nix_err_code(ctx), NIX_ERR_NIX_ERROR);
|
||||
ASSERT_THAT(
|
||||
nix_err_msg(nullptr, ctx, nullptr),
|
||||
testing::HasSubstr("attempt to call something which is not a function but"));
|
||||
|
||||
// Clean up
|
||||
nix_gc_decref(ctx, some_string);
|
||||
|
|
@ -379,7 +371,9 @@ TEST_F(nix_api_expr_test, nix_value_init_apply_lazy_arg)
|
|||
// nix_get_attr_byname isn't lazy (it could have been) so it will throw the exception
|
||||
nix_value * foo = nix_get_attr_byname(ctx, r, state, "foo");
|
||||
ASSERT_EQ(nullptr, foo);
|
||||
ASSERT_THAT(ctx->last_err.value(), testing::HasSubstr("error message for test case nix_value_init_apply_lazy_arg"));
|
||||
ASSERT_THAT(
|
||||
nix_err_msg(nullptr, ctx, nullptr),
|
||||
testing::HasSubstr("error message for test case nix_value_init_apply_lazy_arg"));
|
||||
|
||||
// Clean up
|
||||
nix_gc_decref(ctx, f);
|
||||
|
|
@ -399,4 +393,4 @@ TEST_F(nix_api_expr_test, nix_copy_value)
|
|||
nix_gc_decref(ctx, source);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nixC
|
||||
|
|
|
|||
25
src/libexpr-tests/nix_api_value_internal.cc
Normal file
25
src/libexpr-tests/nix_api_value_internal.cc
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#include "nix_api_store.h"
|
||||
#include "nix_api_util.h"
|
||||
#include "nix_api_expr.h"
|
||||
#include "nix_api_value.h"
|
||||
#include "nix_api_expr_internal.h"
|
||||
|
||||
#include "nix/expr/tests/nix_api_expr.hh"
|
||||
#include "nix/util/tests/string_callback.hh"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace nixC {
|
||||
|
||||
TEST_F(nix_api_expr_test, as_nix_value_ptr)
|
||||
{
|
||||
// nix_alloc_value casts nix::Value to nix_value
|
||||
// It should be obvious from the decl that that works, but if it doesn't,
|
||||
// the whole implementation would be utterly broken.
|
||||
ASSERT_EQ(sizeof(nix::Value), sizeof(nix_value));
|
||||
}
|
||||
|
||||
} // namespace nixC
|
||||
4
src/libexpr-tests/pch/precompiled-headers.hh
Normal file
4
src/libexpr-tests/pch/precompiled-headers.hh
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
#include "nix/expr/tests/libexpr.hh"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -5,86 +5,98 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
TEST(LookupPathElem, parse_justPath) {
|
||||
TEST(LookupPathElem, parse_justPath)
|
||||
{
|
||||
ASSERT_EQ(
|
||||
LookupPath::Elem::parse("foo"),
|
||||
(LookupPath::Elem {
|
||||
.prefix = LookupPath::Prefix { .s = "" },
|
||||
.path = LookupPath::Path { .s = "foo" },
|
||||
(LookupPath::Elem{
|
||||
.prefix = LookupPath::Prefix{.s = ""},
|
||||
.path = LookupPath::Path{.s = "foo"},
|
||||
}));
|
||||
}
|
||||
|
||||
TEST(LookupPathElem, parse_emptyPrefix) {
|
||||
TEST(LookupPathElem, parse_emptyPrefix)
|
||||
{
|
||||
ASSERT_EQ(
|
||||
LookupPath::Elem::parse("=foo"),
|
||||
(LookupPath::Elem {
|
||||
.prefix = LookupPath::Prefix { .s = "" },
|
||||
.path = LookupPath::Path { .s = "foo" },
|
||||
(LookupPath::Elem{
|
||||
.prefix = LookupPath::Prefix{.s = ""},
|
||||
.path = LookupPath::Path{.s = "foo"},
|
||||
}));
|
||||
}
|
||||
|
||||
TEST(LookupPathElem, parse_oneEq) {
|
||||
TEST(LookupPathElem, parse_oneEq)
|
||||
{
|
||||
ASSERT_EQ(
|
||||
LookupPath::Elem::parse("foo=bar"),
|
||||
(LookupPath::Elem {
|
||||
.prefix = LookupPath::Prefix { .s = "foo" },
|
||||
.path = LookupPath::Path { .s = "bar" },
|
||||
(LookupPath::Elem{
|
||||
.prefix = LookupPath::Prefix{.s = "foo"},
|
||||
.path = LookupPath::Path{.s = "bar"},
|
||||
}));
|
||||
}
|
||||
|
||||
TEST(LookupPathElem, parse_twoEqs) {
|
||||
TEST(LookupPathElem, parse_twoEqs)
|
||||
{
|
||||
ASSERT_EQ(
|
||||
LookupPath::Elem::parse("foo=bar=baz"),
|
||||
(LookupPath::Elem {
|
||||
.prefix = LookupPath::Prefix { .s = "foo" },
|
||||
.path = LookupPath::Path { .s = "bar=baz" },
|
||||
(LookupPath::Elem{
|
||||
.prefix = LookupPath::Prefix{.s = "foo"},
|
||||
.path = LookupPath::Path{.s = "bar=baz"},
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
TEST(LookupPathElem, suffixIfPotentialMatch_justPath) {
|
||||
LookupPath::Prefix prefix { .s = "" };
|
||||
ASSERT_EQ(prefix.suffixIfPotentialMatch("any/thing"), std::optional { "any/thing" });
|
||||
TEST(LookupPathElem, suffixIfPotentialMatch_justPath)
|
||||
{
|
||||
LookupPath::Prefix prefix{.s = ""};
|
||||
ASSERT_EQ(prefix.suffixIfPotentialMatch("any/thing"), std::optional{"any/thing"});
|
||||
}
|
||||
|
||||
TEST(LookupPathElem, suffixIfPotentialMatch_misleadingPrefix1) {
|
||||
LookupPath::Prefix prefix { .s = "foo" };
|
||||
TEST(LookupPathElem, suffixIfPotentialMatch_misleadingPrefix1)
|
||||
{
|
||||
LookupPath::Prefix prefix{.s = "foo"};
|
||||
ASSERT_EQ(prefix.suffixIfPotentialMatch("fooX"), std::nullopt);
|
||||
}
|
||||
|
||||
TEST(LookupPathElem, suffixIfPotentialMatch_misleadingPrefix2) {
|
||||
LookupPath::Prefix prefix { .s = "foo" };
|
||||
TEST(LookupPathElem, suffixIfPotentialMatch_misleadingPrefix2)
|
||||
{
|
||||
LookupPath::Prefix prefix{.s = "foo"};
|
||||
ASSERT_EQ(prefix.suffixIfPotentialMatch("fooX/bar"), std::nullopt);
|
||||
}
|
||||
|
||||
TEST(LookupPathElem, suffixIfPotentialMatch_partialPrefix) {
|
||||
LookupPath::Prefix prefix { .s = "fooX" };
|
||||
TEST(LookupPathElem, suffixIfPotentialMatch_partialPrefix)
|
||||
{
|
||||
LookupPath::Prefix prefix{.s = "fooX"};
|
||||
ASSERT_EQ(prefix.suffixIfPotentialMatch("foo"), std::nullopt);
|
||||
}
|
||||
|
||||
TEST(LookupPathElem, suffixIfPotentialMatch_exactPrefix) {
|
||||
LookupPath::Prefix prefix { .s = "foo" };
|
||||
ASSERT_EQ(prefix.suffixIfPotentialMatch("foo"), std::optional { "" });
|
||||
TEST(LookupPathElem, suffixIfPotentialMatch_exactPrefix)
|
||||
{
|
||||
LookupPath::Prefix prefix{.s = "foo"};
|
||||
ASSERT_EQ(prefix.suffixIfPotentialMatch("foo"), std::optional{""});
|
||||
}
|
||||
|
||||
TEST(LookupPathElem, suffixIfPotentialMatch_multiKey) {
|
||||
LookupPath::Prefix prefix { .s = "foo/bar" };
|
||||
ASSERT_EQ(prefix.suffixIfPotentialMatch("foo/bar/baz"), std::optional { "baz" });
|
||||
TEST(LookupPathElem, suffixIfPotentialMatch_multiKey)
|
||||
{
|
||||
LookupPath::Prefix prefix{.s = "foo/bar"};
|
||||
ASSERT_EQ(prefix.suffixIfPotentialMatch("foo/bar/baz"), std::optional{"baz"});
|
||||
}
|
||||
|
||||
TEST(LookupPathElem, suffixIfPotentialMatch_trailingSlash) {
|
||||
LookupPath::Prefix prefix { .s = "foo" };
|
||||
ASSERT_EQ(prefix.suffixIfPotentialMatch("foo/"), std::optional { "" });
|
||||
TEST(LookupPathElem, suffixIfPotentialMatch_trailingSlash)
|
||||
{
|
||||
LookupPath::Prefix prefix{.s = "foo"};
|
||||
ASSERT_EQ(prefix.suffixIfPotentialMatch("foo/"), std::optional{""});
|
||||
}
|
||||
|
||||
TEST(LookupPathElem, suffixIfPotentialMatch_trailingDoubleSlash) {
|
||||
LookupPath::Prefix prefix { .s = "foo" };
|
||||
ASSERT_EQ(prefix.suffixIfPotentialMatch("foo//"), std::optional { "/" });
|
||||
TEST(LookupPathElem, suffixIfPotentialMatch_trailingDoubleSlash)
|
||||
{
|
||||
LookupPath::Prefix prefix{.s = "foo"};
|
||||
ASSERT_EQ(prefix.suffixIfPotentialMatch("foo//"), std::optional{"/"});
|
||||
}
|
||||
|
||||
TEST(LookupPathElem, suffixIfPotentialMatch_trailingPath) {
|
||||
LookupPath::Prefix prefix { .s = "foo" };
|
||||
ASSERT_EQ(prefix.suffixIfPotentialMatch("foo/bar/baz"), std::optional { "bar/baz" });
|
||||
TEST(LookupPathElem, suffixIfPotentialMatch_trailingPath)
|
||||
{
|
||||
LookupPath::Prefix prefix{.s = "foo"};
|
||||
ASSERT_EQ(prefix.suffixIfPotentialMatch("foo/bar/baz"), std::optional{"bar/baz"});
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -1,181 +1,202 @@
|
|||
#include "nix/expr/tests/libexpr.hh"
|
||||
|
||||
namespace nix {
|
||||
// Testing of trivial expressions
|
||||
class TrivialExpressionTest : public LibExprTest {};
|
||||
// Testing of trivial expressions
|
||||
class TrivialExpressionTest : public LibExprTest
|
||||
{};
|
||||
|
||||
TEST_F(TrivialExpressionTest, true) {
|
||||
auto v = eval("true");
|
||||
ASSERT_THAT(v, IsTrue());
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, true)
|
||||
{
|
||||
auto v = eval("true");
|
||||
ASSERT_THAT(v, IsTrue());
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, false) {
|
||||
auto v = eval("false");
|
||||
ASSERT_THAT(v, IsFalse());
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, false)
|
||||
{
|
||||
auto v = eval("false");
|
||||
ASSERT_THAT(v, IsFalse());
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, null) {
|
||||
auto v = eval("null");
|
||||
ASSERT_THAT(v, IsNull());
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, null)
|
||||
{
|
||||
auto v = eval("null");
|
||||
ASSERT_THAT(v, IsNull());
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, 1) {
|
||||
auto v = eval("1");
|
||||
ASSERT_THAT(v, IsIntEq(1));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, 1)
|
||||
{
|
||||
auto v = eval("1");
|
||||
ASSERT_THAT(v, IsIntEq(1));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, 1plus1) {
|
||||
auto v = eval("1+1");
|
||||
ASSERT_THAT(v, IsIntEq(2));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, 1plus1)
|
||||
{
|
||||
auto v = eval("1+1");
|
||||
ASSERT_THAT(v, IsIntEq(2));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, minus1) {
|
||||
auto v = eval("-1");
|
||||
ASSERT_THAT(v, IsIntEq(-1));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, minus1)
|
||||
{
|
||||
auto v = eval("-1");
|
||||
ASSERT_THAT(v, IsIntEq(-1));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, 1minus1) {
|
||||
auto v = eval("1-1");
|
||||
ASSERT_THAT(v, IsIntEq(0));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, 1minus1)
|
||||
{
|
||||
auto v = eval("1-1");
|
||||
ASSERT_THAT(v, IsIntEq(0));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, lambdaAdd) {
|
||||
auto v = eval("let add = a: b: a + b; in add 1 2");
|
||||
ASSERT_THAT(v, IsIntEq(3));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, lambdaAdd)
|
||||
{
|
||||
auto v = eval("let add = a: b: a + b; in add 1 2");
|
||||
ASSERT_THAT(v, IsIntEq(3));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, list) {
|
||||
auto v = eval("[]");
|
||||
ASSERT_THAT(v, IsListOfSize(0));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, list)
|
||||
{
|
||||
auto v = eval("[]");
|
||||
ASSERT_THAT(v, IsListOfSize(0));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, attrs) {
|
||||
auto v = eval("{}");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(0));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, attrs)
|
||||
{
|
||||
auto v = eval("{}");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(0));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, float) {
|
||||
auto v = eval("1.234");
|
||||
ASSERT_THAT(v, IsFloatEq(1.234));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, float)
|
||||
{
|
||||
auto v = eval("1.234");
|
||||
ASSERT_THAT(v, IsFloatEq(1.234));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, updateAttrs) {
|
||||
auto v = eval("{ a = 1; } // { b = 2; a = 3; }");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(2));
|
||||
auto a = v.attrs()->find(createSymbol("a"));
|
||||
ASSERT_NE(a, nullptr);
|
||||
ASSERT_THAT(*a->value, IsIntEq(3));
|
||||
TEST_F(TrivialExpressionTest, updateAttrs)
|
||||
{
|
||||
auto v = eval("{ a = 1; } // { b = 2; a = 3; }");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(2));
|
||||
auto a = v.attrs()->get(createSymbol("a"));
|
||||
ASSERT_NE(a, nullptr);
|
||||
ASSERT_THAT(*a->value, IsIntEq(3));
|
||||
|
||||
auto b = v.attrs()->find(createSymbol("b"));
|
||||
ASSERT_NE(b, nullptr);
|
||||
ASSERT_THAT(*b->value, IsIntEq(2));
|
||||
}
|
||||
auto b = v.attrs()->get(createSymbol("b"));
|
||||
ASSERT_NE(b, nullptr);
|
||||
ASSERT_THAT(*b->value, IsIntEq(2));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, hasAttrOpFalse) {
|
||||
auto v = eval("{} ? a");
|
||||
ASSERT_THAT(v, IsFalse());
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, hasAttrOpFalse)
|
||||
{
|
||||
auto v = eval("{} ? a");
|
||||
ASSERT_THAT(v, IsFalse());
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, hasAttrOpTrue) {
|
||||
auto v = eval("{ a = 123; } ? a");
|
||||
ASSERT_THAT(v, IsTrue());
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, hasAttrOpTrue)
|
||||
{
|
||||
auto v = eval("{ a = 123; } ? a");
|
||||
ASSERT_THAT(v, IsTrue());
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, withFound) {
|
||||
auto v = eval("with { a = 23; }; a");
|
||||
ASSERT_THAT(v, IsIntEq(23));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, withFound)
|
||||
{
|
||||
auto v = eval("with { a = 23; }; a");
|
||||
ASSERT_THAT(v, IsIntEq(23));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, withNotFound) {
|
||||
ASSERT_THROW(eval("with {}; a"), Error);
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, withNotFound)
|
||||
{
|
||||
ASSERT_THROW(eval("with {}; a"), Error);
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, withOverride) {
|
||||
auto v = eval("with { a = 23; }; with { a = 42; }; a");
|
||||
ASSERT_THAT(v, IsIntEq(42));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, withOverride)
|
||||
{
|
||||
auto v = eval("with { a = 23; }; with { a = 42; }; a");
|
||||
ASSERT_THAT(v, IsIntEq(42));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, letOverWith) {
|
||||
auto v = eval("let a = 23; in with { a = 1; }; a");
|
||||
ASSERT_THAT(v, IsIntEq(23));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, letOverWith)
|
||||
{
|
||||
auto v = eval("let a = 23; in with { a = 1; }; a");
|
||||
ASSERT_THAT(v, IsIntEq(23));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, multipleLet) {
|
||||
auto v = eval("let a = 23; in let a = 42; in a");
|
||||
ASSERT_THAT(v, IsIntEq(42));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, multipleLet)
|
||||
{
|
||||
auto v = eval("let a = 23; in let a = 42; in a");
|
||||
ASSERT_THAT(v, IsIntEq(42));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, defaultFunctionArgs) {
|
||||
auto v = eval("({ a ? 123 }: a) {}");
|
||||
ASSERT_THAT(v, IsIntEq(123));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, defaultFunctionArgs)
|
||||
{
|
||||
auto v = eval("({ a ? 123 }: a) {}");
|
||||
ASSERT_THAT(v, IsIntEq(123));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, defaultFunctionArgsOverride) {
|
||||
auto v = eval("({ a ? 123 }: a) { a = 5; }");
|
||||
ASSERT_THAT(v, IsIntEq(5));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, defaultFunctionArgsOverride)
|
||||
{
|
||||
auto v = eval("({ a ? 123 }: a) { a = 5; }");
|
||||
ASSERT_THAT(v, IsIntEq(5));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, defaultFunctionArgsCaptureBack) {
|
||||
auto v = eval("({ a ? 123 }@args: args) {}");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(0));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, defaultFunctionArgsCaptureBack)
|
||||
{
|
||||
auto v = eval("({ a ? 123 }@args: args) {}");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(0));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, defaultFunctionArgsCaptureFront) {
|
||||
auto v = eval("(args@{ a ? 123 }: args) {}");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(0));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, defaultFunctionArgsCaptureFront)
|
||||
{
|
||||
auto v = eval("(args@{ a ? 123 }: args) {}");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(0));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, assertThrows) {
|
||||
ASSERT_THROW(eval("let x = arg: assert arg == 1; 123; in x 2"), Error);
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, assertThrows)
|
||||
{
|
||||
ASSERT_THROW(eval("let x = arg: assert arg == 1; 123; in x 2"), Error);
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, assertPassed) {
|
||||
auto v = eval("let x = arg: assert arg == 1; 123; in x 1");
|
||||
ASSERT_THAT(v, IsIntEq(123));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, assertPassed)
|
||||
{
|
||||
auto v = eval("let x = arg: assert arg == 1; 123; in x 1");
|
||||
ASSERT_THAT(v, IsIntEq(123));
|
||||
}
|
||||
|
||||
class AttrSetMergeTrvialExpressionTest :
|
||||
public TrivialExpressionTest,
|
||||
public testing::WithParamInterface<const char*>
|
||||
{};
|
||||
class AttrSetMergeTrvialExpressionTest : public TrivialExpressionTest, public testing::WithParamInterface<const char *>
|
||||
{};
|
||||
|
||||
TEST_P(AttrSetMergeTrvialExpressionTest, attrsetMergeLazy) {
|
||||
// Usually Nix rejects duplicate keys in an attrset but it does allow
|
||||
// so if it is an attribute set that contains disjoint sets of keys.
|
||||
// The below is equivalent to `{a.b = 1; a.c = 2; }`.
|
||||
// The attribute set `a` will be a Thunk at first as the attributes
|
||||
// have to be merged (or otherwise computed) and that is done in a lazy
|
||||
// manner.
|
||||
TEST_P(AttrSetMergeTrvialExpressionTest, attrsetMergeLazy)
|
||||
{
|
||||
// Usually Nix rejects duplicate keys in an attrset but it does allow
|
||||
// so if it is an attribute set that contains disjoint sets of keys.
|
||||
// The below is equivalent to `{a.b = 1; a.c = 2; }`.
|
||||
// The attribute set `a` will be a Thunk at first as the attributes
|
||||
// have to be merged (or otherwise computed) and that is done in a lazy
|
||||
// manner.
|
||||
|
||||
auto expr = GetParam();
|
||||
auto v = eval(expr);
|
||||
ASSERT_THAT(v, IsAttrsOfSize(1));
|
||||
auto expr = GetParam();
|
||||
auto v = eval(expr);
|
||||
ASSERT_THAT(v, IsAttrsOfSize(1));
|
||||
|
||||
auto a = v.attrs()->find(createSymbol("a"));
|
||||
ASSERT_NE(a, nullptr);
|
||||
auto a = v.attrs()->get(createSymbol("a"));
|
||||
ASSERT_NE(a, nullptr);
|
||||
|
||||
ASSERT_THAT(*a->value, IsThunk());
|
||||
state.forceValue(*a->value, noPos);
|
||||
ASSERT_THAT(*a->value, IsThunk());
|
||||
state.forceValue(*a->value, noPos);
|
||||
|
||||
ASSERT_THAT(*a->value, IsAttrsOfSize(2));
|
||||
ASSERT_THAT(*a->value, IsAttrsOfSize(2));
|
||||
|
||||
auto b = a->value->attrs()->find(createSymbol("b"));
|
||||
ASSERT_NE(b, nullptr);
|
||||
ASSERT_THAT(*b->value, IsIntEq(1));
|
||||
auto b = a->value->attrs()->get(createSymbol("b"));
|
||||
ASSERT_NE(b, nullptr);
|
||||
ASSERT_THAT(*b->value, IsIntEq(1));
|
||||
|
||||
auto c = a->value->attrs()->find(createSymbol("c"));
|
||||
ASSERT_NE(c, nullptr);
|
||||
ASSERT_THAT(*c->value, IsIntEq(2));
|
||||
}
|
||||
auto c = a->value->attrs()->get(createSymbol("c"));
|
||||
ASSERT_NE(c, nullptr);
|
||||
ASSERT_THAT(*c->value, IsIntEq(2));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
attrsetMergeLazy,
|
||||
AttrSetMergeTrvialExpressionTest,
|
||||
testing::Values(
|
||||
"{ a.b = 1; a.c = 2; }",
|
||||
"{ a = { b = 1; }; a = { c = 2; }; }"
|
||||
)
|
||||
);
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
attrsetMergeLazy,
|
||||
AttrSetMergeTrvialExpressionTest,
|
||||
testing::Values("{ a.b = 1; a.c = 2; }", "{ a = { b = 1; }; a = { c = 2; }; }"));
|
||||
|
||||
// The following macros ultimately define 48 tests (16 variations on three
|
||||
// templates). Each template tests an expression that can be written in 2^4
|
||||
|
|
@ -199,28 +220,34 @@ namespace nix {
|
|||
// expanded.
|
||||
#define X_EXPAND_IF0(k, v) k "." v
|
||||
#define X_EXPAND_IF1(k, v) k " = { " v " };"
|
||||
#define X4(w, x, y, z) \
|
||||
TEST_F(TrivialExpressionTest, nestedAttrsetMerge##w##x##y##z) { \
|
||||
auto v = eval("{ a.b = { c = 1; d = 2; }; } == { " \
|
||||
X_EXPAND_IF##w("a", X_EXPAND_IF##x("b", "c = 1;")) " " \
|
||||
X_EXPAND_IF##y("a", X_EXPAND_IF##z("b", "d = 2;")) " }"); \
|
||||
ASSERT_THAT(v, IsTrue()); \
|
||||
}; \
|
||||
TEST_F(TrivialExpressionTest, nestedAttrsetMergeDup##w##x##y##z) { \
|
||||
ASSERT_THROW(eval("{ " \
|
||||
X_EXPAND_IF##w("a", X_EXPAND_IF##x("b", "c = 1;")) " " \
|
||||
X_EXPAND_IF##y("a", X_EXPAND_IF##z("b", "c = 2;")) " }"), Error); \
|
||||
}; \
|
||||
TEST_F(TrivialExpressionTest, nestedAttrsetMergeLet##w##x##y##z) { \
|
||||
auto v = eval("{ b = { c = 1; d = 2; }; } == (let " \
|
||||
X_EXPAND_IF##w("a", X_EXPAND_IF##x("b", "c = 1;")) " " \
|
||||
X_EXPAND_IF##y("a", X_EXPAND_IF##z("b", "d = 2;")) " in a)"); \
|
||||
ASSERT_THAT(v, IsTrue()); \
|
||||
#define X4(w, x, y, z) \
|
||||
TEST_F(TrivialExpressionTest, nestedAttrsetMerge##w##x##y##z) \
|
||||
{ \
|
||||
auto v = eval( \
|
||||
"{ a.b = { c = 1; d = 2; }; } == { " X_EXPAND_IF##w( \
|
||||
"a", X_EXPAND_IF##x("b", "c = 1;")) " " X_EXPAND_IF##y("a", X_EXPAND_IF##z("b", "d = 2;")) " }"); \
|
||||
ASSERT_THAT(v, IsTrue()); \
|
||||
}; \
|
||||
TEST_F(TrivialExpressionTest, nestedAttrsetMergeDup##w##x##y##z) \
|
||||
{ \
|
||||
ASSERT_THROW( \
|
||||
eval( \
|
||||
"{ " X_EXPAND_IF##w("a", X_EXPAND_IF##x("b", "c = 1;")) " " X_EXPAND_IF##y( \
|
||||
"a", X_EXPAND_IF##z("b", "c = 2;")) " }"), \
|
||||
Error); \
|
||||
}; \
|
||||
TEST_F(TrivialExpressionTest, nestedAttrsetMergeLet##w##x##y##z) \
|
||||
{ \
|
||||
auto v = eval( \
|
||||
"{ b = { c = 1; d = 2; }; } == (let " X_EXPAND_IF##w( \
|
||||
"a", X_EXPAND_IF##x("b", "c = 1;")) " " X_EXPAND_IF##y("a", X_EXPAND_IF##z("b", "d = 2;")) " in a)"); \
|
||||
ASSERT_THAT(v, IsTrue()); \
|
||||
};
|
||||
#define X3(...) X4(__VA_ARGS__, 0) X4(__VA_ARGS__, 1)
|
||||
#define X2(...) X3(__VA_ARGS__, 0) X3(__VA_ARGS__, 1)
|
||||
#define X1(...) X2(__VA_ARGS__, 0) X2(__VA_ARGS__, 1)
|
||||
X1(0) X1(1)
|
||||
X1(0)
|
||||
X1(1)
|
||||
#undef X_EXPAND_IF0
|
||||
#undef X_EXPAND_IF1
|
||||
#undef X1
|
||||
|
|
@ -228,74 +255,88 @@ namespace nix {
|
|||
#undef X3
|
||||
#undef X4
|
||||
|
||||
TEST_F(TrivialExpressionTest, functor) {
|
||||
auto v = eval("{ __functor = self: arg: self.v + arg; v = 10; } 5");
|
||||
ASSERT_THAT(v, IsIntEq(15));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, functor)
|
||||
{
|
||||
auto v = eval("{ __functor = self: arg: self.v + arg; v = 10; } 5");
|
||||
ASSERT_THAT(v, IsIntEq(15));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, forwardPipe) {
|
||||
auto v = eval("1 |> builtins.add 2 |> builtins.mul 3");
|
||||
ASSERT_THAT(v, IsIntEq(9));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, forwardPipe)
|
||||
{
|
||||
auto v = eval("1 |> builtins.add 2 |> builtins.mul 3");
|
||||
ASSERT_THAT(v, IsIntEq(9));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, backwardPipe) {
|
||||
auto v = eval("builtins.add 1 <| builtins.mul 2 <| 3");
|
||||
ASSERT_THAT(v, IsIntEq(7));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, backwardPipe)
|
||||
{
|
||||
auto v = eval("builtins.add 1 <| builtins.mul 2 <| 3");
|
||||
ASSERT_THAT(v, IsIntEq(7));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, forwardPipeEvaluationOrder) {
|
||||
auto v = eval("1 |> null |> (x: 2)");
|
||||
ASSERT_THAT(v, IsIntEq(2));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, forwardPipeEvaluationOrder)
|
||||
{
|
||||
auto v = eval("1 |> null |> (x: 2)");
|
||||
ASSERT_THAT(v, IsIntEq(2));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, backwardPipeEvaluationOrder) {
|
||||
auto v = eval("(x: 1) <| null <| 2");
|
||||
ASSERT_THAT(v, IsIntEq(1));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, backwardPipeEvaluationOrder)
|
||||
{
|
||||
auto v = eval("(x: 1) <| null <| 2");
|
||||
ASSERT_THAT(v, IsIntEq(1));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, differentPipeOperatorsDoNotAssociate) {
|
||||
ASSERT_THROW(eval("(x: 1) <| 2 |> (x: 3)"), ParseError);
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, differentPipeOperatorsDoNotAssociate)
|
||||
{
|
||||
ASSERT_THROW(eval("(x: 1) <| 2 |> (x: 3)"), ParseError);
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, differentPipeOperatorsParensLeft) {
|
||||
auto v = eval("((x: 1) <| 2) |> (x: 3)");
|
||||
ASSERT_THAT(v, IsIntEq(3));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, differentPipeOperatorsParensLeft)
|
||||
{
|
||||
auto v = eval("((x: 1) <| 2) |> (x: 3)");
|
||||
ASSERT_THAT(v, IsIntEq(3));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, differentPipeOperatorsParensRight) {
|
||||
auto v = eval("(x: 1) <| (2 |> (x: 3))");
|
||||
ASSERT_THAT(v, IsIntEq(1));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, differentPipeOperatorsParensRight)
|
||||
{
|
||||
auto v = eval("(x: 1) <| (2 |> (x: 3))");
|
||||
ASSERT_THAT(v, IsIntEq(1));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, forwardPipeLowestPrecedence) {
|
||||
auto v = eval("false -> true |> (x: !x)");
|
||||
ASSERT_THAT(v, IsFalse());
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, forwardPipeLowestPrecedence)
|
||||
{
|
||||
auto v = eval("false -> true |> (x: !x)");
|
||||
ASSERT_THAT(v, IsFalse());
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, backwardPipeLowestPrecedence) {
|
||||
auto v = eval("(x: !x) <| false -> true");
|
||||
ASSERT_THAT(v, IsFalse());
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, backwardPipeLowestPrecedence)
|
||||
{
|
||||
auto v = eval("(x: !x) <| false -> true");
|
||||
ASSERT_THAT(v, IsFalse());
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, forwardPipeStrongerThanElse) {
|
||||
auto v = eval("if true then 1 else 2 |> 3");
|
||||
ASSERT_THAT(v, IsIntEq(1));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, forwardPipeStrongerThanElse)
|
||||
{
|
||||
auto v = eval("if true then 1 else 2 |> 3");
|
||||
ASSERT_THAT(v, IsIntEq(1));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, backwardPipeStrongerThanElse) {
|
||||
auto v = eval("if true then 1 else 2 <| 3");
|
||||
ASSERT_THAT(v, IsIntEq(1));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, backwardPipeStrongerThanElse)
|
||||
{
|
||||
auto v = eval("if true then 1 else 2 <| 3");
|
||||
ASSERT_THAT(v, IsIntEq(1));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, bindOr) {
|
||||
auto v = eval("{ or = 1; }");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(1));
|
||||
auto b = v.attrs()->find(createSymbol("or"));
|
||||
ASSERT_NE(b, nullptr);
|
||||
ASSERT_THAT(*b->value, IsIntEq(1));
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, bindOr)
|
||||
{
|
||||
auto v = eval("{ or = 1; }");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(1));
|
||||
auto b = v.attrs()->get(createSymbol("or"));
|
||||
ASSERT_NE(b, nullptr);
|
||||
ASSERT_THAT(*b->value, IsIntEq(1));
|
||||
}
|
||||
|
||||
TEST_F(TrivialExpressionTest, orCantBeUsed) {
|
||||
ASSERT_THROW(eval("let or = 1; in or"), Error);
|
||||
}
|
||||
TEST_F(TrivialExpressionTest, orCantBeUsed)
|
||||
{
|
||||
ASSERT_THROW(eval("let or = 1; in or"), Error);
|
||||
}
|
||||
} /* namespace nix */
|
||||
|
|
|
|||
|
|
@ -10,46 +10,42 @@ namespace nix {
|
|||
|
||||
// Test a few cases of invalid string context elements.
|
||||
|
||||
TEST(NixStringContextElemTest, empty_invalid) {
|
||||
EXPECT_THROW(
|
||||
NixStringContextElem::parse(""),
|
||||
BadNixStringContextElem);
|
||||
TEST(NixStringContextElemTest, empty_invalid)
|
||||
{
|
||||
EXPECT_THROW(NixStringContextElem::parse(""), BadNixStringContextElem);
|
||||
}
|
||||
|
||||
TEST(NixStringContextElemTest, single_bang_invalid) {
|
||||
EXPECT_THROW(
|
||||
NixStringContextElem::parse("!"),
|
||||
BadNixStringContextElem);
|
||||
TEST(NixStringContextElemTest, single_bang_invalid)
|
||||
{
|
||||
EXPECT_THROW(NixStringContextElem::parse("!"), BadNixStringContextElem);
|
||||
}
|
||||
|
||||
TEST(NixStringContextElemTest, double_bang_invalid) {
|
||||
EXPECT_THROW(
|
||||
NixStringContextElem::parse("!!/"),
|
||||
BadStorePath);
|
||||
TEST(NixStringContextElemTest, double_bang_invalid)
|
||||
{
|
||||
EXPECT_THROW(NixStringContextElem::parse("!!/"), BadStorePath);
|
||||
}
|
||||
|
||||
TEST(NixStringContextElemTest, eq_slash_invalid) {
|
||||
EXPECT_THROW(
|
||||
NixStringContextElem::parse("=/"),
|
||||
BadStorePath);
|
||||
TEST(NixStringContextElemTest, eq_slash_invalid)
|
||||
{
|
||||
EXPECT_THROW(NixStringContextElem::parse("=/"), BadStorePath);
|
||||
}
|
||||
|
||||
TEST(NixStringContextElemTest, slash_invalid) {
|
||||
EXPECT_THROW(
|
||||
NixStringContextElem::parse("/"),
|
||||
BadStorePath);
|
||||
TEST(NixStringContextElemTest, slash_invalid)
|
||||
{
|
||||
EXPECT_THROW(NixStringContextElem::parse("/"), BadStorePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Round trip (string <-> data structure) test for
|
||||
* `NixStringContextElem::Opaque`.
|
||||
*/
|
||||
TEST(NixStringContextElemTest, opaque) {
|
||||
TEST(NixStringContextElemTest, opaque)
|
||||
{
|
||||
std::string_view opaque = "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x";
|
||||
auto elem = NixStringContextElem::parse(opaque);
|
||||
auto * p = std::get_if<NixStringContextElem::Opaque>(&elem.raw);
|
||||
ASSERT_TRUE(p);
|
||||
ASSERT_EQ(p->path, StorePath { opaque });
|
||||
ASSERT_EQ(p->path, StorePath{opaque});
|
||||
ASSERT_EQ(elem.to_string(), opaque);
|
||||
}
|
||||
|
||||
|
|
@ -57,12 +53,13 @@ TEST(NixStringContextElemTest, opaque) {
|
|||
* Round trip (string <-> data structure) test for
|
||||
* `NixStringContextElem::DrvDeep`.
|
||||
*/
|
||||
TEST(NixStringContextElemTest, drvDeep) {
|
||||
TEST(NixStringContextElemTest, drvDeep)
|
||||
{
|
||||
std::string_view drvDeep = "=g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv";
|
||||
auto elem = NixStringContextElem::parse(drvDeep);
|
||||
auto * p = std::get_if<NixStringContextElem::DrvDeep>(&elem.raw);
|
||||
ASSERT_TRUE(p);
|
||||
ASSERT_EQ(p->drvPath, StorePath { drvDeep.substr(1) });
|
||||
ASSERT_EQ(p->drvPath, StorePath{drvDeep.substr(1)});
|
||||
ASSERT_EQ(elem.to_string(), drvDeep);
|
||||
}
|
||||
|
||||
|
|
@ -70,15 +67,18 @@ TEST(NixStringContextElemTest, drvDeep) {
|
|||
* Round trip (string <-> data structure) test for a simpler
|
||||
* `NixStringContextElem::Built`.
|
||||
*/
|
||||
TEST(NixStringContextElemTest, built_opaque) {
|
||||
TEST(NixStringContextElemTest, built_opaque)
|
||||
{
|
||||
std::string_view built = "!foo!g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv";
|
||||
auto elem = NixStringContextElem::parse(built);
|
||||
auto * p = std::get_if<NixStringContextElem::Built>(&elem.raw);
|
||||
ASSERT_TRUE(p);
|
||||
ASSERT_EQ(p->output, "foo");
|
||||
ASSERT_EQ(*p->drvPath, ((SingleDerivedPath) SingleDerivedPath::Opaque {
|
||||
.path = StorePath { built.substr(5) },
|
||||
}));
|
||||
ASSERT_EQ(
|
||||
*p->drvPath,
|
||||
((SingleDerivedPath) SingleDerivedPath::Opaque{
|
||||
.path = StorePath{built.substr(5)},
|
||||
}));
|
||||
ASSERT_EQ(elem.to_string(), built);
|
||||
}
|
||||
|
||||
|
|
@ -86,7 +86,8 @@ TEST(NixStringContextElemTest, built_opaque) {
|
|||
* Round trip (string <-> data structure) test for a more complex,
|
||||
* inductive `NixStringContextElem::Built`.
|
||||
*/
|
||||
TEST(NixStringContextElemTest, built_built) {
|
||||
TEST(NixStringContextElemTest, built_built)
|
||||
{
|
||||
/**
|
||||
* We set these in tests rather than the regular globals so we don't have
|
||||
* to worry about race conditions if the tests run concurrently.
|
||||
|
|
@ -102,9 +103,11 @@ TEST(NixStringContextElemTest, built_built) {
|
|||
auto * drvPath = std::get_if<SingleDerivedPath::Built>(&*p->drvPath);
|
||||
ASSERT_TRUE(drvPath);
|
||||
ASSERT_EQ(drvPath->output, "bar");
|
||||
ASSERT_EQ(*drvPath->drvPath, ((SingleDerivedPath) SingleDerivedPath::Opaque {
|
||||
.path = StorePath { built.substr(9) },
|
||||
}));
|
||||
ASSERT_EQ(
|
||||
*drvPath->drvPath,
|
||||
((SingleDerivedPath) SingleDerivedPath::Opaque{
|
||||
.path = StorePath{built.substr(9)},
|
||||
}));
|
||||
ASSERT_EQ(elem.to_string(), built);
|
||||
}
|
||||
|
||||
|
|
@ -112,17 +115,15 @@ TEST(NixStringContextElemTest, built_built) {
|
|||
* Without the right experimental features enabled, we cannot parse a
|
||||
* complex inductive string context element.
|
||||
*/
|
||||
TEST(NixStringContextElemTest, built_built_xp) {
|
||||
TEST(NixStringContextElemTest, built_built_xp)
|
||||
{
|
||||
ASSERT_THROW(
|
||||
NixStringContextElem::parse("!foo!bar!g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv"), MissingExperimentalFeature);
|
||||
NixStringContextElem::parse("!foo!bar!g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv"), MissingExperimentalFeature);
|
||||
}
|
||||
|
||||
#ifndef COVERAGE
|
||||
|
||||
RC_GTEST_PROP(
|
||||
NixStringContextElemTest,
|
||||
prop_round_rip,
|
||||
(const NixStringContextElem & o))
|
||||
RC_GTEST_PROP(NixStringContextElemTest, prop_round_rip, (const NixStringContextElem & o))
|
||||
{
|
||||
ExperimentalFeatureSettings xpSettings;
|
||||
xpSettings.set("experimental-features", "dynamic-derivations");
|
||||
|
|
@ -131,4 +132,4 @@ RC_GTEST_PROP(
|
|||
|
||||
#endif
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -35,14 +35,14 @@ TEST_F(ValuePrintingTests, tBool)
|
|||
TEST_F(ValuePrintingTests, tString)
|
||||
{
|
||||
Value vString;
|
||||
vString.mkString("some-string");
|
||||
vString.mkStringNoCopy("some-string");
|
||||
test(vString, "\"some-string\"");
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, tPath)
|
||||
{
|
||||
Value vPath;
|
||||
vPath.mkString("/foo");
|
||||
vPath.mkStringNoCopy("/foo");
|
||||
test(vPath, "\"/foo\"");
|
||||
}
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ TEST_F(ValuePrintingTests, tAttrs)
|
|||
Value vTwo;
|
||||
vTwo.mkInt(2);
|
||||
|
||||
BindingsBuilder builder(state, state.allocBindings(10));
|
||||
BindingsBuilder builder = state.buildBindings(10);
|
||||
builder.insert(state.symbols.create("one"), &vOne);
|
||||
builder.insert(state.symbols.create("two"), &vTwo);
|
||||
|
||||
|
|
@ -106,14 +106,11 @@ TEST_F(ValuePrintingTests, vApp)
|
|||
|
||||
TEST_F(ValuePrintingTests, vLambda)
|
||||
{
|
||||
Env env {
|
||||
.up = nullptr,
|
||||
.values = { }
|
||||
};
|
||||
Env env{.up = nullptr, .values = {}};
|
||||
PosTable::Origin origin = state.positions.addOrigin(std::monostate(), 1);
|
||||
auto posIdx = state.positions.add(origin, 0);
|
||||
auto body = ExprInt(0);
|
||||
auto formals = Formals {};
|
||||
auto formals = Formals{};
|
||||
|
||||
ExprLambda eLambda(posIdx, createSymbol("a"), &formals, &body);
|
||||
|
||||
|
|
@ -130,9 +127,7 @@ TEST_F(ValuePrintingTests, vLambda)
|
|||
TEST_F(ValuePrintingTests, vPrimOp)
|
||||
{
|
||||
Value vPrimOp;
|
||||
PrimOp primOp{
|
||||
.name = "puppy"
|
||||
};
|
||||
PrimOp primOp{.name = "puppy"};
|
||||
vPrimOp.mkPrimOp(&primOp);
|
||||
|
||||
test(vPrimOp, "«primop puppy»");
|
||||
|
|
@ -140,9 +135,7 @@ TEST_F(ValuePrintingTests, vPrimOp)
|
|||
|
||||
TEST_F(ValuePrintingTests, vPrimOpApp)
|
||||
{
|
||||
PrimOp primOp{
|
||||
.name = "puppy"
|
||||
};
|
||||
PrimOp primOp{.name = "puppy"};
|
||||
Value vPrimOp;
|
||||
vPrimOp.mkPrimOp(&primOp);
|
||||
|
||||
|
|
@ -161,16 +154,19 @@ TEST_F(ValuePrintingTests, vExternal)
|
|||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string typeOf() const override
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual std::ostream & print(std::ostream & str) const override
|
||||
{
|
||||
str << "testing-external!";
|
||||
return str;
|
||||
}
|
||||
} myExternal;
|
||||
|
||||
Value vExternal;
|
||||
vExternal.mkExternal(&myExternal);
|
||||
|
||||
|
|
@ -200,11 +196,11 @@ TEST_F(ValuePrintingTests, depthAttrs)
|
|||
Value vTwo;
|
||||
vTwo.mkInt(2);
|
||||
|
||||
BindingsBuilder builderEmpty(state, state.allocBindings(0));
|
||||
BindingsBuilder builderEmpty = state.buildBindings(0);
|
||||
Value vAttrsEmpty;
|
||||
vAttrsEmpty.mkAttrs(builderEmpty.finish());
|
||||
|
||||
BindingsBuilder builder(state, state.allocBindings(10));
|
||||
BindingsBuilder builder = state.buildBindings(10);
|
||||
builder.insert(state.symbols.create("one"), &vOne);
|
||||
builder.insert(state.symbols.create("two"), &vTwo);
|
||||
builder.insert(state.symbols.create("nested"), &vAttrsEmpty);
|
||||
|
|
@ -212,7 +208,7 @@ TEST_F(ValuePrintingTests, depthAttrs)
|
|||
Value vAttrs;
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
|
||||
BindingsBuilder builder2(state, state.allocBindings(10));
|
||||
BindingsBuilder builder2 = state.buildBindings(10);
|
||||
builder2.insert(state.symbols.create("one"), &vOne);
|
||||
builder2.insert(state.symbols.create("two"), &vTwo);
|
||||
builder2.insert(state.symbols.create("nested"), &vAttrs);
|
||||
|
|
@ -220,10 +216,13 @@ TEST_F(ValuePrintingTests, depthAttrs)
|
|||
Value vNested;
|
||||
vNested.mkAttrs(builder2.finish());
|
||||
|
||||
test(vNested, "{ nested = { ... }; one = 1; two = 2; }", PrintOptions { .maxDepth = 1 });
|
||||
test(vNested, "{ nested = { nested = { ... }; one = 1; two = 2; }; one = 1; two = 2; }", PrintOptions { .maxDepth = 2 });
|
||||
test(vNested, "{ nested = { nested = { }; one = 1; two = 2; }; one = 1; two = 2; }", PrintOptions { .maxDepth = 3 });
|
||||
test(vNested, "{ nested = { nested = { }; one = 1; two = 2; }; one = 1; two = 2; }", PrintOptions { .maxDepth = 4 });
|
||||
test(vNested, "{ nested = { ... }; one = 1; two = 2; }", PrintOptions{.maxDepth = 1});
|
||||
test(
|
||||
vNested,
|
||||
"{ nested = { nested = { ... }; one = 1; two = 2; }; one = 1; two = 2; }",
|
||||
PrintOptions{.maxDepth = 2});
|
||||
test(vNested, "{ nested = { nested = { }; one = 1; two = 2; }; one = 1; two = 2; }", PrintOptions{.maxDepth = 3});
|
||||
test(vNested, "{ nested = { nested = { }; one = 1; two = 2; }; one = 1; two = 2; }", PrintOptions{.maxDepth = 4});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, depthList)
|
||||
|
|
@ -234,14 +233,14 @@ TEST_F(ValuePrintingTests, depthList)
|
|||
Value vTwo;
|
||||
vTwo.mkInt(2);
|
||||
|
||||
BindingsBuilder builder(state, state.allocBindings(10));
|
||||
BindingsBuilder builder = state.buildBindings(10);
|
||||
builder.insert(state.symbols.create("one"), &vOne);
|
||||
builder.insert(state.symbols.create("two"), &vTwo);
|
||||
|
||||
Value vAttrs;
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
|
||||
BindingsBuilder builder2(state, state.allocBindings(10));
|
||||
BindingsBuilder builder2 = state.buildBindings(10);
|
||||
builder2.insert(state.symbols.create("one"), &vOne);
|
||||
builder2.insert(state.symbols.create("two"), &vTwo);
|
||||
builder2.insert(state.symbols.create("nested"), &vAttrs);
|
||||
|
|
@ -256,11 +255,11 @@ TEST_F(ValuePrintingTests, depthList)
|
|||
Value vList;
|
||||
vList.mkList(list);
|
||||
|
||||
test(vList, "[ 1 2 { ... } ]", PrintOptions { .maxDepth = 1 });
|
||||
test(vList, "[ 1 2 { nested = { ... }; one = 1; two = 2; } ]", PrintOptions { .maxDepth = 2 });
|
||||
test(vList, "[ 1 2 { nested = { one = 1; two = 2; }; one = 1; two = 2; } ]", PrintOptions { .maxDepth = 3 });
|
||||
test(vList, "[ 1 2 { nested = { one = 1; two = 2; }; one = 1; two = 2; } ]", PrintOptions { .maxDepth = 4 });
|
||||
test(vList, "[ 1 2 { nested = { one = 1; two = 2; }; one = 1; two = 2; } ]", PrintOptions { .maxDepth = 5 });
|
||||
test(vList, "[ 1 2 { ... } ]", PrintOptions{.maxDepth = 1});
|
||||
test(vList, "[ 1 2 { nested = { ... }; one = 1; two = 2; } ]", PrintOptions{.maxDepth = 2});
|
||||
test(vList, "[ 1 2 { nested = { one = 1; two = 2; }; one = 1; two = 2; } ]", PrintOptions{.maxDepth = 3});
|
||||
test(vList, "[ 1 2 { nested = { one = 1; two = 2; }; one = 1; two = 2; } ]", PrintOptions{.maxDepth = 4});
|
||||
test(vList, "[ 1 2 { nested = { one = 1; two = 2; }; one = 1; two = 2; } ]", PrintOptions{.maxDepth = 5});
|
||||
}
|
||||
|
||||
struct StringPrintingTests : LibExprTest
|
||||
|
|
@ -272,9 +271,7 @@ struct StringPrintingTests : LibExprTest
|
|||
v.mkString(literal);
|
||||
|
||||
std::stringstream out;
|
||||
printValue(state, out, v, PrintOptions {
|
||||
.maxStringLength = maxLength
|
||||
});
|
||||
printValue(state, out, v, PrintOptions{.maxStringLength = maxLength});
|
||||
ASSERT_EQ(out.str(), expected);
|
||||
}
|
||||
};
|
||||
|
|
@ -293,27 +290,21 @@ TEST_F(StringPrintingTests, maxLengthTruncation)
|
|||
TEST_F(ValuePrintingTests, attrsTypeFirst)
|
||||
{
|
||||
Value vType;
|
||||
vType.mkString("puppy");
|
||||
vType.mkStringNoCopy("puppy");
|
||||
|
||||
Value vApple;
|
||||
vApple.mkString("apple");
|
||||
vApple.mkStringNoCopy("apple");
|
||||
|
||||
BindingsBuilder builder(state, state.allocBindings(10));
|
||||
BindingsBuilder builder = state.buildBindings(10);
|
||||
builder.insert(state.symbols.create("type"), &vType);
|
||||
builder.insert(state.symbols.create("apple"), &vApple);
|
||||
|
||||
Value vAttrs;
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
|
||||
test(vAttrs,
|
||||
"{ type = \"puppy\"; apple = \"apple\"; }",
|
||||
PrintOptions {
|
||||
.maxAttrs = 100
|
||||
});
|
||||
test(vAttrs, "{ type = \"puppy\"; apple = \"apple\"; }", PrintOptions{.maxAttrs = 100});
|
||||
|
||||
test(vAttrs,
|
||||
"{ apple = \"apple\"; type = \"puppy\"; }",
|
||||
PrintOptions { });
|
||||
test(vAttrs, "{ apple = \"apple\"; type = \"puppy\"; }", PrintOptions{});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsInt)
|
||||
|
|
@ -321,11 +312,7 @@ TEST_F(ValuePrintingTests, ansiColorsInt)
|
|||
Value v;
|
||||
v.mkInt(10);
|
||||
|
||||
test(v,
|
||||
ANSI_CYAN "10" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
test(v, ANSI_CYAN "10" ANSI_NORMAL, PrintOptions{.ansiColors = true});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsFloat)
|
||||
|
|
@ -333,11 +320,7 @@ TEST_F(ValuePrintingTests, ansiColorsFloat)
|
|||
Value v;
|
||||
v.mkFloat(1.6);
|
||||
|
||||
test(v,
|
||||
ANSI_CYAN "1.6" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
test(v, ANSI_CYAN "1.6" ANSI_NORMAL, PrintOptions{.ansiColors = true});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsBool)
|
||||
|
|
@ -345,36 +328,26 @@ TEST_F(ValuePrintingTests, ansiColorsBool)
|
|||
Value v;
|
||||
v.mkBool(true);
|
||||
|
||||
test(v,
|
||||
ANSI_CYAN "true" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
test(v, ANSI_CYAN "true" ANSI_NORMAL, PrintOptions{.ansiColors = true});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsString)
|
||||
{
|
||||
Value v;
|
||||
v.mkString("puppy");
|
||||
v.mkStringNoCopy("puppy");
|
||||
|
||||
test(v,
|
||||
ANSI_MAGENTA "\"puppy\"" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
test(v, ANSI_MAGENTA "\"puppy\"" ANSI_NORMAL, PrintOptions{.ansiColors = true});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsStringElided)
|
||||
{
|
||||
Value v;
|
||||
v.mkString("puppy");
|
||||
v.mkStringNoCopy("puppy");
|
||||
|
||||
test(v,
|
||||
ANSI_MAGENTA "\"pup\" " ANSI_FAINT "«2 bytes elided»" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.maxStringLength = 3
|
||||
});
|
||||
test(
|
||||
v,
|
||||
ANSI_MAGENTA "\"pup\" " ANSI_FAINT "«2 bytes elided»" ANSI_NORMAL,
|
||||
PrintOptions{.ansiColors = true, .maxStringLength = 3});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsPath)
|
||||
|
|
@ -382,11 +355,7 @@ TEST_F(ValuePrintingTests, ansiColorsPath)
|
|||
Value v;
|
||||
v.mkPath(state.rootPath(CanonPath("puppy")));
|
||||
|
||||
test(v,
|
||||
ANSI_GREEN "/puppy" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
test(v, ANSI_GREEN "/puppy" ANSI_NORMAL, PrintOptions{.ansiColors = true});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsNull)
|
||||
|
|
@ -394,11 +363,7 @@ TEST_F(ValuePrintingTests, ansiColorsNull)
|
|||
Value v;
|
||||
v.mkNull();
|
||||
|
||||
test(v,
|
||||
ANSI_CYAN "null" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
test(v, ANSI_CYAN "null" ANSI_NORMAL, PrintOptions{.ansiColors = true});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsAttrs)
|
||||
|
|
@ -409,107 +374,90 @@ TEST_F(ValuePrintingTests, ansiColorsAttrs)
|
|||
Value vTwo;
|
||||
vTwo.mkInt(2);
|
||||
|
||||
BindingsBuilder builder(state, state.allocBindings(10));
|
||||
BindingsBuilder builder = state.buildBindings(10);
|
||||
builder.insert(state.symbols.create("one"), &vOne);
|
||||
builder.insert(state.symbols.create("two"), &vTwo);
|
||||
|
||||
Value vAttrs;
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
|
||||
test(vAttrs,
|
||||
"{ one = " ANSI_CYAN "1" ANSI_NORMAL "; two = " ANSI_CYAN "2" ANSI_NORMAL "; }",
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
test(
|
||||
vAttrs,
|
||||
"{ one = " ANSI_CYAN "1" ANSI_NORMAL "; two = " ANSI_CYAN "2" ANSI_NORMAL "; }",
|
||||
PrintOptions{.ansiColors = true});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsDerivation)
|
||||
{
|
||||
Value vDerivation;
|
||||
vDerivation.mkString("derivation");
|
||||
vDerivation.mkStringNoCopy("derivation");
|
||||
|
||||
BindingsBuilder builder(state, state.allocBindings(10));
|
||||
builder.insert(state.sType, &vDerivation);
|
||||
BindingsBuilder builder = state.buildBindings(10);
|
||||
builder.insert(state.s.type, &vDerivation);
|
||||
|
||||
Value vAttrs;
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
|
||||
test(vAttrs,
|
||||
ANSI_GREEN "«derivation»" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.force = true,
|
||||
.derivationPaths = true
|
||||
});
|
||||
test(
|
||||
vAttrs,
|
||||
ANSI_GREEN "«derivation»" ANSI_NORMAL,
|
||||
PrintOptions{.ansiColors = true, .force = true, .derivationPaths = true});
|
||||
|
||||
test(vAttrs,
|
||||
"{ type = " ANSI_MAGENTA "\"derivation\"" ANSI_NORMAL "; }",
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.force = true
|
||||
});
|
||||
test(
|
||||
vAttrs,
|
||||
"{ type = " ANSI_MAGENTA "\"derivation\"" ANSI_NORMAL "; }",
|
||||
PrintOptions{.ansiColors = true, .force = true});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsError)
|
||||
{
|
||||
Value throw_ = state.getBuiltin("throw");
|
||||
Value message;
|
||||
message.mkString("uh oh!");
|
||||
message.mkStringNoCopy("uh oh!");
|
||||
Value vError;
|
||||
vError.mkApp(&throw_, &message);
|
||||
|
||||
test(vError,
|
||||
ANSI_RED
|
||||
"«error: uh oh!»"
|
||||
ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.force = true,
|
||||
});
|
||||
test(
|
||||
vError,
|
||||
ANSI_RED "«error: uh oh!»" ANSI_NORMAL,
|
||||
PrintOptions{
|
||||
.ansiColors = true,
|
||||
.force = true,
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsDerivationError)
|
||||
{
|
||||
Value throw_ = state.getBuiltin("throw");
|
||||
Value message;
|
||||
message.mkString("uh oh!");
|
||||
message.mkStringNoCopy("uh oh!");
|
||||
Value vError;
|
||||
vError.mkApp(&throw_, &message);
|
||||
|
||||
Value vDerivation;
|
||||
vDerivation.mkString("derivation");
|
||||
vDerivation.mkStringNoCopy("derivation");
|
||||
|
||||
BindingsBuilder builder(state, state.allocBindings(10));
|
||||
builder.insert(state.sType, &vDerivation);
|
||||
builder.insert(state.sDrvPath, &vError);
|
||||
BindingsBuilder builder = state.buildBindings(10);
|
||||
builder.insert(state.s.type, &vDerivation);
|
||||
builder.insert(state.s.drvPath, &vError);
|
||||
|
||||
Value vAttrs;
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
|
||||
test(vAttrs,
|
||||
"{ drvPath = "
|
||||
ANSI_RED
|
||||
"«error: uh oh!»"
|
||||
ANSI_NORMAL
|
||||
"; type = "
|
||||
ANSI_MAGENTA
|
||||
"\"derivation\""
|
||||
ANSI_NORMAL
|
||||
"; }",
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.force = true
|
||||
});
|
||||
test(
|
||||
vAttrs,
|
||||
"{ drvPath = " ANSI_RED "«error: uh oh!»" ANSI_NORMAL "; type = " ANSI_MAGENTA "\"derivation\"" ANSI_NORMAL
|
||||
"; }",
|
||||
PrintOptions{.ansiColors = true, .force = true});
|
||||
|
||||
test(vAttrs,
|
||||
ANSI_RED
|
||||
"«error: uh oh!»"
|
||||
ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.force = true,
|
||||
.derivationPaths = true,
|
||||
});
|
||||
test(
|
||||
vAttrs,
|
||||
ANSI_RED "«error: uh oh!»" ANSI_NORMAL,
|
||||
PrintOptions{
|
||||
.ansiColors = true,
|
||||
.force = true,
|
||||
.derivationPaths = true,
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsAssert)
|
||||
|
|
@ -523,12 +471,7 @@ TEST_F(ValuePrintingTests, ansiColorsAssert)
|
|||
Value v;
|
||||
state.mkThunk_(v, &expr);
|
||||
|
||||
test(v,
|
||||
ANSI_RED "«error: assertion 'false' failed»" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.force = true
|
||||
});
|
||||
test(v, ANSI_RED "«error: assertion 'false' failed»" ANSI_NORMAL, PrintOptions{.ansiColors = true, .force = true});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsList)
|
||||
|
|
@ -545,77 +488,51 @@ TEST_F(ValuePrintingTests, ansiColorsList)
|
|||
Value vList;
|
||||
vList.mkList(list);
|
||||
|
||||
test(vList,
|
||||
"[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_CYAN "2" ANSI_NORMAL " " ANSI_MAGENTA "«nullptr»" ANSI_NORMAL " ]",
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
test(
|
||||
vList,
|
||||
"[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_CYAN "2" ANSI_NORMAL " " ANSI_MAGENTA "«nullptr»" ANSI_NORMAL " ]",
|
||||
PrintOptions{.ansiColors = true});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsLambda)
|
||||
{
|
||||
Env env {
|
||||
.up = nullptr,
|
||||
.values = { }
|
||||
};
|
||||
Env env{.up = nullptr, .values = {}};
|
||||
PosTable::Origin origin = state.positions.addOrigin(std::monostate(), 1);
|
||||
auto posIdx = state.positions.add(origin, 0);
|
||||
auto body = ExprInt(0);
|
||||
auto formals = Formals {};
|
||||
auto formals = Formals{};
|
||||
|
||||
ExprLambda eLambda(posIdx, createSymbol("a"), &formals, &body);
|
||||
|
||||
Value vLambda;
|
||||
vLambda.mkLambda(&env, &eLambda);
|
||||
|
||||
test(vLambda,
|
||||
ANSI_BLUE "«lambda @ «none»:1:1»" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.force = true
|
||||
});
|
||||
test(vLambda, ANSI_BLUE "«lambda @ «none»:1:1»" ANSI_NORMAL, PrintOptions{.ansiColors = true, .force = true});
|
||||
|
||||
eLambda.setName(createSymbol("puppy"));
|
||||
|
||||
test(vLambda,
|
||||
ANSI_BLUE "«lambda puppy @ «none»:1:1»" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.force = true
|
||||
});
|
||||
test(vLambda, ANSI_BLUE "«lambda puppy @ «none»:1:1»" ANSI_NORMAL, PrintOptions{.ansiColors = true, .force = true});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsPrimOp)
|
||||
{
|
||||
PrimOp primOp{
|
||||
.name = "puppy"
|
||||
};
|
||||
PrimOp primOp{.name = "puppy"};
|
||||
Value v;
|
||||
v.mkPrimOp(&primOp);
|
||||
|
||||
test(v,
|
||||
ANSI_BLUE "«primop puppy»" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
test(v, ANSI_BLUE "«primop puppy»" ANSI_NORMAL, PrintOptions{.ansiColors = true});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsPrimOpApp)
|
||||
{
|
||||
PrimOp primOp{
|
||||
.name = "puppy"
|
||||
};
|
||||
PrimOp primOp{.name = "puppy"};
|
||||
Value vPrimOp;
|
||||
vPrimOp.mkPrimOp(&primOp);
|
||||
|
||||
Value v;
|
||||
v.mkPrimOpApp(&vPrimOp, nullptr);
|
||||
|
||||
test(v,
|
||||
ANSI_BLUE "«partially applied primop puppy»" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
test(v, ANSI_BLUE "«partially applied primop puppy»" ANSI_NORMAL, PrintOptions{.ansiColors = true});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsThunk)
|
||||
|
|
@ -623,11 +540,7 @@ TEST_F(ValuePrintingTests, ansiColorsThunk)
|
|||
Value v;
|
||||
v.mkThunk(nullptr, nullptr);
|
||||
|
||||
test(v,
|
||||
ANSI_MAGENTA "«thunk»" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
test(v, ANSI_MAGENTA "«thunk»" ANSI_NORMAL, PrintOptions{.ansiColors = true});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsBlackhole)
|
||||
|
|
@ -635,37 +548,29 @@ TEST_F(ValuePrintingTests, ansiColorsBlackhole)
|
|||
Value v;
|
||||
v.mkBlackhole();
|
||||
|
||||
test(v,
|
||||
ANSI_RED "«potential infinite recursion»" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
test(v, ANSI_RED "«potential infinite recursion»" ANSI_NORMAL, PrintOptions{.ansiColors = true});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsAttrsRepeated)
|
||||
{
|
||||
BindingsBuilder emptyBuilder(state, state.allocBindings(1));
|
||||
BindingsBuilder emptyBuilder = state.buildBindings(1);
|
||||
|
||||
Value vEmpty;
|
||||
vEmpty.mkAttrs(emptyBuilder.finish());
|
||||
|
||||
BindingsBuilder builder(state, state.allocBindings(10));
|
||||
BindingsBuilder builder = state.buildBindings(10);
|
||||
builder.insert(state.symbols.create("a"), &vEmpty);
|
||||
builder.insert(state.symbols.create("b"), &vEmpty);
|
||||
|
||||
Value vAttrs;
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
|
||||
test(vAttrs,
|
||||
"{ a = { }; b = " ANSI_MAGENTA "«repeated»" ANSI_NORMAL "; }",
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
test(vAttrs, "{ a = { }; b = " ANSI_MAGENTA "«repeated»" ANSI_NORMAL "; }", PrintOptions{.ansiColors = true});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsListRepeated)
|
||||
{
|
||||
BindingsBuilder emptyBuilder(state, state.allocBindings(1));
|
||||
BindingsBuilder emptyBuilder = state.buildBindings(1);
|
||||
|
||||
Value vEmpty;
|
||||
vEmpty.mkAttrs(emptyBuilder.finish());
|
||||
|
|
@ -676,16 +581,12 @@ TEST_F(ValuePrintingTests, ansiColorsListRepeated)
|
|||
Value vList;
|
||||
vList.mkList(list);
|
||||
|
||||
test(vList,
|
||||
"[ { } " ANSI_MAGENTA "«repeated»" ANSI_NORMAL " ]",
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
test(vList, "[ { } " ANSI_MAGENTA "«repeated»" ANSI_NORMAL " ]", PrintOptions{.ansiColors = true});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, listRepeated)
|
||||
{
|
||||
BindingsBuilder emptyBuilder(state, state.allocBindings(1));
|
||||
BindingsBuilder emptyBuilder = state.buildBindings(1);
|
||||
|
||||
Value vEmpty;
|
||||
vEmpty.mkAttrs(emptyBuilder.finish());
|
||||
|
|
@ -696,12 +597,8 @@ TEST_F(ValuePrintingTests, listRepeated)
|
|||
Value vList;
|
||||
vList.mkList(list);
|
||||
|
||||
test(vList, "[ { } «repeated» ]", PrintOptions { });
|
||||
test(vList,
|
||||
"[ { } { } ]",
|
||||
PrintOptions {
|
||||
.trackRepeated = false
|
||||
});
|
||||
test(vList, "[ { } «repeated» ]", PrintOptions{});
|
||||
test(vList, "[ { } { } ]", PrintOptions{.trackRepeated = false});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsAttrsElided)
|
||||
|
|
@ -712,19 +609,17 @@ TEST_F(ValuePrintingTests, ansiColorsAttrsElided)
|
|||
Value vTwo;
|
||||
vTwo.mkInt(2);
|
||||
|
||||
BindingsBuilder builder(state, state.allocBindings(10));
|
||||
BindingsBuilder builder = state.buildBindings(10);
|
||||
builder.insert(state.symbols.create("one"), &vOne);
|
||||
builder.insert(state.symbols.create("two"), &vTwo);
|
||||
|
||||
Value vAttrs;
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
|
||||
test(vAttrs,
|
||||
"{ one = " ANSI_CYAN "1" ANSI_NORMAL "; " ANSI_FAINT "«1 attribute elided»" ANSI_NORMAL " }",
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.maxAttrs = 1
|
||||
});
|
||||
test(
|
||||
vAttrs,
|
||||
"{ one = " ANSI_CYAN "1" ANSI_NORMAL "; " ANSI_FAINT "«1 attribute elided»" ANSI_NORMAL " }",
|
||||
PrintOptions{.ansiColors = true, .maxAttrs = 1});
|
||||
|
||||
Value vThree;
|
||||
vThree.mkInt(3);
|
||||
|
|
@ -732,18 +627,14 @@ TEST_F(ValuePrintingTests, ansiColorsAttrsElided)
|
|||
builder.insert(state.symbols.create("three"), &vThree);
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
|
||||
test(vAttrs,
|
||||
"{ one = " ANSI_CYAN "1" ANSI_NORMAL "; " ANSI_FAINT "«2 attributes elided»" ANSI_NORMAL " }",
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.maxAttrs = 1
|
||||
});
|
||||
test(
|
||||
vAttrs,
|
||||
"{ one = " ANSI_CYAN "1" ANSI_NORMAL "; " ANSI_FAINT "«2 attributes elided»" ANSI_NORMAL " }",
|
||||
PrintOptions{.ansiColors = true, .maxAttrs = 1});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsListElided)
|
||||
{
|
||||
BindingsBuilder emptyBuilder(state, state.allocBindings(1));
|
||||
|
||||
Value vOne;
|
||||
vOne.mkInt(1);
|
||||
|
||||
|
|
@ -751,37 +642,33 @@ TEST_F(ValuePrintingTests, ansiColorsListElided)
|
|||
vTwo.mkInt(2);
|
||||
|
||||
{
|
||||
auto list = state.buildList(2);
|
||||
list.elems[0] = &vOne;
|
||||
list.elems[1] = &vTwo;
|
||||
Value vList;
|
||||
vList.mkList(list);
|
||||
auto list = state.buildList(2);
|
||||
list.elems[0] = &vOne;
|
||||
list.elems[1] = &vTwo;
|
||||
Value vList;
|
||||
vList.mkList(list);
|
||||
|
||||
test(vList,
|
||||
"[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_FAINT "«1 item elided»" ANSI_NORMAL " ]",
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.maxListItems = 1
|
||||
});
|
||||
test(
|
||||
vList,
|
||||
"[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_FAINT "«1 item elided»" ANSI_NORMAL " ]",
|
||||
PrintOptions{.ansiColors = true, .maxListItems = 1});
|
||||
}
|
||||
|
||||
Value vThree;
|
||||
vThree.mkInt(3);
|
||||
|
||||
{
|
||||
auto list = state.buildList(3);
|
||||
list.elems[0] = &vOne;
|
||||
list.elems[1] = &vTwo;
|
||||
list.elems[2] = &vThree;
|
||||
Value vList;
|
||||
vList.mkList(list);
|
||||
auto list = state.buildList(3);
|
||||
list.elems[0] = &vOne;
|
||||
list.elems[1] = &vTwo;
|
||||
list.elems[2] = &vThree;
|
||||
Value vList;
|
||||
vList.mkList(list);
|
||||
|
||||
test(vList,
|
||||
"[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_FAINT "«2 items elided»" ANSI_NORMAL " ]",
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.maxListItems = 1
|
||||
});
|
||||
test(
|
||||
vList,
|
||||
"[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_FAINT "«2 items elided»" ANSI_NORMAL " ]",
|
||||
PrintOptions{.ansiColors = true, .maxListItems = 1});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
#include "nix/expr/attr-path.hh"
|
||||
#include "nix/expr/eval-inline.hh"
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
static Strings parseAttrPath(std::string_view s)
|
||||
{
|
||||
Strings res;
|
||||
|
|
@ -19,18 +17,19 @@ static Strings parseAttrPath(std::string_view s)
|
|||
while (1) {
|
||||
if (i == s.end())
|
||||
throw ParseError("missing closing quote in selection path '%1%'", s);
|
||||
if (*i == '"') break;
|
||||
if (*i == '"')
|
||||
break;
|
||||
cur.push_back(*i++);
|
||||
}
|
||||
} else
|
||||
cur.push_back(*i);
|
||||
++i;
|
||||
}
|
||||
if (!cur.empty()) res.push_back(cur);
|
||||
if (!cur.empty())
|
||||
res.push_back(cur);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s)
|
||||
{
|
||||
std::vector<Symbol> res;
|
||||
|
|
@ -39,9 +38,8 @@ std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s)
|
|||
return res;
|
||||
}
|
||||
|
||||
|
||||
std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::string & attrPath,
|
||||
Bindings & autoArgs, Value & vIn)
|
||||
std::pair<Value *, PosIdx>
|
||||
findAlongAttrPath(EvalState & state, const std::string & attrPath, Bindings & autoArgs, Value & vIn)
|
||||
{
|
||||
Strings tokens = parseAttrPath(attrPath);
|
||||
|
||||
|
|
@ -65,10 +63,12 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
|
|||
if (!attrIndex) {
|
||||
|
||||
if (v->type() != nAttrs)
|
||||
state.error<TypeError>(
|
||||
"the expression selected by the selection path '%1%' should be a set but is %2%",
|
||||
attrPath,
|
||||
showType(*v)).debugThrow();
|
||||
state
|
||||
.error<TypeError>(
|
||||
"the expression selected by the selection path '%1%' should be a set but is %2%",
|
||||
attrPath,
|
||||
showType(*v))
|
||||
.debugThrow();
|
||||
if (attr.empty())
|
||||
throw Error("empty attribute name in selection path '%1%'", attrPath);
|
||||
|
||||
|
|
@ -79,7 +79,8 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
|
|||
attrNames.insert(std::string(state.symbols[attr.name]));
|
||||
|
||||
auto suggestions = Suggestions::bestMatches(attrNames, attr);
|
||||
throw AttrPathNotFound(suggestions, "attribute '%1%' in selection path '%2%' not found", attr, attrPath);
|
||||
throw AttrPathNotFound(
|
||||
suggestions, "attribute '%1%' in selection path '%2%' not found", attr, attrPath);
|
||||
}
|
||||
v = &*a->value;
|
||||
pos = a->pos;
|
||||
|
|
@ -88,29 +89,29 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
|
|||
else {
|
||||
|
||||
if (!v->isList())
|
||||
state.error<TypeError>(
|
||||
"the expression selected by the selection path '%1%' should be a list but is %2%",
|
||||
attrPath,
|
||||
showType(*v)).debugThrow();
|
||||
state
|
||||
.error<TypeError>(
|
||||
"the expression selected by the selection path '%1%' should be a list but is %2%",
|
||||
attrPath,
|
||||
showType(*v))
|
||||
.debugThrow();
|
||||
if (*attrIndex >= v->listSize())
|
||||
throw AttrPathNotFound("list index %1% in selection path '%2%' is out of range", *attrIndex, attrPath);
|
||||
|
||||
v = v->listView()[*attrIndex];
|
||||
pos = noPos;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {v, pos};
|
||||
}
|
||||
|
||||
|
||||
std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v, std::string what)
|
||||
{
|
||||
Value * v2;
|
||||
try {
|
||||
auto dummyArgs = state.allocBindings(0);
|
||||
v2 = findAlongAttrPath(state, "meta.position", *dummyArgs, v).first;
|
||||
auto & dummyArgs = Bindings::emptyBindings;
|
||||
v2 = findAlongAttrPath(state, "meta.position", dummyArgs, v).first;
|
||||
} catch (Error &) {
|
||||
throw NoPositionInfo("package '%s' has no source location information", what);
|
||||
}
|
||||
|
|
@ -118,17 +119,17 @@ std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v
|
|||
// FIXME: is it possible to extract the Pos object instead of doing this
|
||||
// toString + parsing?
|
||||
NixStringContext context;
|
||||
auto path = state.coerceToPath(noPos, *v2, context, "while evaluating the 'meta.position' attribute of a derivation");
|
||||
auto path =
|
||||
state.coerceToPath(noPos, *v2, context, "while evaluating the 'meta.position' attribute of a derivation");
|
||||
|
||||
auto fn = path.path.abs();
|
||||
|
||||
auto fail = [fn]() {
|
||||
throw ParseError("cannot parse 'meta.position' attribute '%s'", fn);
|
||||
};
|
||||
auto fail = [fn]() { throw ParseError("cannot parse 'meta.position' attribute '%s'", fn); };
|
||||
|
||||
try {
|
||||
auto colon = fn.rfind(':');
|
||||
if (colon == std::string::npos) fail();
|
||||
if (colon == std::string::npos)
|
||||
fail();
|
||||
auto lineno = std::stoi(std::string(fn, colon + 1, std::string::npos));
|
||||
return {SourcePath{path.accessor, CanonPath(fn.substr(0, colon))}, lineno};
|
||||
} catch (std::invalid_argument & e) {
|
||||
|
|
@ -137,5 +138,4 @@ std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -3,10 +3,9 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
Bindings Bindings::emptyBindings;
|
||||
|
||||
/* Allocate a new array of attributes for an attribute set with a specific
|
||||
capacity. The space is implicitly reserved after the Bindings
|
||||
|
|
@ -14,40 +13,35 @@ namespace nix {
|
|||
Bindings * EvalState::allocBindings(size_t capacity)
|
||||
{
|
||||
if (capacity == 0)
|
||||
return &emptyBindings;
|
||||
return &Bindings::emptyBindings;
|
||||
if (capacity > std::numeric_limits<Bindings::size_t>::max())
|
||||
throw Error("attribute set of size %d is too big", capacity);
|
||||
nrAttrsets++;
|
||||
nrAttrsInAttrsets += capacity;
|
||||
return new (allocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings((Bindings::size_t) capacity);
|
||||
return new (allocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings();
|
||||
}
|
||||
|
||||
|
||||
Value & BindingsBuilder::alloc(Symbol name, PosIdx pos)
|
||||
{
|
||||
auto value = state.allocValue();
|
||||
auto value = state.get().allocValue();
|
||||
bindings->push_back(Attr(name, value, pos));
|
||||
return *value;
|
||||
}
|
||||
|
||||
|
||||
Value & BindingsBuilder::alloc(std::string_view name, PosIdx pos)
|
||||
{
|
||||
return alloc(state.symbols.create(name), pos);
|
||||
return alloc(state.get().symbols.create(name), pos);
|
||||
}
|
||||
|
||||
|
||||
void Bindings::sort()
|
||||
{
|
||||
if (size_) std::sort(begin(), end());
|
||||
std::sort(attrs, attrs + size_);
|
||||
}
|
||||
|
||||
|
||||
Value & Value::mkAttrs(BindingsBuilder & bindings)
|
||||
{
|
||||
mkAttrs(bindings.finish());
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "nix/expr/eval.hh"
|
||||
#include "nix/expr/eval-inline.hh"
|
||||
#include "nix/store/store-api.hh"
|
||||
#include "nix/store/globals.hh"
|
||||
// Need specialization involving `SymbolStr` just in this one module.
|
||||
#include "nix/util/strings-inline.hh"
|
||||
|
||||
|
|
@ -11,8 +12,10 @@ namespace nix::eval_cache {
|
|||
|
||||
CachedEvalError::CachedEvalError(ref<AttrCursor> cursor, Symbol attr)
|
||||
: EvalError(cursor->root->state, "cached failure of attribute '%s'", cursor->getAttrPathStr(attr))
|
||||
, cursor(cursor), attr(attr)
|
||||
{ }
|
||||
, cursor(cursor)
|
||||
, attr(attr)
|
||||
{
|
||||
}
|
||||
|
||||
void CachedEvalError::force()
|
||||
{
|
||||
|
|
@ -25,7 +28,8 @@ void CachedEvalError::force()
|
|||
}
|
||||
|
||||
// Shouldn't happen.
|
||||
throw EvalError(state, "evaluation of cached failed attribute '%s' unexpectedly succeeded", cursor->getAttrPathStr(attr));
|
||||
throw EvalError(
|
||||
state, "evaluation of cached failed attribute '%s' unexpectedly succeeded", cursor->getAttrPathStr(attr));
|
||||
}
|
||||
|
||||
static const char * schema = R"sql(
|
||||
|
|
@ -59,36 +63,32 @@ struct AttrDb
|
|||
|
||||
SymbolTable & symbols;
|
||||
|
||||
AttrDb(
|
||||
const StoreDirConfig & cfg,
|
||||
const Hash & fingerprint,
|
||||
SymbolTable & symbols)
|
||||
AttrDb(const StoreDirConfig & cfg, const Hash & fingerprint, SymbolTable & symbols)
|
||||
: cfg(cfg)
|
||||
, _state(std::make_unique<Sync<State>>())
|
||||
, symbols(symbols)
|
||||
{
|
||||
auto state(_state->lock());
|
||||
|
||||
Path cacheDir = getCacheDir() + "/eval-cache-v5";
|
||||
auto cacheDir = std::filesystem::path(getCacheDir()) / "eval-cache-v6";
|
||||
createDirs(cacheDir);
|
||||
|
||||
Path dbPath = cacheDir + "/" + fingerprint.to_string(HashFormat::Base16, false) + ".sqlite";
|
||||
auto dbPath = cacheDir / (fingerprint.to_string(HashFormat::Base16, false) + ".sqlite");
|
||||
|
||||
state->db = SQLite(dbPath);
|
||||
state->db.isCache();
|
||||
state->db.exec(schema);
|
||||
|
||||
state->insertAttribute.create(state->db,
|
||||
"insert or replace into Attributes(parent, name, type, value) values (?, ?, ?, ?)");
|
||||
state->insertAttribute.create(
|
||||
state->db, "insert or replace into Attributes(parent, name, type, value) values (?, ?, ?, ?)");
|
||||
|
||||
state->insertAttributeWithContext.create(state->db,
|
||||
"insert or replace into Attributes(parent, name, type, value, context) values (?, ?, ?, ?, ?)");
|
||||
state->insertAttributeWithContext.create(
|
||||
state->db, "insert or replace into Attributes(parent, name, type, value, context) values (?, ?, ?, ?, ?)");
|
||||
|
||||
state->queryAttribute.create(state->db,
|
||||
"select rowid, type, value, context from Attributes where parent = ? and name = ?");
|
||||
state->queryAttribute.create(
|
||||
state->db, "select rowid, type, value, context from Attributes where parent = ? and name = ?");
|
||||
|
||||
state->queryAttributes.create(state->db,
|
||||
"select name from Attributes where parent = ?");
|
||||
state->queryAttributes.create(state->db, "select name from Attributes where parent = ?");
|
||||
|
||||
state->txn = std::make_unique<SQLiteTxn>(state->db);
|
||||
}
|
||||
|
|
@ -108,7 +108,8 @@ struct AttrDb
|
|||
template<typename F>
|
||||
AttrId doSQLite(F && fun)
|
||||
{
|
||||
if (failed) return 0;
|
||||
if (failed)
|
||||
return 0;
|
||||
try {
|
||||
return fun();
|
||||
} catch (SQLiteError &) {
|
||||
|
|
@ -118,116 +119,76 @@ struct AttrDb
|
|||
}
|
||||
}
|
||||
|
||||
AttrId setAttrs(
|
||||
AttrKey key,
|
||||
const std::vector<Symbol> & attrs)
|
||||
AttrId setAttrs(AttrKey key, const std::vector<Symbol> & attrs)
|
||||
{
|
||||
return doSQLite([&]()
|
||||
{
|
||||
return doSQLite([&]() {
|
||||
auto state(_state->lock());
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(AttrType::FullAttrs)
|
||||
(0, false).exec();
|
||||
state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::FullAttrs) (0, false).exec();
|
||||
|
||||
AttrId rowId = state->db.getLastInsertedRowId();
|
||||
assert(rowId);
|
||||
|
||||
for (auto & attr : attrs)
|
||||
state->insertAttribute.use()
|
||||
(rowId)
|
||||
(symbols[attr])
|
||||
(AttrType::Placeholder)
|
||||
(0, false).exec();
|
||||
state->insertAttribute.use()(rowId)(symbols[attr])(AttrType::Placeholder) (0, false).exec();
|
||||
|
||||
return rowId;
|
||||
});
|
||||
}
|
||||
|
||||
AttrId setString(
|
||||
AttrKey key,
|
||||
std::string_view s,
|
||||
const char * * context = nullptr)
|
||||
AttrId setString(AttrKey key, std::string_view s, const char ** context = nullptr)
|
||||
{
|
||||
return doSQLite([&]()
|
||||
{
|
||||
return doSQLite([&]() {
|
||||
auto state(_state->lock());
|
||||
|
||||
if (context) {
|
||||
std::string ctx;
|
||||
for (const char * * p = context; *p; ++p) {
|
||||
if (p != context) ctx.push_back(' ');
|
||||
for (const char ** p = context; *p; ++p) {
|
||||
if (p != context)
|
||||
ctx.push_back(' ');
|
||||
ctx.append(*p);
|
||||
}
|
||||
state->insertAttributeWithContext.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(AttrType::String)
|
||||
(s)
|
||||
(ctx).exec();
|
||||
state->insertAttributeWithContext.use()(key.first)(symbols[key.second])(AttrType::String) (s) (ctx)
|
||||
.exec();
|
||||
} else {
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(AttrType::String)
|
||||
(s).exec();
|
||||
state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::String) (s).exec();
|
||||
}
|
||||
|
||||
return state->db.getLastInsertedRowId();
|
||||
});
|
||||
}
|
||||
|
||||
AttrId setBool(
|
||||
AttrKey key,
|
||||
bool b)
|
||||
AttrId setBool(AttrKey key, bool b)
|
||||
{
|
||||
return doSQLite([&]()
|
||||
{
|
||||
return doSQLite([&]() {
|
||||
auto state(_state->lock());
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(AttrType::Bool)
|
||||
(b ? 1 : 0).exec();
|
||||
state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::Bool) (b ? 1 : 0).exec();
|
||||
|
||||
return state->db.getLastInsertedRowId();
|
||||
});
|
||||
}
|
||||
|
||||
AttrId setInt(
|
||||
AttrKey key,
|
||||
int n)
|
||||
AttrId setInt(AttrKey key, int n)
|
||||
{
|
||||
return doSQLite([&]()
|
||||
{
|
||||
return doSQLite([&]() {
|
||||
auto state(_state->lock());
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(AttrType::Int)
|
||||
(n).exec();
|
||||
state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::Int) (n).exec();
|
||||
|
||||
return state->db.getLastInsertedRowId();
|
||||
});
|
||||
}
|
||||
|
||||
AttrId setListOfStrings(
|
||||
AttrKey key,
|
||||
const std::vector<std::string> & l)
|
||||
AttrId setListOfStrings(AttrKey key, const std::vector<std::string> & l)
|
||||
{
|
||||
return doSQLite([&]()
|
||||
{
|
||||
return doSQLite([&]() {
|
||||
auto state(_state->lock());
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(AttrType::ListOfStrings)
|
||||
(dropEmptyInitThenConcatStringsSep("\t", l)).exec();
|
||||
state->insertAttribute
|
||||
.use()(key.first)(symbols[key.second])(
|
||||
AttrType::ListOfStrings) (dropEmptyInitThenConcatStringsSep("\t", l))
|
||||
.exec();
|
||||
|
||||
return state->db.getLastInsertedRowId();
|
||||
});
|
||||
|
|
@ -235,15 +196,10 @@ struct AttrDb
|
|||
|
||||
AttrId setPlaceholder(AttrKey key)
|
||||
{
|
||||
return doSQLite([&]()
|
||||
{
|
||||
return doSQLite([&]() {
|
||||
auto state(_state->lock());
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(AttrType::Placeholder)
|
||||
(0, false).exec();
|
||||
state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::Placeholder) (0, false).exec();
|
||||
|
||||
return state->db.getLastInsertedRowId();
|
||||
});
|
||||
|
|
@ -251,15 +207,10 @@ struct AttrDb
|
|||
|
||||
AttrId setMissing(AttrKey key)
|
||||
{
|
||||
return doSQLite([&]()
|
||||
{
|
||||
return doSQLite([&]() {
|
||||
auto state(_state->lock());
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(AttrType::Missing)
|
||||
(0, false).exec();
|
||||
state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::Missing) (0, false).exec();
|
||||
|
||||
return state->db.getLastInsertedRowId();
|
||||
});
|
||||
|
|
@ -267,15 +218,10 @@ struct AttrDb
|
|||
|
||||
AttrId setMisc(AttrKey key)
|
||||
{
|
||||
return doSQLite([&]()
|
||||
{
|
||||
return doSQLite([&]() {
|
||||
auto state(_state->lock());
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(AttrType::Misc)
|
||||
(0, false).exec();
|
||||
state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::Misc) (0, false).exec();
|
||||
|
||||
return state->db.getLastInsertedRowId();
|
||||
});
|
||||
|
|
@ -283,15 +229,10 @@ struct AttrDb
|
|||
|
||||
AttrId setFailed(AttrKey key)
|
||||
{
|
||||
return doSQLite([&]()
|
||||
{
|
||||
return doSQLite([&]() {
|
||||
auto state(_state->lock());
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(AttrType::Failed)
|
||||
(0, false).exec();
|
||||
state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::Failed) (0, false).exec();
|
||||
|
||||
return state->db.getLastInsertedRowId();
|
||||
});
|
||||
|
|
@ -302,51 +243,49 @@ struct AttrDb
|
|||
auto state(_state->lock());
|
||||
|
||||
auto queryAttribute(state->queryAttribute.use()(key.first)(symbols[key.second]));
|
||||
if (!queryAttribute.next()) return {};
|
||||
if (!queryAttribute.next())
|
||||
return {};
|
||||
|
||||
auto rowId = (AttrId) queryAttribute.getInt(0);
|
||||
auto type = (AttrType) queryAttribute.getInt(1);
|
||||
|
||||
switch (type) {
|
||||
case AttrType::Placeholder:
|
||||
return {{rowId, placeholder_t()}};
|
||||
case AttrType::FullAttrs: {
|
||||
// FIXME: expensive, should separate this out.
|
||||
std::vector<Symbol> attrs;
|
||||
auto queryAttributes(state->queryAttributes.use()(rowId));
|
||||
while (queryAttributes.next())
|
||||
attrs.emplace_back(symbols.create(queryAttributes.getStr(0)));
|
||||
return {{rowId, attrs}};
|
||||
}
|
||||
case AttrType::String: {
|
||||
NixStringContext context;
|
||||
if (!queryAttribute.isNull(3))
|
||||
for (auto & s : tokenizeString<std::vector<std::string>>(queryAttribute.getStr(3), ";"))
|
||||
context.insert(NixStringContextElem::parse(s));
|
||||
return {{rowId, string_t{queryAttribute.getStr(2), context}}};
|
||||
}
|
||||
case AttrType::Bool:
|
||||
return {{rowId, queryAttribute.getInt(2) != 0}};
|
||||
case AttrType::Int:
|
||||
return {{rowId, int_t{NixInt{queryAttribute.getInt(2)}}}};
|
||||
case AttrType::ListOfStrings:
|
||||
return {{rowId, tokenizeString<std::vector<std::string>>(queryAttribute.getStr(2), "\t")}};
|
||||
case AttrType::Missing:
|
||||
return {{rowId, missing_t()}};
|
||||
case AttrType::Misc:
|
||||
return {{rowId, misc_t()}};
|
||||
case AttrType::Failed:
|
||||
return {{rowId, failed_t()}};
|
||||
default:
|
||||
throw Error("unexpected type in evaluation cache");
|
||||
case AttrType::Placeholder:
|
||||
return {{rowId, placeholder_t()}};
|
||||
case AttrType::FullAttrs: {
|
||||
// FIXME: expensive, should separate this out.
|
||||
std::vector<Symbol> attrs;
|
||||
auto queryAttributes(state->queryAttributes.use()(rowId));
|
||||
while (queryAttributes.next())
|
||||
attrs.emplace_back(symbols.create(queryAttributes.getStr(0)));
|
||||
return {{rowId, attrs}};
|
||||
}
|
||||
case AttrType::String: {
|
||||
NixStringContext context;
|
||||
if (!queryAttribute.isNull(3))
|
||||
for (auto & s : tokenizeString<std::vector<std::string>>(queryAttribute.getStr(3), ";"))
|
||||
context.insert(NixStringContextElem::parse(s));
|
||||
return {{rowId, string_t{queryAttribute.getStr(2), context}}};
|
||||
}
|
||||
case AttrType::Bool:
|
||||
return {{rowId, queryAttribute.getInt(2) != 0}};
|
||||
case AttrType::Int:
|
||||
return {{rowId, int_t{NixInt{queryAttribute.getInt(2)}}}};
|
||||
case AttrType::ListOfStrings:
|
||||
return {{rowId, tokenizeString<std::vector<std::string>>(queryAttribute.getStr(2), "\t")}};
|
||||
case AttrType::Missing:
|
||||
return {{rowId, missing_t()}};
|
||||
case AttrType::Misc:
|
||||
return {{rowId, misc_t()}};
|
||||
case AttrType::Failed:
|
||||
return {{rowId, failed_t()}};
|
||||
default:
|
||||
throw Error("unexpected type in evaluation cache");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static std::shared_ptr<AttrDb> makeAttrDb(
|
||||
const StoreDirConfig & cfg,
|
||||
const Hash & fingerprint,
|
||||
SymbolTable & symbols)
|
||||
static std::shared_ptr<AttrDb> makeAttrDb(const StoreDirConfig & cfg, const Hash & fingerprint, SymbolTable & symbols)
|
||||
{
|
||||
try {
|
||||
return std::make_shared<AttrDb>(cfg, fingerprint, symbols);
|
||||
|
|
@ -357,9 +296,7 @@ static std::shared_ptr<AttrDb> makeAttrDb(
|
|||
}
|
||||
|
||||
EvalCache::EvalCache(
|
||||
std::optional<std::reference_wrapper<const Hash>> useCache,
|
||||
EvalState & state,
|
||||
RootLoader rootLoader)
|
||||
std::optional<std::reference_wrapper<const Hash>> useCache, EvalState & state, RootLoader rootLoader)
|
||||
: db(useCache ? makeAttrDb(*state.store, *useCache, state.symbols) : nullptr)
|
||||
, state(state)
|
||||
, rootLoader(rootLoader)
|
||||
|
|
@ -381,11 +318,10 @@ ref<AttrCursor> EvalCache::getRoot()
|
|||
}
|
||||
|
||||
AttrCursor::AttrCursor(
|
||||
ref<EvalCache> root,
|
||||
Parent parent,
|
||||
Value * value,
|
||||
std::optional<std::pair<AttrId, AttrValue>> && cachedValue)
|
||||
: root(root), parent(parent), cachedValue(std::move(cachedValue))
|
||||
ref<EvalCache> root, Parent parent, Value * value, std::optional<std::pair<AttrId, AttrValue>> && cachedValue)
|
||||
: root(root)
|
||||
, parent(parent)
|
||||
, cachedValue(std::move(cachedValue))
|
||||
{
|
||||
if (value)
|
||||
_value = allocRootValue(value);
|
||||
|
|
@ -394,7 +330,7 @@ AttrCursor::AttrCursor(
|
|||
AttrKey AttrCursor::getKey()
|
||||
{
|
||||
if (!parent)
|
||||
return {0, root->state.sEpsilon};
|
||||
return {0, root->state.s.epsilon};
|
||||
if (!parent->first->cachedValue) {
|
||||
parent->first->cachedValue = root->db->getAttr(parent->first->getKey());
|
||||
assert(parent->first->cachedValue);
|
||||
|
|
@ -470,13 +406,11 @@ Value & AttrCursor::forceValue()
|
|||
|
||||
if (root->db && (!cachedValue || std::get_if<placeholder_t>(&cachedValue->second))) {
|
||||
if (v.type() == nString)
|
||||
cachedValue = {root->db->setString(getKey(), v.c_str(), v.context()),
|
||||
string_t{v.c_str(), {}}};
|
||||
cachedValue = {root->db->setString(getKey(), v.c_str(), v.context()), string_t{v.c_str(), {}}};
|
||||
else if (v.type() == nPath) {
|
||||
auto path = v.path().path;
|
||||
cachedValue = {root->db->setString(getKey(), path.abs()), string_t{path.abs(), {}}};
|
||||
}
|
||||
else if (v.type() == nBool)
|
||||
} else if (v.type() == nBool)
|
||||
cachedValue = {root->db->setBool(getKey(), v.boolean()), v.boolean()};
|
||||
else if (v.type() == nInt)
|
||||
cachedValue = {root->db->setInt(getKey(), v.integer().value), int_t{v.integer()}};
|
||||
|
|
@ -518,14 +452,14 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name)
|
|||
else if (std::get_if<failed_t>(&attr->second))
|
||||
throw CachedEvalError(ref(shared_from_this()), name);
|
||||
else
|
||||
return std::make_shared<AttrCursor>(root,
|
||||
std::make_pair(ref(shared_from_this()), name), nullptr, std::move(attr));
|
||||
return std::make_shared<AttrCursor>(
|
||||
root, std::make_pair(ref(shared_from_this()), name), nullptr, std::move(attr));
|
||||
}
|
||||
// Incomplete attrset, so need to fall thru and
|
||||
// evaluate to see whether 'name' exists
|
||||
} else
|
||||
return nullptr;
|
||||
//error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
|
||||
// error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -533,7 +467,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name)
|
|||
|
||||
if (v.type() != nAttrs)
|
||||
return nullptr;
|
||||
//error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
|
||||
// error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
|
||||
|
||||
auto attr = v.attrs()->get(name);
|
||||
|
||||
|
|
@ -618,17 +552,15 @@ string_t AttrCursor::getStringWithContext()
|
|||
if (auto s = std::get_if<string_t>(&cachedValue->second)) {
|
||||
bool valid = true;
|
||||
for (auto & c : s->second) {
|
||||
const StorePath & path = std::visit(overloaded {
|
||||
[&](const NixStringContextElem::DrvDeep & d) -> const StorePath & {
|
||||
return d.drvPath;
|
||||
const StorePath & path = std::visit(
|
||||
overloaded{
|
||||
[&](const NixStringContextElem::DrvDeep & d) -> const StorePath & { return d.drvPath; },
|
||||
[&](const NixStringContextElem::Built & b) -> const StorePath & {
|
||||
return b.drvPath->getBaseStorePath();
|
||||
},
|
||||
[&](const NixStringContextElem::Opaque & o) -> const StorePath & { return o.path; },
|
||||
},
|
||||
[&](const NixStringContextElem::Built & b) -> const StorePath & {
|
||||
return b.drvPath->getBaseStorePath();
|
||||
},
|
||||
[&](const NixStringContextElem::Opaque & o) -> const StorePath & {
|
||||
return o.path;
|
||||
},
|
||||
}, c.raw);
|
||||
c.raw);
|
||||
if (!root->state.store->isValidPath(path)) {
|
||||
valid = false;
|
||||
break;
|
||||
|
|
@ -649,8 +581,7 @@ string_t AttrCursor::getStringWithContext()
|
|||
NixStringContext context;
|
||||
copyContext(v, context);
|
||||
return {v.c_str(), std::move(context)};
|
||||
}
|
||||
else if (v.type() == nPath)
|
||||
} else if (v.type() == nPath)
|
||||
return {v.path().to_string(), {}};
|
||||
else
|
||||
root->state.error<TypeError>("'%s' is not a string but %s", getAttrPathStr(), showType(v)).debugThrow();
|
||||
|
|
@ -722,7 +653,8 @@ std::vector<std::string> AttrCursor::getListOfStrings()
|
|||
std::vector<std::string> res;
|
||||
|
||||
for (auto elem : v.listView())
|
||||
res.push_back(std::string(root->state.forceStringNoCtx(*elem, noPos, "while evaluating an attribute for caching")));
|
||||
res.push_back(
|
||||
std::string(root->state.forceStringNoCtx(*elem, noPos, "while evaluating an attribute for caching")));
|
||||
|
||||
if (root->db)
|
||||
cachedValue = {root->db->setListOfStrings(getKey(), res), res};
|
||||
|
|
@ -770,7 +702,7 @@ bool AttrCursor::isDerivation()
|
|||
|
||||
StorePath AttrCursor::forceDerivation()
|
||||
{
|
||||
auto aDrvPath = getAttr(root->state.sDrvPath);
|
||||
auto aDrvPath = getAttr(root->state.s.drvPath);
|
||||
auto drvPath = root->state.store->parseStorePath(aDrvPath->getString());
|
||||
drvPath.requireDerivation();
|
||||
if (!root->state.store->isValidPath(drvPath) && !settings.readOnlyMode) {
|
||||
|
|
@ -778,10 +710,10 @@ StorePath AttrCursor::forceDerivation()
|
|||
been garbage-collected. So force it to be regenerated. */
|
||||
aDrvPath->forceValue();
|
||||
if (!root->state.store->isValidPath(drvPath))
|
||||
throw Error("don't know how to recreate store derivation '%s'!",
|
||||
root->state.store->printStorePath(drvPath));
|
||||
throw Error(
|
||||
"don't know how to recreate store derivation '%s'!", root->state.store->printStorePath(drvPath));
|
||||
}
|
||||
return drvPath;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix::eval_cache
|
||||
|
|
|
|||
|
|
@ -44,12 +44,13 @@ EvalErrorBuilder<T> & EvalErrorBuilder<T>::withFrame(const Env & env, const Expr
|
|||
// NOTE: This is abusing side-effects.
|
||||
// TODO: check compatibility with nested debugger calls.
|
||||
// TODO: What side-effects??
|
||||
error.state.debugTraces.push_front(DebugTrace{
|
||||
.pos = expr.getPos(),
|
||||
.expr = expr,
|
||||
.env = env,
|
||||
.hint = HintFmt("Fake frame for debugging purposes"),
|
||||
.isError = true});
|
||||
error.state.debugTraces.push_front(
|
||||
DebugTrace{
|
||||
.pos = expr.getPos(),
|
||||
.expr = expr,
|
||||
.env = env,
|
||||
.hint = HintFmt("Fake frame for debugging purposes"),
|
||||
.isError = true});
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
@ -96,7 +97,8 @@ template<class T>
|
|||
void EvalErrorBuilder<T>::panic()
|
||||
{
|
||||
logError(error.info());
|
||||
printError("This is a bug! An unexpected condition occurred, causing the Nix evaluator to have to stop. If you could share a reproducible example or a core dump, please open an issue at https://github.com/NixOS/nix/issues");
|
||||
printError(
|
||||
"This is a bug! An unexpected condition occurred, causing the Nix evaluator to have to stop. If you could share a reproducible example or a core dump, please open an issue at https://github.com/NixOS/nix/issues");
|
||||
abort();
|
||||
}
|
||||
|
||||
|
|
@ -112,4 +114,4 @@ template class EvalErrorBuilder<InfiniteRecursionError>;
|
|||
template class EvalErrorBuilder<InvalidPathError>;
|
||||
template class EvalErrorBuilder<IFDError>;
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -15,9 +15,8 @@
|
|||
# include <pthread_np.h>
|
||||
# endif
|
||||
|
||||
# include <gc/gc.h>
|
||||
# include <gc/gc_cpp.h>
|
||||
# include <gc/gc_allocator.h>
|
||||
# include <gc/gc_tiny_fl.h> // For GC_GRANULE_BYTES
|
||||
|
||||
# include <boost/coroutine2/coroutine.hpp>
|
||||
# include <boost/coroutine2/protected_fixedsize_stack.hpp>
|
||||
|
|
@ -28,6 +27,18 @@
|
|||
namespace nix {
|
||||
|
||||
#if NIX_USE_BOEHMGC
|
||||
|
||||
/*
|
||||
* Ensure that Boehm satisfies our alignment requirements. This is the default configuration [^]
|
||||
* and this assertion should never break for any platform. Let's assert it just in case.
|
||||
*
|
||||
* This alignment is particularly useful to be able to use aligned
|
||||
* load/store instructions for loading/writing Values.
|
||||
*
|
||||
* [^]: https://github.com/bdwgc/bdwgc/blob/54ac18ccbc5a833dd7edaff94a10ab9b65044d61/include/gc/gc_tiny_fl.h#L31-L33
|
||||
*/
|
||||
static_assert(sizeof(void *) * 2 == GC_GRANULE_BYTES, "Boehm GC must use GC_GRANULE_WORDS = 2");
|
||||
|
||||
/* Called when the Boehm GC runs out of memory. */
|
||||
static void * oomHandler(size_t requested)
|
||||
{
|
||||
|
|
@ -53,6 +64,9 @@ static inline void initGCReal()
|
|||
|
||||
GC_INIT();
|
||||
|
||||
/* Enable parallel marking. */
|
||||
GC_allow_register_threads();
|
||||
|
||||
/* Register valid displacements in case we are using alignment niches
|
||||
for storing the type information. This way tagged pointers are considered
|
||||
to be valid, even when they are not aligned. */
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
#include "nix/expr/eval-profiler-settings.hh"
|
||||
#include "nix/util/configuration.hh"
|
||||
#include "nix/util/logging.hh" /* Needs to be included before config-impl.hh */
|
||||
#include "nix/util/config-impl.hh"
|
||||
#include "nix/util/abstract-setting-to-json.hh"
|
||||
|
||||
|
|
@ -46,4 +45,4 @@ NLOHMANN_JSON_SERIALIZE_ENUM(
|
|||
/* Explicit instantiation of templates */
|
||||
template class BaseSetting<EvalProfilerMode>;
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ FrameInfo SampleStack::getPrimOpFrameInfo(const PrimOp & primOp, std::span<Value
|
|||
/* Error context strings don't actually matter, since we ignore all eval errors. */
|
||||
state.forceAttrs(*args[0], pos, "");
|
||||
auto attrs = args[0]->attrs();
|
||||
auto nameAttr = state.getAttr(state.sName, attrs, "");
|
||||
auto nameAttr = state.getAttr(state.s.name, attrs, "");
|
||||
auto drvName = std::string(state.forceStringNoCtx(*nameAttr->value, pos, ""));
|
||||
return DerivationStrictFrameInfo{.callPos = pos, .drvName = std::move(drvName)};
|
||||
} catch (...) {
|
||||
|
|
@ -211,7 +211,7 @@ FrameInfo SampleStack::getFrameInfoFromValueAndPos(const Value & v, std::span<Va
|
|||
/* Resolve primOp eagerly. Must not hold on to a reference to a Value. */
|
||||
return PrimOpFrameInfo{.expr = v.primOpAppPrimOp(), .callPos = pos};
|
||||
else if (state.isFunctor(v)) {
|
||||
const auto functor = v.attrs()->get(state.sFunctor);
|
||||
const auto functor = v.attrs()->get(state.s.functor);
|
||||
if (auto pos_ = posCache.lookup(pos); std::holds_alternative<std::monostate>(pos_.origin))
|
||||
/* HACK: In case callsite position is unresolved. */
|
||||
return FunctorFrameInfo{.pos = functor->pos};
|
||||
|
|
@ -352,4 +352,4 @@ ref<EvalProfiler> makeSampleStackProfiler(EvalState & state, std::filesystem::pa
|
|||
return make_ref<SampleStack>(state, profileFile, period);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -19,12 +19,14 @@ Strings EvalSettings::parseNixPath(const std::string & s)
|
|||
auto start2 = p;
|
||||
|
||||
while (p != s.end() && *p != ':') {
|
||||
if (*p == '=') start2 = p + 1;
|
||||
if (*p == '=')
|
||||
start2 = p + 1;
|
||||
++p;
|
||||
}
|
||||
|
||||
if (p == s.end()) {
|
||||
if (p != start) res.push_back(std::string(start, p));
|
||||
if (p != start)
|
||||
res.push_back(std::string(start, p));
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -32,10 +34,12 @@ Strings EvalSettings::parseNixPath(const std::string & s)
|
|||
auto prefix = std::string(start2, s.end());
|
||||
if (EvalSettings::isPseudoUrl(prefix) || hasPrefix(prefix, "flake:")) {
|
||||
++p;
|
||||
while (p != s.end() && *p != ':') ++p;
|
||||
while (p != s.end() && *p != ':')
|
||||
++p;
|
||||
}
|
||||
res.push_back(std::string(start, p));
|
||||
if (p == s.end()) break;
|
||||
if (p == s.end())
|
||||
break;
|
||||
}
|
||||
|
||||
++p;
|
||||
|
|
@ -75,11 +79,14 @@ Strings EvalSettings::getDefaultNixPath()
|
|||
|
||||
bool EvalSettings::isPseudoUrl(std::string_view s)
|
||||
{
|
||||
if (s.compare(0, 8, "channel:") == 0) return true;
|
||||
if (s.compare(0, 8, "channel:") == 0)
|
||||
return true;
|
||||
size_t pos = s.find("://");
|
||||
if (pos == std::string::npos) return false;
|
||||
if (pos == std::string::npos)
|
||||
return false;
|
||||
std::string scheme(s, 0, pos);
|
||||
return scheme == "http" || scheme == "https" || scheme == "file" || scheme == "channel" || scheme == "git" || scheme == "s3" || scheme == "ssh";
|
||||
return scheme == "http" || scheme == "https" || scheme == "file" || scheme == "channel" || scheme == "git"
|
||||
|| scheme == "s3" || scheme == "ssh";
|
||||
}
|
||||
|
||||
std::string EvalSettings::resolvePseudoUrl(std::string_view url)
|
||||
|
|
@ -98,9 +105,7 @@ const std::string & EvalSettings::getCurrentSystem() const
|
|||
|
||||
Path getNixDefExpr()
|
||||
{
|
||||
return settings.useXDGBaseDirectories
|
||||
? getStateDir() + "/defexpr"
|
||||
: getHome() + "/.nix-defexpr";
|
||||
return settings.useXDGBaseDirectories ? getStateDir() + "/defexpr" : getHome() + "/.nix-defexpr";
|
||||
}
|
||||
|
||||
} // namespace nix
|
||||
1406
src/libexpr/eval.cc
1406
src/libexpr/eval.cc
File diff suppressed because it is too large
Load diff
|
|
@ -19,4 +19,4 @@ void FunctionCallTrace::postFunctionCallHook(
|
|||
printMsg(lvlInfo, "function-trace exited %1% at %2%", state.positions[pos], ns.count());
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -7,18 +7,19 @@
|
|||
#include <cstring>
|
||||
#include <regex>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
PackageInfo::PackageInfo(EvalState & state, std::string attrPath, const Bindings * attrs)
|
||||
: state(&state), attrs(attrs), attrPath(std::move(attrPath))
|
||||
: state(&state)
|
||||
, attrs(attrs)
|
||||
, attrPath(std::move(attrPath))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
PackageInfo::PackageInfo(EvalState & state, ref<Store> store, const std::string & drvPathWithOutputs)
|
||||
: state(&state), attrs(nullptr), attrPath("")
|
||||
: state(&state)
|
||||
, attrs(nullptr)
|
||||
, attrPath("")
|
||||
{
|
||||
auto [drvPath, selectedOutputs] = parsePathWithOutputs(*store, drvPathWithOutputs);
|
||||
|
||||
|
|
@ -31,10 +32,7 @@ PackageInfo::PackageInfo(EvalState & state, ref<Store> store, const std::string
|
|||
if (selectedOutputs.size() > 1)
|
||||
throw Error("building more than one derivation output is not supported, in '%s'", drvPathWithOutputs);
|
||||
|
||||
outputName =
|
||||
selectedOutputs.empty()
|
||||
? getOr(drv.env, "outputName", "out")
|
||||
: *selectedOutputs.begin();
|
||||
outputName = selectedOutputs.empty() ? getOr(drv.env, "outputName", "out") : *selectedOutputs.begin();
|
||||
|
||||
auto i = drv.outputs.find(outputName);
|
||||
if (i == drv.outputs.end())
|
||||
|
|
@ -44,34 +42,35 @@ PackageInfo::PackageInfo(EvalState & state, ref<Store> store, const std::string
|
|||
outPath = {output.path(*store, drv.name, outputName)};
|
||||
}
|
||||
|
||||
|
||||
std::string PackageInfo::queryName() const
|
||||
{
|
||||
if (name == "" && attrs) {
|
||||
auto i = attrs->find(state->sName);
|
||||
if (i == attrs->end()) state->error<TypeError>("derivation name missing").debugThrow();
|
||||
auto i = attrs->get(state->s.name);
|
||||
if (!i)
|
||||
state->error<TypeError>("derivation name missing").debugThrow();
|
||||
name = state->forceStringNoCtx(*i->value, noPos, "while evaluating the 'name' attribute of a derivation");
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
std::string PackageInfo::querySystem() const
|
||||
{
|
||||
if (system == "" && attrs) {
|
||||
auto i = attrs->find(state->sSystem);
|
||||
system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, i->pos, "while evaluating the 'system' attribute of a derivation");
|
||||
auto i = attrs->get(state->s.system);
|
||||
system =
|
||||
!i ? "unknown"
|
||||
: state->forceStringNoCtx(*i->value, i->pos, "while evaluating the 'system' attribute of a derivation");
|
||||
}
|
||||
return system;
|
||||
}
|
||||
|
||||
|
||||
std::optional<StorePath> PackageInfo::queryDrvPath() const
|
||||
{
|
||||
if (!drvPath && attrs) {
|
||||
if (auto i = attrs->get(state->sDrvPath)) {
|
||||
if (auto i = attrs->get(state->s.drvPath)) {
|
||||
NixStringContext context;
|
||||
auto found = state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the 'drvPath' attribute of a derivation");
|
||||
auto found = state->coerceToStorePath(
|
||||
i->pos, *i->value, context, "while evaluating the 'drvPath' attribute of a derivation");
|
||||
try {
|
||||
found.requireDerivation();
|
||||
} catch (Error & e) {
|
||||
|
|
@ -85,7 +84,6 @@ std::optional<StorePath> PackageInfo::queryDrvPath() const
|
|||
return drvPath.value_or(std::nullopt);
|
||||
}
|
||||
|
||||
|
||||
StorePath PackageInfo::requireDrvPath() const
|
||||
{
|
||||
if (auto drvPath = queryDrvPath())
|
||||
|
|
@ -93,44 +91,49 @@ StorePath PackageInfo::requireDrvPath() const
|
|||
throw Error("derivation does not contain a 'drvPath' attribute");
|
||||
}
|
||||
|
||||
|
||||
StorePath PackageInfo::queryOutPath() const
|
||||
{
|
||||
if (!outPath && attrs) {
|
||||
auto i = attrs->find(state->sOutPath);
|
||||
auto i = attrs->get(state->s.outPath);
|
||||
NixStringContext context;
|
||||
if (i != attrs->end())
|
||||
outPath = state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the output path of a derivation");
|
||||
if (i)
|
||||
outPath = state->coerceToStorePath(
|
||||
i->pos, *i->value, context, "while evaluating the output path of a derivation");
|
||||
}
|
||||
if (!outPath)
|
||||
throw UnimplementedError("CA derivations are not yet supported");
|
||||
return *outPath;
|
||||
}
|
||||
|
||||
|
||||
PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall)
|
||||
{
|
||||
if (outputs.empty()) {
|
||||
/* Get the ‘outputs’ list. */
|
||||
const Attr * i;
|
||||
if (attrs && (i = attrs->get(state->sOutputs))) {
|
||||
if (attrs && (i = attrs->get(state->s.outputs))) {
|
||||
state->forceList(*i->value, i->pos, "while evaluating the 'outputs' attribute of a derivation");
|
||||
|
||||
/* For each output... */
|
||||
for (auto elem : i->value->listView()) {
|
||||
std::string output(state->forceStringNoCtx(*elem, i->pos, "while evaluating the name of an output of a derivation"));
|
||||
std::string output(
|
||||
state->forceStringNoCtx(*elem, i->pos, "while evaluating the name of an output of a derivation"));
|
||||
|
||||
if (withPaths) {
|
||||
/* Evaluate the corresponding set. */
|
||||
auto out = attrs->get(state->symbols.create(output));
|
||||
if (!out) continue; // FIXME: throw error?
|
||||
if (!out)
|
||||
continue; // FIXME: throw error?
|
||||
state->forceAttrs(*out->value, i->pos, "while evaluating an output of a derivation");
|
||||
|
||||
/* And evaluate its ‘outPath’ attribute. */
|
||||
auto outPath = out->value->attrs()->get(state->sOutPath);
|
||||
if (!outPath) continue; // FIXME: throw error?
|
||||
auto outPath = out->value->attrs()->get(state->s.outPath);
|
||||
if (!outPath)
|
||||
continue; // FIXME: throw error?
|
||||
NixStringContext context;
|
||||
outputs.emplace(output, state->coerceToStorePath(outPath->pos, *outPath->value, context, "while evaluating an output path of a derivation"));
|
||||
outputs.emplace(
|
||||
output,
|
||||
state->coerceToStorePath(
|
||||
outPath->pos, *outPath->value, context, "while evaluating an output path of a derivation"));
|
||||
} else
|
||||
outputs.emplace(output, std::nullopt);
|
||||
}
|
||||
|
|
@ -142,7 +145,8 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT
|
|||
return outputs;
|
||||
|
||||
const Attr * i;
|
||||
if (attrs && (i = attrs->get(state->sOutputSpecified)) && state->forceBool(*i->value, i->pos, "while evaluating the 'outputSpecified' attribute of a derivation")) {
|
||||
if (attrs && (i = attrs->get(state->s.outputSpecified))
|
||||
&& state->forceBool(*i->value, i->pos, "while evaluating the 'outputSpecified' attribute of a derivation")) {
|
||||
Outputs result;
|
||||
auto out = outputs.find(queryOutputName());
|
||||
if (out == outputs.end())
|
||||
|
|
@ -154,95 +158,103 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT
|
|||
else {
|
||||
/* Check for `meta.outputsToInstall` and return `outputs` reduced to that. */
|
||||
const Value * outTI = queryMeta("outputsToInstall");
|
||||
if (!outTI) return outputs;
|
||||
if (!outTI)
|
||||
return outputs;
|
||||
auto errMsg = Error("this derivation has bad 'meta.outputsToInstall'");
|
||||
/* ^ this shows during `nix-env -i` right under the bad derivation */
|
||||
if (!outTI->isList()) throw errMsg;
|
||||
/* ^ this shows during `nix-env -i` right under the bad derivation */
|
||||
if (!outTI->isList())
|
||||
throw errMsg;
|
||||
Outputs result;
|
||||
for (auto elem : outTI->listView()) {
|
||||
if (elem->type() != nString) throw errMsg;
|
||||
if (elem->type() != nString)
|
||||
throw errMsg;
|
||||
auto out = outputs.find(elem->c_str());
|
||||
if (out == outputs.end()) throw errMsg;
|
||||
if (out == outputs.end())
|
||||
throw errMsg;
|
||||
result.insert(*out);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::string PackageInfo::queryOutputName() const
|
||||
{
|
||||
if (outputName == "" && attrs) {
|
||||
auto i = attrs->get(state->sOutputName);
|
||||
outputName = i ? state->forceStringNoCtx(*i->value, noPos, "while evaluating the output name of a derivation") : "";
|
||||
auto i = attrs->get(state->s.outputName);
|
||||
outputName =
|
||||
i ? state->forceStringNoCtx(*i->value, noPos, "while evaluating the output name of a derivation") : "";
|
||||
}
|
||||
return outputName;
|
||||
}
|
||||
|
||||
|
||||
const Bindings * PackageInfo::getMeta()
|
||||
{
|
||||
if (meta) return meta;
|
||||
if (!attrs) return 0;
|
||||
auto a = attrs->get(state->sMeta);
|
||||
if (!a) return 0;
|
||||
if (meta)
|
||||
return meta;
|
||||
if (!attrs)
|
||||
return 0;
|
||||
auto a = attrs->get(state->s.meta);
|
||||
if (!a)
|
||||
return 0;
|
||||
state->forceAttrs(*a->value, a->pos, "while evaluating the 'meta' attribute of a derivation");
|
||||
meta = a->value->attrs();
|
||||
return meta;
|
||||
}
|
||||
|
||||
|
||||
StringSet PackageInfo::queryMetaNames()
|
||||
{
|
||||
StringSet res;
|
||||
if (!getMeta()) return res;
|
||||
if (!getMeta())
|
||||
return res;
|
||||
for (auto & i : *meta)
|
||||
res.emplace(state->symbols[i.name]);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
bool PackageInfo::checkMeta(Value & v)
|
||||
{
|
||||
state->forceValue(v, v.determinePos(noPos));
|
||||
if (v.type() == nList) {
|
||||
for (auto elem : v.listView())
|
||||
if (!checkMeta(*elem)) return false;
|
||||
if (!checkMeta(*elem))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
else if (v.type() == nAttrs) {
|
||||
if (v.attrs()->get(state->sOutPath)) return false;
|
||||
} else if (v.type() == nAttrs) {
|
||||
if (v.attrs()->get(state->s.outPath))
|
||||
return false;
|
||||
for (auto & i : *v.attrs())
|
||||
if (!checkMeta(*i.value)) return false;
|
||||
if (!checkMeta(*i.value))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
else return v.type() == nInt || v.type() == nBool || v.type() == nString ||
|
||||
v.type() == nFloat;
|
||||
} else
|
||||
return v.type() == nInt || v.type() == nBool || v.type() == nString || v.type() == nFloat;
|
||||
}
|
||||
|
||||
|
||||
Value * PackageInfo::queryMeta(const std::string & name)
|
||||
{
|
||||
if (!getMeta()) return 0;
|
||||
if (!getMeta())
|
||||
return 0;
|
||||
auto a = meta->get(state->symbols.create(name));
|
||||
if (!a || !checkMeta(*a->value)) return 0;
|
||||
if (!a || !checkMeta(*a->value))
|
||||
return 0;
|
||||
return a->value;
|
||||
}
|
||||
|
||||
|
||||
std::string PackageInfo::queryMetaString(const std::string & name)
|
||||
{
|
||||
Value * v = queryMeta(name);
|
||||
if (!v || v->type() != nString) return "";
|
||||
if (!v || v->type() != nString)
|
||||
return "";
|
||||
return v->c_str();
|
||||
}
|
||||
|
||||
|
||||
NixInt PackageInfo::queryMetaInt(const std::string & name, NixInt def)
|
||||
{
|
||||
Value * v = queryMeta(name);
|
||||
if (!v) return def;
|
||||
if (v->type() == nInt) return v->integer();
|
||||
if (!v)
|
||||
return def;
|
||||
if (v->type() == nInt)
|
||||
return v->integer();
|
||||
if (v->type() == nString) {
|
||||
/* Backwards compatibility with before we had support for
|
||||
integer meta fields. */
|
||||
|
|
@ -255,8 +267,10 @@ NixInt PackageInfo::queryMetaInt(const std::string & name, NixInt def)
|
|||
NixFloat PackageInfo::queryMetaFloat(const std::string & name, NixFloat def)
|
||||
{
|
||||
Value * v = queryMeta(name);
|
||||
if (!v) return def;
|
||||
if (v->type() == nFloat) return v->fpoint();
|
||||
if (!v)
|
||||
return def;
|
||||
if (v->type() == nFloat)
|
||||
return v->fpoint();
|
||||
if (v->type() == nString) {
|
||||
/* Backwards compatibility with before we had support for
|
||||
float meta fields. */
|
||||
|
|
@ -266,22 +280,24 @@ NixFloat PackageInfo::queryMetaFloat(const std::string & name, NixFloat def)
|
|||
return def;
|
||||
}
|
||||
|
||||
|
||||
bool PackageInfo::queryMetaBool(const std::string & name, bool def)
|
||||
{
|
||||
Value * v = queryMeta(name);
|
||||
if (!v) return def;
|
||||
if (v->type() == nBool) return v->boolean();
|
||||
if (!v)
|
||||
return def;
|
||||
if (v->type() == nBool)
|
||||
return v->boolean();
|
||||
if (v->type() == nString) {
|
||||
/* Backwards compatibility with before we had support for
|
||||
Boolean meta fields. */
|
||||
if (v->string_view() == "true") return true;
|
||||
if (v->string_view() == "false") return false;
|
||||
if (v->string_view() == "true")
|
||||
return true;
|
||||
if (v->string_view() == "false")
|
||||
return false;
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
void PackageInfo::setMeta(const std::string & name, Value * v)
|
||||
{
|
||||
getMeta();
|
||||
|
|
@ -291,30 +307,35 @@ void PackageInfo::setMeta(const std::string & name, Value * v)
|
|||
for (auto i : *meta)
|
||||
if (i.name != sym)
|
||||
attrs.insert(i);
|
||||
if (v) attrs.insert(sym, v);
|
||||
if (v)
|
||||
attrs.insert(sym, v);
|
||||
meta = attrs.finish();
|
||||
}
|
||||
|
||||
|
||||
/* Cache for already considered attrsets. */
|
||||
typedef std::set<const Bindings *> Done;
|
||||
|
||||
|
||||
/* Evaluate value `v'. If it evaluates to a set of type `derivation',
|
||||
then put information about it in `drvs' (unless it's already in `done').
|
||||
The result boolean indicates whether it makes sense
|
||||
for the caller to recursively search for derivations in `v'. */
|
||||
static bool getDerivation(EvalState & state, Value & v,
|
||||
const std::string & attrPath, PackageInfos & drvs, Done & done,
|
||||
static bool getDerivation(
|
||||
EvalState & state,
|
||||
Value & v,
|
||||
const std::string & attrPath,
|
||||
PackageInfos & drvs,
|
||||
Done & done,
|
||||
bool ignoreAssertionFailures)
|
||||
{
|
||||
try {
|
||||
state.forceValue(v, v.determinePos(noPos));
|
||||
if (!state.isDerivation(v)) return true;
|
||||
if (!state.isDerivation(v))
|
||||
return true;
|
||||
|
||||
/* Remove spurious duplicates (e.g., a set like `rec { x =
|
||||
derivation {...}; y = x;}'. */
|
||||
if (!done.insert(v.attrs()).second) return false;
|
||||
if (!done.insert(v.attrs()).second)
|
||||
return false;
|
||||
|
||||
PackageInfo drv(state, attrPath, v.attrs());
|
||||
|
||||
|
|
@ -325,42 +346,44 @@ static bool getDerivation(EvalState & state, Value & v,
|
|||
return false;
|
||||
|
||||
} catch (AssertionError & e) {
|
||||
if (ignoreAssertionFailures) return false;
|
||||
if (ignoreAssertionFailures)
|
||||
return false;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::optional<PackageInfo> getDerivation(EvalState & state, Value & v,
|
||||
bool ignoreAssertionFailures)
|
||||
std::optional<PackageInfo> getDerivation(EvalState & state, Value & v, bool ignoreAssertionFailures)
|
||||
{
|
||||
Done done;
|
||||
PackageInfos drvs;
|
||||
getDerivation(state, v, "", drvs, done, ignoreAssertionFailures);
|
||||
if (drvs.size() != 1) return {};
|
||||
if (drvs.size() != 1)
|
||||
return {};
|
||||
return std::move(drvs.front());
|
||||
}
|
||||
|
||||
|
||||
static std::string addToPath(const std::string & s1, std::string_view s2)
|
||||
{
|
||||
return s1.empty() ? std::string(s2) : s1 + "." + s2;
|
||||
}
|
||||
|
||||
|
||||
static std::regex attrRegex("[A-Za-z_][A-Za-z0-9-_+]*");
|
||||
|
||||
|
||||
static void getDerivations(EvalState & state, Value & vIn,
|
||||
const std::string & pathPrefix, Bindings & autoArgs,
|
||||
PackageInfos & drvs, Done & done,
|
||||
static void getDerivations(
|
||||
EvalState & state,
|
||||
Value & vIn,
|
||||
const std::string & pathPrefix,
|
||||
Bindings & autoArgs,
|
||||
PackageInfos & drvs,
|
||||
Done & done,
|
||||
bool ignoreAssertionFailures)
|
||||
{
|
||||
Value v;
|
||||
state.autoCallFunction(autoArgs, vIn, v);
|
||||
|
||||
/* Process the expression. */
|
||||
if (!getDerivation(state, v, pathPrefix, drvs, done, ignoreAssertionFailures)) ;
|
||||
if (!getDerivation(state, v, pathPrefix, drvs, done, ignoreAssertionFailures))
|
||||
;
|
||||
|
||||
else if (v.type() == nAttrs) {
|
||||
|
||||
|
|
@ -387,9 +410,12 @@ static void getDerivations(EvalState & state, Value & vIn,
|
|||
should we recurse into it? => Only if it has a
|
||||
`recurseForDerivations = true' attribute. */
|
||||
if (i->value->type() == nAttrs) {
|
||||
auto j = i->value->attrs()->get(state.sRecurseForDerivations);
|
||||
if (j && state.forceBool(*j->value, j->pos, "while evaluating the attribute `recurseForDerivations`"))
|
||||
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
auto j = i->value->attrs()->get(state.s.recurseForDerivations);
|
||||
if (j
|
||||
&& state.forceBool(
|
||||
*j->value, j->pos, "while evaluating the attribute `recurseForDerivations`"))
|
||||
getDerivations(
|
||||
state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
}
|
||||
}
|
||||
} catch (Error & e) {
|
||||
|
|
@ -412,13 +438,16 @@ static void getDerivations(EvalState & state, Value & vIn,
|
|||
state.error<TypeError>("expression does not evaluate to a derivation (or a set or list of those)").debugThrow();
|
||||
}
|
||||
|
||||
|
||||
void getDerivations(EvalState & state, Value & v, const std::string & pathPrefix,
|
||||
Bindings & autoArgs, PackageInfos & drvs, bool ignoreAssertionFailures)
|
||||
void getDerivations(
|
||||
EvalState & state,
|
||||
Value & v,
|
||||
const std::string & pathPrefix,
|
||||
Bindings & autoArgs,
|
||||
PackageInfos & drvs,
|
||||
bool ignoreAssertionFailures)
|
||||
{
|
||||
Done done;
|
||||
getDerivations(state, v, pathPrefix, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -11,11 +11,8 @@ namespace nix {
|
|||
MakeError(AttrPathNotFound, Error);
|
||||
MakeError(NoPositionInfo, Error);
|
||||
|
||||
std::pair<Value *, PosIdx> findAlongAttrPath(
|
||||
EvalState & state,
|
||||
const std::string & attrPath,
|
||||
Bindings & autoArgs,
|
||||
Value & vIn);
|
||||
std::pair<Value *, PosIdx>
|
||||
findAlongAttrPath(EvalState & state, const std::string & attrPath, Bindings & autoArgs, Value & vIn);
|
||||
|
||||
/**
|
||||
* Heuristic to find the filename and lineno or a nix value.
|
||||
|
|
@ -24,4 +21,4 @@ std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v
|
|||
|
||||
std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -5,10 +5,11 @@
|
|||
#include "nix/expr/symbol-table.hh"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <concepts>
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
class EvalState;
|
||||
struct Value;
|
||||
|
||||
|
|
@ -23,17 +24,21 @@ struct Attr
|
|||
way we keep Attr size at two words with no wasted space. */
|
||||
Symbol name;
|
||||
PosIdx pos;
|
||||
Value * value;
|
||||
Value * value = nullptr;
|
||||
Attr(Symbol name, Value * value, PosIdx pos = noPos)
|
||||
: name(name), pos(pos), value(value) { };
|
||||
Attr() { };
|
||||
auto operator <=> (const Attr & a) const
|
||||
: name(name)
|
||||
, pos(pos)
|
||||
, value(value) {};
|
||||
Attr() {};
|
||||
|
||||
auto operator<=>(const Attr & a) const
|
||||
{
|
||||
return name <=> a.name;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(Attr) == 2 * sizeof(uint32_t) + sizeof(Value *),
|
||||
static_assert(
|
||||
sizeof(Attr) == 2 * sizeof(uint32_t) + sizeof(Value *),
|
||||
"performance of the evaluator is highly sensitive to the size of Attr. "
|
||||
"avoid introducing any padding into Attr if at all possible, and do not "
|
||||
"introduce new fields that need not be present for almost every instance.");
|
||||
|
|
@ -50,49 +55,108 @@ public:
|
|||
typedef uint32_t size_t;
|
||||
PosIdx pos;
|
||||
|
||||
/**
|
||||
* An instance of bindings objects with 0 attributes.
|
||||
* This object must never be modified.
|
||||
*/
|
||||
static Bindings emptyBindings;
|
||||
|
||||
private:
|
||||
size_t size_, capacity_;
|
||||
size_t size_ = 0;
|
||||
Attr attrs[0];
|
||||
|
||||
Bindings(size_t capacity) : size_(0), capacity_(capacity) { }
|
||||
Bindings(const Bindings & bindings) = delete;
|
||||
Bindings() = default;
|
||||
Bindings(const Bindings &) = delete;
|
||||
Bindings(Bindings &&) = delete;
|
||||
Bindings & operator=(const Bindings &) = delete;
|
||||
Bindings & operator=(Bindings &&) = delete;
|
||||
|
||||
public:
|
||||
size_t size() const { return size_; }
|
||||
size_t size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
bool empty() const { return !size_; }
|
||||
bool empty() const
|
||||
{
|
||||
return !size_;
|
||||
}
|
||||
|
||||
typedef Attr * iterator;
|
||||
class iterator
|
||||
{
|
||||
public:
|
||||
using value_type = Attr;
|
||||
using pointer = const value_type *;
|
||||
using reference = const value_type &;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
typedef const Attr * const_iterator;
|
||||
friend class Bindings;
|
||||
|
||||
private:
|
||||
pointer ptr = nullptr;
|
||||
|
||||
explicit iterator(pointer ptr)
|
||||
: ptr(ptr)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
iterator() = default;
|
||||
|
||||
reference operator*() const
|
||||
{
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
const value_type * operator->() const
|
||||
{
|
||||
return ptr;
|
||||
}
|
||||
|
||||
iterator & operator++()
|
||||
{
|
||||
++ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator++(int)
|
||||
{
|
||||
pointer tmp = ptr;
|
||||
++*this;
|
||||
return iterator(tmp);
|
||||
}
|
||||
|
||||
bool operator==(const iterator & rhs) const = default;
|
||||
};
|
||||
|
||||
using const_iterator = iterator;
|
||||
|
||||
void push_back(const Attr & attr)
|
||||
{
|
||||
assert(size_ < capacity_);
|
||||
attrs[size_++] = attr;
|
||||
}
|
||||
|
||||
const_iterator find(Symbol name) const
|
||||
{
|
||||
Attr key(name, 0);
|
||||
const_iterator i = std::lower_bound(begin(), end(), key);
|
||||
if (i != end() && i->name == name) return i;
|
||||
return end();
|
||||
}
|
||||
|
||||
const Attr * get(Symbol name) const
|
||||
{
|
||||
Attr key(name, 0);
|
||||
const_iterator i = std::lower_bound(begin(), end(), key);
|
||||
if (i != end() && i->name == name) return &*i;
|
||||
auto first = attrs;
|
||||
auto last = attrs + size_;
|
||||
const Attr * i = std::lower_bound(first, last, key);
|
||||
if (i != last && i->name == name)
|
||||
return i;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
iterator begin() { return &attrs[0]; }
|
||||
iterator end() { return &attrs[size_]; }
|
||||
const_iterator begin() const
|
||||
{
|
||||
return const_iterator(attrs);
|
||||
}
|
||||
|
||||
const_iterator begin() const { return &attrs[0]; }
|
||||
const_iterator end() const { return &attrs[size_]; }
|
||||
const_iterator end() const
|
||||
{
|
||||
return const_iterator(attrs + size_);
|
||||
}
|
||||
|
||||
Attr & operator[](size_t pos)
|
||||
{
|
||||
|
|
@ -106,8 +170,6 @@ public:
|
|||
|
||||
void sort();
|
||||
|
||||
size_t capacity() const { return capacity_; }
|
||||
|
||||
/**
|
||||
* Returns the attributes in lexicographically sorted order.
|
||||
*/
|
||||
|
|
@ -127,24 +189,36 @@ public:
|
|||
friend class EvalState;
|
||||
};
|
||||
|
||||
static_assert(std::forward_iterator<Bindings::iterator>);
|
||||
static_assert(std::ranges::forward_range<Bindings>);
|
||||
|
||||
/**
|
||||
* A wrapper around Bindings that ensures that its always in sorted
|
||||
* order at the end. The only way to consume a BindingsBuilder is to
|
||||
* call finish(), which sorts the bindings.
|
||||
*/
|
||||
class BindingsBuilder
|
||||
class BindingsBuilder final
|
||||
{
|
||||
Bindings * bindings;
|
||||
|
||||
public:
|
||||
// needed by std::back_inserter
|
||||
using value_type = Attr;
|
||||
using size_type = Bindings::size_t;
|
||||
|
||||
EvalState & state;
|
||||
private:
|
||||
Bindings * bindings;
|
||||
Bindings::size_t capacity_;
|
||||
|
||||
BindingsBuilder(EvalState & state, Bindings * bindings)
|
||||
: bindings(bindings), state(state)
|
||||
{ }
|
||||
friend class EvalState;
|
||||
|
||||
BindingsBuilder(EvalState & state, Bindings * bindings, size_type capacity)
|
||||
: bindings(bindings)
|
||||
, capacity_(capacity)
|
||||
, state(state)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
std::reference_wrapper<EvalState> state;
|
||||
|
||||
void insert(Symbol name, Value * value, PosIdx pos = noPos)
|
||||
{
|
||||
|
|
@ -158,6 +232,7 @@ public:
|
|||
|
||||
void push_back(const Attr & attr)
|
||||
{
|
||||
assert(bindings->size() < capacity_);
|
||||
bindings->push_back(attr);
|
||||
}
|
||||
|
||||
|
|
@ -176,19 +251,19 @@ public:
|
|||
return bindings;
|
||||
}
|
||||
|
||||
size_t capacity()
|
||||
size_t capacity() const noexcept
|
||||
{
|
||||
return bindings->capacity();
|
||||
return capacity_;
|
||||
}
|
||||
|
||||
void grow(Bindings * newBindings)
|
||||
void grow(BindingsBuilder newBindings)
|
||||
{
|
||||
for (auto & i : *bindings)
|
||||
newBindings->push_back(i);
|
||||
bindings = newBindings;
|
||||
newBindings.push_back(i);
|
||||
std::swap(*this, newBindings);
|
||||
}
|
||||
|
||||
friend struct ExprAttrs;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -43,10 +43,7 @@ class EvalCache : public std::enable_shared_from_this<EvalCache>
|
|||
|
||||
public:
|
||||
|
||||
EvalCache(
|
||||
std::optional<std::reference_wrapper<const Hash>> useCache,
|
||||
EvalState & state,
|
||||
RootLoader rootLoader);
|
||||
EvalCache(std::optional<std::reference_wrapper<const Hash>> useCache, EvalState & state, RootLoader rootLoader);
|
||||
|
||||
ref<AttrCursor> getRoot();
|
||||
};
|
||||
|
|
@ -63,11 +60,23 @@ enum AttrType {
|
|||
Int = 8,
|
||||
};
|
||||
|
||||
struct placeholder_t {};
|
||||
struct missing_t {};
|
||||
struct misc_t {};
|
||||
struct failed_t {};
|
||||
struct int_t { NixInt x; };
|
||||
struct placeholder_t
|
||||
{};
|
||||
|
||||
struct missing_t
|
||||
{};
|
||||
|
||||
struct misc_t
|
||||
{};
|
||||
|
||||
struct failed_t
|
||||
{};
|
||||
|
||||
struct int_t
|
||||
{
|
||||
NixInt x;
|
||||
};
|
||||
|
||||
typedef uint64_t AttrId;
|
||||
typedef std::pair<AttrId, Symbol> AttrKey;
|
||||
typedef std::pair<std::string, NixStringContext> string_t;
|
||||
|
|
@ -81,8 +90,8 @@ typedef std::variant<
|
|||
failed_t,
|
||||
bool,
|
||||
int_t,
|
||||
std::vector<std::string>
|
||||
> AttrValue;
|
||||
std::vector<std::string>>
|
||||
AttrValue;
|
||||
|
||||
class AttrCursor : public std::enable_shared_from_this<AttrCursor>
|
||||
{
|
||||
|
|
@ -161,4 +170,4 @@ public:
|
|||
StorePath forceDerivation();
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix::eval_cache
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ struct InvalidPathError : public EvalError
|
|||
{
|
||||
public:
|
||||
Path path;
|
||||
|
||||
InvalidPathError(EvalState & state, const Path & path)
|
||||
: EvalError(state, "path '%s' is not valid", path)
|
||||
{
|
||||
|
|
@ -119,4 +120,4 @@ public:
|
|||
[[gnu::noinline, gnu::noreturn]] void panic();
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -3,12 +3,13 @@
|
|||
|
||||
#include <cstddef>
|
||||
|
||||
// For `NIX_USE_BOEHMGC`, and if that's set, `GC_THREADS`
|
||||
// For `NIX_USE_BOEHMGC`
|
||||
#include "nix/expr/config.hh"
|
||||
|
||||
#if NIX_USE_BOEHMGC
|
||||
|
||||
# define GC_INCLUDE_NEW
|
||||
# define GC_THREADS 1
|
||||
|
||||
# include <gc/gc.h>
|
||||
# include <gc/gc_cpp.h>
|
||||
|
|
|
|||
|
|
@ -6,9 +6,6 @@
|
|||
#include "nix/expr/eval-error.hh"
|
||||
#include "nix/expr/eval-settings.hh"
|
||||
|
||||
// For `NIX_USE_BOEHMGC`, and if that's set, `GC_THREADS`
|
||||
#include "nix/expr/config.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
|
|
@ -23,11 +20,11 @@ inline void * allocBytes(size_t n)
|
|||
#else
|
||||
p = calloc(n, 1);
|
||||
#endif
|
||||
if (!p) throw std::bad_alloc();
|
||||
if (!p)
|
||||
throw std::bad_alloc();
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
[[gnu::always_inline]]
|
||||
Value * EvalState::allocValue()
|
||||
{
|
||||
|
|
@ -38,7 +35,8 @@ Value * EvalState::allocValue()
|
|||
have to explicitly clear the first word of every object we take. */
|
||||
if (!*valueAllocCache) {
|
||||
*valueAllocCache = GC_malloc_many(sizeof(Value));
|
||||
if (!*valueAllocCache) throw std::bad_alloc();
|
||||
if (!*valueAllocCache)
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
/* GC_NEXT is a convenience macro for accessing the first word of an object.
|
||||
|
|
@ -54,7 +52,6 @@ Value * EvalState::allocValue()
|
|||
return (Value *) p;
|
||||
}
|
||||
|
||||
|
||||
[[gnu::always_inline]]
|
||||
Env & EvalState::allocEnv(size_t size)
|
||||
{
|
||||
|
|
@ -68,7 +65,8 @@ Env & EvalState::allocEnv(size_t size)
|
|||
/* see allocValue for explanations. */
|
||||
if (!*env1AllocCache) {
|
||||
*env1AllocCache = GC_malloc_many(sizeof(Env) + sizeof(Value *));
|
||||
if (!*env1AllocCache) throw std::bad_alloc();
|
||||
if (!*env1AllocCache)
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
void * p = *env1AllocCache;
|
||||
|
|
@ -84,7 +82,6 @@ Env & EvalState::allocEnv(size_t size)
|
|||
return *env;
|
||||
}
|
||||
|
||||
|
||||
[[gnu::always_inline]]
|
||||
void EvalState::forceValue(Value & v, const PosIdx pos)
|
||||
{
|
||||
|
|
@ -94,7 +91,7 @@ void EvalState::forceValue(Value & v, const PosIdx pos)
|
|||
Expr * expr = v.thunk().expr;
|
||||
try {
|
||||
v.mkBlackhole();
|
||||
//checkInterrupt();
|
||||
// checkInterrupt();
|
||||
if (env) [[likely]]
|
||||
expr->eval(*this, *env, v);
|
||||
else
|
||||
|
|
@ -104,54 +101,47 @@ void EvalState::forceValue(Value & v, const PosIdx pos)
|
|||
tryFixupBlackHolePos(v, pos);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else if (v.isApp())
|
||||
} else if (v.isApp())
|
||||
callFunction(*v.app().left, *v.app().right, v, pos);
|
||||
}
|
||||
|
||||
|
||||
[[gnu::always_inline]]
|
||||
inline void EvalState::forceAttrs(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||
{
|
||||
forceAttrs(v, [&]() { return pos; }, errorCtx);
|
||||
}
|
||||
|
||||
|
||||
template <typename Callable>
|
||||
template<typename Callable>
|
||||
[[gnu::always_inline]]
|
||||
inline void EvalState::forceAttrs(Value & v, Callable getPos, std::string_view errorCtx)
|
||||
{
|
||||
PosIdx pos = getPos();
|
||||
forceValue(v, pos);
|
||||
if (v.type() != nAttrs) {
|
||||
error<TypeError>(
|
||||
"expected a set but found %1%: %2%",
|
||||
showType(v),
|
||||
ValuePrinter(*this, v, errorPrintOptions)
|
||||
).withTrace(pos, errorCtx).debugThrow();
|
||||
error<TypeError>("expected a set but found %1%: %2%", showType(v), ValuePrinter(*this, v, errorPrintOptions))
|
||||
.withTrace(pos, errorCtx)
|
||||
.debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[[gnu::always_inline]]
|
||||
inline void EvalState::forceList(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||
{
|
||||
forceValue(v, pos);
|
||||
if (!v.isList()) {
|
||||
error<TypeError>(
|
||||
"expected a list but found %1%: %2%",
|
||||
showType(v),
|
||||
ValuePrinter(*this, v, errorPrintOptions)
|
||||
).withTrace(pos, errorCtx).debugThrow();
|
||||
error<TypeError>("expected a list but found %1%: %2%", showType(v), ValuePrinter(*this, v, errorPrintOptions))
|
||||
.withTrace(pos, errorCtx)
|
||||
.debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
[[gnu::always_inline]]
|
||||
inline CallDepth EvalState::addCallDepth(const PosIdx pos) {
|
||||
inline CallDepth EvalState::addCallDepth(const PosIdx pos)
|
||||
{
|
||||
if (callDepth > settings.maxCallDepth)
|
||||
error<EvalBaseError>("stack overflow; max-call-depth exceeded").atPos(pos).debugThrow();
|
||||
|
||||
return CallDepth(callDepth);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -13,4 +13,4 @@ EvalProfilerMode BaseSetting<EvalProfilerMode>::parse(const std::string & str) c
|
|||
template<>
|
||||
std::string BaseSetting<EvalProfilerMode>::to_string() const;
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -111,4 +111,4 @@ public:
|
|||
|
||||
ref<EvalProfiler> makeSampleStackProfiler(EvalState & state, std::filesystem::path profileFile, uint64_t frequency);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -74,7 +74,9 @@ struct EvalSettings : Config
|
|||
)"};
|
||||
|
||||
Setting<Strings> nixPath{
|
||||
this, {}, "nix-path",
|
||||
this,
|
||||
{},
|
||||
"nix-path",
|
||||
R"(
|
||||
List of search paths to use for [lookup path](@docroot@/language/constructs/lookup-path.md) resolution.
|
||||
This setting determines the value of
|
||||
|
|
@ -107,10 +109,14 @@ struct EvalSettings : Config
|
|||
> If [restricted evaluation](@docroot@/command-ref/conf-file.md#conf-restrict-eval) is enabled, the default value is empty.
|
||||
>
|
||||
> If [pure evaluation](#conf-pure-eval) is enabled, `builtins.nixPath` *always* evaluates to the empty list `[ ]`.
|
||||
)", {}, false};
|
||||
)",
|
||||
{},
|
||||
false};
|
||||
|
||||
Setting<std::string> currentSystem{
|
||||
this, "", "eval-system",
|
||||
this,
|
||||
"",
|
||||
"eval-system",
|
||||
R"(
|
||||
This option defines
|
||||
[`builtins.currentSystem`](@docroot@/language/builtins.md#builtins-currentSystem)
|
||||
|
|
@ -130,7 +136,9 @@ struct EvalSettings : Config
|
|||
const std::string & getCurrentSystem() const;
|
||||
|
||||
Setting<bool> restrictEval{
|
||||
this, false, "restrict-eval",
|
||||
this,
|
||||
false,
|
||||
"restrict-eval",
|
||||
R"(
|
||||
If set to `true`, the Nix evaluator doesn't allow access to any
|
||||
files outside of
|
||||
|
|
@ -139,7 +147,10 @@ struct EvalSettings : Config
|
|||
[`allowed-uris`](@docroot@/command-ref/conf-file.md#conf-allowed-uris).
|
||||
)"};
|
||||
|
||||
Setting<bool> pureEval{this, false, "pure-eval",
|
||||
Setting<bool> pureEval{
|
||||
this,
|
||||
false,
|
||||
"pure-eval",
|
||||
R"(
|
||||
Pure evaluation mode ensures that the result of Nix expressions is fully determined by explicitly declared inputs, and not influenced by external state:
|
||||
|
||||
|
|
@ -149,21 +160,23 @@ struct EvalSettings : Config
|
|||
- [`builtins.currentTime`](@docroot@/language/builtins.md#builtins-currentTime)
|
||||
- [`builtins.nixPath`](@docroot@/language/builtins.md#builtins-nixPath)
|
||||
- [`builtins.storePath`](@docroot@/language/builtins.md#builtins-storePath)
|
||||
)"
|
||||
};
|
||||
)"};
|
||||
|
||||
Setting<bool> traceImportFromDerivation{
|
||||
this, false, "trace-import-from-derivation",
|
||||
this,
|
||||
false,
|
||||
"trace-import-from-derivation",
|
||||
R"(
|
||||
By default, Nix allows [Import from Derivation](@docroot@/language/import-from-derivation.md).
|
||||
|
||||
When this setting is `true`, Nix logs a warning indicating that it performed such an import.
|
||||
This option has no effect if `allow-import-from-derivation` is disabled.
|
||||
)"
|
||||
};
|
||||
)"};
|
||||
|
||||
Setting<bool> enableImportFromDerivation{
|
||||
this, true, "allow-import-from-derivation",
|
||||
this,
|
||||
true,
|
||||
"allow-import-from-derivation",
|
||||
R"(
|
||||
By default, Nix allows [Import from Derivation](@docroot@/language/import-from-derivation.md).
|
||||
|
||||
|
|
@ -173,7 +186,10 @@ struct EvalSettings : Config
|
|||
regardless of the state of the store.
|
||||
)"};
|
||||
|
||||
Setting<Strings> allowedUris{this, {}, "allowed-uris",
|
||||
Setting<Strings> allowedUris{
|
||||
this,
|
||||
{},
|
||||
"allowed-uris",
|
||||
R"(
|
||||
A list of URI prefixes to which access is allowed in restricted
|
||||
evaluation mode. For example, when set to
|
||||
|
|
@ -186,7 +202,10 @@ struct EvalSettings : Config
|
|||
- or the prefix is a URI scheme ended by a colon `:` and the URI has the same scheme.
|
||||
)"};
|
||||
|
||||
Setting<bool> traceFunctionCalls{this, false, "trace-function-calls",
|
||||
Setting<bool> traceFunctionCalls{
|
||||
this,
|
||||
false,
|
||||
"trace-function-calls",
|
||||
R"(
|
||||
If set to `true`, the Nix evaluator traces every function call.
|
||||
Nix prints a log message at the "vomit" level for every function
|
||||
|
|
@ -204,7 +223,10 @@ struct EvalSettings : Config
|
|||
`flamegraph.pl`.
|
||||
)"};
|
||||
|
||||
Setting<EvalProfilerMode> evalProfilerMode{this, EvalProfilerMode::disabled, "eval-profiler",
|
||||
Setting<EvalProfilerMode> evalProfilerMode{
|
||||
this,
|
||||
EvalProfilerMode::disabled,
|
||||
"eval-profiler",
|
||||
R"(
|
||||
Enables evaluation profiling. The following modes are supported:
|
||||
|
||||
|
|
@ -215,38 +237,56 @@ struct EvalSettings : Config
|
|||
See [Using the `eval-profiler`](@docroot@/advanced-topics/eval-profiler.md).
|
||||
)"};
|
||||
|
||||
Setting<Path> evalProfileFile{this, "nix.profile", "eval-profile-file",
|
||||
Setting<Path> evalProfileFile{
|
||||
this,
|
||||
"nix.profile",
|
||||
"eval-profile-file",
|
||||
R"(
|
||||
Specifies the file where [evaluation profile](#conf-eval-profiler) is saved.
|
||||
)"};
|
||||
|
||||
Setting<uint32_t> evalProfilerFrequency{this, 99, "eval-profiler-frequency",
|
||||
Setting<uint32_t> evalProfilerFrequency{
|
||||
this,
|
||||
99,
|
||||
"eval-profiler-frequency",
|
||||
R"(
|
||||
Specifies the sampling rate in hertz for sampling evaluation profilers.
|
||||
Use `0` to sample the stack after each function call.
|
||||
See [`eval-profiler`](#conf-eval-profiler).
|
||||
)"};
|
||||
|
||||
Setting<bool> useEvalCache{this, true, "eval-cache",
|
||||
Setting<bool> useEvalCache{
|
||||
this,
|
||||
true,
|
||||
"eval-cache",
|
||||
R"(
|
||||
Whether to use the flake evaluation cache.
|
||||
Certain commands won't have to evaluate when invoked for the second time with a particular version of a flake.
|
||||
Intermediate results are not cached.
|
||||
)"};
|
||||
|
||||
Setting<bool> ignoreExceptionsDuringTry{this, false, "ignore-try",
|
||||
Setting<bool> ignoreExceptionsDuringTry{
|
||||
this,
|
||||
false,
|
||||
"ignore-try",
|
||||
R"(
|
||||
If set to true, ignore exceptions inside 'tryEval' calls when evaluating Nix expressions in
|
||||
debug mode (using the --debugger flag). By default the debugger pauses on all exceptions.
|
||||
)"};
|
||||
|
||||
Setting<bool> traceVerbose{this, false, "trace-verbose",
|
||||
Setting<bool> traceVerbose{
|
||||
this,
|
||||
false,
|
||||
"trace-verbose",
|
||||
"Whether `builtins.traceVerbose` should trace its first argument when evaluated."};
|
||||
|
||||
Setting<unsigned int> maxCallDepth{this, 10000, "max-call-depth",
|
||||
"The maximum function call depth to allow before erroring."};
|
||||
Setting<unsigned int> maxCallDepth{
|
||||
this, 10000, "max-call-depth", "The maximum function call depth to allow before erroring."};
|
||||
|
||||
Setting<bool> builtinsTraceDebugger{this, false, "debugger-on-trace",
|
||||
Setting<bool> builtinsTraceDebugger{
|
||||
this,
|
||||
false,
|
||||
"debugger-on-trace",
|
||||
R"(
|
||||
If set to true and the `--debugger` flag is given, the following functions
|
||||
enter the debugger like [`builtins.break`](@docroot@/language/builtins.md#builtins-break):
|
||||
|
|
@ -259,7 +299,10 @@ struct EvalSettings : Config
|
|||
This is useful for debugging warnings in third-party Nix code.
|
||||
)"};
|
||||
|
||||
Setting<bool> builtinsDebuggerOnWarn{this, false, "debugger-on-warn",
|
||||
Setting<bool> builtinsDebuggerOnWarn{
|
||||
this,
|
||||
false,
|
||||
"debugger-on-warn",
|
||||
R"(
|
||||
If set to true and the `--debugger` flag is given, [`builtins.warn`](@docroot@/language/builtins.md#builtins-warn)
|
||||
will enter the debugger like [`builtins.break`](@docroot@/language/builtins.md#builtins-break).
|
||||
|
|
@ -269,7 +312,10 @@ struct EvalSettings : Config
|
|||
Use [`debugger-on-trace`](#conf-debugger-on-trace) to also enter the debugger on legacy warnings that are logged with [`builtins.trace`](@docroot@/language/builtins.md#builtins-trace).
|
||||
)"};
|
||||
|
||||
Setting<bool> builtinsAbortOnWarn{this, false, "abort-on-warn",
|
||||
Setting<bool> builtinsAbortOnWarn{
|
||||
this,
|
||||
false,
|
||||
"abort-on-warn",
|
||||
R"(
|
||||
If set to true, [`builtins.warn`](@docroot@/language/builtins.md#builtins-warn) throws an error when logging a warning.
|
||||
|
||||
|
|
@ -281,6 +327,21 @@ struct EvalSettings : Config
|
|||
|
||||
This option can be enabled by setting `NIX_ABORT_ON_WARN=1` in the environment.
|
||||
)"};
|
||||
|
||||
Setting<bool> warnShortPathLiterals{
|
||||
this,
|
||||
false,
|
||||
"warn-short-path-literals",
|
||||
R"(
|
||||
If set to true, the Nix evaluator will warn when encountering relative path literals
|
||||
that don't start with `./` or `../`.
|
||||
|
||||
For example, with this setting enabled, `foo/bar` would emit a warning
|
||||
suggesting to use `./foo/bar` instead.
|
||||
|
||||
This is useful for improving code readability and making path literals
|
||||
more explicit.
|
||||
)"};
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -288,4 +349,4 @@ struct EvalSettings : Config
|
|||
*/
|
||||
Path getNixDefExpr();
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
// For `NIX_USE_BOEHMGC`, and if that's set, `GC_THREADS`
|
||||
#include "nix/expr/config.hh"
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <functional>
|
||||
|
|
@ -34,39 +36,46 @@ namespace nix {
|
|||
constexpr size_t maxPrimOpArity = 8;
|
||||
|
||||
class Store;
|
||||
|
||||
namespace fetchers {
|
||||
struct Settings;
|
||||
struct InputCache;
|
||||
}
|
||||
} // namespace fetchers
|
||||
struct EvalSettings;
|
||||
class EvalState;
|
||||
class StorePath;
|
||||
struct SingleDerivedPath;
|
||||
enum RepairFlag : bool;
|
||||
struct MemorySourceAccessor;
|
||||
|
||||
namespace eval_cache {
|
||||
class EvalCache;
|
||||
class EvalCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments a count on construction and decrements on destruction.
|
||||
*/
|
||||
class CallDepth {
|
||||
size_t & count;
|
||||
class CallDepth
|
||||
{
|
||||
size_t & count;
|
||||
|
||||
public:
|
||||
CallDepth(size_t & count) : count(count) {
|
||||
++count;
|
||||
}
|
||||
~CallDepth() {
|
||||
--count;
|
||||
}
|
||||
CallDepth(size_t & count)
|
||||
: count(count)
|
||||
{
|
||||
++count;
|
||||
}
|
||||
|
||||
~CallDepth()
|
||||
{
|
||||
--count;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Function that implements a primop.
|
||||
*/
|
||||
using PrimOpFun = void(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||
using PrimOpFun = void(EvalState & state, const PosIdx pos, Value ** args, Value & v);
|
||||
|
||||
/**
|
||||
* Info about a primitive operation, and its implementation
|
||||
|
|
@ -151,9 +160,11 @@ struct Constant
|
|||
bool impureOnly = false;
|
||||
};
|
||||
|
||||
typedef std::map<std::string, Value *, std::less<std::string>, traceable_allocator<std::pair<const std::string, Value *> > > ValMap;
|
||||
typedef std::
|
||||
map<std::string, Value *, std::less<std::string>, traceable_allocator<std::pair<const std::string, Value *>>>
|
||||
ValMap;
|
||||
|
||||
typedef std::unordered_map<PosIdx, DocComment> DocCommentMap;
|
||||
typedef boost::unordered_flat_map<PosIdx, DocComment, std::hash<PosIdx>> DocCommentMap;
|
||||
|
||||
struct Env
|
||||
{
|
||||
|
|
@ -161,23 +172,25 @@ struct Env
|
|||
Value * values[0];
|
||||
};
|
||||
|
||||
void printEnvBindings(const EvalState &es, const Expr & expr, const Env & env);
|
||||
void printEnvBindings(const EvalState & es, const Expr & expr, const Env & env);
|
||||
void printEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env & env, int lvl = 0);
|
||||
|
||||
std::unique_ptr<ValMap> mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env & env);
|
||||
|
||||
void copyContext(const Value & v, NixStringContext & context, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
|
||||
void copyContext(
|
||||
const Value & v,
|
||||
NixStringContext & context,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
|
||||
std::string printValue(EvalState & state, Value & v);
|
||||
std::ostream & operator << (std::ostream & os, const ValueType t);
|
||||
|
||||
std::ostream & operator<<(std::ostream & os, const ValueType t);
|
||||
|
||||
struct RegexCache;
|
||||
|
||||
std::shared_ptr<RegexCache> makeRegexCache();
|
||||
|
||||
struct DebugTrace {
|
||||
struct DebugTrace
|
||||
{
|
||||
/* WARNING: Converting PosIdx -> Pos should be done with extra care. This is
|
||||
due to the fact that operator[] of PosTable is incredibly expensive. */
|
||||
std::variant<Pos, PosIdx> pos;
|
||||
|
|
@ -202,73 +215,106 @@ struct DebugTrace {
|
|||
}
|
||||
};
|
||||
|
||||
struct StaticEvalSymbols
|
||||
{
|
||||
Symbol with, outPath, drvPath, type, meta, name, value, system, overrides, outputs, outputName, ignoreNulls, file,
|
||||
line, column, functor, toString, right, wrong, structuredAttrs, json, allowedReferences, allowedRequisites,
|
||||
disallowedReferences, disallowedRequisites, maxSize, maxClosureSize, builder, args, contentAddressed, impure,
|
||||
outputHash, outputHashAlgo, outputHashMode, recurseForDerivations, description, self, epsilon, startSet,
|
||||
operator_, key, path, prefix, outputSpecified;
|
||||
|
||||
Expr::AstSymbols exprSymbols;
|
||||
|
||||
static constexpr auto preallocate()
|
||||
{
|
||||
StaticSymbolTable alloc;
|
||||
|
||||
StaticEvalSymbols staticSymbols = {
|
||||
.with = alloc.create("<with>"),
|
||||
.outPath = alloc.create("outPath"),
|
||||
.drvPath = alloc.create("drvPath"),
|
||||
.type = alloc.create("type"),
|
||||
.meta = alloc.create("meta"),
|
||||
.name = alloc.create("name"),
|
||||
.value = alloc.create("value"),
|
||||
.system = alloc.create("system"),
|
||||
.overrides = alloc.create("__overrides"),
|
||||
.outputs = alloc.create("outputs"),
|
||||
.outputName = alloc.create("outputName"),
|
||||
.ignoreNulls = alloc.create("__ignoreNulls"),
|
||||
.file = alloc.create("file"),
|
||||
.line = alloc.create("line"),
|
||||
.column = alloc.create("column"),
|
||||
.functor = alloc.create("__functor"),
|
||||
.toString = alloc.create("__toString"),
|
||||
.right = alloc.create("right"),
|
||||
.wrong = alloc.create("wrong"),
|
||||
.structuredAttrs = alloc.create("__structuredAttrs"),
|
||||
.json = alloc.create("__json"),
|
||||
.allowedReferences = alloc.create("allowedReferences"),
|
||||
.allowedRequisites = alloc.create("allowedRequisites"),
|
||||
.disallowedReferences = alloc.create("disallowedReferences"),
|
||||
.disallowedRequisites = alloc.create("disallowedRequisites"),
|
||||
.maxSize = alloc.create("maxSize"),
|
||||
.maxClosureSize = alloc.create("maxClosureSize"),
|
||||
.builder = alloc.create("builder"),
|
||||
.args = alloc.create("args"),
|
||||
.contentAddressed = alloc.create("__contentAddressed"),
|
||||
.impure = alloc.create("__impure"),
|
||||
.outputHash = alloc.create("outputHash"),
|
||||
.outputHashAlgo = alloc.create("outputHashAlgo"),
|
||||
.outputHashMode = alloc.create("outputHashMode"),
|
||||
.recurseForDerivations = alloc.create("recurseForDerivations"),
|
||||
.description = alloc.create("description"),
|
||||
.self = alloc.create("self"),
|
||||
.epsilon = alloc.create(""),
|
||||
.startSet = alloc.create("startSet"),
|
||||
.operator_ = alloc.create("operator"),
|
||||
.key = alloc.create("key"),
|
||||
.path = alloc.create("path"),
|
||||
.prefix = alloc.create("prefix"),
|
||||
.outputSpecified = alloc.create("outputSpecified"),
|
||||
.exprSymbols = {
|
||||
.sub = alloc.create("__sub"),
|
||||
.lessThan = alloc.create("__lessThan"),
|
||||
.mul = alloc.create("__mul"),
|
||||
.div = alloc.create("__div"),
|
||||
.or_ = alloc.create("or"),
|
||||
.findFile = alloc.create("__findFile"),
|
||||
.nixPath = alloc.create("__nixPath"),
|
||||
.body = alloc.create("body"),
|
||||
}};
|
||||
|
||||
return std::pair{staticSymbols, alloc};
|
||||
}
|
||||
|
||||
static consteval StaticEvalSymbols create()
|
||||
{
|
||||
return preallocate().first;
|
||||
}
|
||||
|
||||
static constexpr StaticSymbolTable staticSymbolTable()
|
||||
{
|
||||
return preallocate().second;
|
||||
}
|
||||
};
|
||||
|
||||
class EvalState : public std::enable_shared_from_this<EvalState>
|
||||
{
|
||||
public:
|
||||
static constexpr StaticEvalSymbols s = StaticEvalSymbols::create();
|
||||
|
||||
const fetchers::Settings & fetchSettings;
|
||||
const EvalSettings & settings;
|
||||
SymbolTable symbols;
|
||||
PosTable positions;
|
||||
|
||||
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
|
||||
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
|
||||
sFile, sLine, sColumn, sFunctor, sToString,
|
||||
sRight, sWrong, sStructuredAttrs, sJson,
|
||||
sAllowedReferences, sAllowedRequisites, sDisallowedReferences, sDisallowedRequisites,
|
||||
sMaxSize, sMaxClosureSize,
|
||||
sBuilder, sArgs,
|
||||
sContentAddressed, sImpure,
|
||||
sOutputHash, sOutputHashAlgo, sOutputHashMode,
|
||||
sRecurseForDerivations,
|
||||
sDescription, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath,
|
||||
sPrefix,
|
||||
sOutputSpecified;
|
||||
|
||||
const Expr::AstSymbols exprSymbols;
|
||||
|
||||
/**
|
||||
* If set, force copying files to the Nix store even if they
|
||||
* already exist there.
|
||||
*/
|
||||
RepairFlag repair;
|
||||
|
||||
Bindings emptyBindings;
|
||||
|
||||
/**
|
||||
* Empty list constant.
|
||||
*/
|
||||
Value vEmptyList;
|
||||
|
||||
/**
|
||||
* `null` constant.
|
||||
*
|
||||
* This is _not_ a singleton. Pointer equality is _not_ sufficient.
|
||||
*/
|
||||
Value vNull;
|
||||
|
||||
/**
|
||||
* `true` constant.
|
||||
*
|
||||
* This is _not_ a singleton. Pointer equality is _not_ sufficient.
|
||||
*/
|
||||
Value vTrue;
|
||||
|
||||
/**
|
||||
* `true` constant.
|
||||
*
|
||||
* This is _not_ a singleton. Pointer equality is _not_ sufficient.
|
||||
*/
|
||||
Value vFalse;
|
||||
|
||||
/** `"regular"` */
|
||||
Value vStringRegular;
|
||||
/** `"directory"` */
|
||||
Value vStringDirectory;
|
||||
/** `"symlink"` */
|
||||
Value vStringSymlink;
|
||||
/** `"unknown"` */
|
||||
Value vStringUnknown;
|
||||
|
||||
/**
|
||||
* The accessor corresponding to `store`.
|
||||
*/
|
||||
|
|
@ -309,19 +355,21 @@ public:
|
|||
/**
|
||||
* Debugger
|
||||
*/
|
||||
ReplExitStatus (* debugRepl)(ref<EvalState> es, const ValMap & extraEnv);
|
||||
ReplExitStatus (*debugRepl)(ref<EvalState> es, const ValMap & extraEnv);
|
||||
bool debugStop;
|
||||
bool inDebugger = false;
|
||||
int trylevel;
|
||||
std::list<DebugTrace> debugTraces;
|
||||
std::map<const Expr*, const std::shared_ptr<const StaticEnv>> exprEnvs;
|
||||
boost::unordered_flat_map<const Expr *, const std::shared_ptr<const StaticEnv>> exprEnvs;
|
||||
|
||||
const std::shared_ptr<const StaticEnv> getStaticEnv(const Expr & expr) const
|
||||
{
|
||||
auto i = exprEnvs.find(&expr);
|
||||
if (i != exprEnvs.end())
|
||||
return i->second;
|
||||
else
|
||||
return std::shared_ptr<const StaticEnv>();;
|
||||
return std::shared_ptr<const StaticEnv>();
|
||||
;
|
||||
}
|
||||
|
||||
/** Whether a debug repl can be started. If `false`, `runDebugRepl(error)` will return without starting a repl. */
|
||||
|
|
@ -340,7 +388,8 @@ public:
|
|||
|
||||
template<class T, typename... Args>
|
||||
[[nodiscard, gnu::noinline]]
|
||||
EvalErrorBuilder<T> & error(const Args & ... args) {
|
||||
EvalErrorBuilder<T> & error(const Args &... args)
|
||||
{
|
||||
// `EvalErrorBuilder::debugThrow` performs the corresponding `delete`.
|
||||
return *new EvalErrorBuilder<T>(*this, args...);
|
||||
}
|
||||
|
|
@ -354,29 +403,42 @@ private:
|
|||
|
||||
/* Cache for calls to addToStore(); maps source paths to the store
|
||||
paths. */
|
||||
Sync<std::unordered_map<SourcePath, StorePath>> srcToStore;
|
||||
boost::concurrent_flat_map<SourcePath, StorePath, std::hash<SourcePath>> srcToStore;
|
||||
|
||||
/**
|
||||
* A cache from path names to parse trees.
|
||||
*/
|
||||
typedef std::unordered_map<SourcePath, Expr *, std::hash<SourcePath>, std::equal_to<SourcePath>, traceable_allocator<std::pair<const SourcePath, Expr *>>> FileParseCache;
|
||||
typedef boost::unordered_flat_map<
|
||||
SourcePath,
|
||||
Expr *,
|
||||
std::hash<SourcePath>,
|
||||
std::equal_to<SourcePath>,
|
||||
traceable_allocator<std::pair<const SourcePath, Expr *>>>
|
||||
FileParseCache;
|
||||
FileParseCache fileParseCache;
|
||||
|
||||
/**
|
||||
* A cache from path names to values.
|
||||
*/
|
||||
typedef std::unordered_map<SourcePath, Value, std::hash<SourcePath>, std::equal_to<SourcePath>, traceable_allocator<std::pair<const SourcePath, Value>>> FileEvalCache;
|
||||
typedef boost::unordered_flat_map<
|
||||
SourcePath,
|
||||
Value,
|
||||
std::hash<SourcePath>,
|
||||
std::equal_to<SourcePath>,
|
||||
traceable_allocator<std::pair<const SourcePath, Value>>>
|
||||
FileEvalCache;
|
||||
FileEvalCache fileEvalCache;
|
||||
|
||||
/**
|
||||
* Associate source positions of certain AST nodes with their preceding doc comment, if they have one.
|
||||
* Grouped by file.
|
||||
*/
|
||||
std::unordered_map<SourcePath, DocCommentMap> positionToDocComment;
|
||||
boost::unordered_flat_map<SourcePath, DocCommentMap, std::hash<SourcePath>> positionToDocComment;
|
||||
|
||||
LookupPath lookupPath;
|
||||
|
||||
std::map<std::string, std::optional<SourcePath>> lookupPathResolved;
|
||||
boost::unordered_flat_map<std::string, std::optional<SourcePath>, StringViewHash, std::equal_to<>>
|
||||
lookupPathResolved;
|
||||
|
||||
/**
|
||||
* Cache used by prim_match().
|
||||
|
|
@ -405,7 +467,10 @@ public:
|
|||
std::shared_ptr<Store> buildStore = nullptr);
|
||||
~EvalState();
|
||||
|
||||
LookupPath getLookupPath() { return lookupPath; }
|
||||
LookupPath getLookupPath()
|
||||
{
|
||||
return lookupPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a `SourcePath` that refers to `path` in the root
|
||||
|
|
@ -486,9 +551,7 @@ public:
|
|||
*
|
||||
* If it is not found, return `std::nullopt`.
|
||||
*/
|
||||
std::optional<SourcePath> resolveLookupPathPath(
|
||||
const LookupPath::Path & elem,
|
||||
bool initAccessControl = false);
|
||||
std::optional<SourcePath> resolveLookupPathPath(const LookupPath::Path & elem, bool initAccessControl = false);
|
||||
|
||||
/**
|
||||
* Evaluate an expression to normal form
|
||||
|
|
@ -530,7 +593,7 @@ public:
|
|||
|
||||
void forceAttrs(Value & v, const PosIdx pos, std::string_view errorCtx);
|
||||
|
||||
template <typename Callable>
|
||||
template<typename Callable>
|
||||
inline void forceAttrs(Value & v, Callable getPos, std::string_view errorCtx);
|
||||
|
||||
inline void forceList(Value & v, const PosIdx pos, std::string_view errorCtx);
|
||||
|
|
@ -539,20 +602,25 @@ public:
|
|||
*/
|
||||
void forceFunction(Value & v, const PosIdx pos, std::string_view errorCtx);
|
||||
std::string_view forceString(Value & v, const PosIdx pos, std::string_view errorCtx);
|
||||
std::string_view forceString(Value & v, NixStringContext & context, const PosIdx pos, std::string_view errorCtx, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
std::string_view forceString(
|
||||
Value & v,
|
||||
NixStringContext & context,
|
||||
const PosIdx pos,
|
||||
std::string_view errorCtx,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
std::string_view forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx);
|
||||
|
||||
/**
|
||||
* Get attribute from an attribute set and throw an error if it doesn't exist.
|
||||
*/
|
||||
Bindings::const_iterator getAttr(Symbol attrSym, const Bindings * attrSet, std::string_view errorCtx);
|
||||
const Attr * getAttr(Symbol attrSym, const Bindings * attrSet, std::string_view errorCtx);
|
||||
|
||||
template<typename... Args>
|
||||
[[gnu::noinline]]
|
||||
void addErrorTrace(Error & e, const Args & ... formatArgs) const;
|
||||
void addErrorTrace(Error & e, const Args &... formatArgs) const;
|
||||
template<typename... Args>
|
||||
[[gnu::noinline]]
|
||||
void addErrorTrace(Error & e, const PosIdx pos, const Args & ... formatArgs) const;
|
||||
void addErrorTrace(Error & e, const PosIdx pos, const Args &... formatArgs) const;
|
||||
|
||||
public:
|
||||
/**
|
||||
|
|
@ -561,8 +629,8 @@ public:
|
|||
*/
|
||||
bool isDerivation(Value & v);
|
||||
|
||||
std::optional<std::string> tryAttrsToString(const PosIdx pos, Value & v,
|
||||
NixStringContext & context, bool coerceMore = false, bool copyToStore = true);
|
||||
std::optional<std::string> tryAttrsToString(
|
||||
const PosIdx pos, Value & v, NixStringContext & context, bool coerceMore = false, bool copyToStore = true);
|
||||
|
||||
/**
|
||||
* String coercion.
|
||||
|
|
@ -572,9 +640,13 @@ public:
|
|||
* booleans and lists to a string. If `copyToStore` is set,
|
||||
* referenced paths are copied to the Nix store as a side effect.
|
||||
*/
|
||||
BackedStringView coerceToString(const PosIdx pos, Value & v, NixStringContext & context,
|
||||
BackedStringView coerceToString(
|
||||
const PosIdx pos,
|
||||
Value & v,
|
||||
NixStringContext & context,
|
||||
std::string_view errorCtx,
|
||||
bool coerceMore = false, bool copyToStore = true,
|
||||
bool coerceMore = false,
|
||||
bool copyToStore = true,
|
||||
bool canonicalizePath = true);
|
||||
|
||||
StorePath copyPathToStore(NixStringContext & context, const SourcePath & path);
|
||||
|
|
@ -596,7 +668,11 @@ public:
|
|||
/**
|
||||
* Part of `coerceToSingleDerivedPath()` without any store IO which is exposed for unit testing only.
|
||||
*/
|
||||
std::pair<SingleDerivedPath, std::string_view> coerceToSingleDerivedPathUnchecked(const PosIdx pos, Value & v, std::string_view errorCtx, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
std::pair<SingleDerivedPath, std::string_view> coerceToSingleDerivedPathUnchecked(
|
||||
const PosIdx pos,
|
||||
Value & v,
|
||||
std::string_view errorCtx,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
|
||||
/**
|
||||
* Coerce to `SingleDerivedPath`.
|
||||
|
|
@ -636,7 +712,13 @@ public:
|
|||
/**
|
||||
* Internal primops not exposed to the user.
|
||||
*/
|
||||
std::unordered_map<std::string, Value *, std::hash<std::string>, std::equal_to<std::string>, traceable_allocator<std::pair<const std::string, Value *>>> internalPrimOps;
|
||||
boost::unordered_flat_map<
|
||||
std::string,
|
||||
Value *,
|
||||
StringViewHash,
|
||||
std::equal_to<>,
|
||||
traceable_allocator<std::pair<const std::string, Value *>>>
|
||||
internalPrimOps;
|
||||
|
||||
/**
|
||||
* Name and documentation about every constant.
|
||||
|
|
@ -710,7 +792,8 @@ private:
|
|||
std::shared_ptr<StaticEnv> & staticEnv);
|
||||
|
||||
/**
|
||||
* Current Nix call stack depth, used with `max-call-depth` setting to throw stack overflow hopefully before we run out of system stack.
|
||||
* Current Nix call stack depth, used with `max-call-depth` setting to throw stack overflow hopefully before we run
|
||||
* out of system stack.
|
||||
*/
|
||||
size_t callDepth = 0;
|
||||
|
||||
|
|
@ -762,7 +845,7 @@ public:
|
|||
|
||||
BindingsBuilder buildBindings(size_t capacity)
|
||||
{
|
||||
return BindingsBuilder(*this, allocBindings(capacity));
|
||||
return BindingsBuilder(*this, allocBindings(capacity), capacity);
|
||||
}
|
||||
|
||||
ListBuilder buildList(size_t size)
|
||||
|
|
@ -773,7 +856,7 @@ public:
|
|||
/**
|
||||
* Return a boolean `Value *` without allocating.
|
||||
*/
|
||||
Value *getBool(bool b);
|
||||
Value * getBool(bool b);
|
||||
|
||||
void mkThunk_(Value & v, Expr * expr);
|
||||
void mkPos(Value & v, PosIdx pos);
|
||||
|
|
@ -817,9 +900,7 @@ public:
|
|||
*
|
||||
* A combination of `mkStorePathString` and `mkOutputString`.
|
||||
*/
|
||||
void mkSingleDerivedPathString(
|
||||
const SingleDerivedPath & p,
|
||||
Value & v);
|
||||
void mkSingleDerivedPathString(const SingleDerivedPath & p, Value & v);
|
||||
|
||||
void concatLists(Value & v, size_t nrLists, Value * const * lists, const PosIdx pos, std::string_view errorCtx);
|
||||
|
||||
|
|
@ -850,22 +931,22 @@ public:
|
|||
* @param[out] maybePaths if not nullptr, all built or referenced store paths will be added to this set
|
||||
* @return a mapping from the placeholders used to construct the associated value to their final store path.
|
||||
*/
|
||||
[[nodiscard]] StringMap realiseContext(const NixStringContext & context, StorePathSet * maybePaths = nullptr, bool isIFD = true);
|
||||
[[nodiscard]] StringMap
|
||||
realiseContext(const NixStringContext & context, StorePathSet * maybePaths = nullptr, bool isIFD = true);
|
||||
|
||||
/**
|
||||
* Realise the given string with context, and return the string with outputs instead of downstream output placeholders.
|
||||
* Realise the given string with context, and return the string with outputs instead of downstream output
|
||||
* placeholders.
|
||||
* @param[in] str the string to realise
|
||||
* @param[out] paths all referenced store paths will be added to this set
|
||||
* @return the realised string
|
||||
* @throw EvalError if the value is not a string, path or derivation (see `coerceToString`)
|
||||
*/
|
||||
std::string realiseString(Value & str, StorePathSet * storePathsOutMaybe, bool isIFD = true, const PosIdx pos = noPos);
|
||||
std::string
|
||||
realiseString(Value & str, StorePathSet * storePathsOutMaybe, bool isIFD = true, const PosIdx pos = noPos);
|
||||
|
||||
/* Call the binary path filter predicate used builtins.path etc. */
|
||||
bool callPathFilter(
|
||||
Value * filterFun,
|
||||
const SourcePath & path,
|
||||
PosIdx pos);
|
||||
bool callPathFilter(Value * filterFun, const SourcePath & path, PosIdx pos);
|
||||
|
||||
DocComment getDocCommentForPos(PosIdx pos);
|
||||
|
||||
|
|
@ -884,8 +965,7 @@ private:
|
|||
* Like `mkSingleDerivedPathStringRaw` but just creates a raw string
|
||||
* Value, which would also have a string context.
|
||||
*/
|
||||
std::string mkSingleDerivedPathStringRaw(
|
||||
const SingleDerivedPath & p);
|
||||
std::string mkSingleDerivedPathStringRaw(const SingleDerivedPath & p);
|
||||
|
||||
unsigned long nrEnvs = 0;
|
||||
unsigned long nrValuesInEnvs = 0;
|
||||
|
|
@ -903,10 +983,10 @@ private:
|
|||
|
||||
bool countCalls;
|
||||
|
||||
typedef std::map<std::string, size_t> PrimOpCalls;
|
||||
typedef boost::unordered_flat_map<std::string, size_t, StringViewHash, std::equal_to<>> PrimOpCalls;
|
||||
PrimOpCalls primOpCalls;
|
||||
|
||||
typedef std::map<ExprLambda *, size_t> FunctionCalls;
|
||||
typedef boost::unordered_flat_map<ExprLambda *, size_t> FunctionCalls;
|
||||
FunctionCalls functionCalls;
|
||||
|
||||
/** Evaluation/call profiler. */
|
||||
|
|
@ -914,7 +994,7 @@ private:
|
|||
|
||||
void incrFunctionCall(ExprLambda * fun);
|
||||
|
||||
typedef std::map<PosIdx, size_t> AttrSelects;
|
||||
typedef boost::unordered_flat_map<PosIdx, size_t, std::hash<PosIdx>> AttrSelects;
|
||||
AttrSelects attrSelects;
|
||||
|
||||
friend struct ExprOpUpdate;
|
||||
|
|
@ -925,20 +1005,23 @@ private:
|
|||
friend struct ExprFloat;
|
||||
friend struct ExprPath;
|
||||
friend struct ExprSelect;
|
||||
friend void prim_getAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||
friend void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||
friend void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||
friend void prim_getAttr(EvalState & state, const PosIdx pos, Value ** args, Value & v);
|
||||
friend void prim_match(EvalState & state, const PosIdx pos, Value ** args, Value & v);
|
||||
friend void prim_split(EvalState & state, const PosIdx pos, Value ** args, Value & v);
|
||||
|
||||
friend struct Value;
|
||||
friend class ListBuilder;
|
||||
};
|
||||
|
||||
struct DebugTraceStacker {
|
||||
struct DebugTraceStacker
|
||||
{
|
||||
DebugTraceStacker(EvalState & evalState, DebugTrace t);
|
||||
|
||||
~DebugTraceStacker()
|
||||
{
|
||||
evalState.debugTraces.pop_front();
|
||||
}
|
||||
|
||||
EvalState & evalState;
|
||||
DebugTrace trace;
|
||||
};
|
||||
|
|
@ -964,6 +1047,6 @@ SourcePath resolveExprPath(SourcePath path, bool addDefaultNix = true);
|
|||
*/
|
||||
bool isAllowedURI(std::string_view uri, const Strings & allowedPaths);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
||||
#include "nix/expr/eval-inline.hh"
|
||||
|
|
|
|||
|
|
@ -22,4 +22,4 @@ public:
|
|||
postFunctionCallHook(EvalState & state, const Value & v, std::span<Value *> args, const PosIdx pos) override;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@ namespace nix {
|
|||
/**
|
||||
* A GC compatible vector that may used a reserved portion of `nItems` on the stack instead of allocating on the heap.
|
||||
*/
|
||||
template <typename T, size_t nItems>
|
||||
template<typename T, size_t nItems>
|
||||
using SmallVector = boost::container::small_vector<T, nItems, traceable_allocator<T>>;
|
||||
|
||||
/**
|
||||
* A vector of value pointers. See `SmallVector`.
|
||||
*/
|
||||
template <size_t nItems>
|
||||
template<size_t nItems>
|
||||
using SmallValueVector = SmallVector<Value *, nItems>;
|
||||
|
||||
/**
|
||||
|
|
@ -23,7 +23,7 @@ using SmallValueVector = SmallVector<Value *, nItems>;
|
|||
*
|
||||
* See also `SmallValueVector`.
|
||||
*/
|
||||
template <size_t nItems>
|
||||
template<size_t nItems>
|
||||
using SmallTemporaryValueVector = SmallVector<Value, nItems>;
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
#include <string>
|
||||
#include <map>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
|
|
@ -33,7 +32,7 @@ private:
|
|||
*/
|
||||
bool failed = false;
|
||||
|
||||
const Bindings * attrs = nullptr, * meta = nullptr;
|
||||
const Bindings *attrs = nullptr, *meta = nullptr;
|
||||
|
||||
const Bindings * getMeta();
|
||||
|
||||
|
|
@ -45,7 +44,8 @@ public:
|
|||
*/
|
||||
std::string attrPath;
|
||||
|
||||
PackageInfo(EvalState & state) : state(&state) { };
|
||||
PackageInfo(EvalState & state)
|
||||
: state(&state) {};
|
||||
PackageInfo(EvalState & state, std::string attrPath, const Bindings * attrs);
|
||||
PackageInfo(EvalState & state, ref<Store> store, const std::string & drvPathWithOutputs);
|
||||
|
||||
|
|
@ -74,28 +74,46 @@ public:
|
|||
MetaValue queryMetaInfo(EvalState & state, const string & name) const;
|
||||
*/
|
||||
|
||||
void setName(const std::string & s) { name = s; }
|
||||
void setDrvPath(StorePath path) { drvPath = {{std::move(path)}}; }
|
||||
void setOutPath(StorePath path) { outPath = {{std::move(path)}}; }
|
||||
void setName(const std::string & s)
|
||||
{
|
||||
name = s;
|
||||
}
|
||||
|
||||
void setFailed() { failed = true; };
|
||||
bool hasFailed() { return failed; };
|
||||
void setDrvPath(StorePath path)
|
||||
{
|
||||
drvPath = {{std::move(path)}};
|
||||
}
|
||||
|
||||
void setOutPath(StorePath path)
|
||||
{
|
||||
outPath = {{std::move(path)}};
|
||||
}
|
||||
|
||||
void setFailed()
|
||||
{
|
||||
failed = true;
|
||||
};
|
||||
|
||||
bool hasFailed()
|
||||
{
|
||||
return failed;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
typedef std::list<PackageInfo, traceable_allocator<PackageInfo>> PackageInfos;
|
||||
|
||||
|
||||
/**
|
||||
* If value `v` denotes a derivation, return a PackageInfo object
|
||||
* describing it. Otherwise return nothing.
|
||||
*/
|
||||
std::optional<PackageInfo> getDerivation(EvalState & state,
|
||||
Value & v, bool ignoreAssertionFailures);
|
||||
std::optional<PackageInfo> getDerivation(EvalState & state, Value & v, bool ignoreAssertionFailures);
|
||||
|
||||
void getDerivations(EvalState & state, Value & v, const std::string & pathPrefix,
|
||||
Bindings & autoArgs, PackageInfos & drvs,
|
||||
void getDerivations(
|
||||
EvalState & state,
|
||||
Value & v,
|
||||
const std::string & pathPrefix,
|
||||
Bindings & autoArgs,
|
||||
PackageInfos & drvs,
|
||||
bool ignoreAssertionFailures);
|
||||
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -14,4 +14,4 @@ MakeError(JSONParseError, Error);
|
|||
|
||||
void parseJSON(EvalState & state, const std::string_view & s, Value & v);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
# Public headers directory
|
||||
|
||||
include_dirs = [include_directories('../..')]
|
||||
include_dirs = [ include_directories('../..') ]
|
||||
|
||||
config_pub_h = configure_file(
|
||||
configuration : configdata_pub,
|
||||
output : 'config.hh',
|
||||
)
|
||||
|
||||
headers = [config_pub_h] + files(
|
||||
headers = [ config_pub_h ] + files(
|
||||
'attr-path.hh',
|
||||
'attr-set.hh',
|
||||
'eval-cache.hh',
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ struct StaticEnv;
|
|||
struct Value;
|
||||
|
||||
/**
|
||||
* A documentation comment, in the sense of [RFC 145](https://github.com/NixOS/rfcs/blob/master/rfcs/0145-doc-strings.md)
|
||||
* A documentation comment, in the sense of [RFC
|
||||
* 145](https://github.com/NixOS/rfcs/blob/master/rfcs/0145-doc-strings.md)
|
||||
*
|
||||
* Note that this does not implement the following:
|
||||
* - argument attribute names ("formals"): TBD
|
||||
|
|
@ -34,7 +35,8 @@ struct Value;
|
|||
* `f: g: final: prev: <...>`. The parameters `final` and `prev` are part
|
||||
* of the overlay concept, while distracting from the function's purpose.
|
||||
*/
|
||||
struct DocComment {
|
||||
struct DocComment
|
||||
{
|
||||
|
||||
/**
|
||||
* Start of the comment, including the opening, ie `/` and `**`.
|
||||
|
|
@ -53,10 +55,12 @@ struct DocComment {
|
|||
* therefore baking optionality into it is also useful, to avoiding the memory
|
||||
* overhead of `std::optional`.
|
||||
*/
|
||||
operator bool() const { return static_cast<bool>(begin); }
|
||||
operator bool() const
|
||||
{
|
||||
return static_cast<bool>(begin);
|
||||
}
|
||||
|
||||
std::string getInnerText(const PosTable & positions) const;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -66,52 +70,69 @@ struct AttrName
|
|||
{
|
||||
Symbol symbol;
|
||||
Expr * expr = nullptr;
|
||||
AttrName(Symbol s) : symbol(s) {};
|
||||
AttrName(Expr * e) : expr(e) {};
|
||||
AttrName(Symbol s)
|
||||
: symbol(s) {};
|
||||
AttrName(Expr * e)
|
||||
: expr(e) {};
|
||||
};
|
||||
|
||||
typedef std::vector<AttrName> AttrPath;
|
||||
|
||||
std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath);
|
||||
|
||||
|
||||
/* Abstract syntax of Nix expressions. */
|
||||
|
||||
struct Expr
|
||||
{
|
||||
struct AstSymbols {
|
||||
struct AstSymbols
|
||||
{
|
||||
Symbol sub, lessThan, mul, div, or_, findFile, nixPath, body;
|
||||
};
|
||||
|
||||
|
||||
static unsigned long nrExprs;
|
||||
Expr() {
|
||||
|
||||
Expr()
|
||||
{
|
||||
nrExprs++;
|
||||
}
|
||||
virtual ~Expr() { };
|
||||
|
||||
virtual ~Expr() {};
|
||||
virtual void show(const SymbolTable & symbols, std::ostream & str) const;
|
||||
virtual void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env);
|
||||
virtual void eval(EvalState & state, Env & env, Value & v);
|
||||
virtual Value * maybeThunk(EvalState & state, Env & env);
|
||||
virtual void setName(Symbol name);
|
||||
virtual void setDocComment(DocComment docComment) { };
|
||||
virtual PosIdx getPos() const { return noPos; }
|
||||
virtual void setDocComment(DocComment docComment) {};
|
||||
|
||||
virtual PosIdx getPos() const
|
||||
{
|
||||
return noPos;
|
||||
}
|
||||
|
||||
// These are temporary methods to be used only in parser.y
|
||||
virtual void resetCursedOr() { };
|
||||
virtual void warnIfCursedOr(const SymbolTable & symbols, const PosTable & positions) { };
|
||||
virtual void resetCursedOr() {};
|
||||
virtual void warnIfCursedOr(const SymbolTable & symbols, const PosTable & positions) {};
|
||||
};
|
||||
|
||||
#define COMMON_METHODS \
|
||||
#define COMMON_METHODS \
|
||||
void show(const SymbolTable & symbols, std::ostream & str) const override; \
|
||||
void eval(EvalState & state, Env & env, Value & v) override; \
|
||||
void eval(EvalState & state, Env & env, Value & v) override; \
|
||||
void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) override;
|
||||
|
||||
struct ExprInt : Expr
|
||||
{
|
||||
Value v;
|
||||
ExprInt(NixInt n) { v.mkInt(n); };
|
||||
ExprInt(NixInt::Inner n) { v.mkInt(n); };
|
||||
|
||||
ExprInt(NixInt n)
|
||||
{
|
||||
v.mkInt(n);
|
||||
};
|
||||
|
||||
ExprInt(NixInt::Inner n)
|
||||
{
|
||||
v.mkInt(n);
|
||||
};
|
||||
|
||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
|
@ -119,7 +140,12 @@ struct ExprInt : Expr
|
|||
struct ExprFloat : Expr
|
||||
{
|
||||
Value v;
|
||||
ExprFloat(NixFloat nf) { v.mkFloat(nf); };
|
||||
|
||||
ExprFloat(NixFloat nf)
|
||||
{
|
||||
v.mkFloat(nf);
|
||||
};
|
||||
|
||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
|
@ -128,7 +154,13 @@ struct ExprString : Expr
|
|||
{
|
||||
std::string s;
|
||||
Value v;
|
||||
ExprString(std::string &&s) : s(std::move(s)) { v.mkString(this->s.data()); };
|
||||
|
||||
ExprString(std::string && s)
|
||||
: s(std::move(s))
|
||||
{
|
||||
v.mkStringNoCopy(this->s.data());
|
||||
};
|
||||
|
||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
|
@ -138,10 +170,14 @@ struct ExprPath : Expr
|
|||
ref<SourceAccessor> accessor;
|
||||
std::string s;
|
||||
Value v;
|
||||
ExprPath(ref<SourceAccessor> accessor, std::string s) : accessor(accessor), s(std::move(s))
|
||||
|
||||
ExprPath(ref<SourceAccessor> accessor, std::string s)
|
||||
: accessor(accessor)
|
||||
, s(std::move(s))
|
||||
{
|
||||
v.mkPath(&*accessor, this->s.c_str());
|
||||
}
|
||||
|
||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
|
@ -170,10 +206,18 @@ struct ExprVar : Expr
|
|||
Level level = 0;
|
||||
Displacement displ = 0;
|
||||
|
||||
ExprVar(Symbol name) : name(name) { };
|
||||
ExprVar(const PosIdx & pos, Symbol name) : pos(pos), name(name) { };
|
||||
ExprVar(Symbol name)
|
||||
: name(name) {};
|
||||
ExprVar(const PosIdx & pos, Symbol name)
|
||||
: pos(pos)
|
||||
, name(name) {};
|
||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||
PosIdx getPos() const override { return pos; }
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
|
|
@ -184,7 +228,8 @@ struct ExprVar : Expr
|
|||
*/
|
||||
struct ExprInheritFrom : ExprVar
|
||||
{
|
||||
ExprInheritFrom(PosIdx pos, Displacement displ): ExprVar(pos, {})
|
||||
ExprInheritFrom(PosIdx pos, Displacement displ)
|
||||
: ExprVar(pos, {})
|
||||
{
|
||||
this->level = 0;
|
||||
this->displ = displ;
|
||||
|
|
@ -197,11 +242,26 @@ struct ExprInheritFrom : ExprVar
|
|||
struct ExprSelect : Expr
|
||||
{
|
||||
PosIdx pos;
|
||||
Expr * e, * def;
|
||||
Expr *e, *def;
|
||||
AttrPath attrPath;
|
||||
ExprSelect(const PosIdx & pos, Expr * e, AttrPath attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(std::move(attrPath)) { };
|
||||
ExprSelect(const PosIdx & pos, Expr * e, Symbol name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); };
|
||||
PosIdx getPos() const override { return pos; }
|
||||
ExprSelect(const PosIdx & pos, Expr * e, AttrPath attrPath, Expr * def)
|
||||
: pos(pos)
|
||||
, e(e)
|
||||
, def(def)
|
||||
, attrPath(std::move(attrPath)) {};
|
||||
|
||||
ExprSelect(const PosIdx & pos, Expr * e, Symbol name)
|
||||
: pos(pos)
|
||||
, e(e)
|
||||
, def(0)
|
||||
{
|
||||
attrPath.push_back(AttrName(name));
|
||||
};
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the `a.b.c` part of `a.b.c.d`. This exists mostly for the purpose of :doc in the repl.
|
||||
|
|
@ -209,7 +269,8 @@ struct ExprSelect : Expr
|
|||
* @param[out] attrs The attribute set that should contain the last attribute name (if it exists).
|
||||
* @return The last attribute name in `attrPath`
|
||||
*
|
||||
* @note This does *not* evaluate the final attribute, and does not fail if that's the only attribute that does not exist.
|
||||
* @note This does *not* evaluate the final attribute, and does not fail if that's the only attribute that does not
|
||||
* exist.
|
||||
*/
|
||||
Symbol evalExceptFinalSelect(EvalState & state, Env & env, Value & attrs);
|
||||
|
||||
|
|
@ -220,8 +281,15 @@ struct ExprOpHasAttr : Expr
|
|||
{
|
||||
Expr * e;
|
||||
AttrPath attrPath;
|
||||
ExprOpHasAttr(Expr * e, AttrPath attrPath) : e(e), attrPath(std::move(attrPath)) { };
|
||||
PosIdx getPos() const override { return e->getPos(); }
|
||||
ExprOpHasAttr(Expr * e, AttrPath attrPath)
|
||||
: e(e)
|
||||
, attrPath(std::move(attrPath)) {};
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return e->getPos();
|
||||
}
|
||||
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
|
|
@ -229,7 +297,9 @@ struct ExprAttrs : Expr
|
|||
{
|
||||
bool recursive;
|
||||
PosIdx pos;
|
||||
struct AttrDef {
|
||||
|
||||
struct AttrDef
|
||||
{
|
||||
enum class Kind {
|
||||
/** `attr = expr;` */
|
||||
Plain,
|
||||
|
|
@ -244,8 +314,10 @@ struct ExprAttrs : Expr
|
|||
PosIdx pos;
|
||||
Displacement displ = 0; // displacement
|
||||
AttrDef(Expr * e, const PosIdx & pos, Kind kind = Kind::Plain)
|
||||
: kind(kind), e(e), pos(pos) { };
|
||||
AttrDef() { };
|
||||
: kind(kind)
|
||||
, e(e)
|
||||
, pos(pos) {};
|
||||
AttrDef() {};
|
||||
|
||||
template<typename T>
|
||||
const T & chooseByKind(const T & plain, const T & inherited, const T & inheritedFrom) const
|
||||
|
|
@ -261,24 +333,37 @@ struct ExprAttrs : Expr
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<Symbol, AttrDef> AttrDefs;
|
||||
AttrDefs attrs;
|
||||
std::unique_ptr<std::vector<Expr *>> inheritFromExprs;
|
||||
struct DynamicAttrDef {
|
||||
Expr * nameExpr, * valueExpr;
|
||||
|
||||
struct DynamicAttrDef
|
||||
{
|
||||
Expr *nameExpr, *valueExpr;
|
||||
PosIdx pos;
|
||||
DynamicAttrDef(Expr * nameExpr, Expr * valueExpr, const PosIdx & pos)
|
||||
: nameExpr(nameExpr), valueExpr(valueExpr), pos(pos) { };
|
||||
: nameExpr(nameExpr)
|
||||
, valueExpr(valueExpr)
|
||||
, pos(pos) {};
|
||||
};
|
||||
|
||||
typedef std::vector<DynamicAttrDef> DynamicAttrDefs;
|
||||
DynamicAttrDefs dynamicAttrs;
|
||||
ExprAttrs(const PosIdx &pos) : recursive(false), pos(pos) { };
|
||||
ExprAttrs() : recursive(false) { };
|
||||
PosIdx getPos() const override { return pos; }
|
||||
ExprAttrs(const PosIdx & pos)
|
||||
: recursive(false)
|
||||
, pos(pos) {};
|
||||
ExprAttrs()
|
||||
: recursive(false) {};
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
COMMON_METHODS
|
||||
|
||||
std::shared_ptr<const StaticEnv> bindInheritSources(
|
||||
EvalState & es, const std::shared_ptr<const StaticEnv> & env);
|
||||
std::shared_ptr<const StaticEnv> bindInheritSources(EvalState & es, const std::shared_ptr<const StaticEnv> & env);
|
||||
Env * buildInheritFromEnv(EvalState & state, Env & up);
|
||||
void showBindings(const SymbolTable & symbols, std::ostream & str) const;
|
||||
};
|
||||
|
|
@ -286,7 +371,7 @@ struct ExprAttrs : Expr
|
|||
struct ExprList : Expr
|
||||
{
|
||||
std::vector<Expr *> elems;
|
||||
ExprList() { };
|
||||
ExprList() {};
|
||||
COMMON_METHODS
|
||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||
|
||||
|
|
@ -314,19 +399,18 @@ struct Formals
|
|||
|
||||
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; });
|
||||
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 SymbolTable & symbols) const
|
||||
{
|
||||
std::vector<Formal> result(formals.begin(), formals.end());
|
||||
std::sort(result.begin(), result.end(),
|
||||
[&] (const Formal & a, const Formal & b) {
|
||||
std::string_view sa = symbols[a.name], sb = symbols[b.name];
|
||||
return sa < sb;
|
||||
});
|
||||
std::sort(result.begin(), result.end(), [&](const Formal & a, const Formal & b) {
|
||||
std::string_view sa = symbols[a.name], sb = symbols[b.name];
|
||||
return sa < sb;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
|
@ -341,17 +425,31 @@ struct ExprLambda : Expr
|
|||
DocComment docComment;
|
||||
|
||||
ExprLambda(PosIdx pos, Symbol arg, Formals * formals, Expr * body)
|
||||
: pos(pos), arg(arg), formals(formals), body(body)
|
||||
{
|
||||
};
|
||||
: pos(pos)
|
||||
, arg(arg)
|
||||
, formals(formals)
|
||||
, body(body) {};
|
||||
|
||||
ExprLambda(PosIdx pos, Formals * formals, Expr * body)
|
||||
: pos(pos), formals(formals), body(body)
|
||||
: pos(pos)
|
||||
, formals(formals)
|
||||
, body(body)
|
||||
{
|
||||
}
|
||||
|
||||
void setName(Symbol name) override;
|
||||
std::string showNamePos(const EvalState & state) const;
|
||||
inline bool hasFormals() const { return formals != nullptr; }
|
||||
PosIdx getPos() const override { return pos; }
|
||||
|
||||
inline bool hasFormals() const
|
||||
{
|
||||
return formals != nullptr;
|
||||
}
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
virtual void setDocComment(DocComment docComment) override;
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
|
@ -362,13 +460,28 @@ struct ExprCall : Expr
|
|||
std::vector<Expr *> args;
|
||||
PosIdx pos;
|
||||
std::optional<PosIdx> cursedOrEndPos; // used during parsing to warn about https://github.com/NixOS/nix/issues/11118
|
||||
|
||||
ExprCall(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args)
|
||||
: fun(fun), args(args), pos(pos), cursedOrEndPos({})
|
||||
{ }
|
||||
: fun(fun)
|
||||
, args(args)
|
||||
, pos(pos)
|
||||
, cursedOrEndPos({})
|
||||
{
|
||||
}
|
||||
|
||||
ExprCall(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args, PosIdx && cursedOrEndPos)
|
||||
: fun(fun), args(args), pos(pos), cursedOrEndPos(cursedOrEndPos)
|
||||
{ }
|
||||
PosIdx getPos() const override { return pos; }
|
||||
: fun(fun)
|
||||
, args(args)
|
||||
, pos(pos)
|
||||
, cursedOrEndPos(cursedOrEndPos)
|
||||
{
|
||||
}
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
virtual void resetCursedOr() override;
|
||||
virtual void warnIfCursedOr(const SymbolTable & symbols, const PosTable & positions) override;
|
||||
COMMON_METHODS
|
||||
|
|
@ -378,73 +491,119 @@ struct ExprLet : Expr
|
|||
{
|
||||
ExprAttrs * attrs;
|
||||
Expr * body;
|
||||
ExprLet(ExprAttrs * attrs, Expr * body) : attrs(attrs), body(body) { };
|
||||
ExprLet(ExprAttrs * attrs, Expr * body)
|
||||
: attrs(attrs)
|
||||
, body(body) {};
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprWith : Expr
|
||||
{
|
||||
PosIdx pos;
|
||||
Expr * attrs, * body;
|
||||
Expr *attrs, *body;
|
||||
size_t prevWith;
|
||||
ExprWith * parentWith;
|
||||
ExprWith(const PosIdx & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { };
|
||||
PosIdx getPos() const override { return pos; }
|
||||
ExprWith(const PosIdx & pos, Expr * attrs, Expr * body)
|
||||
: pos(pos)
|
||||
, attrs(attrs)
|
||||
, body(body) {};
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprIf : Expr
|
||||
{
|
||||
PosIdx pos;
|
||||
Expr * cond, * then, * else_;
|
||||
ExprIf(const PosIdx & pos, Expr * cond, Expr * then, Expr * else_) : pos(pos), cond(cond), then(then), else_(else_) { };
|
||||
PosIdx getPos() const override { return pos; }
|
||||
Expr *cond, *then, *else_;
|
||||
ExprIf(const PosIdx & pos, Expr * cond, Expr * then, Expr * else_)
|
||||
: pos(pos)
|
||||
, cond(cond)
|
||||
, then(then)
|
||||
, else_(else_) {};
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprAssert : Expr
|
||||
{
|
||||
PosIdx pos;
|
||||
Expr * cond, * body;
|
||||
ExprAssert(const PosIdx & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { };
|
||||
PosIdx getPos() const override { return pos; }
|
||||
Expr *cond, *body;
|
||||
ExprAssert(const PosIdx & pos, Expr * cond, Expr * body)
|
||||
: pos(pos)
|
||||
, cond(cond)
|
||||
, body(body) {};
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprOpNot : Expr
|
||||
{
|
||||
Expr * e;
|
||||
ExprOpNot(Expr * e) : e(e) { };
|
||||
PosIdx getPos() const override { return e->getPos(); }
|
||||
ExprOpNot(Expr * e)
|
||||
: e(e) {};
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return e->getPos();
|
||||
}
|
||||
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
#define MakeBinOp(name, s) \
|
||||
struct name : Expr \
|
||||
{ \
|
||||
PosIdx pos; \
|
||||
Expr * e1, * e2; \
|
||||
name(Expr * e1, Expr * e2) : e1(e1), e2(e2) { }; \
|
||||
name(const PosIdx & pos, Expr * e1, Expr * e2) : pos(pos), e1(e1), e2(e2) { }; \
|
||||
void show(const SymbolTable & symbols, std::ostream & str) const override \
|
||||
{ \
|
||||
str << "("; e1->show(symbols, str); str << " " s " "; e2->show(symbols, str); str << ")"; \
|
||||
} \
|
||||
#define MakeBinOp(name, s) \
|
||||
struct name : Expr \
|
||||
{ \
|
||||
PosIdx pos; \
|
||||
Expr *e1, *e2; \
|
||||
name(Expr * e1, Expr * e2) \
|
||||
: e1(e1) \
|
||||
, e2(e2) {}; \
|
||||
name(const PosIdx & pos, Expr * e1, Expr * e2) \
|
||||
: pos(pos) \
|
||||
, e1(e1) \
|
||||
, e2(e2) {}; \
|
||||
void show(const SymbolTable & symbols, std::ostream & str) const override \
|
||||
{ \
|
||||
str << "("; \
|
||||
e1->show(symbols, str); \
|
||||
str << " " s " "; \
|
||||
e2->show(symbols, str); \
|
||||
str << ")"; \
|
||||
} \
|
||||
void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) override \
|
||||
{ \
|
||||
e1->bindVars(es, env); e2->bindVars(es, env); \
|
||||
} \
|
||||
void eval(EvalState & state, Env & env, Value & v) override; \
|
||||
PosIdx getPos() const override { return pos; } \
|
||||
};
|
||||
{ \
|
||||
e1->bindVars(es, env); \
|
||||
e2->bindVars(es, env); \
|
||||
} \
|
||||
void eval(EvalState & state, Env & env, Value & v) override; \
|
||||
PosIdx getPos() const override \
|
||||
{ \
|
||||
return pos; \
|
||||
} \
|
||||
}
|
||||
|
||||
MakeBinOp(ExprOpEq, "==")
|
||||
MakeBinOp(ExprOpNEq, "!=")
|
||||
MakeBinOp(ExprOpAnd, "&&")
|
||||
MakeBinOp(ExprOpOr, "||")
|
||||
MakeBinOp(ExprOpImpl, "->")
|
||||
MakeBinOp(ExprOpUpdate, "//")
|
||||
MakeBinOp(ExprOpConcatLists, "++")
|
||||
MakeBinOp(ExprOpEq, "==");
|
||||
MakeBinOp(ExprOpNEq, "!=");
|
||||
MakeBinOp(ExprOpAnd, "&&");
|
||||
MakeBinOp(ExprOpOr, "||");
|
||||
MakeBinOp(ExprOpImpl, "->");
|
||||
MakeBinOp(ExprOpUpdate, "//");
|
||||
MakeBinOp(ExprOpConcatLists, "++");
|
||||
|
||||
struct ExprConcatStrings : Expr
|
||||
{
|
||||
|
|
@ -452,16 +611,29 @@ struct ExprConcatStrings : Expr
|
|||
bool forceString;
|
||||
std::vector<std::pair<PosIdx, Expr *>> * es;
|
||||
ExprConcatStrings(const PosIdx & pos, bool forceString, std::vector<std::pair<PosIdx, Expr *>> * es)
|
||||
: pos(pos), forceString(forceString), es(es) { };
|
||||
PosIdx getPos() const override { return pos; }
|
||||
: pos(pos)
|
||||
, forceString(forceString)
|
||||
, es(es) {};
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprPos : Expr
|
||||
{
|
||||
PosIdx pos;
|
||||
ExprPos(const PosIdx & pos) : pos(pos) { };
|
||||
PosIdx getPos() const override { return pos; }
|
||||
ExprPos(const PosIdx & pos)
|
||||
: pos(pos) {};
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
|
|
@ -469,14 +641,16 @@ struct ExprPos : Expr
|
|||
struct ExprBlackHole : Expr
|
||||
{
|
||||
void show(const SymbolTable & symbols, std::ostream & str) const override {}
|
||||
|
||||
void eval(EvalState & state, Env & env, Value & v) override;
|
||||
|
||||
void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) override {}
|
||||
|
||||
[[noreturn]] static void throwInfiniteRecursionError(EvalState & state, Value & v);
|
||||
};
|
||||
|
||||
extern ExprBlackHole eBlackHole;
|
||||
|
||||
|
||||
/* Static environments are used to map variable names onto (level,
|
||||
displacement) pairs used to obtain the value of the variable at
|
||||
runtime. */
|
||||
|
|
@ -498,8 +672,9 @@ struct StaticEnv
|
|||
|
||||
void sort()
|
||||
{
|
||||
std::stable_sort(vars.begin(), vars.end(),
|
||||
[](const Vars::value_type & a, const Vars::value_type & b) { return a.first < b.first; });
|
||||
std::stable_sort(vars.begin(), vars.end(), [](const Vars::value_type & a, const Vars::value_type & b) {
|
||||
return a.first < b.first;
|
||||
});
|
||||
}
|
||||
|
||||
void deduplicate()
|
||||
|
|
@ -507,7 +682,8 @@ struct StaticEnv
|
|||
auto it = vars.begin(), jt = it, end = vars.end();
|
||||
while (jt != end) {
|
||||
*it = *jt++;
|
||||
while (jt != end && it->first == jt->first) *it = *jt++;
|
||||
while (jt != end && it->first == jt->first)
|
||||
*it = *jt++;
|
||||
it++;
|
||||
}
|
||||
vars.erase(it, end);
|
||||
|
|
@ -517,10 +693,10 @@ struct StaticEnv
|
|||
{
|
||||
Vars::value_type key(name, 0);
|
||||
auto i = std::lower_bound(vars.begin(), vars.end(), key);
|
||||
if (i != vars.end() && i->first == name) return i;
|
||||
if (i != vars.end() && i->first == name)
|
||||
return i;
|
||||
return vars.end();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -17,7 +17,11 @@ struct StringToken
|
|||
const char * p;
|
||||
size_t l;
|
||||
bool hasIndentation;
|
||||
operator std::string_view() const { return {p, l}; }
|
||||
|
||||
operator std::string_view() const
|
||||
{
|
||||
return {p, l};
|
||||
}
|
||||
};
|
||||
|
||||
// This type must be trivially copyable; see YYLTYPE_IS_TRIVIAL in parser.y.
|
||||
|
|
@ -29,12 +33,14 @@ struct ParserLocation
|
|||
// backup to recover from yyless(0)
|
||||
int stashedBeginOffset, stashedEndOffset;
|
||||
|
||||
void stash() {
|
||||
void stash()
|
||||
{
|
||||
stashedBeginOffset = beginOffset;
|
||||
stashedEndOffset = endOffset;
|
||||
}
|
||||
|
||||
void unstash() {
|
||||
void unstash()
|
||||
{
|
||||
beginOffset = stashedBeginOffset;
|
||||
endOffset = stashedEndOffset;
|
||||
}
|
||||
|
|
@ -65,7 +71,7 @@ struct LexerState
|
|||
/**
|
||||
* @brief Maps some positions to a DocComment, where the comment is relevant to the location.
|
||||
*/
|
||||
std::unordered_map<PosIdx, DocComment> & positionToDocComment;
|
||||
DocCommentMap & positionToDocComment;
|
||||
|
||||
PosTable & positions;
|
||||
PosTable::Origin origin;
|
||||
|
|
@ -82,37 +88,35 @@ struct ParserState
|
|||
SourcePath basePath;
|
||||
PosTable::Origin origin;
|
||||
const ref<SourceAccessor> rootFS;
|
||||
const Expr::AstSymbols & s;
|
||||
static constexpr Expr::AstSymbols s = StaticEvalSymbols::create().exprSymbols;
|
||||
const EvalSettings & settings;
|
||||
|
||||
void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos);
|
||||
void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos);
|
||||
void addAttr(ExprAttrs * attrs, AttrPath && attrPath, const ParserLocation & loc, Expr * e, const ParserLocation & exprLoc);
|
||||
void addAttr(
|
||||
ExprAttrs * attrs, AttrPath && attrPath, const ParserLocation & loc, Expr * e, const ParserLocation & exprLoc);
|
||||
void addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def);
|
||||
Formals * validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {});
|
||||
Expr * stripIndentation(const PosIdx pos,
|
||||
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es);
|
||||
Expr * stripIndentation(const PosIdx pos, std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es);
|
||||
PosIdx at(const ParserLocation & loc);
|
||||
};
|
||||
|
||||
inline void ParserState::dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos)
|
||||
{
|
||||
throw ParseError({
|
||||
.msg = HintFmt("attribute '%1%' already defined at %2%",
|
||||
showAttrPath(symbols, attrPath), positions[prevPos]),
|
||||
.pos = positions[pos]
|
||||
});
|
||||
throw ParseError(
|
||||
{.msg = HintFmt("attribute '%1%' already defined at %2%", showAttrPath(symbols, attrPath), positions[prevPos]),
|
||||
.pos = positions[pos]});
|
||||
}
|
||||
|
||||
inline void ParserState::dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos)
|
||||
{
|
||||
throw ParseError({
|
||||
.msg = HintFmt("attribute '%1%' already defined at %2%", symbols[attr], positions[prevPos]),
|
||||
.pos = positions[pos]
|
||||
});
|
||||
throw ParseError(
|
||||
{.msg = HintFmt("attribute '%1%' already defined at %2%", symbols[attr], positions[prevPos]),
|
||||
.pos = positions[pos]});
|
||||
}
|
||||
|
||||
inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, const ParserLocation & loc, Expr * e, const ParserLocation & exprLoc)
|
||||
inline void ParserState::addAttr(
|
||||
ExprAttrs * attrs, AttrPath && attrPath, const ParserLocation & loc, Expr * e, const ParserLocation & exprLoc)
|
||||
{
|
||||
AttrPath::iterator i;
|
||||
// All attrpaths have at least one attr
|
||||
|
|
@ -159,7 +163,8 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, const
|
|||
* Precondition: attrPath is used for error messages and should already contain
|
||||
* symbol as its last element.
|
||||
*/
|
||||
inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def)
|
||||
inline void
|
||||
ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def)
|
||||
{
|
||||
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(symbol);
|
||||
if (j != attrs->attrs.end()) {
|
||||
|
|
@ -189,12 +194,14 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const S
|
|||
attrPath.pop_back();
|
||||
}
|
||||
ae->attrs.clear();
|
||||
jAttrs->dynamicAttrs.insert(jAttrs->dynamicAttrs.end(),
|
||||
jAttrs->dynamicAttrs.insert(
|
||||
jAttrs->dynamicAttrs.end(),
|
||||
std::make_move_iterator(ae->dynamicAttrs.begin()),
|
||||
std::make_move_iterator(ae->dynamicAttrs.end()));
|
||||
ae->dynamicAttrs.clear();
|
||||
if (ae->inheritFromExprs) {
|
||||
jAttrs->inheritFromExprs->insert(jAttrs->inheritFromExprs->end(),
|
||||
jAttrs->inheritFromExprs->insert(
|
||||
jAttrs->inheritFromExprs->end(),
|
||||
std::make_move_iterator(ae->inheritFromExprs->begin()),
|
||||
std::make_move_iterator(ae->inheritFromExprs->end()));
|
||||
ae->inheritFromExprs = nullptr;
|
||||
|
|
@ -211,10 +218,9 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const S
|
|||
|
||||
inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Symbol arg)
|
||||
{
|
||||
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::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, PosIdx>> duplicate;
|
||||
for (size_t i = 0; i + 1 < formals->formals.size(); i++) {
|
||||
|
|
@ -224,24 +230,22 @@ inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Sym
|
|||
duplicate = std::min(thisDup, duplicate.value_or(thisDup));
|
||||
}
|
||||
if (duplicate)
|
||||
throw ParseError({
|
||||
.msg = HintFmt("duplicate formal function argument '%1%'", symbols[duplicate->first]),
|
||||
.pos = positions[duplicate->second]
|
||||
});
|
||||
throw ParseError(
|
||||
{.msg = HintFmt("duplicate formal function argument '%1%'", symbols[duplicate->first]),
|
||||
.pos = positions[duplicate->second]});
|
||||
|
||||
if (arg && formals->has(arg))
|
||||
throw ParseError({
|
||||
.msg = HintFmt("duplicate formal function argument '%1%'", symbols[arg]),
|
||||
.pos = positions[pos]
|
||||
});
|
||||
throw ParseError(
|
||||
{.msg = HintFmt("duplicate formal function argument '%1%'", symbols[arg]), .pos = positions[pos]});
|
||||
|
||||
return formals;
|
||||
}
|
||||
|
||||
inline Expr * ParserState::stripIndentation(const PosIdx pos,
|
||||
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es)
|
||||
inline Expr *
|
||||
ParserState::stripIndentation(const PosIdx pos, std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es)
|
||||
{
|
||||
if (es.empty()) return new ExprString("");
|
||||
if (es.empty())
|
||||
return new ExprString("");
|
||||
|
||||
/* Figure out the minimum indentation. Note that by design
|
||||
whitespace-only final lines are not taken into account. (So
|
||||
|
|
@ -255,7 +259,8 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos,
|
|||
/* Anti-quotations and escaped characters end the current start-of-line whitespace. */
|
||||
if (atStartOfLine) {
|
||||
atStartOfLine = false;
|
||||
if (curIndent < minIndent) minIndent = curIndent;
|
||||
if (curIndent < minIndent)
|
||||
minIndent = curIndent;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
|
@ -269,7 +274,8 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos,
|
|||
curIndent = 0;
|
||||
} else {
|
||||
atStartOfLine = false;
|
||||
if (curIndent < minIndent) minIndent = curIndent;
|
||||
if (curIndent < minIndent)
|
||||
minIndent = curIndent;
|
||||
}
|
||||
} else if (str->p[j] == '\n') {
|
||||
atStartOfLine = true;
|
||||
|
|
@ -284,20 +290,19 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos,
|
|||
size_t curDropped = 0;
|
||||
size_t n = es.size();
|
||||
auto i = es.begin();
|
||||
const auto trimExpr = [&] (Expr * e) {
|
||||
const auto trimExpr = [&](Expr * e) {
|
||||
atStartOfLine = false;
|
||||
curDropped = 0;
|
||||
es2->emplace_back(i->first, e);
|
||||
};
|
||||
const auto trimString = [&] (const StringToken & t) {
|
||||
const auto trimString = [&](const StringToken & t) {
|
||||
std::string s2;
|
||||
for (size_t j = 0; j < t.l; ++j) {
|
||||
if (atStartOfLine) {
|
||||
if (t.p[j] == ' ') {
|
||||
if (curDropped++ >= minIndent)
|
||||
s2 += t.p[j];
|
||||
}
|
||||
else if (t.p[j] == '\n') {
|
||||
} else if (t.p[j] == '\n') {
|
||||
curDropped = 0;
|
||||
s2 += t.p[j];
|
||||
} else {
|
||||
|
|
@ -307,7 +312,8 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos,
|
|||
}
|
||||
} else {
|
||||
s2 += t.p[j];
|
||||
if (t.p[j] == '\n') atStartOfLine = true;
|
||||
if (t.p[j] == '\n')
|
||||
atStartOfLine = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -325,20 +331,20 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos,
|
|||
}
|
||||
};
|
||||
for (; i != es.end(); ++i, --n) {
|
||||
std::visit(overloaded { trimExpr, trimString }, i->second);
|
||||
std::visit(overloaded{trimExpr, trimString}, i->second);
|
||||
}
|
||||
|
||||
// If there is nothing at all, return the empty string directly.
|
||||
// This also ensures that equivalent empty strings result in the same ast, which is helpful when testing formatters.
|
||||
if (es2->size() == 0) {
|
||||
auto *const result = new ExprString("");
|
||||
auto * const result = new ExprString("");
|
||||
delete es2;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* If this is a single string, then don't do a concatenation. */
|
||||
if (es2->size() == 1 && dynamic_cast<ExprString *>((*es2)[0].second)) {
|
||||
auto *const result = (*es2)[0].second;
|
||||
auto * const result = (*es2)[0].second;
|
||||
delete es2;
|
||||
return result;
|
||||
}
|
||||
|
|
@ -355,4 +361,4 @@ inline PosIdx ParserState::at(const ParserLocation & loc)
|
|||
return positions.add(origin, loc.beginOffset);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -49,13 +49,13 @@ struct RegisterPrimOp
|
|||
/**
|
||||
* Load a ValueInitializer from a DSO and return whatever it initializes
|
||||
*/
|
||||
void prim_importNative(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||
void prim_importNative(EvalState & state, const PosIdx pos, Value ** args, Value & v);
|
||||
|
||||
/**
|
||||
* Execute a program and parse its output
|
||||
*/
|
||||
void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||
void prim_exec(EvalState & state, const PosIdx pos, Value ** args, Value & v);
|
||||
|
||||
void makePositionThunks(EvalState & state, const PosIdx pos, Value & line, Value & column);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -16,10 +16,6 @@ namespace nix {
|
|||
* See: https://github.com/NixOS/nix/issues/9730
|
||||
*/
|
||||
void printAmbiguous(
|
||||
Value &v,
|
||||
const SymbolTable &symbols,
|
||||
std::ostream &str,
|
||||
std::set<const void *> *seen,
|
||||
int depth);
|
||||
Value & v, const SymbolTable & symbols, std::ostream & str, std::set<const void *> * seen, int depth);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ struct PrintOptions
|
|||
* `PrintOptions` for unknown and therefore potentially large values in error messages,
|
||||
* to avoid printing "too much" output.
|
||||
*/
|
||||
static PrintOptions errorPrintOptions = PrintOptions {
|
||||
static PrintOptions errorPrintOptions = PrintOptions{
|
||||
.ansiColors = true,
|
||||
.maxDepth = 10,
|
||||
.maxAttrs = 10,
|
||||
|
|
@ -118,4 +118,4 @@ static PrintOptions errorPrintOptions = PrintOptions {
|
|||
.maxStringLength = 1024,
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -26,10 +26,14 @@ struct Value;
|
|||
* @param s The logical string
|
||||
*/
|
||||
std::ostream & printLiteralString(std::ostream & o, std::string_view s);
|
||||
inline std::ostream & printLiteralString(std::ostream & o, const char * s) {
|
||||
|
||||
inline std::ostream & printLiteralString(std::ostream & o, const char * s)
|
||||
{
|
||||
return printLiteralString(o, std::string_view(s));
|
||||
}
|
||||
inline std::ostream & printLiteralString(std::ostream & o, const std::string & s) {
|
||||
|
||||
inline std::ostream & printLiteralString(std::ostream & o, const std::string & s)
|
||||
{
|
||||
return printLiteralString(o, std::string_view(s));
|
||||
}
|
||||
|
||||
|
|
@ -60,27 +64,31 @@ bool isReservedKeyword(const std::string_view str);
|
|||
*/
|
||||
std::ostream & printIdentifier(std::ostream & o, std::string_view s);
|
||||
|
||||
void printValue(EvalState & state, std::ostream & str, Value & v, PrintOptions options = PrintOptions {});
|
||||
void printValue(EvalState & state, std::ostream & str, Value & v, PrintOptions options = PrintOptions{});
|
||||
|
||||
/**
|
||||
* A partially-applied form of `printValue` which can be formatted using `<<`
|
||||
* without allocating an intermediate string.
|
||||
*/
|
||||
class ValuePrinter {
|
||||
friend std::ostream & operator << (std::ostream & output, const ValuePrinter & printer);
|
||||
class ValuePrinter
|
||||
{
|
||||
friend std::ostream & operator<<(std::ostream & output, const ValuePrinter & printer);
|
||||
private:
|
||||
EvalState & state;
|
||||
Value & value;
|
||||
PrintOptions options;
|
||||
|
||||
public:
|
||||
ValuePrinter(EvalState & state, Value & value, PrintOptions options = PrintOptions {})
|
||||
: state(state), value(value), options(options) { }
|
||||
ValuePrinter(EvalState & state, Value & value, PrintOptions options = PrintOptions{})
|
||||
: state(state)
|
||||
, value(value)
|
||||
, options(options)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & output, const ValuePrinter & printer);
|
||||
|
||||
|
||||
/**
|
||||
* `ValuePrinter` does its own ANSI formatting, so we don't color it
|
||||
* magenta.
|
||||
|
|
@ -88,4 +96,4 @@ std::ostream & operator<<(std::ostream & output, const ValuePrinter & printer);
|
|||
template<>
|
||||
HintFmt & HintFmt::operator%(const ValuePrinter & value);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -17,4 +17,4 @@ enum class ReplExitStatus {
|
|||
Continue,
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -105,4 +105,4 @@ struct LookupPath::Elem
|
|||
static LookupPath::Elem parse(std::string_view rawElem);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue