1
1
Fork 0
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:
Graham Dennis 2025-09-15 10:43:05 +10:00
commit 9bba7ac3e8
887 changed files with 34455 additions and 28681 deletions

View file

@ -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,
],

View file

@ -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

View file

@ -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,
],

View file

@ -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); // Weve built it, so we must
// have the realisation
store.printStorePath(p.drvPath->outPath()),
outputName);
auto thisRealisation = store.queryRealisation(DrvOutput{*drvOutput, outputName});
assert(thisRealisation); // Weve 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

View file

@ -8,4 +8,4 @@ void InstallableValueCommand::run(ref<Store> store, ref<Installable> installable
run(store, installableValue);
}
}
} // namespace nix

View file

@ -402,4 +402,4 @@ void MixOutLinkBase::createOutLinksMaybe(const std::vector<BuiltPathWithResult>
createOutLinks(outLink, toBuiltPaths(buildables), *store2);
}
}
} // namespace nix

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -20,4 +20,4 @@ struct InstallableValueCommand : InstallableCommand
void run(ref<Store> store, ref<Installable> installable) override;
};
}
} // namespace nix

View file

@ -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

View file

@ -33,4 +33,4 @@ struct CompatibilitySettings : public Config
)"};
};
};
}; // namespace nix

View file

@ -12,4 +12,4 @@ namespace nix {
*/
Strings editorFor(const SourcePath & file, uint32_t line);
}
} // namespace nix

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -14,4 +14,4 @@ namespace nix {
*/
std::string renderMarkdownToTerminal(std::string_view markdown);
}
} // namespace nix

View file

@ -1,6 +1,6 @@
# Public headers directory
include_dirs = [include_directories('../..')]
include_dirs = [ include_directories('../..') ]
headers = files(
'built-path.hh',

View file

@ -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

View file

@ -19,4 +19,4 @@ extern const StringSet networkProxyVariables;
*/
bool haveNetworkProxyConnection();
}
} // namespace nix

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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',
)

View file

@ -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

View file

@ -47,4 +47,4 @@ bool haveNetworkProxyConnection()
return false;
}
}
} // namespace nix

View file

@ -53,7 +53,8 @@ mkMesonLibrary (finalAttrs: {
buildInputs = [
({ inherit editline readline; }.${readlineFlavor})
] ++ lib.optional enableMarkdown lowdown;
]
++ lib.optional enableMarkdown lowdown;
propagatedBuildInputs = [
nix-util

View 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"

View file

@ -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

View file

@ -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

View file

@ -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,
)

View file

@ -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"

View file

@ -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

View file

@ -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"

View file

@ -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"

View file

@ -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 */

View file

@ -6,5 +6,4 @@ headers = files(
'libexpr.hh',
'nix_api_expr.hh',
'value/context.hh',
# hack for trailing newline
)

View file

@ -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

View file

@ -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

View file

@ -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,
)

View file

@ -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

View file

@ -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

View file

@ -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);
}

View file

@ -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 */

View file

@ -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");

View file

@ -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',
)

View file

@ -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)

View file

@ -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

View file

@ -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

View 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

View 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

View file

@ -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

View file

@ -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 */

View file

@ -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

View file

@ -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});
}
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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. */

View file

@ -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

View file

@ -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

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -19,4 +19,4 @@ void FunctionCallTrace::postFunctionCallHook(
printMsg(lvlInfo, "function-trace exited %1% at %2%", state.positions[pos], ns.count());
}
}
} // namespace nix

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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>

View file

@ -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

View file

@ -13,4 +13,4 @@ EvalProfilerMode BaseSetting<EvalProfilerMode>::parse(const std::string & str) c
template<>
std::string BaseSetting<EvalProfilerMode>::to_string() const;
}
} // namespace nix

View file

@ -111,4 +111,4 @@ public:
ref<EvalProfiler> makeSampleStackProfiler(EvalState & state, std::filesystem::path profileFile, uint64_t frequency);
}
} // namespace nix

View file

@ -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

View file

@ -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"

View file

@ -22,4 +22,4 @@ public:
postFunctionCallHook(EvalState & state, const Value & v, std::span<Value *> args, const PosIdx pos) override;
};
}
} // namespace nix

View file

@ -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

View file

@ -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

View file

@ -14,4 +14,4 @@ MakeError(JSONParseError, Error);
void parseJSON(EvalState & state, const std::string_view & s, Value & v);
}
} // namespace nix

View file

@ -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',

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -17,4 +17,4 @@ enum class ReplExitStatus {
Continue,
};
}
} // namespace nix

View file

@ -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