1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-08 19:46:02 +01:00

Apply clang-format universally.

* It is tough to contribute to a project that doesn't use a formatter,
* It is extra hard to contribute to a project which has configured the formatter, but ignores it for some files
* Code formatting makes it harder to hide obscure / weird bugs by accident or on purpose,

Let's rip the bandaid off?

Note that PRs currently in flight should be able to be merged relatively easily by applying `clang-format` to their tip prior to merge.

Co-authored-by: Graham Christensen <graham@grahamc.com>
This commit is contained in:
Sergei Zimmerman 2025-07-18 22:49:28 +03:00
parent a5cfab671b
commit 0e35cd6f3e
No known key found for this signature in database
573 changed files with 23551 additions and 23346 deletions

View file

@ -49,15 +49,15 @@
#include <unistd.h> #include <unistd.h>
#ifndef _WIN32 #ifndef _WIN32
# include <grp.h> # include <grp.h>
# include <netdb.h> # include <netdb.h>
# include <pwd.h> # include <pwd.h>
# include <sys/resource.h> # include <sys/resource.h>
# include <sys/select.h> # include <sys/select.h>
# include <sys/socket.h> # include <sys/socket.h>
# include <sys/utsname.h> # include <sys/utsname.h>
# include <sys/wait.h> # include <sys/wait.h>
# include <termios.h> # include <termios.h>
#endif #endif
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>

View file

@ -6,7 +6,7 @@
#include <tuple> #include <tuple>
#include <iomanip> #include <iomanip>
#ifdef __APPLE__ #ifdef __APPLE__
#include <sys/time.h> # include <sys/time.h>
#endif #endif
#include "nix/store/machines.hh" #include "nix/store/machines.hh"
@ -26,8 +26,7 @@
using namespace nix; using namespace nix;
using std::cin; using std::cin;
static void handleAlarm(int sig) { static void handleAlarm(int sig) {}
}
std::string escapeUri(std::string uri) std::string escapeUri(std::string uri)
{ {
@ -42,13 +41,15 @@ static AutoCloseFD openSlotLock(const Machine & m, uint64_t slot)
return openLockFile(fmt("%s/%s-%d", currentLoad, escapeUri(m.storeUri.render()), slot), true); return openLockFile(fmt("%s/%s-%d", currentLoad, escapeUri(m.storeUri.render()), slot), true);
} }
static bool allSupportedLocally(Store & store, const StringSet& requiredFeatures) { static bool allSupportedLocally(Store & store, const StringSet & requiredFeatures)
{
for (auto & feature : requiredFeatures) for (auto & feature : requiredFeatures)
if (!store.config.systemFeatures.get().count(feature)) return false; if (!store.config.systemFeatures.get().count(feature))
return false;
return true; return true;
} }
static int main_build_remote(int argc, char * * argv) static int main_build_remote(int argc, char ** argv)
{ {
{ {
logger = makeJSONLogger(getStandardError()); logger = makeJSONLogger(getStandardError());
@ -85,7 +86,7 @@ static int main_build_remote(int argc, char * * argv)
that gets cleared on reboot, but it wouldn't work on macOS. */ that gets cleared on reboot, but it wouldn't work on macOS. */
auto currentLoadName = "/current-load"; auto currentLoadName = "/current-load";
if (auto localStore = store.dynamic_pointer_cast<LocalFSStore>()) if (auto localStore = store.dynamic_pointer_cast<LocalFSStore>())
currentLoad = std::string { localStore->config.stateDir } + currentLoadName; currentLoad = std::string{localStore->config.stateDir} + currentLoadName;
else else
currentLoad = settings.nixStateDir + currentLoadName; currentLoad = settings.nixStateDir + currentLoadName;
@ -107,8 +108,11 @@ static int main_build_remote(int argc, char * * argv)
try { try {
auto s = readString(source); auto s = readString(source);
if (s != "try") return 0; if (s != "try")
} catch (EndOfFile &) { return 0; } return 0;
} catch (EndOfFile &) {
return 0;
}
auto amWilling = readInt(source); auto amWilling = readInt(source);
auto neededSystem = readString(source); auto neededSystem = readString(source);
@ -117,10 +121,10 @@ static int main_build_remote(int argc, char * * argv)
/* It would be possible to build locally after some builds clear out, /* It would be possible to build locally after some builds clear out,
so don't show the warning now: */ so don't show the warning now: */
bool couldBuildLocally = maxBuildJobs > 0 bool couldBuildLocally =
&& ( neededSystem == settings.thisSystem maxBuildJobs > 0
|| settings.extraPlatforms.get().count(neededSystem) > 0) && (neededSystem == settings.thisSystem || settings.extraPlatforms.get().count(neededSystem) > 0)
&& allSupportedLocally(*store, requiredFeatures); && allSupportedLocally(*store, requiredFeatures);
/* It's possible to build this locally right now: */ /* It's possible to build this locally right now: */
bool canBuildLocally = amWilling && couldBuildLocally; bool canBuildLocally = amWilling && couldBuildLocally;
@ -139,11 +143,8 @@ static int main_build_remote(int argc, char * * argv)
for (auto & m : machines) { for (auto & m : machines) {
debug("considering building on remote machine '%s'", m.storeUri.render()); debug("considering building on remote machine '%s'", m.storeUri.render());
if (m.enabled && if (m.enabled && m.systemSupported(neededSystem) && m.allSupported(requiredFeatures)
m.systemSupported(neededSystem) && && m.mandatoryMet(requiredFeatures)) {
m.allSupported(requiredFeatures) &&
m.mandatoryMet(requiredFeatures))
{
rightType = true; rightType = true;
AutoCloseFD free; AutoCloseFD free;
uint64_t load = 0; uint64_t load = 0;
@ -185,8 +186,7 @@ static int main_build_remote(int argc, char * * argv)
if (!bestSlotLock) { if (!bestSlotLock) {
if (rightType && !canBuildLocally) if (rightType && !canBuildLocally)
std::cerr << "# postpone\n"; std::cerr << "# postpone\n";
else else {
{
// build the hint template. // build the hint template.
std::string errorText = std::string errorText =
"Failed to find a machine for remote build!\n" "Failed to find a machine for remote build!\n"
@ -205,16 +205,11 @@ static int main_build_remote(int argc, char * * argv)
drvstr = "<unknown>"; drvstr = "<unknown>";
auto error = HintFmt::fromFormatString(errorText); auto error = HintFmt::fromFormatString(errorText);
error error % drvstr % neededSystem % concatStringsSep<StringSet>(", ", requiredFeatures)
% drvstr
% neededSystem
% concatStringsSep<StringSet>(", ", requiredFeatures)
% machines.size(); % machines.size();
for (auto & m : machines) for (auto & m : machines)
error error % concatStringsSep<StringSet>(", ", m.systemTypes) % m.maxJobs
% concatStringsSep<StringSet>(", ", m.systemTypes)
% m.maxJobs
% concatStringsSep<StringSet>(", ", m.supportedFeatures) % concatStringsSep<StringSet>(", ", m.supportedFeatures)
% concatStringsSep<StringSet>(", ", m.mandatoryFeatures); % concatStringsSep<StringSet>(", ", m.mandatoryFeatures);
@ -242,9 +237,7 @@ static int main_build_remote(int argc, char * * argv)
sshStore->connect(); sshStore->connect();
} catch (std::exception & e) { } catch (std::exception & e) {
auto msg = chomp(drainFD(5, false)); auto msg = chomp(drainFD(5, false));
printError("cannot build on '%s': %s%s", printError("cannot build on '%s': %s%s", storeUri, e.what(), msg.empty() ? "" : ": " + msg);
storeUri, e.what(),
msg.empty() ? "" : ": " + msg);
bestMachine->enabled = false; bestMachine->enabled = false;
continue; continue;
} }
@ -253,7 +246,7 @@ static int main_build_remote(int argc, char * * argv)
} }
} }
connected: connected:
close(5); close(5);
assert(sshStore); assert(sshStore);
@ -265,13 +258,14 @@ connected:
AutoCloseFD uploadLock; AutoCloseFD uploadLock;
{ {
auto setUpdateLock = [&](auto && fileName){ auto setUpdateLock = [&](auto && fileName) {
uploadLock = openLockFile(currentLoad + "/" + escapeUri(fileName) + ".upload-lock", true); uploadLock = openLockFile(currentLoad + "/" + escapeUri(fileName) + ".upload-lock", true);
}; };
try { try {
setUpdateLock(storeUri); setUpdateLock(storeUri);
} catch (SysError & e) { } catch (SysError & e) {
if (e.errNo != ENAMETOOLONG) throw; if (e.errNo != ENAMETOOLONG)
throw;
// Try again hashing the store URL so we have a shorter path // Try again hashing the store URL so we have a shorter path
auto h = hashString(HashAlgorithm::MD5, storeUri); auto h = hashString(HashAlgorithm::MD5, storeUri);
setUpdateLock(h.to_string(HashFormat::Base64, false)); setUpdateLock(h.to_string(HashFormat::Base64, false));
@ -315,7 +309,7 @@ connected:
// //
// This condition mirrors that: that code enforces the "rules" outlined there; // This condition mirrors that: that code enforces the "rules" outlined there;
// we do the best we can given those "rules". // we do the best we can given those "rules".
if (trustedOrLegacy || drv.type().isCA()) { if (trustedOrLegacy || drv.type().isCA()) {
// Hijack the inputs paths of the derivation to include all // Hijack the inputs paths of the derivation to include all
// the paths that come from the `inputDrvs` set. We dont do // the paths that come from the `inputDrvs` set. We dont do
// that for the derivations whose `inputDrvs` is empty // that for the derivations whose `inputDrvs` is empty
@ -330,28 +324,26 @@ connected:
optResult = sshStore->buildDerivation(*drvPath, (const BasicDerivation &) drv); optResult = sshStore->buildDerivation(*drvPath, (const BasicDerivation &) drv);
auto & result = *optResult; auto & result = *optResult;
if (!result.success()) if (!result.success())
throw Error("build of '%s' on '%s' failed: %s", store->printStorePath(*drvPath), storeUri, result.errorMsg); throw Error(
"build of '%s' on '%s' failed: %s", store->printStorePath(*drvPath), storeUri, result.errorMsg);
} else { } else {
copyClosure(*store, *sshStore, StorePathSet {*drvPath}, NoRepair, NoCheckSigs, substitute); copyClosure(*store, *sshStore, StorePathSet{*drvPath}, NoRepair, NoCheckSigs, substitute);
auto res = sshStore->buildPathsWithResults({ auto res = sshStore->buildPathsWithResults({DerivedPath::Built{
DerivedPath::Built { .drvPath = makeConstantStorePathRef(*drvPath),
.drvPath = makeConstantStorePathRef(*drvPath), .outputs = OutputsSpec::All{},
.outputs = OutputsSpec::All {}, }});
}
});
// One path to build should produce exactly one build result // One path to build should produce exactly one build result
assert(res.size() == 1); assert(res.size() == 1);
optResult = std::move(res[0]); optResult = std::move(res[0]);
} }
auto outputHashes = staticOutputHashes(*store, drv); auto outputHashes = staticOutputHashes(*store, drv);
std::set<Realisation> missingRealisations; std::set<Realisation> missingRealisations;
StorePathSet missingPaths; StorePathSet missingPaths;
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations) && !drv.type().hasKnownOutputPaths()) { if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations) && !drv.type().hasKnownOutputPaths()) {
for (auto & outputName : wantedOutputs) { for (auto & outputName : wantedOutputs) {
auto thisOutputHash = outputHashes.at(outputName); auto thisOutputHash = outputHashes.at(outputName);
auto thisOutputId = DrvOutput{ thisOutputHash, outputName }; auto thisOutputId = DrvOutput{thisOutputHash, outputName};
if (!store->queryRealisation(thisOutputId)) { if (!store->queryRealisation(thisOutputId)) {
debug("missing output %s", outputName); debug("missing output %s", outputName);
assert(optResult); assert(optResult);

View file

@ -10,23 +10,13 @@
namespace nix { namespace nix {
// Custom implementation to avoid `ref` ptr equality // Custom implementation to avoid `ref` ptr equality
GENERATE_CMP_EXT( GENERATE_CMP_EXT(, std::strong_ordering, SingleBuiltPathBuilt, *me->drvPath, me->output);
,
std::strong_ordering,
SingleBuiltPathBuilt,
*me->drvPath,
me->output);
// Custom implementation to avoid `ref` ptr equality // Custom implementation to avoid `ref` ptr equality
// TODO no `GENERATE_CMP_EXT` because no `std::set::operator<=>` on // TODO no `GENERATE_CMP_EXT` because no `std::set::operator<=>` on
// Darwin, per header. // Darwin, per header.
GENERATE_EQUAL( GENERATE_EQUAL(, BuiltPathBuilt ::, BuiltPathBuilt, *me->drvPath, me->outputs);
,
BuiltPathBuilt ::,
BuiltPathBuilt,
*me->drvPath,
me->outputs);
StorePath SingleBuiltPath::outPath() const StorePath SingleBuiltPath::outPath() const
{ {
@ -34,8 +24,8 @@ StorePath SingleBuiltPath::outPath() const
overloaded{ overloaded{
[](const SingleBuiltPath::Opaque & p) { return p.path; }, [](const SingleBuiltPath::Opaque & p) { return p.path; },
[](const SingleBuiltPath::Built & b) { return b.output.second; }, [](const SingleBuiltPath::Built & b) { return b.output.second; },
}, raw() },
); raw());
} }
StorePathSet BuiltPath::outPaths() const StorePathSet BuiltPath::outPaths() const
@ -49,13 +39,13 @@ StorePathSet BuiltPath::outPaths() const
res.insert(path); res.insert(path);
return res; return res;
}, },
}, raw() },
); raw());
} }
SingleDerivedPath::Built SingleBuiltPath::Built::discardOutputPath() const SingleDerivedPath::Built SingleBuiltPath::Built::discardOutputPath() const
{ {
return SingleDerivedPath::Built { return SingleDerivedPath::Built{
.drvPath = make_ref<SingleDerivedPath>(drvPath->discardOutputPath()), .drvPath = make_ref<SingleDerivedPath>(drvPath->discardOutputPath()),
.output = output.first, .output = output.first,
}; };
@ -65,14 +55,10 @@ SingleDerivedPath SingleBuiltPath::discardOutputPath() const
{ {
return std::visit( return std::visit(
overloaded{ overloaded{
[](const SingleBuiltPath::Opaque & p) -> SingleDerivedPath { [](const SingleBuiltPath::Opaque & p) -> SingleDerivedPath { return p; },
return p; [](const SingleBuiltPath::Built & b) -> SingleDerivedPath { return b.discardOutputPath(); },
}, },
[](const SingleBuiltPath::Built & b) -> SingleDerivedPath { raw());
return b.discardOutputPath();
},
}, raw()
);
} }
nlohmann::json BuiltPath::Built::toJSON(const StoreDirConfig & store) const 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 nlohmann::json SingleBuiltPath::toJSON(const StoreDirConfig & store) const
{ {
return std::visit([&](const auto & buildable) { return std::visit([&](const auto & buildable) { return buildable.toJSON(store); }, raw());
return buildable.toJSON(store);
}, raw());
} }
nlohmann::json BuiltPath::toJSON(const StoreDirConfig & store) const nlohmann::json BuiltPath::toJSON(const StoreDirConfig & store) const
{ {
return std::visit([&](const auto & buildable) { return std::visit([&](const auto & buildable) { return buildable.toJSON(store); }, raw());
return buildable.toJSON(store);
}, raw());
} }
RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const
@ -116,20 +98,18 @@ RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const
overloaded{ overloaded{
[&](const BuiltPath::Opaque & p) { res.insert(p.path); }, [&](const BuiltPath::Opaque & p) { res.insert(p.path); },
[&](const BuiltPath::Built & p) { [&](const BuiltPath::Built & p) {
auto drvHashes = auto drvHashes = staticOutputHashes(store, store.readDerivation(p.drvPath->outPath()));
staticOutputHashes(store, store.readDerivation(p.drvPath->outPath())); for (auto & [outputName, outputPath] : p.outputs) {
for (auto& [outputName, outputPath] : p.outputs) { if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
if (experimentalFeatureSettings.isEnabled(
Xp::CaDerivations)) {
auto drvOutput = get(drvHashes, outputName); auto drvOutput = get(drvHashes, outputName);
if (!drvOutput) if (!drvOutput)
throw Error( throw Error(
"the derivation '%s' has unrealised output '%s' (derived-path.cc/toRealisedPaths)", "the derivation '%s' has unrealised output '%s' (derived-path.cc/toRealisedPaths)",
store.printStorePath(p.drvPath->outPath()), outputName); store.printStorePath(p.drvPath->outPath()),
auto thisRealisation = store.queryRealisation( outputName);
DrvOutput{*drvOutput, outputName}); auto thisRealisation = store.queryRealisation(DrvOutput{*drvOutput, outputName});
assert(thisRealisation); // Weve built it, so we must assert(thisRealisation); // Weve built it, so we must
// have the realisation // have the realisation
res.insert(*thisRealisation); res.insert(*thisRealisation);
} else { } else {
res.insert(outputPath); res.insert(outputPath);
@ -141,4 +121,4 @@ RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const
return res; return res;
} }
} } // namespace nix

View file

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

View file

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

View file

@ -18,12 +18,11 @@
namespace nix { namespace nix {
fetchers::Settings fetchSettings; fetchers::Settings fetchSettings;
static GlobalConfig::Register rFetchSettings(&fetchSettings); static GlobalConfig::Register rFetchSettings(&fetchSettings);
EvalSettings evalSettings { EvalSettings evalSettings{
settings.readOnlyMode, settings.readOnlyMode,
{ {
{ {
@ -31,10 +30,11 @@ EvalSettings evalSettings {
[](EvalState & state, std::string_view rest) { [](EvalState & state, std::string_view rest) {
experimentalFeatureSettings.require(Xp::Flakes); experimentalFeatureSettings.require(Xp::Flakes);
// FIXME `parseFlakeRef` should take a `std::string_view`. // 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); debug("fetching flake search path element '%s''", rest);
auto [accessor, lockedRef] = flakeRef.resolve(state.store).lazyFetch(state.store); auto [accessor, lockedRef] = flakeRef.resolve(state.store).lazyFetch(state.store);
auto storePath = nix::fetchToStore(*state.store, SourcePath(accessor), FetchMode::Copy, lockedRef.input.getName()); auto storePath =
nix::fetchToStore(*state.store, SourcePath(accessor), FetchMode::Copy, lockedRef.input.getName());
state.allowPath(storePath); state.allowPath(storePath);
return state.storePath(storePath); return state.storePath(storePath);
}, },
@ -44,17 +44,14 @@ EvalSettings evalSettings {
static GlobalConfig::Register rEvalSettings(&evalSettings); static GlobalConfig::Register rEvalSettings(&evalSettings);
flake::Settings flakeSettings; flake::Settings flakeSettings;
static GlobalConfig::Register rFlakeSettings(&flakeSettings); static GlobalConfig::Register rFlakeSettings(&flakeSettings);
CompatibilitySettings compatibilitySettings{};
CompatibilitySettings compatibilitySettings {};
static GlobalConfig::Register rCompatibilitySettings(&compatibilitySettings); static GlobalConfig::Register rCompatibilitySettings(&compatibilitySettings);
MixEvalArgs::MixEvalArgs() MixEvalArgs::MixEvalArgs()
{ {
addFlag({ addFlag({
@ -62,7 +59,9 @@ MixEvalArgs::MixEvalArgs()
.description = "Pass the value *expr* as the argument *name* to Nix functions.", .description = "Pass the value *expr* as the argument *name* to Nix functions.",
.category = category, .category = category,
.labels = {"name", "expr"}, .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({ addFlag({
@ -70,7 +69,9 @@ MixEvalArgs::MixEvalArgs()
.description = "Pass the string *string* as the argument *name* to Nix functions.", .description = "Pass the string *string* as the argument *name* to Nix functions.",
.category = category, .category = category,
.labels = {"name", "string"}, .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({ addFlag({
@ -78,7 +79,9 @@ MixEvalArgs::MixEvalArgs()
.description = "Pass the contents of file *path* as the argument *name* to Nix functions.", .description = "Pass the contents of file *path* as the argument *name* to Nix functions.",
.category = category, .category = category,
.labels = {"name", "path"}, .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, .completer = completePath,
}); });
@ -102,18 +105,14 @@ MixEvalArgs::MixEvalArgs()
)", )",
.category = category, .category = category,
.labels = {"path"}, .labels = {"path"},
.handler = {[&](std::string s) { .handler = {[&](std::string s) { lookupPath.elements.emplace_back(LookupPath::Elem::parse(s)); }},
lookupPath.elements.emplace_back(LookupPath::Elem::parse(s));
}},
}); });
addFlag({ addFlag({
.longName = "impure", .longName = "impure",
.description = "Allow access to mutable paths and repositories.", .description = "Allow access to mutable paths and repositories.",
.category = category, .category = category,
.handler = {[&]() { .handler = {[&]() { evalSettings.pureEval = false; }},
evalSettings.pureEval = false;
}},
}); });
addFlag({ addFlag({
@ -125,7 +124,8 @@ MixEvalArgs::MixEvalArgs()
auto from = parseFlakeRef(fetchSettings, _from, std::filesystem::current_path().string()); auto from = parseFlakeRef(fetchSettings, _from, std::filesystem::current_path().string());
auto to = parseFlakeRef(fetchSettings, _to, std::filesystem::current_path().string()); auto to = parseFlakeRef(fetchSettings, _to, std::filesystem::current_path().string());
fetchers::Attrs extraAttrs; fetchers::Attrs extraAttrs;
if (to.subdir != "") extraAttrs["dir"] = to.subdir; if (to.subdir != "")
extraAttrs["dir"] = to.subdir;
fetchers::overrideRegistry(from.input, to.input, extraAttrs); fetchers::overrideRegistry(from.input, to.input, extraAttrs);
}}, }},
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) { .completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
@ -136,7 +136,7 @@ MixEvalArgs::MixEvalArgs()
addFlag({ addFlag({
.longName = "eval-store", .longName = "eval-store",
.description = .description =
R"( R"(
The [URL of the Nix store](@docroot@/store/types/index.md#store-url-format) 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. to use for evaluation, i.e. to store derivations (`.drv` files) and inputs referenced by them.
)", )",
@ -151,20 +151,21 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
auto res = state.buildBindings(autoArgs.size()); auto res = state.buildBindings(autoArgs.size());
for (auto & [name, arg] : autoArgs) { for (auto & [name, arg] : autoArgs) {
auto v = state.allocValue(); auto v = state.allocValue();
std::visit(overloaded { std::visit(
[&](const AutoArgExpr & arg) { overloaded{
state.mkThunk_(*v, state.parseExprFromString(arg.expr, compatibilitySettings.nixShellShebangArgumentsRelativeToScript ? state.rootPath(absPath(getCommandBaseDir())) : state.rootPath("."))); [&](const AutoArgExpr & arg) {
}, state.mkThunk_(
[&](const AutoArgString & arg) { *v,
v->mkString(arg.s); state.parseExprFromString(
}, arg.expr,
[&](const AutoArgFile & arg) { compatibilitySettings.nixShellShebangArgumentsRelativeToScript
v->mkString(readFile(arg.path.string())); ? state.rootPath(absPath(getCommandBaseDir()))
}, : state.rootPath(".")));
[&](const AutoArgStdin & arg) { },
v->mkString(readFile(STDIN_FILENO)); [&](const AutoArgString & arg) { v->mkString(arg.s); },
} [&](const AutoArgFile & arg) { v->mkString(readFile(arg.path.string())); },
}, arg); [&](const AutoArgStdin & arg) { v->mkString(readFile(STDIN_FILENO)); }},
arg);
res.insert(state.symbols.create(name), v); res.insert(state.symbols.create(name), v);
} }
return res.finish(); return res.finish();
@ -173,10 +174,7 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * baseDir) SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * baseDir)
{ {
if (EvalSettings::isPseudoUrl(s)) { if (EvalSettings::isPseudoUrl(s)) {
auto accessor = fetchers::downloadTarball( auto accessor = fetchers::downloadTarball(state.store, state.fetchSettings, EvalSettings::resolvePseudoUrl(s));
state.store,
state.fetchSettings,
EvalSettings::resolvePseudoUrl(s));
auto storePath = fetchToStore(*state.store, SourcePath(accessor), FetchMode::Copy); auto storePath = fetchToStore(*state.store, SourcePath(accessor), FetchMode::Copy);
return state.storePath(storePath); return state.storePath(storePath);
} }
@ -185,7 +183,8 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * bas
experimentalFeatureSettings.require(Xp::Flakes); experimentalFeatureSettings.require(Xp::Flakes);
auto flakeRef = parseFlakeRef(fetchSettings, std::string(s.substr(6)), {}, true, false); auto flakeRef = parseFlakeRef(fetchSettings, std::string(s.substr(6)), {}, true, false);
auto [accessor, lockedRef] = flakeRef.resolve(state.store).lazyFetch(state.store); auto [accessor, lockedRef] = flakeRef.resolve(state.store).lazyFetch(state.store);
auto storePath = nix::fetchToStore(*state.store, SourcePath(accessor), FetchMode::Copy, lockedRef.input.getName()); auto storePath =
nix::fetchToStore(*state.store, SourcePath(accessor), FetchMode::Copy, lockedRef.input.getName());
state.allowPath(storePath); state.allowPath(storePath);
return state.storePath(storePath); return state.storePath(storePath);
} }
@ -199,4 +198,4 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * bas
return state.rootPath(baseDir ? absPath(s, *baseDir) : absPath(s)); 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); throw Error("cannot open '%s' in an editor because it has no physical path", file);
auto editor = getEnv("EDITOR").value_or("cat"); auto editor = getEnv("EDITOR").value_or("cat");
auto args = tokenizeString<Strings>(editor); auto args = tokenizeString<Strings>(editor);
if (line > 0 && ( if (line > 0
editor.find("emacs") != std::string::npos || && (editor.find("emacs") != std::string::npos || editor.find("nano") != std::string::npos
editor.find("nano") != std::string::npos || || editor.find("vim") != std::string::npos || editor.find("kak") != std::string::npos))
editor.find("vim") != std::string::npos ||
editor.find("kak") != std::string::npos))
args.push_back(fmt("+%d", line)); args.push_back(fmt("+%d", line));
args.push_back(path->string()); args.push_back(path->string());
return args; return args;
} }
} } // namespace nix

View file

@ -8,7 +8,8 @@ namespace nix {
struct SingleBuiltPath; struct SingleBuiltPath;
struct SingleBuiltPathBuilt { struct SingleBuiltPathBuilt
{
ref<SingleBuiltPath> drvPath; ref<SingleBuiltPath> drvPath;
std::pair<std::string, StorePath> output; std::pair<std::string, StorePath> output;
@ -18,26 +19,25 @@ struct SingleBuiltPathBuilt {
static SingleBuiltPathBuilt parse(const StoreDirConfig & store, std::string_view, std::string_view); static SingleBuiltPathBuilt parse(const StoreDirConfig & store, std::string_view, std::string_view);
nlohmann::json toJSON(const StoreDirConfig & store) const; nlohmann::json toJSON(const StoreDirConfig & store) const;
bool operator ==(const SingleBuiltPathBuilt &) const noexcept; bool operator==(const SingleBuiltPathBuilt &) const noexcept;
std::strong_ordering operator <=>(const SingleBuiltPathBuilt &) const noexcept; std::strong_ordering operator<=>(const SingleBuiltPathBuilt &) const noexcept;
}; };
using _SingleBuiltPathRaw = std::variant< using _SingleBuiltPathRaw = std::variant<DerivedPathOpaque, SingleBuiltPathBuilt>;
DerivedPathOpaque,
SingleBuiltPathBuilt
>;
struct SingleBuiltPath : _SingleBuiltPathRaw { struct SingleBuiltPath : _SingleBuiltPathRaw
{
using Raw = _SingleBuiltPathRaw; using Raw = _SingleBuiltPathRaw;
using Raw::Raw; using Raw::Raw;
using Opaque = DerivedPathOpaque; using Opaque = DerivedPathOpaque;
using Built = SingleBuiltPathBuilt; using Built = SingleBuiltPathBuilt;
bool operator == (const SingleBuiltPath &) const = default; bool operator==(const SingleBuiltPath &) const = default;
auto 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); return static_cast<const Raw &>(*this);
} }
@ -51,7 +51,7 @@ struct SingleBuiltPath : _SingleBuiltPathRaw {
static inline ref<SingleBuiltPath> staticDrv(StorePath drvPath) 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. * See 'BuiltPath' for more an explanation.
*/ */
struct BuiltPathBuilt { struct BuiltPathBuilt
{
ref<SingleBuiltPath> drvPath; ref<SingleBuiltPath> drvPath;
std::map<std::string, StorePath> outputs; 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. // 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; std::string to_string(const StoreDirConfig & store) const;
static BuiltPathBuilt parse(const StoreDirConfig & store, std::string_view, std::string_view); static BuiltPathBuilt parse(const StoreDirConfig & store, std::string_view, std::string_view);
nlohmann::json toJSON(const StoreDirConfig & store) const; nlohmann::json toJSON(const StoreDirConfig & store) const;
}; };
using _BuiltPathRaw = std::variant< using _BuiltPathRaw = std::variant<DerivedPath::Opaque, BuiltPathBuilt>;
DerivedPath::Opaque,
BuiltPathBuilt
>;
/** /**
* A built path. Similar to a DerivedPath, but enriched with the corresponding * A built path. Similar to a DerivedPath, but enriched with the corresponding
* output path(s). * output path(s).
*/ */
struct BuiltPath : _BuiltPathRaw { struct BuiltPath : _BuiltPathRaw
{
using Raw = _BuiltPathRaw; using Raw = _BuiltPathRaw;
using Raw::Raw; using Raw::Raw;
using Opaque = DerivedPathOpaque; using Opaque = DerivedPathOpaque;
using Built = BuiltPathBuilt; using Built = BuiltPathBuilt;
bool operator == (const BuiltPath &) const = default; 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;
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); return static_cast<const Raw &>(*this);
} }
@ -104,4 +105,4 @@ struct BuiltPath : _BuiltPathRaw {
typedef std::vector<BuiltPath> BuiltPaths; 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; void run(ref<Store> store, ref<Installable> installable) override;
}; };
} } // namespace nix

View file

@ -12,7 +12,9 @@ namespace nix {
class Store; class Store;
namespace fetchers { struct Settings; } namespace fetchers {
struct Settings;
}
class EvalState; class EvalState;
struct EvalSettings; struct EvalSettings;
@ -20,7 +22,9 @@ struct CompatibilitySettings;
class Bindings; class Bindings;
struct SourcePath; struct SourcePath;
namespace flake { struct Settings; } namespace flake {
struct Settings;
}
/** /**
* @todo Get rid of global setttings variables * @todo Get rid of global setttings variables
@ -55,10 +59,23 @@ struct MixEvalArgs : virtual Args, virtual MixRepair
std::optional<std::string> evalStoreUrl; std::optional<std::string> evalStoreUrl;
private: private:
struct AutoArgExpr { std::string expr; }; struct AutoArgExpr
struct AutoArgString { std::string s; }; {
struct AutoArgFile { std::filesystem::path path; }; std::string expr;
struct AutoArgStdin { }; };
struct AutoArgString
{
std::string s;
};
struct AutoArgFile
{
std::filesystem::path path;
};
struct AutoArgStdin
{};
using AutoArg = std::variant<AutoArgExpr, AutoArgString, AutoArgFile, AutoArgStdin>; using AutoArg = std::variant<AutoArgExpr, AutoArgString, AutoArgFile, AutoArgStdin>;
@ -70,4 +87,4 @@ private:
*/ */
SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * baseDir = nullptr); 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); Strings editorFor(const SourcePath & file, uint32_t line);
} } // namespace nix

View file

@ -39,7 +39,10 @@ class InstallableAttrPath : public InstallableValue
const std::string & attrPath, const std::string & attrPath,
ExtendedOutputsSpec extendedOutputsSpec); ExtendedOutputsSpec extendedOutputsSpec);
std::string what() const override { return attrPath; }; std::string what() const override
{
return attrPath;
};
std::pair<Value *, PosIdx> toValue(EvalState & state) override; std::pair<Value *, PosIdx> toValue(EvalState & state) override;
@ -55,4 +58,4 @@ public:
ExtendedOutputsSpec extendedOutputsSpec); ExtendedOutputsSpec extendedOutputsSpec);
}; };
} } // namespace nix

View file

@ -11,8 +11,10 @@ struct InstallableDerivedPath : Installable
DerivedPath derivedPath; DerivedPath derivedPath;
InstallableDerivedPath(ref<Store> store, 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; std::string what() const override;
@ -20,10 +22,8 @@ struct InstallableDerivedPath : Installable
std::optional<StorePath> getStorePath() override; std::optional<StorePath> getStorePath() override;
static InstallableDerivedPath parse( static InstallableDerivedPath
ref<Store> store, parse(ref<Store> store, std::string_view prefix, ExtendedOutputsSpec extendedOutputsSpec);
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 * Extra struct to get around C++ designated initializer limitations
*/ */
struct Flake { struct Flake
{
FlakeRef originalRef; FlakeRef originalRef;
FlakeRef lockedRef; FlakeRef lockedRef;
}; };
@ -26,8 +27,10 @@ struct ExtraPathInfoFlake : ExtraPathInfoValue
Flake flake; Flake flake;
ExtraPathInfoFlake(Value && v, Flake && f) ExtraPathInfoFlake(Value && v, Flake && f)
: ExtraPathInfoValue(std::move(v)), flake(std::move(f)) : ExtraPathInfoValue(std::move(v))
{ } , flake(std::move(f))
{
}
}; };
struct InstallableFlake : InstallableValue struct InstallableFlake : InstallableValue
@ -49,7 +52,10 @@ struct InstallableFlake : InstallableValue
Strings prefixes, Strings prefixes,
const flake::LockFlags & lockFlags); 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(); std::vector<std::string> getActualAttrPaths();
@ -61,8 +67,7 @@ struct InstallableFlake : InstallableValue
* Get a cursor to every attrpath in getActualAttrPaths() that * Get a cursor to every attrpath in getActualAttrPaths() that
* exists. However if none exists, throw an exception. * exists. However if none exists, throw an exception.
*/ */
std::vector<ref<eval_cache::AttrCursor>> std::vector<ref<eval_cache::AttrCursor>> getCursors(EvalState & state) override;
getCursors(EvalState & state) override;
std::shared_ptr<flake::LockedFlake> getLockedFlake() const; std::shared_ptr<flake::LockedFlake> getLockedFlake() const;
@ -79,11 +84,9 @@ struct InstallableFlake : InstallableValue
*/ */
static inline FlakeRef defaultNixpkgsFlakeRef() 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( ref<eval_cache::EvalCache> openEvalCache(EvalState & state, std::shared_ptr<flake::LockedFlake> lockedFlake);
EvalState & state,
std::shared_ptr<flake::LockedFlake> lockedFlake);
} } // namespace nix

View file

@ -9,7 +9,10 @@ namespace nix {
struct PackageInfo; struct PackageInfo;
struct SourceExprCommand; struct SourceExprCommand;
namespace eval_cache { class EvalCache; class AttrCursor; } namespace eval_cache {
class EvalCache;
class AttrCursor;
} // namespace eval_cache
struct App struct App
{ {
@ -37,7 +40,8 @@ struct ExtraPathInfoValue : ExtraPathInfo
/** /**
* Extra struct to get around C++ designated initializer limitations * Extra struct to get around C++ designated initializer limitations
*/ */
struct Value { struct Value
{
/** /**
* An optional priority for use with "build envs". See Package * An optional priority for use with "build envs". See Package
*/ */
@ -61,7 +65,8 @@ struct ExtraPathInfoValue : ExtraPathInfo
ExtraPathInfoValue(Value && v) ExtraPathInfoValue(Value && v)
: value(std::move(v)) : value(std::move(v))
{ } {
}
virtual ~ExtraPathInfoValue() = default; virtual ~ExtraPathInfoValue() = default;
}; };
@ -74,9 +79,12 @@ struct InstallableValue : Installable
{ {
ref<EvalState> state; 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; 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 * However if none exists, throw exception instead of returning
* empty vector. * empty vector.
*/ */
virtual std::vector<ref<eval_cache::AttrCursor>> virtual std::vector<ref<eval_cache::AttrCursor>> getCursors(EvalState & state);
getCursors(EvalState & state);
/** /**
* Get the first and most preferred cursor this Installable could * Get the first and most preferred cursor this Installable could
* refer to, or throw an exception if none exists. * refer to, or throw an exception if none exists.
*/ */
virtual ref<eval_cache::AttrCursor> virtual ref<eval_cache::AttrCursor> getCursor(EvalState & state);
getCursor(EvalState & state);
UnresolvedApp toApp(EvalState & state); UnresolvedApp toApp(EvalState & state);
@ -116,7 +122,8 @@ protected:
* @result A derived path (with empty info, for now) if the value * @result A derived path (with empty info, for now) if the value
* matched the above criteria. * 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 struct Installable
{ {
virtual ~Installable() { } virtual ~Installable() {}
/** /**
* What Installable is this? * What Installable is this?
@ -168,37 +168,19 @@ struct Installable
BuildMode bMode = bmNormal); BuildMode bMode = bmNormal);
static std::set<StorePath> toStorePathSet( static std::set<StorePath> toStorePathSet(
ref<Store> evalStore, ref<Store> evalStore, ref<Store> store, Realise mode, OperateOn operateOn, const Installables & installables);
ref<Store> store,
Realise mode,
OperateOn operateOn,
const Installables & installables);
static std::vector<StorePath> toStorePaths( static std::vector<StorePath> toStorePaths(
ref<Store> evalStore, ref<Store> evalStore, ref<Store> store, Realise mode, OperateOn operateOn, const Installables & installables);
ref<Store> store,
Realise mode,
OperateOn operateOn,
const Installables & installables);
static StorePath toStorePath( static StorePath toStorePath(
ref<Store> evalStore, ref<Store> evalStore, ref<Store> store, Realise mode, OperateOn operateOn, ref<Installable> installable);
ref<Store> store,
Realise mode,
OperateOn operateOn,
ref<Installable> installable);
static std::set<StorePath> toDerivations( static std::set<StorePath>
ref<Store> store, toDerivations(ref<Store> store, const Installables & installables, bool useDeriver = false);
const Installables & installables,
bool useDeriver = false);
static BuiltPaths toBuiltPaths( static BuiltPaths toBuiltPaths(
ref<Store> evalStore, ref<Store> evalStore, ref<Store> store, Realise mode, OperateOn operateOn, const Installables & installables);
ref<Store> store,
Realise mode,
OperateOn operateOn,
const Installables & installables);
}; };
} } // namespace nix

View file

@ -7,13 +7,14 @@
namespace nix { namespace nix {
typedef std::function<void(int, char * *)> MainFunction; typedef std::function<void(int, char **)> MainFunction;
struct RegisterLegacyCommand struct RegisterLegacyCommand
{ {
typedef std::map<std::string, MainFunction> Commands; typedef std::map<std::string, MainFunction> Commands;
static Commands & commands() { static Commands & commands()
{
static Commands commands; static Commands commands;
return 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); std::string renderMarkdownToTerminal(std::string_view markdown);
} } // namespace nix

View file

@ -4,18 +4,22 @@
namespace nix::flag { namespace nix::flag {
Args::Flag hashAlgo(std::string && longName, HashAlgorithm * ha); Args::Flag hashAlgo(std::string && longName, HashAlgorithm * ha);
static inline Args::Flag hashAlgo(HashAlgorithm * ha) static inline Args::Flag hashAlgo(HashAlgorithm * ha)
{ {
return hashAlgo("hash-algo", ha); return hashAlgo("hash-algo", ha);
} }
Args::Flag hashAlgoOpt(std::string && longName, std::optional<HashAlgorithm> * oha); Args::Flag hashAlgoOpt(std::string && longName, std::optional<HashAlgorithm> * oha);
Args::Flag hashFormatWithDefault(std::string && longName, HashFormat * hf); Args::Flag hashFormatWithDefault(std::string && longName, HashFormat * hf);
Args::Flag hashFormatOpt(std::string && longName, std::optional<HashFormat> * ohf); Args::Flag hashFormatOpt(std::string && longName, std::optional<HashFormat> * ohf);
static inline Args::Flag hashAlgoOpt(std::optional<HashAlgorithm> * oha) static inline Args::Flag hashAlgoOpt(std::optional<HashAlgorithm> * oha)
{ {
return hashAlgoOpt("hash-algo", oha); return hashAlgoOpt("hash-algo", oha);
} }
Args::Flag fileIngestionMethod(FileIngestionMethod * method); Args::Flag fileIngestionMethod(FileIngestionMethod * method);
Args::Flag contentAddressMethod(ContentAddressMethod * method); Args::Flag contentAddressMethod(ContentAddressMethod * method);
} } // namespace nix::flag

View file

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

View file

@ -11,10 +11,11 @@ namespace nix {
namespace detail { namespace detail {
/** Provides the completion hooks for the repl, without exposing its complete /** Provides the completion hooks for the repl, without exposing its complete
* internals. */ * internals. */
struct ReplCompleterMixin { struct ReplCompleterMixin
{
virtual StringSet completePrefix(const std::string & prefix) = 0; virtual StringSet completePrefix(const std::string & prefix) = 0;
}; };
}; }; // namespace detail
enum class ReplPromptType { enum class ReplPromptType {
ReplPrompt, ReplPrompt,
@ -29,7 +30,7 @@ public:
virtual Guard init(detail::ReplCompleterMixin * repl) = 0; virtual Guard init(detail::ReplCompleterMixin * repl) = 0;
/** Returns a boolean of whether the interacter got EOF */ /** Returns a boolean of whether the interacter got EOF */
virtual bool getLine(std::string & input, ReplPromptType promptType) = 0; virtual bool getLine(std::string & input, ReplPromptType promptType) = 0;
virtual ~ReplInteracter(){}; virtual ~ReplInteracter() {};
}; };
class ReadlineLikeInteracter : public virtual ReplInteracter class ReadlineLikeInteracter : public virtual ReplInteracter
@ -40,9 +41,10 @@ public:
: historyFile(historyFile) : historyFile(historyFile)
{ {
} }
virtual Guard init(detail::ReplCompleterMixin * repl) override; virtual Guard init(detail::ReplCompleterMixin * repl) override;
virtual bool getLine(std::string & input, ReplPromptType promptType) override; virtual bool getLine(std::string & input, ReplPromptType promptType) override;
virtual ~ReadlineLikeInteracter() override; virtual ~ReadlineLikeInteracter() override;
}; };
}; }; // namespace nix

View file

@ -12,12 +12,12 @@ struct AbstractNixRepl
AbstractNixRepl(ref<EvalState> state) AbstractNixRepl(ref<EvalState> state)
: state(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); using RunNix = void(Path program, const Strings & args, const std::optional<std::string> & input);
@ -33,13 +33,11 @@ struct AbstractNixRepl
std::function<AnnotatedValues()> getValues, std::function<AnnotatedValues()> getValues,
RunNix * runNix = nullptr); RunNix * runNix = nullptr);
static ReplExitStatus runSimple( static ReplExitStatus runSimple(ref<EvalState> evalState, const ValMap & extraEnv);
ref<EvalState> evalState,
const ValMap & extraEnv);
virtual void initEnv() = 0; virtual void initEnv() = 0;
virtual ReplExitStatus mainLoop() = 0; virtual ReplExitStatus mainLoop() = 0;
}; };
} } // namespace nix

View file

@ -35,7 +35,8 @@ InstallableAttrPath::InstallableAttrPath(
, v(allocRootValue(v)) , v(allocRootValue(v))
, attrPath(attrPath) , attrPath(attrPath)
, extendedOutputsSpec(std::move(extendedOutputsSpec)) , extendedOutputsSpec(std::move(extendedOutputsSpec))
{ } {
}
std::pair<Value *, PosIdx> InstallableAttrPath::toValue(EvalState & state) std::pair<Value *, PosIdx> InstallableAttrPath::toValue(EvalState & state)
{ {
@ -48,12 +49,9 @@ DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths()
{ {
auto [v, pos] = toValue(*state); auto [v, pos] = toValue(*state);
if (std::optional derivedPathWithInfo = trySinglePathToDerivedPaths( if (std::optional derivedPathWithInfo =
*v, trySinglePathToDerivedPaths(*v, pos, fmt("while evaluating the attribute '%s'", attrPath))) {
pos, return {*derivedPathWithInfo};
fmt("while evaluating the attribute '%s'", attrPath)))
{
return { *derivedPathWithInfo };
} }
Bindings & autoArgs = *cmd.getAutoArgs(*state); Bindings & autoArgs = *cmd.getAutoArgs(*state);
@ -70,19 +68,19 @@ DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths()
if (!drvPath) if (!drvPath)
throw Error("'%s' is not a derivation", what()); throw Error("'%s' is not a derivation", what());
auto newOutputs = std::visit(overloaded { auto newOutputs = std::visit(
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec { overloaded{
StringSet outputsToInstall; [&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
for (auto & output : packageInfo.queryOutputs(false, true)) StringSet outputsToInstall;
outputsToInstall.insert(output.first); for (auto & output : packageInfo.queryOutputs(false, true))
if (outputsToInstall.empty()) outputsToInstall.insert(output.first);
outputsToInstall.insert("out"); if (outputsToInstall.empty())
return OutputsSpec::Names { std::move(outputsToInstall) }; outputsToInstall.insert("out");
return OutputsSpec::Names{std::move(outputsToInstall)};
},
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec { return e; },
}, },
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec { extendedOutputsSpec.raw);
return e;
},
}, extendedOutputsSpec.raw);
auto [iter, didInsert] = byDrvPath.emplace(*drvPath, newOutputs); auto [iter, didInsert] = byDrvPath.emplace(*drvPath, newOutputs);
@ -93,11 +91,12 @@ DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths()
DerivedPathsWithInfo res; DerivedPathsWithInfo res;
for (auto & [drvPath, outputs] : byDrvPath) for (auto & [drvPath, outputs] : byDrvPath)
res.push_back({ res.push_back({
.path = DerivedPath::Built { .path =
.drvPath = makeConstantStorePathRef(drvPath), DerivedPath::Built{
.outputs = outputs, .drvPath = makeConstantStorePathRef(drvPath),
}, .outputs = outputs,
.info = make_ref<ExtraPathInfoValue>(ExtraPathInfoValue::Value { },
.info = make_ref<ExtraPathInfoValue>(ExtraPathInfoValue::Value{
.extendedOutputsSpec = outputs, .extendedOutputsSpec = outputs,
/* FIXME: reconsider backwards compatibility above /* FIXME: reconsider backwards compatibility above
so we can fill in this info. */ so we can fill in this info. */
@ -115,10 +114,12 @@ InstallableAttrPath InstallableAttrPath::parse(
ExtendedOutputsSpec extendedOutputsSpec) ExtendedOutputsSpec extendedOutputsSpec)
{ {
return { return {
state, cmd, v, state,
prefix == "." ? "" : std::string { prefix }, cmd,
v,
prefix == "." ? "" : std::string{prefix},
std::move(extendedOutputsSpec), std::move(extendedOutputsSpec),
}; };
} }
} } // namespace nix

View file

@ -21,35 +21,35 @@ std::optional<StorePath> InstallableDerivedPath::getStorePath()
return derivedPath.getBaseStorePath(); return derivedPath.getBaseStorePath();
} }
InstallableDerivedPath InstallableDerivedPath::parse( InstallableDerivedPath
ref<Store> store, InstallableDerivedPath::parse(ref<Store> store, std::string_view prefix, ExtendedOutputsSpec extendedOutputsSpec)
std::string_view prefix,
ExtendedOutputsSpec extendedOutputsSpec)
{ {
auto derivedPath = std::visit(overloaded { auto derivedPath = std::visit(
// If the user did not use ^, we treat the output more overloaded{
// liberally: we accept a symlink chain or an actual // If the user did not use ^, we treat the output more
// store path. // liberally: we accept a symlink chain or an actual
[&](const ExtendedOutputsSpec::Default &) -> DerivedPath { // store path.
auto storePath = store->followLinksToStorePath(prefix); [&](const ExtendedOutputsSpec::Default &) -> DerivedPath {
return DerivedPath::Opaque { auto storePath = store->followLinksToStorePath(prefix);
.path = std::move(storePath), 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. extendedOutputsSpec.raw);
[&](const ExtendedOutputsSpec::Explicit & outputSpec) -> DerivedPath { return InstallableDerivedPath{
auto drv = make_ref<SingleDerivedPath>(SingleDerivedPath::parse(*store, prefix));
drvRequireExperiment(*drv);
return DerivedPath::Built {
.drvPath = std::move(drv),
.outputs = outputSpec,
};
},
}, extendedOutputsSpec.raw);
return InstallableDerivedPath {
store, store,
std::move(derivedPath), std::move(derivedPath),
}; };
} }
} } // namespace nix

View file

@ -28,8 +28,8 @@ namespace nix {
std::vector<std::string> InstallableFlake::getActualAttrPaths() std::vector<std::string> InstallableFlake::getActualAttrPaths()
{ {
std::vector<std::string> res; std::vector<std::string> res;
if (attrPaths.size() == 1 && attrPaths.front().starts_with(".")){ if (attrPaths.size() == 1 && attrPaths.front().starts_with(".")) {
attrPaths.front().erase(0,1); attrPaths.front().erase(0, 1);
res.push_back(attrPaths.front()); res.push_back(attrPaths.front());
return res; return res;
} }
@ -47,8 +47,11 @@ static std::string showAttrPaths(const std::vector<std::string> & paths)
{ {
std::string s; std::string s;
for (const auto & [n, i] : enumerate(paths)) { for (const auto & [n, i] : enumerate(paths)) {
if (n > 0) s += n + 1 == paths.size() ? " or " : ", "; if (n > 0)
s += '\''; s += i; s += '\''; s += n + 1 == paths.size() ? " or " : ", ";
s += '\'';
s += i;
s += '\'';
} }
return s; return s;
} }
@ -62,12 +65,12 @@ InstallableFlake::InstallableFlake(
Strings attrPaths, Strings attrPaths,
Strings prefixes, Strings prefixes,
const flake::LockFlags & lockFlags) const flake::LockFlags & lockFlags)
: InstallableValue(state), : InstallableValue(state)
flakeRef(flakeRef), , flakeRef(flakeRef)
attrPaths(fragment == "" ? attrPaths : Strings{(std::string) fragment}), , attrPaths(fragment == "" ? attrPaths : Strings{(std::string) fragment})
prefixes(fragment == "" ? Strings{} : prefixes), , prefixes(fragment == "" ? Strings{} : prefixes)
extendedOutputsSpec(std::move(extendedOutputsSpec)), , extendedOutputsSpec(std::move(extendedOutputsSpec))
lockFlags(lockFlags) , lockFlags(lockFlags)
{ {
if (cmd && cmd->getAutoArgs(*state)->size()) if (cmd && cmd->getAutoArgs(*state)->size())
throw UsageError("'--arg' and '--argstr' are incompatible with flakes"); throw UsageError("'--arg' and '--argstr' are incompatible with flakes");
@ -87,18 +90,14 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
auto v = attr->forceValue(); auto v = attr->forceValue();
if (std::optional derivedPathWithInfo = trySinglePathToDerivedPaths( if (std::optional derivedPathWithInfo = trySinglePathToDerivedPaths(
v, v, noPos, fmt("while evaluating the flake output attribute '%s'", attrPath))) {
noPos, return {*derivedPathWithInfo};
fmt("while evaluating the flake output attribute '%s'", attrPath)))
{
return { *derivedPathWithInfo };
} else { } else {
throw Error( throw Error(
"expected flake output attribute '%s' to be a derivation or path but found %s: %s", "expected flake output attribute '%s' to be a derivation or path but found %s: %s",
attrPath, attrPath,
showType(v), showType(v),
ValuePrinter(*this->state, v, errorPrintOptions) ValuePrinter(*this->state, v, errorPrintOptions));
);
} }
} }
@ -113,39 +112,40 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
} }
return {{ return {{
.path = DerivedPath::Built { .path =
.drvPath = makeConstantStorePathRef(std::move(drvPath)), DerivedPath::Built{
.outputs = std::visit(overloaded { .drvPath = makeConstantStorePathRef(std::move(drvPath)),
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec { .outputs = std::visit(
StringSet outputsToInstall; overloaded{
if (auto aOutputSpecified = attr->maybeGetAttr(state->sOutputSpecified)) { [&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
if (aOutputSpecified->getBool()) { StringSet outputsToInstall;
if (auto aOutputName = attr->maybeGetAttr("outputName")) if (auto aOutputSpecified = attr->maybeGetAttr(state->sOutputSpecified)) {
outputsToInstall = { aOutputName->getString() }; if (aOutputSpecified->getBool()) {
} if (auto aOutputName = attr->maybeGetAttr("outputName"))
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) { outputsToInstall = {aOutputName->getString()};
if (auto aOutputsToInstall = aMeta->maybeGetAttr("outputsToInstall")) }
for (auto & s : aOutputsToInstall->getListOfStrings()) } else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
outputsToInstall.insert(s); if (auto aOutputsToInstall = aMeta->maybeGetAttr("outputsToInstall"))
} for (auto & s : aOutputsToInstall->getListOfStrings())
outputsToInstall.insert(s);
}
if (outputsToInstall.empty()) if (outputsToInstall.empty())
outputsToInstall.insert("out"); outputsToInstall.insert("out");
return OutputsSpec::Names { std::move(outputsToInstall) }; return OutputsSpec::Names{std::move(outputsToInstall)};
}, },
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec { [&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec { return e; },
return e; },
}, extendedOutputsSpec.raw),
}, extendedOutputsSpec.raw), },
},
.info = make_ref<ExtraPathInfoFlake>( .info = make_ref<ExtraPathInfoFlake>(
ExtraPathInfoValue::Value { ExtraPathInfoValue::Value{
.priority = priority, .priority = priority,
.attrPath = attrPath, .attrPath = attrPath,
.extendedOutputsSpec = extendedOutputsSpec, .extendedOutputsSpec = extendedOutputsSpec,
}, },
ExtraPathInfoFlake::Flake { ExtraPathInfoFlake::Flake{
.originalRef = flakeRef, .originalRef = flakeRef,
.lockedRef = getLockedFlake()->flake.lockedRef, .lockedRef = getLockedFlake()->flake.lockedRef,
}), }),
@ -157,8 +157,7 @@ std::pair<Value *, PosIdx> InstallableFlake::toValue(EvalState & state)
return {&getCursor(state)->forceValue(), noPos}; return {&getCursor(state)->forceValue(), noPos};
} }
std::vector<ref<eval_cache::AttrCursor>> std::vector<ref<eval_cache::AttrCursor>> InstallableFlake::getCursors(EvalState & state)
InstallableFlake::getCursors(EvalState & state)
{ {
auto evalCache = openEvalCache(state, getLockedFlake()); auto evalCache = openEvalCache(state, getLockedFlake());
@ -181,11 +180,7 @@ InstallableFlake::getCursors(EvalState & state)
} }
if (res.size() == 0) if (res.size() == 0)
throw Error( throw Error(suggestions, "flake '%s' does not provide attribute %s", flakeRef, showAttrPaths(attrPaths));
suggestions,
"flake '%s' does not provide attribute %s",
flakeRef,
showAttrPaths(attrPaths));
return res; return res;
} }
@ -196,8 +191,8 @@ std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
flake::LockFlags lockFlagsApplyConfig = lockFlags; flake::LockFlags lockFlagsApplyConfig = lockFlags;
// FIXME why this side effect? // FIXME why this side effect?
lockFlagsApplyConfig.applyNixConfig = true; lockFlagsApplyConfig.applyNixConfig = true;
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake( _lockedFlake =
flakeSettings, *state, flakeRef, lockFlagsApplyConfig)); std::make_shared<flake::LockedFlake>(lockFlake(flakeSettings, *state, flakeRef, lockFlagsApplyConfig));
} }
return _lockedFlake; return _lockedFlake;
} }
@ -216,4 +211,4 @@ FlakeRef InstallableFlake::nixpkgsFlakeRef() const
return defaultNixpkgsFlakeRef(); return defaultNixpkgsFlakeRef();
} }
} } // namespace nix

View file

@ -4,17 +4,14 @@
namespace nix { namespace nix {
std::vector<ref<eval_cache::AttrCursor>> std::vector<ref<eval_cache::AttrCursor>> InstallableValue::getCursors(EvalState & state)
InstallableValue::getCursors(EvalState & state)
{ {
auto evalCache = auto evalCache =
std::make_shared<nix::eval_cache::EvalCache>(std::nullopt, state, std::make_shared<nix::eval_cache::EvalCache>(std::nullopt, state, [&]() { return toValue(state).first; });
[&]() { return toValue(state).first; });
return {evalCache->getRoot()}; return {evalCache->getRoot()};
} }
ref<eval_cache::AttrCursor> ref<eval_cache::AttrCursor> InstallableValue::getCursor(EvalState & state)
InstallableValue::getCursor(EvalState & state)
{ {
/* Although getCursors should return at least one element, in case it doesn't, /* Although getCursors should return at least one element, in case it doesn't,
bound check to avoid an undefined behavior for vector[0] */ 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>(); auto castedInstallable = installable.dynamic_pointer_cast<InstallableValue>();
if (!castedInstallable) if (!castedInstallable)
throw nonValueInstallable(*installable); 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) { if (v.type() == nPath) {
auto storePath = fetchToStore(*state->store, v.path(), FetchMode::Copy); auto storePath = fetchToStore(*state->store, v.path(), FetchMode::Copy);
return {{ return {{
.path = DerivedPath::Opaque { .path =
.path = std::move(storePath), DerivedPath::Opaque{
}, .path = std::move(storePath),
},
.info = make_ref<ExtraPathInfo>(), .info = make_ref<ExtraPathInfo>(),
}}; }};
} }
else if (v.type() == nString) { else if (v.type() == nString) {
return {{ return {{
.path = DerivedPath::fromSingle( .path = DerivedPath::fromSingle(state->coerceToSingleDerivedPath(pos, v, errorCtx)),
state->coerceToSingleDerivedPath(pos, v, errorCtx)),
.info = make_ref<ExtraPathInfo>(), .info = make_ref<ExtraPathInfo>(),
}}; }};
} }
else return std::nullopt; else
return std::nullopt;
} }
} } // namespace nix

View file

@ -61,7 +61,8 @@ MixFlakeOptions::MixFlakeOptions()
.category = category, .category = category,
.handler = {[&]() { .handler = {[&]() {
lockFlags.recreateLockFile = true; 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.", .description = "Write the given lock file instead of `flake.lock` within the top-level flake.",
.category = category, .category = category,
.labels = {"flake-lock-path"}, .labels = {"flake-lock-path"},
.handler = {[&](std::string lockFilePath) { .handler = {[&](std::string lockFilePath) { lockFlags.outputLockFilePath = lockFilePath; }},
lockFlags.outputLockFilePath = lockFilePath;
}},
.completer = completePath, .completer = completePath,
}); });
@ -175,12 +174,12 @@ MixFlakeOptions::MixFlakeOptions()
flakeSettings, flakeSettings,
*evalState, *evalState,
parseFlakeRef(fetchSettings, flakeRef, absPath(getCommandBaseDir())), parseFlakeRef(fetchSettings, flakeRef, absPath(getCommandBaseDir())),
{ .writeLockFile = false }); {.writeLockFile = false});
for (auto & [inputName, input] : flake.lockFile.root->inputs) { for (auto & [inputName, input] : flake.lockFile.root->inputs) {
auto input2 = flake.lockFile.findInput({inputName}); // resolve 'follows' nodes auto input2 = flake.lockFile.findInput({inputName}); // resolve 'follows' nodes
if (auto input3 = std::dynamic_pointer_cast<const flake::LockedNode>(input2)) { if (auto input3 = std::dynamic_pointer_cast<const flake::LockedNode>(input2)) {
overrideRegistry( overrideRegistry(
fetchers::Input::fromAttrs(fetchSettings, {{"type","indirect"}, {"id", inputName}}), fetchers::Input::fromAttrs(fetchSettings, {{"type", "indirect"}, {"id", inputName}}),
input3->lockedRef.input, input3->lockedRef.input,
{}); {});
} }
@ -209,7 +208,8 @@ SourceExprCommand::SourceExprCommand()
addFlag({ addFlag({
.longName = "expr", .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, .category = installablesCategory,
.labels = {"expr"}, .labels = {"expr"},
.handler = {&expr}, .handler = {&expr},
@ -220,32 +220,26 @@ MixReadOnlyOption::MixReadOnlyOption()
{ {
addFlag({ addFlag({
.longName = "read-only", .longName = "read-only",
.description = .description = "Do not instantiate each evaluated derivation. "
"Do not instantiate each evaluated derivation. " "This improves performance, but can cause errors when accessing "
"This improves performance, but can cause errors when accessing " "store paths of derivations during evaluation.",
"store paths of derivations during evaluation.",
.handler = {&settings.readOnlyMode, true}, .handler = {&settings.readOnlyMode, true},
}); });
} }
Strings SourceExprCommand::getDefaultFlakeAttrPaths() Strings SourceExprCommand::getDefaultFlakeAttrPaths()
{ {
return { return {"packages." + settings.thisSystem.get() + ".default", "defaultPackage." + settings.thisSystem.get()};
"packages." + settings.thisSystem.get() + ".default",
"defaultPackage." + settings.thisSystem.get()
};
} }
Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes() Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes()
{ {
return { return {// As a convenience, look for the attribute in
// As a convenience, look for the attribute in // 'outputs.packages'.
// 'outputs.packages'. "packages." + settings.thisSystem.get() + ".",
"packages." + settings.thisSystem.get() + ".", // As a temporary hack until Nixpkgs is properly converted
// As a temporary hack until Nixpkgs is properly converted // to provide a clean 'packages' set, look in 'legacyPackages'.
// to provide a clean 'packages' set, look in 'legacyPackages'. "legacyPackages." + settings.thisSystem.get() + "."};
"legacyPackages." + settings.thisSystem.get() + "."
};
} }
Args::CompleterClosure SourceExprCommand::getCompleteInstallable() Args::CompleterClosure SourceExprCommand::getCompleteInstallable()
@ -263,10 +257,7 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s
evalSettings.pureEval = false; evalSettings.pureEval = false;
auto state = getEvalState(); auto state = getEvalState();
auto e = auto e = state->parseExprFromFile(resolveExprPath(lookupFileArg(*state, *file)));
state->parseExprFromFile(
resolveExprPath(
lookupFileArg(*state, *file)));
Value root; Value root;
state->eval(e, root); state->eval(e, root);
@ -285,7 +276,7 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s
} }
auto [v, pos] = findAlongAttrPath(*state, prefix_, *autoArgs, root); auto [v, pos] = findAlongAttrPath(*state, prefix_, *autoArgs, root);
Value &v1(*v); Value & v1(*v);
state->forceValue(v1, pos); state->forceValue(v1, pos);
Value v2; Value v2;
state->autoCallFunction(*autoArgs, v1, v2); state->autoCallFunction(*autoArgs, v1, v2);
@ -310,7 +301,7 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s
getDefaultFlakeAttrPaths(), getDefaultFlakeAttrPaths(),
prefix); prefix);
} }
} catch (EvalError&) { } catch (EvalError &) {
// Don't want eval errors to mess-up with the completion engine, so let's just swallow them // Don't want eval errors to mess-up with the completion engine, so let's just swallow them
} }
} }
@ -334,22 +325,23 @@ void completeFlakeRefWithFragment(
auto fragment = prefix.substr(hash + 1); auto fragment = prefix.substr(hash + 1);
std::string prefixRoot = ""; std::string prefixRoot = "";
if (fragment.starts_with(".")){ if (fragment.starts_with(".")) {
fragment = fragment.substr(1); fragment = fragment.substr(1);
prefixRoot = "."; prefixRoot = ".";
} }
auto flakeRefS = std::string(prefix.substr(0, hash)); auto flakeRefS = std::string(prefix.substr(0, hash));
// TODO: ideally this would use the command base directory instead of assuming ".". // 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, auto evalCache = openEvalCache(
std::make_shared<flake::LockedFlake>(lockFlake( *evalState,
flakeSettings, *evalState, flakeRef, lockFlags))); std::make_shared<flake::LockedFlake>(lockFlake(flakeSettings, *evalState, flakeRef, lockFlags)));
auto root = evalCache->getRoot(); auto root = evalCache->getRoot();
if (prefixRoot == "."){ if (prefixRoot == ".") {
attrPathPrefixes.clear(); attrPathPrefixes.clear();
} }
/* Complete 'fragment' relative to all the /* Complete 'fragment' relative to all the
@ -369,7 +361,8 @@ void completeFlakeRefWithFragment(
} }
auto attr = root->findAlongAttrPath(attrPath); auto attr = root->findAlongAttrPath(attrPath);
if (!attr) continue; if (!attr)
continue;
for (auto & attr2 : (*attr)->getAttrs()) { for (auto & attr2 : (*attr)->getAttrs()) {
if (hasPrefix(evalState->symbols[attr2], lastAttr)) { if (hasPrefix(evalState->symbols[attr2], lastAttr)) {
@ -377,7 +370,9 @@ void completeFlakeRefWithFragment(
/* Strip the attrpath prefix. */ /* Strip the attrpath prefix. */
attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size()); attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size());
// FIXME: handle names with dots // 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 +382,8 @@ void completeFlakeRefWithFragment(
if (fragment.empty()) { if (fragment.empty()) {
for (auto & attrPath : defaultFlakeAttrPaths) { for (auto & attrPath : defaultFlakeAttrPaths) {
auto attr = root->findAlongAttrPath(parseAttrPath(*evalState, attrPath)); auto attr = root->findAlongAttrPath(parseAttrPath(*evalState, attrPath));
if (!attr) continue; if (!attr)
continue;
completions.add(flakeRefS + "#" + prefixRoot); completions.add(flakeRefS + "#" + prefixRoot);
} }
} }
@ -427,14 +423,12 @@ DerivedPathWithInfo Installable::toDerivedPath()
{ {
auto buildables = toDerivedPaths(); auto buildables = toDerivedPaths();
if (buildables.size() != 1) 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]); return std::move(buildables[0]);
} }
static StorePath getDeriver( static StorePath getDeriver(ref<Store> store, const Installable & i, const StorePath & drvPath)
ref<Store> store,
const Installable & i,
const StorePath & drvPath)
{ {
auto derivers = store->queryValidDerivers(drvPath); auto derivers = store->queryValidDerivers(drvPath);
if (derivers.empty()) if (derivers.empty())
@ -443,35 +437,35 @@ static StorePath getDeriver(
return *derivers.begin(); return *derivers.begin();
} }
ref<eval_cache::EvalCache> openEvalCache( ref<eval_cache::EvalCache> openEvalCache(EvalState & state, std::shared_ptr<flake::LockedFlake> lockedFlake)
EvalState & state,
std::shared_ptr<flake::LockedFlake> lockedFlake)
{ {
auto fingerprint = evalSettings.useEvalCache && evalSettings.pureEval auto fingerprint = evalSettings.useEvalCache && evalSettings.pureEval
? lockedFlake->getFingerprint(state.store, state.fetchSettings) ? lockedFlake->getFingerprint(state.store, state.fetchSettings)
: std::nullopt; : std::nullopt;
auto rootLoader = [&state, lockedFlake]() auto rootLoader = [&state, lockedFlake]() {
{ /* For testing whether the evaluation cache is
/* For testing whether the evaluation cache is complete. */
complete. */ if (getEnv("NIX_ALLOW_EVAL").value_or("1") == "0")
if (getEnv("NIX_ALLOW_EVAL").value_or("1") == "0") throw Error("not everything is cached, but evaluation is not allowed");
throw Error("not everything is cached, but evaluation is not allowed");
auto vFlake = state.allocValue(); auto vFlake = state.allocValue();
flake::callFlake(state, *lockedFlake, *vFlake); 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")); auto aOutputs = vFlake->attrs()->get(state.symbols.create("outputs"));
assert(aOutputs); assert(aOutputs);
return aOutputs->value; return aOutputs->value;
}; };
if (fingerprint) { if (fingerprint) {
auto search = state.evalCaches.find(fingerprint.value()); auto search = state.evalCaches.find(fingerprint.value());
if (search == state.evalCaches.end()) { 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; return search->second;
} else { } else {
@ -479,8 +473,7 @@ ref<eval_cache::EvalCache> openEvalCache(
} }
} }
Installables SourceExprCommand::parseInstallables( Installables SourceExprCommand::parseInstallables(ref<Store> store, std::vector<std::string> ss)
ref<Store> store, std::vector<std::string> ss)
{ {
Installables result; Installables result;
@ -501,12 +494,10 @@ Installables SourceExprCommand::parseInstallables(
if (file == "-") { if (file == "-") {
auto e = state->parseStdin(); auto e = state->parseStdin();
state->eval(e, *vFile); state->eval(e, *vFile);
} } else if (file) {
else if (file) {
auto dir = absPath(getCommandBaseDir()); auto dir = absPath(getCommandBaseDir());
state->evalFile(lookupFileArg(*state, *file, &dir), *vFile); state->evalFile(lookupFileArg(*state, *file, &dir), *vFile);
} } else {
else {
Path dir = absPath(getCommandBaseDir()); Path dir = absPath(getCommandBaseDir());
auto e = state->parseExprFromString(*expr, state->rootPath(dir)); auto e = state->parseExprFromString(*expr, state->rootPath(dir));
state->eval(e, *vFile); state->eval(e, *vFile);
@ -515,9 +506,8 @@ Installables SourceExprCommand::parseInstallables(
for (auto & s : ss) { for (auto & s : ss) {
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(s); auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(s);
result.push_back( result.push_back(
make_ref<InstallableAttrPath>( make_ref<InstallableAttrPath>(InstallableAttrPath::parse(
InstallableAttrPath::parse( state, *this, vFile, std::move(prefix), std::move(extendedOutputsSpec))));
state, *this, vFile, std::move(prefix), std::move(extendedOutputsSpec))));
} }
} else { } else {
@ -532,8 +522,9 @@ Installables SourceExprCommand::parseInstallables(
if (prefix.find('/') != std::string::npos) { if (prefix.find('/') != std::string::npos) {
try { try {
result.push_back(make_ref<InstallableDerivedPath>( result.push_back(
InstallableDerivedPath::parse(store, prefix, extendedOutputsSpec.raw))); make_ref<InstallableDerivedPath>(
InstallableDerivedPath::parse(store, prefix, extendedOutputsSpec.raw)));
continue; continue;
} catch (BadStorePath &) { } catch (BadStorePath &) {
} catch (...) { } catch (...) {
@ -543,9 +534,10 @@ Installables SourceExprCommand::parseInstallables(
} }
try { try {
auto [flakeRef, fragment] = parseFlakeRefWithFragment( auto [flakeRef, fragment] =
fetchSettings, std::string { prefix }, absPath(getCommandBaseDir())); parseFlakeRefWithFragment(fetchSettings, std::string{prefix}, absPath(getCommandBaseDir()));
result.push_back(make_ref<InstallableFlake>( result.push_back(
make_ref<InstallableFlake>(
this, this,
getEvalState(), getEvalState(),
std::move(flakeRef), std::move(flakeRef),
@ -566,8 +558,7 @@ Installables SourceExprCommand::parseInstallables(
return result; return result;
} }
ref<Installable> SourceExprCommand::parseInstallable( ref<Installable> SourceExprCommand::parseInstallable(ref<Store> store, const std::string & installable)
ref<Store> store, const std::string & installable)
{ {
auto installables = parseInstallables(store, {installable}); auto installables = parseInstallables(store, {installable});
assert(installables.size() == 1); assert(installables.size() == 1);
@ -578,20 +569,18 @@ static SingleBuiltPath getBuiltPath(ref<Store> evalStore, ref<Store> store, cons
{ {
return std::visit( return std::visit(
overloaded{ overloaded{
[&](const SingleDerivedPath::Opaque & bo) -> SingleBuiltPath { [&](const SingleDerivedPath::Opaque & bo) -> SingleBuiltPath { return SingleBuiltPath::Opaque{bo.path}; },
return SingleBuiltPath::Opaque { bo.path };
},
[&](const SingleDerivedPath::Built & bfd) -> SingleBuiltPath { [&](const SingleDerivedPath::Built & bfd) -> SingleBuiltPath {
auto drvPath = getBuiltPath(evalStore, store, *bfd.drvPath); auto drvPath = getBuiltPath(evalStore, store, *bfd.drvPath);
// Resolving this instead of `bfd` will yield the same result, but avoid duplicative work. // Resolving this instead of `bfd` will yield the same result, but avoid duplicative work.
SingleDerivedPath::Built truncatedBfd { SingleDerivedPath::Built truncatedBfd{
.drvPath = makeConstantStorePathRef(drvPath.outPath()), .drvPath = makeConstantStorePathRef(drvPath.outPath()),
.output = bfd.output, .output = bfd.output,
}; };
auto outputPath = resolveDerivedPath(*store, truncatedBfd, &*evalStore); auto outputPath = resolveDerivedPath(*store, truncatedBfd, &*evalStore);
return SingleBuiltPath::Built { return SingleBuiltPath::Built{
.drvPath = make_ref<SingleBuiltPath>(std::move(drvPath)), .drvPath = make_ref<SingleBuiltPath>(std::move(drvPath)),
.output = { bfd.output, outputPath }, .output = {bfd.output, outputPath},
}; };
}, },
}, },
@ -599,11 +588,7 @@ static SingleBuiltPath getBuiltPath(ref<Store> evalStore, ref<Store> store, cons
} }
std::vector<BuiltPathWithResult> Installable::build( std::vector<BuiltPathWithResult> Installable::build(
ref<Store> evalStore, ref<Store> evalStore, ref<Store> store, Realise mode, const Installables & installables, BuildMode bMode)
ref<Store> store,
Realise mode,
const Installables & installables,
BuildMode bMode)
{ {
std::vector<BuiltPathWithResult> res; std::vector<BuiltPathWithResult> res;
for (auto & [_, builtPathWithResult] : build2(evalStore, store, mode, installables, bMode)) for (auto & [_, builtPathWithResult] : build2(evalStore, store, mode, installables, bMode))
@ -611,9 +596,7 @@ std::vector<BuiltPathWithResult> Installable::build(
return res; return res;
} }
static void throwBuildErrors( static void throwBuildErrors(std::vector<KeyedBuildResult> & buildResults, const Store & store)
std::vector<KeyedBuildResult> & buildResults,
const Store & store)
{ {
std::vector<KeyedBuildResult> failed; std::vector<KeyedBuildResult> failed;
for (auto & buildResult : buildResults) { for (auto & buildResult : buildResults) {
@ -630,10 +613,11 @@ static void throwBuildErrors(
StringSet failedPaths; StringSet failedPaths;
for (; failedResult != failed.end(); failedResult++) { for (; failedResult != failed.end(); failedResult++) {
if (!failedResult->errorMsg.empty()) { if (!failedResult->errorMsg.empty()) {
logError(ErrorInfo{ logError(
.level = lvlError, ErrorInfo{
.msg = failedResult->errorMsg, .level = lvlError,
}); .msg = failedResult->errorMsg,
});
} }
failedPaths.insert(failedResult->path.to_string(store)); failedPaths.insert(failedResult->path.to_string(store));
} }
@ -643,11 +627,7 @@ static void throwBuildErrors(
} }
std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> Installable::build2( std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> Installable::build2(
ref<Store> evalStore, ref<Store> evalStore, ref<Store> store, Realise mode, const Installables & installables, BuildMode bMode)
ref<Store> store,
Realise mode,
const Installables & installables,
BuildMode bMode)
{ {
if (mode == Realise::Nothing) if (mode == Realise::Nothing)
settings.readOnlyMode = true; settings.readOnlyMode = true;
@ -678,22 +658,25 @@ std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> Installable::build
for (auto & path : pathsToBuild) { for (auto & path : pathsToBuild) {
for (auto & aux : backmap[path]) { for (auto & aux : backmap[path]) {
std::visit(overloaded { std::visit(
[&](const DerivedPath::Built & bfd) { overloaded{
auto outputs = resolveDerivedPath(*store, bfd, &*evalStore); [&](const DerivedPath::Built & bfd) {
res.push_back({aux.installable, { auto outputs = resolveDerivedPath(*store, bfd, &*evalStore);
.path = BuiltPath::Built { res.push_back(
.drvPath = make_ref<SingleBuiltPath>(getBuiltPath(evalStore, store, *bfd.drvPath)), {aux.installable,
.outputs = outputs, {.path =
}, BuiltPath::Built{
.info = aux.info}}); .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) { path.raw());
res.push_back({aux.installable, {
.path = BuiltPath::Opaque { bo.path },
.info = aux.info}});
},
}, path.raw());
} }
} }
@ -707,26 +690,30 @@ std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> Installable::build
throwBuildErrors(buildResults, *store); throwBuildErrors(buildResults, *store);
for (auto & buildResult : buildResults) { for (auto & buildResult : buildResults) {
for (auto & aux : backmap[buildResult.path]) { for (auto & aux : backmap[buildResult.path]) {
std::visit(overloaded { std::visit(
[&](const DerivedPath::Built & bfd) { overloaded{
std::map<std::string, StorePath> outputs; [&](const DerivedPath::Built & bfd) {
for (auto & [outputName, realisation] : buildResult.builtOutputs) std::map<std::string, StorePath> outputs;
outputs.emplace(outputName, realisation.outPath); for (auto & [outputName, realisation] : buildResult.builtOutputs)
res.push_back({aux.installable, { outputs.emplace(outputName, realisation.outPath);
.path = BuiltPath::Built { res.push_back(
.drvPath = make_ref<SingleBuiltPath>(getBuiltPath(evalStore, store, *bfd.drvPath)), {aux.installable,
.outputs = outputs, {.path =
}, BuiltPath::Built{
.info = aux.info, .drvPath =
.result = buildResult}}); 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) { buildResult.path.raw());
res.push_back({aux.installable, {
.path = BuiltPath::Opaque { bo.path },
.info = aux.info,
.result = buildResult}});
},
}, buildResult.path.raw());
} }
} }
@ -741,11 +728,7 @@ std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> Installable::build
} }
BuiltPaths Installable::toBuiltPaths( BuiltPaths Installable::toBuiltPaths(
ref<Store> evalStore, ref<Store> evalStore, ref<Store> store, Realise mode, OperateOn operateOn, const Installables & installables)
ref<Store> store,
Realise mode,
OperateOn operateOn,
const Installables & installables)
{ {
if (operateOn == OperateOn::Output) { if (operateOn == OperateOn::Output) {
BuiltPaths res; BuiltPaths res;
@ -764,10 +747,7 @@ BuiltPaths Installable::toBuiltPaths(
} }
StorePathSet Installable::toStorePathSet( StorePathSet Installable::toStorePathSet(
ref<Store> evalStore, ref<Store> evalStore, ref<Store> store, Realise mode, OperateOn operateOn, const Installables & installables)
ref<Store> store,
Realise mode, OperateOn operateOn,
const Installables & installables)
{ {
StorePathSet outPaths; StorePathSet outPaths;
for (auto & path : toBuiltPaths(evalStore, store, mode, operateOn, installables)) { for (auto & path : toBuiltPaths(evalStore, store, mode, operateOn, installables)) {
@ -778,10 +758,7 @@ StorePathSet Installable::toStorePathSet(
} }
StorePaths Installable::toStorePaths( StorePaths Installable::toStorePaths(
ref<Store> evalStore, ref<Store> evalStore, ref<Store> store, Realise mode, OperateOn operateOn, const Installables & installables)
ref<Store> store,
Realise mode, OperateOn operateOn,
const Installables & installables)
{ {
StorePaths outPaths; StorePaths outPaths;
for (auto & path : toBuiltPaths(evalStore, store, mode, operateOn, installables)) { for (auto & path : toBuiltPaths(evalStore, store, mode, operateOn, installables)) {
@ -792,10 +769,7 @@ StorePaths Installable::toStorePaths(
} }
StorePath Installable::toStorePath( StorePath Installable::toStorePath(
ref<Store> evalStore, ref<Store> evalStore, ref<Store> store, Realise mode, OperateOn operateOn, ref<Installable> installable)
ref<Store> store,
Realise mode, OperateOn operateOn,
ref<Installable> installable)
{ {
auto paths = toStorePathSet(evalStore, store, mode, operateOn, {installable}); auto paths = toStorePathSet(evalStore, store, mode, operateOn, {installable});
@ -805,28 +779,23 @@ StorePath Installable::toStorePath(
return *paths.begin(); return *paths.begin();
} }
StorePathSet Installable::toDerivations( StorePathSet Installable::toDerivations(ref<Store> store, const Installables & installables, bool useDeriver)
ref<Store> store,
const Installables & installables,
bool useDeriver)
{ {
StorePathSet drvPaths; StorePathSet drvPaths;
for (const auto & i : installables) for (const auto & i : installables)
for (const auto & b : i->toDerivedPaths()) for (const auto & b : i->toDerivedPaths())
std::visit(overloaded { std::visit(
[&](const DerivedPath::Opaque & bo) { overloaded{
drvPaths.insert( [&](const DerivedPath::Opaque & bo) {
bo.path.isDerivation() drvPaths.insert(
? bo.path bo.path.isDerivation() ? bo.path
: useDeriver : useDeriver ? getDeriver(store, *i, bo.path)
? getDeriver(store, *i, bo.path) : throw Error("argument '%s' did not evaluate to a derivation", i->what()));
: 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) { b.path.raw());
drvPaths.insert(resolveDerivedPath(*store, *bfd.drvPath));
},
}, b.path.raw());
return drvPaths; return drvPaths;
} }
@ -861,10 +830,7 @@ std::vector<FlakeRef> RawInstallablesCommand::getFlakeRefsForCompletion()
std::vector<FlakeRef> res; std::vector<FlakeRef> res;
res.reserve(rawInstallables.size()); res.reserve(rawInstallables.size());
for (const auto & i : rawInstallables) for (const auto & i : rawInstallables)
res.push_back(parseFlakeRefWithFragment( res.push_back(parseFlakeRefWithFragment(fetchSettings, expandTilde(i), absPath(getCommandBaseDir())).first);
fetchSettings,
expandTilde(i),
absPath(getCommandBaseDir())).first);
return res; return res;
} }
@ -883,12 +849,7 @@ void RawInstallablesCommand::run(ref<Store> store)
std::vector<FlakeRef> InstallableCommand::getFlakeRefsForCompletion() std::vector<FlakeRef> InstallableCommand::getFlakeRefsForCompletion()
{ {
return { return {parseFlakeRefWithFragment(fetchSettings, expandTilde(_installable), absPath(getCommandBaseDir())).first};
parseFlakeRefWithFragment(
fetchSettings,
expandTilde(_installable),
absPath(getCommandBaseDir())).first
};
} }
void InstallablesCommand::run(ref<Store> store, std::vector<std::string> && rawInstallables) void InstallablesCommand::run(ref<Store> store, std::vector<std::string> && rawInstallables)
@ -928,4 +889,4 @@ BuiltPaths toBuiltPaths(const std::vector<BuiltPathWithResult> & builtPathsWithR
return res; return res;
} }
} } // namespace nix

View file

@ -18,25 +18,24 @@ static std::string doRenderMarkdownToTerminal(std::string_view markdown)
{ {
int windowWidth = getWindowSize().second; int windowWidth = getWindowSize().second;
#if HAVE_LOWDOWN_1_4 # if HAVE_LOWDOWN_1_4
struct lowdown_opts_term opts_term { struct lowdown_opts_term opts_term{
.cols = (size_t) std::max(windowWidth - 5, 60), .cols = (size_t) std::max(windowWidth - 5, 60),
.hmargin = 0, .hmargin = 0,
.vmargin = 0, .vmargin = 0,
}; };
#endif # endif
struct lowdown_opts opts struct lowdown_opts opts{
{
.type = LOWDOWN_TERM, .type = LOWDOWN_TERM,
#if HAVE_LOWDOWN_1_4 # if HAVE_LOWDOWN_1_4
.term = opts_term, .term = opts_term,
#endif # endif
.maxdepth = 20, .maxdepth = 20,
#if !HAVE_LOWDOWN_1_4 # if !HAVE_LOWDOWN_1_4
.cols = (size_t) std::max(windowWidth - 5, 60), .cols = (size_t) std::max(windowWidth - 5, 60),
.hmargin = 0, .hmargin = 0,
.vmargin = 0, .vmargin = 0,
#endif # endif
.feat = LOWDOWN_COMMONMARK | LOWDOWN_FENCED | LOWDOWN_DEFLIST | LOWDOWN_TABLES, .feat = LOWDOWN_COMMONMARK | LOWDOWN_FENCED | LOWDOWN_DEFLIST | LOWDOWN_TABLES,
.oflags = LOWDOWN_TERM_NOLINK, .oflags = LOWDOWN_TERM_NOLINK,
}; };

View file

@ -1,7 +1,6 @@
#include "nix/cmd/misc-store-flags.hh" #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) 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) Args::Flag hashFormatWithDefault(std::string && longName, HashFormat * hf)
{ {
assert(*hf == nix::HashFormat::SRI); assert(*hf == nix::HashFormat::SRI);
return Args::Flag { return Args::Flag{
.longName = std::move(longName), .longName = std::move(longName),
.description = "Hash format (`base16`, `nix32`, `base64`, `sri`). Default: `sri`.", .description = "Hash format (`base16`, `nix32`, `base64`, `sri`). Default: `sri`.",
.labels = {"hash-format"}, .labels = {"hash-format"},
.handler = {[hf](std::string s) { .handler = {[hf](std::string s) { *hf = parseHashFormat(s); }},
*hf = parseHashFormat(s); .completer = hashFormatCompleter,
}},
.completer = hashFormatCompleter,
}; };
} }
Args::Flag hashFormatOpt(std::string && longName, std::optional<HashFormat> * ohf) Args::Flag hashFormatOpt(std::string && longName, std::optional<HashFormat> * ohf)
{ {
return Args::Flag { return Args::Flag{
.longName = std::move(longName), .longName = std::move(longName),
.description = "Hash format (`base16`, `nix32`, `base64`, `sri`).", .description = "Hash format (`base16`, `nix32`, `base64`, `sri`).",
.labels = {"hash-format"}, .labels = {"hash-format"},
.handler = {[ohf](std::string s) { .handler = {[ohf](std::string s) { *ohf = std::optional<HashFormat>{parseHashFormat(s)}; }},
*ohf = std::optional<HashFormat>{parseHashFormat(s)}; .completer = hashFormatCompleter,
}},
.completer = hashFormatCompleter,
}; };
} }
@ -48,34 +43,31 @@ static void hashAlgoCompleter(AddCompletions & completions, size_t index, std::s
Args::Flag hashAlgo(std::string && longName, HashAlgorithm * ha) Args::Flag hashAlgo(std::string && longName, HashAlgorithm * ha)
{ {
return Args::Flag { return Args::Flag{
.longName = std::move(longName), .longName = std::move(longName),
.description = "Hash algorithm (`blake3`, `md5`, `sha1`, `sha256`, or `sha512`).", .description = "Hash algorithm (`blake3`, `md5`, `sha1`, `sha256`, or `sha512`).",
.labels = {"hash-algo"}, .labels = {"hash-algo"},
.handler = {[ha](std::string s) { .handler = {[ha](std::string s) { *ha = parseHashAlgo(s); }},
*ha = parseHashAlgo(s); .completer = hashAlgoCompleter,
}},
.completer = hashAlgoCompleter,
}; };
} }
Args::Flag hashAlgoOpt(std::string && longName, std::optional<HashAlgorithm> * oha) Args::Flag hashAlgoOpt(std::string && longName, std::optional<HashAlgorithm> * oha)
{ {
return Args::Flag { return Args::Flag{
.longName = std::move(longName), .longName = std::move(longName),
.description = "Hash algorithm (`blake3`, `md5`, `sha1`, `sha256`, or `sha512`). Can be omitted for SRI hashes.", .description =
.labels = {"hash-algo"}, "Hash algorithm (`blake3`, `md5`, `sha1`, `sha256`, or `sha512`). Can be omitted for SRI hashes.",
.handler = {[oha](std::string s) { .labels = {"hash-algo"},
*oha = std::optional<HashAlgorithm>{parseHashAlgo(s)}; .handler = {[oha](std::string s) { *oha = std::optional<HashAlgorithm>{parseHashAlgo(s)}; }},
}}, .completer = hashAlgoCompleter,
.completer = hashAlgoCompleter,
}; };
} }
Args::Flag fileIngestionMethod(FileIngestionMethod * method) Args::Flag fileIngestionMethod(FileIngestionMethod * method)
{ {
return Args::Flag { return Args::Flag{
.longName = "mode", .longName = "mode",
// FIXME indentation carefully made for context, this is messed up. // FIXME indentation carefully made for context, this is messed up.
.description = R"( .description = R"(
How to compute the hash of the input. How to compute the hash of the input.
@ -92,16 +84,14 @@ Args::Flag fileIngestionMethod(FileIngestionMethod * method)
it to the hash function. it to the hash function.
)", )",
.labels = {"file-ingestion-method"}, .labels = {"file-ingestion-method"},
.handler = {[method](std::string s) { .handler = {[method](std::string s) { *method = parseFileIngestionMethod(s); }},
*method = parseFileIngestionMethod(s);
}},
}; };
} }
Args::Flag contentAddressMethod(ContentAddressMethod * method) Args::Flag contentAddressMethod(ContentAddressMethod * method)
{ {
return Args::Flag { return Args::Flag{
.longName = "mode", .longName = "mode",
// FIXME indentation carefully made for context, this is messed up. // FIXME indentation carefully made for context, this is messed up.
.description = R"( .description = R"(
How to compute the content-address of the store object. 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`. for regular usage prefer `nar` and `flat`.
)", )",
.labels = {"content-address-method"}, .labels = {"content-address-method"},
.handler = {[method](std::string s) { .handler = {[method](std::string s) { *method = ContentAddressMethod::parse(s); }},
*method = ContentAddressMethod::parse(s);
}},
}; };
} }
} } // namespace nix::flag

View file

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

View file

@ -3,8 +3,8 @@
#include <cstdio> #include <cstdio>
#if USE_READLINE #if USE_READLINE
#include <readline/history.h> # include <readline/history.h>
#include <readline/readline.h> # include <readline/readline.h>
#else #else
// editline < 1.15.2 don't wrap their API for C++ usage // editline < 1.15.2 don't wrap their API for C++ usage
// (added in https://github.com/troglobit/editline/commit/91398ceb3427b730995357e9d120539fb9bb7461). // (added in https://github.com/troglobit/editline/commit/91398ceb3427b730995357e9d120539fb9bb7461).
@ -12,7 +12,7 @@
// For compatibility with these versions, we wrap the API here // For compatibility with these versions, we wrap the API here
// (wrapping multiple times on newer versions is no problem). // (wrapping multiple times on newer versions is no problem).
extern "C" { extern "C" {
#include <editline.h> # include <editline.h>
} }
#endif #endif
@ -33,7 +33,7 @@ void sigintHandler(int signo)
{ {
g_signal_received = signo; g_signal_received = signo;
} }
}; }; // namespace
static detail::ReplCompleterMixin * curRepl; // ugly static detail::ReplCompleterMixin * curRepl; // ugly
@ -183,8 +183,7 @@ bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptT
// editline doesn't echo the input to the output when non-interactive, unlike readline // 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 // this results in a different behavior when running tests. The echoing is
// quite useful for reading the test output, so we add it here. // 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 #if !USE_READLINE
// This is probably not right for multi-line input, but we don't use that // This is probably not right for multi-line input, but we don't use that
// in the characterisation tests, so it's fine. // in the characterisation tests, so it's fine.
@ -205,4 +204,4 @@ ReadlineLikeInteracter::~ReadlineLikeInteracter()
write_history(historyFile.c_str()); write_history(historyFile.c_str());
} }
}; }; // namespace nix

View file

@ -54,10 +54,7 @@ enum class ProcessLineResult {
PromptAgain, PromptAgain,
}; };
struct NixRepl struct NixRepl : AbstractNixRepl, detail::ReplCompleterMixin, gc
: AbstractNixRepl
, detail::ReplCompleterMixin
, gc
{ {
size_t debugTraceIndex; size_t debugTraceIndex;
@ -79,8 +76,12 @@ struct NixRepl
std::unique_ptr<ReplInteracter> interacter; std::unique_ptr<ReplInteracter> interacter;
NixRepl(const LookupPath & lookupPath, nix::ref<Store> store,ref<EvalState> state, NixRepl(
std::function<AnnotatedValues()> getValues, RunNix * runNix); const LookupPath & lookupPath,
nix::ref<Store> store,
ref<EvalState> state,
std::function<AnnotatedValues()> getValues,
RunNix * runNix);
virtual ~NixRepl() = default; virtual ~NixRepl() = default;
ReplExitStatus mainLoop() override; ReplExitStatus mainLoop() override;
@ -101,20 +102,22 @@ struct NixRepl
void evalString(std::string s, Value & v); void evalString(std::string s, Value & v);
void loadDebugTraceEnv(DebugTrace & dt); void loadDebugTraceEnv(DebugTrace & dt);
void printValue(std::ostream & str, void printValue(std::ostream & str, Value & v, unsigned int maxDepth = std::numeric_limits<unsigned int>::max())
Value & v,
unsigned int maxDepth = std::numeric_limits<unsigned int>::max())
{ {
// Hide the progress bar during printing because it might interfere // Hide the progress bar during printing because it might interfere
auto suspension = logger->suspend(); auto suspension = logger->suspend();
::nix::printValue(*state, str, v, PrintOptions { ::nix::printValue(
.ansiColors = true, *state,
.force = true, str,
.derivationPaths = true, v,
.maxDepth = maxDepth, PrintOptions{
.prettyIndent = 2, .ansiColors = true,
.errors = ErrorPrintBehavior::ThrowTopLevel, .force = true,
}); .derivationPaths = true,
.maxDepth = maxDepth,
.prettyIndent = 2,
.errors = ErrorPrintBehavior::ThrowTopLevel,
});
} }
}; };
@ -122,13 +125,17 @@ std::string removeWhitespace(std::string s)
{ {
s = chomp(s); s = chomp(s);
size_t n = s.find_first_not_of(" \n\r\t"); 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; return s;
} }
NixRepl::NixRepl(
NixRepl::NixRepl(const LookupPath & lookupPath, nix::ref<Store> store, ref<EvalState> state, const LookupPath & lookupPath,
std::function<NixRepl::AnnotatedValues()> getValues, RunNix * runNix) nix::ref<Store> store,
ref<EvalState> state,
std::function<NixRepl::AnnotatedValues()> getValues,
RunNix * runNix)
: AbstractNixRepl(state) : AbstractNixRepl(state)
, debugTraceIndex(0) , debugTraceIndex(0)
, getValues(getValues) , getValues(getValues)
@ -184,7 +191,8 @@ ReplExitStatus NixRepl::mainLoop()
auto suspension = logger->suspend(); auto suspension = logger->suspend();
// When continuing input from previous lines, don't print a prompt, just align to the same // When continuing input from previous lines, don't print a prompt, just align to the same
// number of chars as the prompt. // 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. // Ctrl-D should exit the debugger.
state->debugStop = false; state->debugStop = false;
logger->cout(""); logger->cout("");
@ -196,14 +204,14 @@ ReplExitStatus NixRepl::mainLoop()
} }
try { try {
switch (processLine(input)) { switch (processLine(input)) {
case ProcessLineResult::Quit: case ProcessLineResult::Quit:
return ReplExitStatus::QuitAll; return ReplExitStatus::QuitAll;
case ProcessLineResult::Continue: case ProcessLineResult::Continue:
return ReplExitStatus::Continue; return ReplExitStatus::Continue;
case ProcessLineResult::PromptAgain: case ProcessLineResult::PromptAgain:
break; break;
default: default:
unreachable(); unreachable();
} }
} catch (ParseError & e) { } catch (ParseError & e) {
if (e.msg().find("unexpected end of file") != std::string::npos) { if (e.msg().find("unexpected end of file") != std::string::npos) {
@ -211,7 +219,7 @@ ReplExitStatus NixRepl::mainLoop()
// input without clearing the input so far. // input without clearing the input so far.
continue; continue;
} else { } else {
printMsg(lvlError, e.msg()); printMsg(lvlError, e.msg());
} }
} catch (EvalError & e) { } catch (EvalError & e) {
printMsg(lvlError, e.msg()); printMsg(lvlError, e.msg());
@ -260,7 +268,8 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
/* This is a variable name; look it up in the current scope. */ /* This is a variable name; look it up in the current scope. */
StringSet::iterator i = varNames.lower_bound(cur); StringSet::iterator i = varNames.lower_bound(cur);
while (i != varNames.end()) { while (i != varNames.end()) {
if (i->substr(0, cur.size()) != cur) break; if (i->substr(0, cur.size()) != cur)
break;
completions.insert(prev + *i); completions.insert(prev + *i);
i++; i++;
} }
@ -279,11 +288,15 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
Expr * e = parseString(expr); Expr * e = parseString(expr);
Value v; Value v;
e->eval(*state, *env, 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()) { for (auto & i : *v.attrs()) {
std::string_view name = state->symbols[i.name]; 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)); completions.insert(concatStrings(prev, expr, ".", name));
} }
@ -301,24 +314,23 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
return completions; return completions;
} }
// FIXME: DRY and match or use the parser // FIXME: DRY and match or use the parser
static bool isVarName(std::string_view s) static bool isVarName(std::string_view s)
{ {
if (s.size() == 0) return false; if (s.size() == 0)
return false;
char c = s[0]; 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) for (auto & i : s)
if (!((i >= 'a' && i <= 'z') || if (!((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || (i >= '0' && i <= '9') || i == '_' || i == '-'
(i >= 'A' && i <= 'Z') || || i == '\''))
(i >= '0' && i <= '9') ||
i == '_' || i == '-' || i == '\''))
return false; return false;
return true; return true;
} }
StorePath NixRepl::getDerivationPath(Value & v)
StorePath NixRepl::getDerivationPath(Value & v) { {
auto packageInfo = getDerivation(*state, v, false); auto packageInfo = getDerivation(*state, v, false);
if (!packageInfo) if (!packageInfo)
throw Error("expression does not evaluate to a derivation, so I can't build it"); throw Error("expression does not evaluate to a derivation, so I can't build it");
@ -357,52 +369,49 @@ ProcessLineResult NixRepl::processLine(std::string line)
if (line[0] == ':') { if (line[0] == ':') {
size_t p = line.find_first_of(" \n\r\t"); size_t p = line.find_first_of(" \n\r\t");
command = line.substr(0, p); 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 { } else {
arg = line; arg = line;
} }
if (command == ":?" || command == ":help") { if (command == ":?" || command == ":help") {
// FIXME: convert to Markdown, include in the 'nix repl' manpage. // FIXME: convert to Markdown, include in the 'nix repl' manpage.
std::cout std::cout << "The following commands are available:\n"
<< "The following commands are available:\n" << "\n"
<< "\n" << " <expr> Evaluate and print expression\n"
<< " <expr> Evaluate and print expression\n" << " <x> = <expr> Bind expression to variable\n"
<< " <x> = <expr> Bind expression to variable\n" << " :a, :add <expr> Add attributes from resulting set to scope\n"
<< " :a, :add <expr> Add attributes from resulting set to scope\n" << " :b <expr> Build a derivation\n"
<< " :b <expr> Build a derivation\n" << " :bl <expr> Build a derivation, creating GC roots in the\n"
<< " :bl <expr> Build a derivation, creating GC roots in the\n" << " working directory\n"
<< " working directory\n" << " :e, :edit <expr> Open package or function in $EDITOR\n"
<< " :e, :edit <expr> Open package or function in $EDITOR\n" << " :i <expr> Build derivation, then install result into\n"
<< " :i <expr> Build derivation, then install result into\n" << " current profile\n"
<< " current profile\n" << " :l, :load <path> Load Nix expression and add it to scope\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"
<< " :lf, :load-flake <ref> Load Nix flake and add it to scope\n" << " :p, :print <expr> Evaluate and print expression recursively\n"
<< " :p, :print <expr> Evaluate and print expression recursively\n" << " Strings are printed directly, without escaping.\n"
<< " Strings are printed directly, without escaping.\n" << " :q, :quit Exit nix-repl\n"
<< " :q, :quit Exit nix-repl\n" << " :r, :reload Reload all files\n"
<< " :r, :reload Reload all files\n" << " :sh <expr> Build dependencies of derivation, then start\n"
<< " :sh <expr> Build dependencies of derivation, then start\n" << " nix-shell\n"
<< " nix-shell\n" << " :t <expr> Describe result of evaluation\n"
<< " :t <expr> Describe result of evaluation\n" << " :u <expr> Build derivation, then start nix-shell\n"
<< " :u <expr> Build derivation, then start nix-shell\n" << " :doc <expr> Show documentation of a builtin function\n"
<< " :doc <expr> Show documentation of a builtin function\n" << " :log <expr> Show logs for a derivation\n"
<< " :log <expr> Show logs for a derivation\n" << " :te, :trace-enable [bool] Enable, disable or toggle showing traces for\n"
<< " :te, :trace-enable [bool] Enable, disable or toggle showing traces for\n" << " errors\n"
<< " errors\n" << " :?, :help Brings up this help menu\n";
<< " :?, :help Brings up this help menu\n"
;
if (state->debugRepl) { if (state->debugRepl) {
std::cout std::cout << "\n"
<< "\n" << " Debug mode commands\n"
<< " Debug mode commands\n" << " :env Show env stack\n"
<< " :env Show env stack\n" << " :bt, :backtrace Show trace stack\n"
<< " :bt, :backtrace Show trace stack\n" << " :st Show current trace\n"
<< " :st Show current trace\n" << " :st <idx> Change to another trace in the stack\n"
<< " :st <idx> Change to another trace in the stack\n" << " :c, :continue Go until end of program, exception, or builtins.break\n"
<< " :c, :continue Go until end of program, exception, or builtins.break\n" << " :s, :step Go one step\n";
<< " :s, :step Go one step\n"
;
} }
} }
@ -427,17 +436,18 @@ ProcessLineResult NixRepl::processLine(std::string line)
try { try {
// change the DebugTrace index. // change the DebugTrace index.
debugTraceIndex = stoi(arg); debugTraceIndex = stoi(arg);
} catch (...) { } } catch (...) {
}
for (const auto & [idx, i] : enumerate(state->debugTraces)) { for (const auto & [idx, i] : enumerate(state->debugTraces)) {
if (idx == debugTraceIndex) { if (idx == debugTraceIndex) {
std::cout << "\n" << ANSI_BLUE << idx << ANSI_NORMAL << ": "; std::cout << "\n" << ANSI_BLUE << idx << ANSI_NORMAL << ": ";
showDebugTrace(std::cout, state->positions, i); showDebugTrace(std::cout, state->positions, i);
std::cout << std::endl; std::cout << std::endl;
printEnvBindings(*state, i.expr, i.env); printEnvBindings(*state, i.expr, i.env);
loadDebugTraceEnv(i); loadDebugTraceEnv(i);
break; break;
} }
} }
} }
@ -477,7 +487,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
Value v; Value v;
evalString(arg, 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) { if (v.type() == nPath || v.type() == nString) {
NixStringContext context; NixStringContext context;
auto path = state->coerceToPath(noPos, v, context, "while evaluating the filename to edit"); auto path = state->coerceToPath(noPos, v, context, "while evaluating the filename to edit");
@ -501,7 +511,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
// runProgram redirects stdout to a StringSink, // runProgram redirects stdout to a StringSink,
// using runProgram2 to allow editors to display their UI // 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 // Reload right after exiting the editor
state->resetFileCache(); state->resetFileCache();
@ -532,9 +542,9 @@ ProcessLineResult NixRepl::processLine(std::string line)
if (command == ":b" || command == ":bl") { if (command == ":b" || command == ":bl") {
state->store->buildPaths({ state->store->buildPaths({
DerivedPath::Built { DerivedPath::Built{
.drvPath = makeConstantStorePathRef(drvPath), .drvPath = makeConstantStorePathRef(drvPath),
.outputs = OutputsSpec::All { }, .outputs = OutputsSpec::All{},
}, },
}); });
auto drv = state->store->readDerivation(drvPath); auto drv = state->store->readDerivation(drvPath);
@ -553,9 +563,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
runNix("nix-env", {"-i", drvPathRaw}); runNix("nix-env", {"-i", drvPathRaw});
} else if (command == ":log") { } else if (command == ":log") {
settings.readOnlyMode = true; settings.readOnlyMode = true;
Finally roModeReset([&]() { Finally roModeReset([&]() { settings.readOnlyMode = false; });
settings.readOnlyMode = false;
});
auto subs = getDefaultSubstituters(); auto subs = getDefaultSubstituters();
subs.push_front(state->store); subs.push_front(state->store);
@ -578,7 +586,8 @@ ProcessLineResult NixRepl::processLine(std::string line)
break; 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 { } else {
runNix("nix-shell", {drvPathRaw}); runNix("nix-shell", {drvPathRaw});
} }
@ -641,9 +650,8 @@ ProcessLineResult NixRepl::processLine(std::string line)
for (auto & arg : args) for (auto & arg : args)
arg = "*" + arg + "*"; arg = "*" + arg + "*";
markdown += markdown += "**Synopsis:** `builtins." + (std::string) (*doc->name) + "` " + concatStringsSep(" ", args)
"**Synopsis:** `builtins." + (std::string) (*doc->name) + "` " + "\n\n";
+ concatStringsSep(" ", args) + "\n\n";
} }
markdown += stripIndentation(doc->doc); markdown += stripIndentation(doc->doc);
@ -684,11 +692,8 @@ ProcessLineResult NixRepl::processLine(std::string line)
else { else {
size_t p = line.find('='); size_t p = line.find('=');
std::string name; std::string name;
if (p != std::string::npos && if (p != std::string::npos && p < line.size() && line[p + 1] != '='
p < line.size() && && isVarName(name = removeWhitespace(line.substr(0, p)))) {
line[p + 1] != '=' &&
isVarName(name = removeWhitespace(line.substr(0, p))))
{
Expr * e = parseString(line.substr(p + 1)); Expr * e = parseString(line.substr(p + 1));
Value & v(*state->allocValue()); Value & v(*state->allocValue());
v.mkThunk(env, e); v.mkThunk(env, e);
@ -736,9 +741,13 @@ void NixRepl::loadFlake(const std::string & flakeRefS)
Value v; Value v;
flake::callFlake(*state, flake::callFlake(
flake::lockFlake(flakeSettings, *state, flakeRef, *state,
flake::LockFlags { flake::lockFlake(
flakeSettings,
*state,
flakeRef,
flake::LockFlags{
.updateLockFile = false, .updateLockFile = false,
.useRegistries = !evalSettings.pureEval, .useRegistries = !evalSettings.pureEval,
.allowUnlocked = !evalSettings.pureEval, .allowUnlocked = !evalSettings.pureEval,
@ -747,7 +756,6 @@ void NixRepl::loadFlake(const std::string & flakeRefS)
addAttrsToScope(v); addAttrsToScope(v);
} }
void NixRepl::initEnv() void NixRepl::initEnv()
{ {
env = &state->allocEnv(envSize); env = &state->allocEnv(envSize);
@ -760,7 +768,6 @@ void NixRepl::initEnv()
varNames.emplace(state->symbols[i.first]); varNames.emplace(state->symbols[i.first]);
} }
void NixRepl::reloadFilesAndFlakes() void NixRepl::reloadFilesAndFlakes()
{ {
initEnv(); initEnv();
@ -769,7 +776,6 @@ void NixRepl::reloadFilesAndFlakes()
loadFlakes(); loadFlakes();
} }
void NixRepl::loadFiles() void NixRepl::loadFiles()
{ {
Strings old = loadedFiles; Strings old = loadedFiles;
@ -786,7 +792,6 @@ void NixRepl::loadFiles()
} }
} }
void NixRepl::loadFlakes() void NixRepl::loadFlakes()
{ {
Strings old = loadedFlakes; Strings old = loadedFlakes;
@ -798,10 +803,12 @@ void NixRepl::loadFlakes()
} }
} }
void NixRepl::addAttrsToScope(Value & attrs) 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) if (displ + attrs.attrs()->size() >= envSize)
throw Error("environment full; cannot add more variables"); throw Error("environment full; cannot add more variables");
@ -815,7 +822,6 @@ void NixRepl::addAttrsToScope(Value & attrs)
notice("Added %1% variables.", attrs.attrs()->size()); notice("Added %1% variables.", attrs.attrs()->size());
} }
void NixRepl::addVarToScope(const Symbol name, Value & v) void NixRepl::addVarToScope(const Symbol name, Value & v)
{ {
if (displ >= envSize) if (displ >= envSize)
@ -828,13 +834,11 @@ void NixRepl::addVarToScope(const Symbol name, Value & v)
varNames.emplace(state->symbols[name]); varNames.emplace(state->symbols[name]);
} }
Expr * NixRepl::parseString(std::string s) Expr * NixRepl::parseString(std::string s)
{ {
return state->parseExprFromString(std::move(s), state->rootPath("."), staticEnv); return state->parseExprFromString(std::move(s), state->rootPath("."), staticEnv);
} }
void NixRepl::evalString(std::string s, Value & v) void NixRepl::evalString(std::string s, Value & v)
{ {
Expr * e = parseString(s); Expr * e = parseString(s);
@ -842,46 +846,39 @@ void NixRepl::evalString(std::string s, Value & v)
state->forceValue(v, v.determinePos(noPos)); state->forceValue(v, v.determinePos(noPos));
} }
void NixRepl::runNix(Path program, const Strings & args, const std::optional<std::string> & input) void NixRepl::runNix(Path program, const Strings & args, const std::optional<std::string> & input)
{ {
if (runNixPtr) if (runNixPtr)
(*runNixPtr)(program, args, input); (*runNixPtr)(program, args, input);
else 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( std::unique_ptr<AbstractNixRepl> AbstractNixRepl::create(
const LookupPath & lookupPath, nix::ref<Store> store, ref<EvalState> state, const LookupPath & lookupPath,
std::function<AnnotatedValues()> getValues, RunNix * runNix) nix::ref<Store> store,
ref<EvalState> state,
std::function<AnnotatedValues()> getValues,
RunNix * runNix)
{ {
return std::make_unique<NixRepl>( return std::make_unique<NixRepl>(lookupPath, std::move(store), state, getValues, runNix);
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; NixRepl::AnnotatedValues values;
return values; return values;
}; };
LookupPath lookupPath = {}; LookupPath lookupPath = {};
auto repl = std::make_unique<NixRepl>( auto repl = std::make_unique<NixRepl>(
lookupPath, lookupPath,
openStore(), openStore(),
evalState, evalState,
getValues, getValues,
/*runNix=*/nullptr /*runNix=*/nullptr);
);
repl->initEnv(); repl->initEnv();
@ -892,4 +889,4 @@ ReplExitStatus AbstractNixRepl::runSimple(
return repl->mainLoop(); return repl->mainLoop();
} }
} } // namespace nix

View file

@ -31,13 +31,11 @@
* @param init Function that takes a T* and returns the initializer for T * @param init Function that takes a T* and returns the initializer for T
* @return Pointer to allocated and initialized object * @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) static T * unsafe_new_with_self(F && init)
{ {
// Allocate // Allocate
void * p = ::operator new( void * p = ::operator new(sizeof(T), static_cast<std::align_val_t>(alignof(T)));
sizeof(T),
static_cast<std::align_val_t>(alignof(T)));
// Initialize with placement new // Initialize with placement new
return new (p) T(init(static_cast<T *>(p))); return new (p) T(init(static_cast<T *>(p)));
} }
@ -86,12 +84,13 @@ nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, n
NIXC_CATCH_ERRS 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) if (context)
context->last_err_code = NIX_OK; context->last_err_code = NIX_OK;
try { 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); state->state.forceValue(value->value, nix::noPos);
} }
NIXC_CATCH_ERRS NIXC_CATCH_ERRS
@ -152,7 +151,8 @@ nix_err nix_eval_state_builder_load(nix_c_context * context, nix_eval_state_buil
NIXC_CATCH_ERRS 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) if (context)
context->last_err_code = NIX_OK; context->last_err_code = NIX_OK;
@ -175,11 +175,7 @@ EvalState * nix_eval_state_build(nix_c_context * context, nix_eval_state_builder
return EvalState{ return EvalState{
.fetchSettings = std::move(builder->fetchSettings), .fetchSettings = std::move(builder->fetchSettings),
.settings = std::move(builder->settings), .settings = std::move(builder->settings),
.state = nix::EvalState( .state = nix::EvalState(builder->lookupPath, builder->store, self->fetchSettings, self->settings),
builder->lookupPath,
builder->store,
self->fetchSettings,
self->settings),
}; };
}); });
} }
@ -195,11 +191,10 @@ EvalState * nix_state_create(nix_c_context * context, const char ** lookupPath_c
if (nix_eval_state_builder_load(context, builder) != NIX_OK) if (nix_eval_state_builder_load(context, builder) != NIX_OK)
return nullptr; return nullptr;
if (nix_eval_state_builder_set_lookup_path(context, builder, lookupPath_c) if (nix_eval_state_builder_set_lookup_path(context, builder, lookupPath_c) != NIX_OK)
!= NIX_OK)
return nullptr; return nullptr;
auto *state = nix_eval_state_build(context, builder); auto * state = nix_eval_state_build(context, builder);
nix_eval_state_builder_free(builder); nix_eval_state_builder_free(builder);
return state; return state;
} }
@ -265,20 +260,23 @@ nix_err nix_gc_incref(nix_c_context * context, const void *)
context->last_err_code = NIX_OK; context->last_err_code = NIX_OK;
return NIX_OK; return NIX_OK;
} }
nix_err nix_gc_decref(nix_c_context * context, const void *) nix_err nix_gc_decref(nix_c_context * context, const void *)
{ {
if (context) if (context)
context->last_err_code = NIX_OK; context->last_err_code = NIX_OK;
return NIX_OK; return NIX_OK;
} }
void nix_gc_now() {} void nix_gc_now() {}
#endif #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); 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); return nix_gc_decref(context, (const void *) x);
} }

View file

@ -48,11 +48,13 @@ class NixCExternalValue : public nix::ExternalValueBase
public: public:
NixCExternalValue(NixCExternalValueDesc & desc, void * v) NixCExternalValue(NixCExternalValueDesc & desc, void * v)
: desc(desc) : desc(desc)
, v(v){}; , v(v) {};
void * get_ptr() void * get_ptr()
{ {
return v; return v;
} }
/** /**
* Print out the value * Print out the value
*/ */
@ -155,11 +157,17 @@ public:
} }
nix_string_context ctx{context}; nix_string_context ctx{context};
desc.printValueAsXML( desc.printValueAsXML(
v, (EvalState *) &state, strict, location, &doc, &ctx, &drvsSeen, v,
(EvalState *) &state,
strict,
location,
&doc,
&ctx,
&drvsSeen,
*reinterpret_cast<const uint32_t *>(&pos)); *reinterpret_cast<const uint32_t *>(&pos));
} }
virtual ~NixCExternalValue() override{}; virtual ~NixCExternalValue() override {};
}; };
ExternalValue * nix_create_external_value(nix_c_context * context, NixCExternalValueDesc * desc, void * v) ExternalValue * nix_create_external_value(nix_c_context * context, NixCExternalValueDesc * desc, void * v)

View file

@ -16,141 +16,159 @@
#include "nix/store/tests/libstore.hh" #include "nix/store/tests/libstore.hh"
namespace nix { namespace nix {
class LibExprTest : public LibStoreTest { class LibExprTest : public LibStoreTest
public: {
static void SetUpTestSuite() { public:
LibStoreTest::SetUpTestSuite(); static void SetUpTestSuite()
initGC(); {
} 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;
} }
MATCHER(IsList, "") { protected:
return arg.type() == nList; LibExprTest()
: LibStoreTest()
, state({}, store, fetchSettings, evalSettings, nullptr)
{
evalSettings.nixPath = {};
} }
MATCHER(IsString, "") { Value eval(std::string input, bool forceValue = true)
return arg.type() == nString; {
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, "") { Value * maybeThunk(std::string input, bool forceValue = true)
return arg.type() == nNull; {
Expr * e = state.parseExprFromString(input, state.rootPath(CanonPath::root));
assert(e);
return e->maybeThunk(state, state.baseEnv);
} }
MATCHER(IsThunk, "") { Symbol createSymbol(const char * value)
return arg.type() == nThunk; {
return state.symbols.create(value);
} }
MATCHER(IsAttrs, "") { bool readOnlyMode = true;
return arg.type() == nAttrs; fetchers::Settings fetchSettings{};
} EvalSettings evalSettings{readOnlyMode};
EvalState state;
};
MATCHER_P(IsStringEq, s, fmt("The string is equal to \"%1%\"", s)) { MATCHER(IsListType, "")
if (arg.type() != nString) { {
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 false;
} }
return std::string_view(arg.c_str()) == s;
} }
return true;
}
MATCHER_P(IsIntEq, v, fmt("The string is equal to \"%1%\"", v)) { MATCHER_P(IsListOfSize, n, fmt("Is a list of size [%1%]", n))
if (arg.type() != nInt) { {
return false; if (arg.type() != nList) {
} *result_listener << "Expected list got " << arg.type();
return arg.integer().value == v; 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)) { MATCHER_P(IsAttrsOfSize, n, fmt("Is a set of size [%1%]", n))
if (arg.type() != nFloat) { {
return false; if (arg.type() != nAttrs) {
} *result_listener << "Expected set got " << arg.type();
return arg.fpoint() == v; 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;
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;
}
} /* namespace nix */ } /* namespace nix */

View file

@ -18,6 +18,7 @@ protected:
state = nix_state_create(nullptr, nullptr, store); state = nix_state_create(nullptr, nullptr, store);
value = nix_alloc_value(nullptr, state); value = nix_alloc_value(nullptr, state);
} }
~nix_api_expr_test() ~nix_api_expr_test()
{ {
nix_gc_decref(nullptr, value); nix_gc_decref(nullptr, value);
@ -28,4 +29,4 @@ protected:
nix_value * value; nix_value * value;
}; };
} } // namespace nixC

View file

@ -9,23 +9,27 @@ namespace rc {
using namespace nix; using namespace nix;
template<> template<>
struct Arbitrary<NixStringContextElem::Opaque> { struct Arbitrary<NixStringContextElem::Opaque>
{
static Gen<NixStringContextElem::Opaque> arbitrary(); static Gen<NixStringContextElem::Opaque> arbitrary();
}; };
template<> template<>
struct Arbitrary<NixStringContextElem::Built> { struct Arbitrary<NixStringContextElem::Built>
{
static Gen<NixStringContextElem::Built> arbitrary(); static Gen<NixStringContextElem::Built> arbitrary();
}; };
template<> template<>
struct Arbitrary<NixStringContextElem::DrvDeep> { struct Arbitrary<NixStringContextElem::DrvDeep>
{
static Gen<NixStringContextElem::DrvDeep> arbitrary(); static Gen<NixStringContextElem::DrvDeep> arbitrary();
}; };
template<> template<>
struct Arbitrary<NixStringContextElem> { struct Arbitrary<NixStringContextElem>
{
static Gen<NixStringContextElem> arbitrary(); static Gen<NixStringContextElem> arbitrary();
}; };
} } // namespace rc

View file

@ -36,4 +36,4 @@ Gen<NixStringContextElem> Arbitrary<NixStringContextElem>::arbitrary()
}); });
} }
} } // namespace rc

View file

@ -8,36 +8,30 @@
namespace nix { namespace nix {
// Testing of trivial expressions // Testing of trivial expressions
class DerivedPathExpressionTest : public LibExprTest {}; class DerivedPathExpressionTest : public LibExprTest
{};
// FIXME: `RC_GTEST_FIXTURE_PROP` isn't calling `SetUpTestSuite` because it is // FIXME: `RC_GTEST_FIXTURE_PROP` isn't calling `SetUpTestSuite` because it is
// no a real fixture. // no a real fixture.
// //
// See https://github.com/emil-e/rapidcheck/blob/master/doc/gtest.md#rc_gtest_fixture_propfixture-name-args // 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 #ifndef COVERAGE
RC_GTEST_FIXTURE_PROP( RC_GTEST_FIXTURE_PROP(DerivedPathExpressionTest, prop_opaque_path_round_trip, (const SingleDerivedPath::Opaque & o))
DerivedPathExpressionTest,
prop_opaque_path_round_trip,
(const SingleDerivedPath::Opaque & o))
{ {
auto * v = state.allocValue(); auto * v = state.allocValue();
state.mkStorePathString(o.path, *v); state.mkStorePathString(o.path, *v);
auto d = state.coerceToSingleDerivedPath(noPos, *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 // TODO use DerivedPath::Built for parameter once it supports a single output
// path only. // path only.
RC_GTEST_FIXTURE_PROP( RC_GTEST_FIXTURE_PROP(
DerivedPathExpressionTest, DerivedPathExpressionTest, prop_derived_path_built_placeholder_round_trip, (const SingleDerivedPath::Built & b))
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 * We set these in tests rather than the regular globals so we don't have
@ -49,7 +43,7 @@ RC_GTEST_FIXTURE_PROP(
auto * v = state.allocValue(); auto * v = state.allocValue();
state.mkOutputString(*v, b, std::nullopt, mockXpSettings); state.mkOutputString(*v, b, std::nullopt, mockXpSettings);
auto [d, _] = state.coerceToSingleDerivedPathUnchecked(noPos, *v, "", mockXpSettings); auto [d, _] = state.coerceToSingleDerivedPathUnchecked(noPos, *v, "", mockXpSettings);
RC_ASSERT(SingleDerivedPath { b } == d); RC_ASSERT(SingleDerivedPath{b} == d);
} }
RC_GTEST_FIXTURE_PROP( RC_GTEST_FIXTURE_PROP(
@ -63,7 +57,7 @@ RC_GTEST_FIXTURE_PROP(
auto * v = state.allocValue(); auto * v = state.allocValue();
state.mkOutputString(*v, b, outPath, mockXpSettings); state.mkOutputString(*v, b, outPath, mockXpSettings);
auto [d, _] = state.coerceToSingleDerivedPathUnchecked(noPos, *v, "", mockXpSettings); auto [d, _] = state.coerceToSingleDerivedPathUnchecked(noPos, *v, "", mockXpSettings);
RC_ASSERT(SingleDerivedPath { b } == d); RC_ASSERT(SingleDerivedPath{b} == d);
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,8 @@
namespace nix { namespace nix {
TEST(nix_isAllowedURI, http_example_com) { TEST(nix_isAllowedURI, http_example_com)
{
Strings allowed; Strings allowed;
allowed.push_back("http://example.com"); allowed.push_back("http://example.com");
@ -20,7 +21,8 @@ TEST(nix_isAllowedURI, http_example_com) {
ASSERT_FALSE(isAllowedURI("http://example.org/foo", allowed)); ASSERT_FALSE(isAllowedURI("http://example.org/foo", allowed));
} }
TEST(nix_isAllowedURI, http_example_com_foo) { TEST(nix_isAllowedURI, http_example_com_foo)
{
Strings allowed; Strings allowed;
allowed.push_back("http://example.com/foo"); 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)); // ASSERT_TRUE(isAllowedURI("http://example.com/foo?ok=1", allowed));
} }
TEST(nix_isAllowedURI, http) { TEST(nix_isAllowedURI, http)
{
Strings allowed; Strings allowed;
allowed.push_back("http://"); allowed.push_back("http://");
@ -48,7 +51,8 @@ TEST(nix_isAllowedURI, http) {
ASSERT_FALSE(isAllowedURI("http:foo", allowed)); ASSERT_FALSE(isAllowedURI("http:foo", allowed));
} }
TEST(nix_isAllowedURI, https) { TEST(nix_isAllowedURI, https)
{
Strings allowed; Strings allowed;
allowed.push_back("https://"); allowed.push_back("https://");
@ -58,7 +62,8 @@ TEST(nix_isAllowedURI, https) {
ASSERT_FALSE(isAllowedURI("http://example.com/https:", allowed)); ASSERT_FALSE(isAllowedURI("http://example.com/https:", allowed));
} }
TEST(nix_isAllowedURI, absolute_path) { TEST(nix_isAllowedURI, absolute_path)
{
Strings allowed; Strings allowed;
allowed.push_back("/var/evil"); // bad idea 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)); ASSERT_FALSE(isAllowedURI("http://example.com//var/evil/foo", allowed));
} }
TEST(nix_isAllowedURI, file_url) { TEST(nix_isAllowedURI, file_url)
{
Strings allowed; Strings allowed;
allowed.push_back("file:///var/evil"); // bad idea allowed.push_back("file:///var/evil"); // bad idea
@ -103,7 +109,8 @@ TEST(nix_isAllowedURI, file_url) {
ASSERT_FALSE(isAllowedURI("file://", allowed)); ASSERT_FALSE(isAllowedURI("file://", allowed));
} }
TEST(nix_isAllowedURI, github_all) { TEST(nix_isAllowedURI, github_all)
{
Strings allowed; Strings allowed;
allowed.push_back("github:"); allowed.push_back("github:");
ASSERT_TRUE(isAllowedURI("github:", allowed)); ASSERT_TRUE(isAllowedURI("github:", allowed));
@ -117,7 +124,8 @@ TEST(nix_isAllowedURI, github_all) {
ASSERT_FALSE(isAllowedURI("github", allowed)); ASSERT_FALSE(isAllowedURI("github", allowed));
} }
TEST(nix_isAllowedURI, github_org) { TEST(nix_isAllowedURI, github_org)
{
Strings allowed; Strings allowed;
allowed.push_back("github:foo"); allowed.push_back("github:foo");
ASSERT_FALSE(isAllowedURI("github:", allowed)); 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)); 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; Strings allowed;
allowed.push_back("https://foo/bar:"); allowed.push_back("https://foo/bar:");
ASSERT_TRUE(isAllowedURI("https://foo/bar:", allowed)); 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)); 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 evaled = maybeThunk("builtins");
auto & builtins = state.getBuiltins(); auto & builtins = state.getBuiltins();
ASSERT_TRUE(builtins.type() == nAttrs); ASSERT_TRUE(builtins.type() == nAttrs);
ASSERT_EQ(evaled, &builtins); ASSERT_EQ(evaled, &builtins);
} }
TEST_F(EvalStateTest, getBuiltin_ok) { TEST_F(EvalStateTest, getBuiltin_ok)
{
auto & builtin = state.getBuiltin("toString"); auto & builtin = state.getBuiltin("toString");
ASSERT_TRUE(builtin.type() == nFunction); ASSERT_TRUE(builtin.type() == nFunction);
// FIXME // FIXME
@ -157,7 +169,8 @@ TEST_F(EvalStateTest, getBuiltin_ok) {
ASSERT_EQ(state.forceBool(builtin2, noPos, "in unit test"), true); 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); ASSERT_THROW(state.getBuiltin("nonexistent"), EvalError);
} }

View file

@ -4,65 +4,75 @@
namespace nix { namespace nix {
// Testing the conversion to JSON // Testing the conversion to JSON
class JSONValueTest : public LibExprTest { class JSONValueTest : public LibExprTest
protected: {
std::string getJSONValue(Value& value) { protected:
std::stringstream ss; std::string getJSONValue(Value & value)
NixStringContext ps; {
printValueAsJSON(state, true, value, noPos, ss, ps); std::stringstream ss;
return ss.str(); 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");
} }
};
TEST_F(JSONValueTest, BoolFalse) { TEST_F(JSONValueTest, null)
Value v; {
v.mkBool(false); Value v;
ASSERT_EQ(getJSONValue(v),"false"); v.mkNull();
} ASSERT_EQ(getJSONValue(v), "null");
}
TEST_F(JSONValueTest, BoolTrue) { TEST_F(JSONValueTest, BoolFalse)
Value v; {
v.mkBool(true); Value v;
ASSERT_EQ(getJSONValue(v), "true"); v.mkBool(false);
} ASSERT_EQ(getJSONValue(v), "false");
}
TEST_F(JSONValueTest, IntPositive) { TEST_F(JSONValueTest, BoolTrue)
Value v; {
v.mkInt(100); Value v;
ASSERT_EQ(getJSONValue(v), "100"); v.mkBool(true);
} ASSERT_EQ(getJSONValue(v), "true");
}
TEST_F(JSONValueTest, IntNegative) { TEST_F(JSONValueTest, IntPositive)
Value v; {
v.mkInt(-100); Value v;
ASSERT_EQ(getJSONValue(v), "-100"); v.mkInt(100);
} ASSERT_EQ(getJSONValue(v), "100");
}
TEST_F(JSONValueTest, String) { TEST_F(JSONValueTest, IntNegative)
Value v; {
v.mkString("test"); Value v;
ASSERT_EQ(getJSONValue(v), "\"test\""); v.mkInt(-100);
} ASSERT_EQ(getJSONValue(v), "-100");
}
TEST_F(JSONValueTest, StringQuotes) { TEST_F(JSONValueTest, String)
Value v; {
Value v;
v.mkString("test");
ASSERT_EQ(getJSONValue(v), "\"test\"");
}
v.mkString("test\""); TEST_F(JSONValueTest, StringQuotes)
ASSERT_EQ(getJSONValue(v), "\"test\\\"\""); {
} Value v;
// The dummy store doesn't support writing files. Fails with this exception message: v.mkString("test\"");
// C++ exception with description "error: operation 'addToStoreFromDump' is ASSERT_EQ(getJSONValue(v), "\"test\\\"\"");
// not supported by store 'dummy'" thrown in the test body. }
TEST_F(JSONValueTest, DISABLED_Path) {
Value v; // The dummy store doesn't support writing files. Fails with this exception message:
v.mkPath(state.rootPath(CanonPath("/test"))); // C++ exception with description "error: operation 'addToStoreFromDump' is
ASSERT_EQ(getJSONValue(v), "\"/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x\""); // 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 */ } /* namespace nix */

View file

@ -5,7 +5,8 @@
using namespace nix; 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") { if (argc > 1 && std::string_view(argv[1]) == "__build-remote") {
printError("test-build-remote: not supported in libexpr unit tests"); printError("test-build-remote: not supported in libexpr unit tests");
return 1; 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. // Disable build hook. We won't be testing remote builds in these unit tests. If we do, fix the above build hook.
settings.buildHook = {}; 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.: // When building and testing nix within the host's Nix sandbox, our store dir will be located in the host's
// Host // sandboxBuildDir, e.g.: Host
// storeDir = /nix/store // storeDir = /nix/store
// sandboxBuildDir = /build // sandboxBuildDir = /build
// This process // This process
// storeDir = /build/foo/bar/store // storeDir = /build/foo/bar/store
// sandboxBuildDir = /build // 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"; 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: // Avoid this error, when already running in a sandbox:
// sandbox-exec: sandbox_apply: Operation not permitted // sandbox-exec: sandbox_apply: Operation not permitted
settings.sandboxMode = smDisabled; settings.sandboxMode = smDisabled;
setEnv("_NIX_TEST_NO_SANDBOX", "1"); setEnv("_NIX_TEST_NO_SANDBOX", "1");
#endif #endif
// For pipe operator tests in trivial.cc // For pipe operator tests in trivial.cc
experimentalFeatureSettings.set("experimental-features", "pipe-operators"); experimentalFeatureSettings.set("experimental-features", "pipe-operators");

View file

@ -394,6 +394,7 @@ static void primop_bad_return_thunk(
{ {
nix_init_apply(context, ret, args[0], args[1]); nix_init_apply(context, ret, args[0], args[1]);
} }
TEST_F(nix_api_expr_test, nix_expr_primop_bad_return_thunk) TEST_F(nix_api_expr_test, nix_expr_primop_bad_return_thunk)
{ {
PrimOp * primop = PrimOp * primop =

View file

@ -27,6 +27,7 @@ public:
private: private:
int _x; int _x;
static void print_function(void * self, nix_printer * printer) {} static void print_function(void * self, nix_printer * printer) {}
static void show_type_function(void * self, nix_string_return * res) {} static void show_type_function(void * self, nix_string_return * res) {}
@ -68,4 +69,4 @@ TEST_F(nix_api_expr_test, nix_expr_eval_external)
nix_state_free(stateFn); nix_state_free(stateFn);
} }
} } // namespace nixC

View file

@ -120,6 +120,7 @@ TEST_F(nix_api_expr_test, nix_value_set_get_path_invalid)
ASSERT_EQ(nullptr, nix_get_path_string(ctx, value)); ASSERT_EQ(nullptr, nix_get_path_string(ctx, value));
assert_ctx_err(); assert_ctx_err();
} }
TEST_F(nix_api_expr_test, nix_value_set_get_path) TEST_F(nix_api_expr_test, nix_value_set_get_path)
{ {
const char * p = "/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname"; const char * p = "/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname";
@ -399,4 +400,4 @@ TEST_F(nix_api_expr_test, nix_copy_value)
nix_gc_decref(ctx, source); nix_gc_decref(ctx, source);
} }
} } // namespace nixC

File diff suppressed because it is too large Load diff

View file

@ -5,86 +5,98 @@
namespace nix { namespace nix {
TEST(LookupPathElem, parse_justPath) { TEST(LookupPathElem, parse_justPath)
{
ASSERT_EQ( ASSERT_EQ(
LookupPath::Elem::parse("foo"), LookupPath::Elem::parse("foo"),
(LookupPath::Elem { (LookupPath::Elem{
.prefix = LookupPath::Prefix { .s = "" }, .prefix = LookupPath::Prefix{.s = ""},
.path = LookupPath::Path { .s = "foo" }, .path = LookupPath::Path{.s = "foo"},
})); }));
} }
TEST(LookupPathElem, parse_emptyPrefix) { TEST(LookupPathElem, parse_emptyPrefix)
{
ASSERT_EQ( ASSERT_EQ(
LookupPath::Elem::parse("=foo"), LookupPath::Elem::parse("=foo"),
(LookupPath::Elem { (LookupPath::Elem{
.prefix = LookupPath::Prefix { .s = "" }, .prefix = LookupPath::Prefix{.s = ""},
.path = LookupPath::Path { .s = "foo" }, .path = LookupPath::Path{.s = "foo"},
})); }));
} }
TEST(LookupPathElem, parse_oneEq) { TEST(LookupPathElem, parse_oneEq)
{
ASSERT_EQ( ASSERT_EQ(
LookupPath::Elem::parse("foo=bar"), LookupPath::Elem::parse("foo=bar"),
(LookupPath::Elem { (LookupPath::Elem{
.prefix = LookupPath::Prefix { .s = "foo" }, .prefix = LookupPath::Prefix{.s = "foo"},
.path = LookupPath::Path { .s = "bar" }, .path = LookupPath::Path{.s = "bar"},
})); }));
} }
TEST(LookupPathElem, parse_twoEqs) { TEST(LookupPathElem, parse_twoEqs)
{
ASSERT_EQ( ASSERT_EQ(
LookupPath::Elem::parse("foo=bar=baz"), LookupPath::Elem::parse("foo=bar=baz"),
(LookupPath::Elem { (LookupPath::Elem{
.prefix = LookupPath::Prefix { .s = "foo" }, .prefix = LookupPath::Prefix{.s = "foo"},
.path = LookupPath::Path { .s = "bar=baz" }, .path = LookupPath::Path{.s = "bar=baz"},
})); }));
} }
TEST(LookupPathElem, suffixIfPotentialMatch_justPath)
TEST(LookupPathElem, suffixIfPotentialMatch_justPath) { {
LookupPath::Prefix prefix { .s = "" }; LookupPath::Prefix prefix{.s = ""};
ASSERT_EQ(prefix.suffixIfPotentialMatch("any/thing"), std::optional { "any/thing" }); ASSERT_EQ(prefix.suffixIfPotentialMatch("any/thing"), std::optional{"any/thing"});
} }
TEST(LookupPathElem, suffixIfPotentialMatch_misleadingPrefix1) { TEST(LookupPathElem, suffixIfPotentialMatch_misleadingPrefix1)
LookupPath::Prefix prefix { .s = "foo" }; {
LookupPath::Prefix prefix{.s = "foo"};
ASSERT_EQ(prefix.suffixIfPotentialMatch("fooX"), std::nullopt); ASSERT_EQ(prefix.suffixIfPotentialMatch("fooX"), std::nullopt);
} }
TEST(LookupPathElem, suffixIfPotentialMatch_misleadingPrefix2) { TEST(LookupPathElem, suffixIfPotentialMatch_misleadingPrefix2)
LookupPath::Prefix prefix { .s = "foo" }; {
LookupPath::Prefix prefix{.s = "foo"};
ASSERT_EQ(prefix.suffixIfPotentialMatch("fooX/bar"), std::nullopt); ASSERT_EQ(prefix.suffixIfPotentialMatch("fooX/bar"), std::nullopt);
} }
TEST(LookupPathElem, suffixIfPotentialMatch_partialPrefix) { TEST(LookupPathElem, suffixIfPotentialMatch_partialPrefix)
LookupPath::Prefix prefix { .s = "fooX" }; {
LookupPath::Prefix prefix{.s = "fooX"};
ASSERT_EQ(prefix.suffixIfPotentialMatch("foo"), std::nullopt); ASSERT_EQ(prefix.suffixIfPotentialMatch("foo"), std::nullopt);
} }
TEST(LookupPathElem, suffixIfPotentialMatch_exactPrefix) { TEST(LookupPathElem, suffixIfPotentialMatch_exactPrefix)
LookupPath::Prefix prefix { .s = "foo" }; {
ASSERT_EQ(prefix.suffixIfPotentialMatch("foo"), std::optional { "" }); LookupPath::Prefix prefix{.s = "foo"};
ASSERT_EQ(prefix.suffixIfPotentialMatch("foo"), std::optional{""});
} }
TEST(LookupPathElem, suffixIfPotentialMatch_multiKey) { TEST(LookupPathElem, suffixIfPotentialMatch_multiKey)
LookupPath::Prefix prefix { .s = "foo/bar" }; {
ASSERT_EQ(prefix.suffixIfPotentialMatch("foo/bar/baz"), std::optional { "baz" }); LookupPath::Prefix prefix{.s = "foo/bar"};
ASSERT_EQ(prefix.suffixIfPotentialMatch("foo/bar/baz"), std::optional{"baz"});
} }
TEST(LookupPathElem, suffixIfPotentialMatch_trailingSlash) { TEST(LookupPathElem, suffixIfPotentialMatch_trailingSlash)
LookupPath::Prefix prefix { .s = "foo" }; {
ASSERT_EQ(prefix.suffixIfPotentialMatch("foo/"), std::optional { "" }); LookupPath::Prefix prefix{.s = "foo"};
ASSERT_EQ(prefix.suffixIfPotentialMatch("foo/"), std::optional{""});
} }
TEST(LookupPathElem, suffixIfPotentialMatch_trailingDoubleSlash) { TEST(LookupPathElem, suffixIfPotentialMatch_trailingDoubleSlash)
LookupPath::Prefix prefix { .s = "foo" }; {
ASSERT_EQ(prefix.suffixIfPotentialMatch("foo//"), std::optional { "/" }); LookupPath::Prefix prefix{.s = "foo"};
ASSERT_EQ(prefix.suffixIfPotentialMatch("foo//"), std::optional{"/"});
} }
TEST(LookupPathElem, suffixIfPotentialMatch_trailingPath) { TEST(LookupPathElem, suffixIfPotentialMatch_trailingPath)
LookupPath::Prefix prefix { .s = "foo" }; {
ASSERT_EQ(prefix.suffixIfPotentialMatch("foo/bar/baz"), std::optional { "bar/baz" }); 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" #include "nix/expr/tests/libexpr.hh"
namespace nix { namespace nix {
// Testing of trivial expressions // Testing of trivial expressions
class TrivialExpressionTest : public LibExprTest {}; class TrivialExpressionTest : public LibExprTest
{};
TEST_F(TrivialExpressionTest, true) { TEST_F(TrivialExpressionTest, true)
auto v = eval("true"); {
ASSERT_THAT(v, IsTrue()); auto v = eval("true");
} ASSERT_THAT(v, IsTrue());
}
TEST_F(TrivialExpressionTest, false) { TEST_F(TrivialExpressionTest, false)
auto v = eval("false"); {
ASSERT_THAT(v, IsFalse()); auto v = eval("false");
} ASSERT_THAT(v, IsFalse());
}
TEST_F(TrivialExpressionTest, null) { TEST_F(TrivialExpressionTest, null)
auto v = eval("null"); {
ASSERT_THAT(v, IsNull()); auto v = eval("null");
} ASSERT_THAT(v, IsNull());
}
TEST_F(TrivialExpressionTest, 1) { TEST_F(TrivialExpressionTest, 1)
auto v = eval("1"); {
ASSERT_THAT(v, IsIntEq(1)); auto v = eval("1");
} ASSERT_THAT(v, IsIntEq(1));
}
TEST_F(TrivialExpressionTest, 1plus1) { TEST_F(TrivialExpressionTest, 1plus1)
auto v = eval("1+1"); {
ASSERT_THAT(v, IsIntEq(2)); auto v = eval("1+1");
} ASSERT_THAT(v, IsIntEq(2));
}
TEST_F(TrivialExpressionTest, minus1) { TEST_F(TrivialExpressionTest, minus1)
auto v = eval("-1"); {
ASSERT_THAT(v, IsIntEq(-1)); auto v = eval("-1");
} ASSERT_THAT(v, IsIntEq(-1));
}
TEST_F(TrivialExpressionTest, 1minus1) { TEST_F(TrivialExpressionTest, 1minus1)
auto v = eval("1-1"); {
ASSERT_THAT(v, IsIntEq(0)); auto v = eval("1-1");
} ASSERT_THAT(v, IsIntEq(0));
}
TEST_F(TrivialExpressionTest, lambdaAdd) { TEST_F(TrivialExpressionTest, lambdaAdd)
auto v = eval("let add = a: b: a + b; in add 1 2"); {
ASSERT_THAT(v, IsIntEq(3)); auto v = eval("let add = a: b: a + b; in add 1 2");
} ASSERT_THAT(v, IsIntEq(3));
}
TEST_F(TrivialExpressionTest, list) { TEST_F(TrivialExpressionTest, list)
auto v = eval("[]"); {
ASSERT_THAT(v, IsListOfSize(0)); auto v = eval("[]");
} ASSERT_THAT(v, IsListOfSize(0));
}
TEST_F(TrivialExpressionTest, attrs) { TEST_F(TrivialExpressionTest, attrs)
auto v = eval("{}"); {
ASSERT_THAT(v, IsAttrsOfSize(0)); auto v = eval("{}");
} ASSERT_THAT(v, IsAttrsOfSize(0));
}
TEST_F(TrivialExpressionTest, float) { TEST_F(TrivialExpressionTest, float)
auto v = eval("1.234"); {
ASSERT_THAT(v, IsFloatEq(1.234)); auto v = eval("1.234");
} ASSERT_THAT(v, IsFloatEq(1.234));
}
TEST_F(TrivialExpressionTest, updateAttrs) { TEST_F(TrivialExpressionTest, updateAttrs)
auto v = eval("{ a = 1; } // { b = 2; a = 3; }"); {
ASSERT_THAT(v, IsAttrsOfSize(2)); auto v = eval("{ a = 1; } // { b = 2; a = 3; }");
auto a = v.attrs()->find(createSymbol("a")); ASSERT_THAT(v, IsAttrsOfSize(2));
ASSERT_NE(a, nullptr); auto a = v.attrs()->find(createSymbol("a"));
ASSERT_THAT(*a->value, IsIntEq(3)); ASSERT_NE(a, nullptr);
ASSERT_THAT(*a->value, IsIntEq(3));
auto b = v.attrs()->find(createSymbol("b")); auto b = v.attrs()->find(createSymbol("b"));
ASSERT_NE(b, nullptr); ASSERT_NE(b, nullptr);
ASSERT_THAT(*b->value, IsIntEq(2)); ASSERT_THAT(*b->value, IsIntEq(2));
} }
TEST_F(TrivialExpressionTest, hasAttrOpFalse) { TEST_F(TrivialExpressionTest, hasAttrOpFalse)
auto v = eval("{} ? a"); {
ASSERT_THAT(v, IsFalse()); auto v = eval("{} ? a");
} ASSERT_THAT(v, IsFalse());
}
TEST_F(TrivialExpressionTest, hasAttrOpTrue) { TEST_F(TrivialExpressionTest, hasAttrOpTrue)
auto v = eval("{ a = 123; } ? a"); {
ASSERT_THAT(v, IsTrue()); auto v = eval("{ a = 123; } ? a");
} ASSERT_THAT(v, IsTrue());
}
TEST_F(TrivialExpressionTest, withFound) { TEST_F(TrivialExpressionTest, withFound)
auto v = eval("with { a = 23; }; a"); {
ASSERT_THAT(v, IsIntEq(23)); auto v = eval("with { a = 23; }; a");
} ASSERT_THAT(v, IsIntEq(23));
}
TEST_F(TrivialExpressionTest, withNotFound) { TEST_F(TrivialExpressionTest, withNotFound)
ASSERT_THROW(eval("with {}; a"), Error); {
} ASSERT_THROW(eval("with {}; a"), Error);
}
TEST_F(TrivialExpressionTest, withOverride) { TEST_F(TrivialExpressionTest, withOverride)
auto v = eval("with { a = 23; }; with { a = 42; }; a"); {
ASSERT_THAT(v, IsIntEq(42)); auto v = eval("with { a = 23; }; with { a = 42; }; a");
} ASSERT_THAT(v, IsIntEq(42));
}
TEST_F(TrivialExpressionTest, letOverWith) { TEST_F(TrivialExpressionTest, letOverWith)
auto v = eval("let a = 23; in with { a = 1; }; a"); {
ASSERT_THAT(v, IsIntEq(23)); auto v = eval("let a = 23; in with { a = 1; }; a");
} ASSERT_THAT(v, IsIntEq(23));
}
TEST_F(TrivialExpressionTest, multipleLet) { TEST_F(TrivialExpressionTest, multipleLet)
auto v = eval("let a = 23; in let a = 42; in a"); {
ASSERT_THAT(v, IsIntEq(42)); auto v = eval("let a = 23; in let a = 42; in a");
} ASSERT_THAT(v, IsIntEq(42));
}
TEST_F(TrivialExpressionTest, defaultFunctionArgs) { TEST_F(TrivialExpressionTest, defaultFunctionArgs)
auto v = eval("({ a ? 123 }: a) {}"); {
ASSERT_THAT(v, IsIntEq(123)); auto v = eval("({ a ? 123 }: a) {}");
} ASSERT_THAT(v, IsIntEq(123));
}
TEST_F(TrivialExpressionTest, defaultFunctionArgsOverride) { TEST_F(TrivialExpressionTest, defaultFunctionArgsOverride)
auto v = eval("({ a ? 123 }: a) { a = 5; }"); {
ASSERT_THAT(v, IsIntEq(5)); auto v = eval("({ a ? 123 }: a) { a = 5; }");
} ASSERT_THAT(v, IsIntEq(5));
}
TEST_F(TrivialExpressionTest, defaultFunctionArgsCaptureBack) { TEST_F(TrivialExpressionTest, defaultFunctionArgsCaptureBack)
auto v = eval("({ a ? 123 }@args: args) {}"); {
ASSERT_THAT(v, IsAttrsOfSize(0)); auto v = eval("({ a ? 123 }@args: args) {}");
} ASSERT_THAT(v, IsAttrsOfSize(0));
}
TEST_F(TrivialExpressionTest, defaultFunctionArgsCaptureFront) { TEST_F(TrivialExpressionTest, defaultFunctionArgsCaptureFront)
auto v = eval("(args@{ a ? 123 }: args) {}"); {
ASSERT_THAT(v, IsAttrsOfSize(0)); auto v = eval("(args@{ a ? 123 }: args) {}");
} ASSERT_THAT(v, IsAttrsOfSize(0));
}
TEST_F(TrivialExpressionTest, assertThrows) { TEST_F(TrivialExpressionTest, assertThrows)
ASSERT_THROW(eval("let x = arg: assert arg == 1; 123; in x 2"), Error); {
} ASSERT_THROW(eval("let x = arg: assert arg == 1; 123; in x 2"), Error);
}
TEST_F(TrivialExpressionTest, assertPassed) { TEST_F(TrivialExpressionTest, assertPassed)
auto v = eval("let x = arg: assert arg == 1; 123; in x 1"); {
ASSERT_THAT(v, IsIntEq(123)); auto v = eval("let x = arg: assert arg == 1; 123; in x 1");
} ASSERT_THAT(v, IsIntEq(123));
}
class AttrSetMergeTrvialExpressionTest : class AttrSetMergeTrvialExpressionTest : public TrivialExpressionTest, public testing::WithParamInterface<const char *>
public TrivialExpressionTest, {};
public testing::WithParamInterface<const char*>
{};
TEST_P(AttrSetMergeTrvialExpressionTest, attrsetMergeLazy) { 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. // Usually Nix rejects duplicate keys in an attrset but it does allow
// The below is equivalent to `{a.b = 1; a.c = 2; }`. // so if it is an attribute set that contains disjoint sets of keys.
// The attribute set `a` will be a Thunk at first as the attribuets // The below is equivalent to `{a.b = 1; a.c = 2; }`.
// have to be merged (or otherwise computed) and that is done in a lazy // The attribute set `a` will be a Thunk at first as the attribuets
// manner. // have to be merged (or otherwise computed) and that is done in a lazy
// manner.
auto expr = GetParam(); auto expr = GetParam();
auto v = eval(expr); auto v = eval(expr);
ASSERT_THAT(v, IsAttrsOfSize(1)); ASSERT_THAT(v, IsAttrsOfSize(1));
auto a = v.attrs()->find(createSymbol("a")); auto a = v.attrs()->find(createSymbol("a"));
ASSERT_NE(a, nullptr); ASSERT_NE(a, nullptr);
ASSERT_THAT(*a->value, IsThunk()); ASSERT_THAT(*a->value, IsThunk());
state.forceValue(*a->value, noPos); 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")); auto b = a->value->attrs()->find(createSymbol("b"));
ASSERT_NE(b, nullptr); ASSERT_NE(b, nullptr);
ASSERT_THAT(*b->value, IsIntEq(1)); ASSERT_THAT(*b->value, IsIntEq(1));
auto c = a->value->attrs()->find(createSymbol("c")); auto c = a->value->attrs()->find(createSymbol("c"));
ASSERT_NE(c, nullptr); ASSERT_NE(c, nullptr);
ASSERT_THAT(*c->value, IsIntEq(2)); ASSERT_THAT(*c->value, IsIntEq(2));
} }
INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(
attrsetMergeLazy, attrsetMergeLazy,
AttrSetMergeTrvialExpressionTest, AttrSetMergeTrvialExpressionTest,
testing::Values( testing::Values("{ a.b = 1; a.c = 2; }", "{ a = { b = 1; }; a = { c = 2; }; }"));
"{ a.b = 1; a.c = 2; }",
"{ a = { b = 1; }; a = { c = 2; }; }"
)
);
// The following macros ultimately define 48 tests (16 variations on three // The following macros ultimately define 48 tests (16 variations on three
// templates). Each template tests an expression that can be written in 2^4 // templates). Each template tests an expression that can be written in 2^4
@ -199,28 +220,34 @@ namespace nix {
// expanded. // expanded.
#define X_EXPAND_IF0(k, v) k "." v #define X_EXPAND_IF0(k, v) k "." v
#define X_EXPAND_IF1(k, v) k " = { " v " };" #define X_EXPAND_IF1(k, v) k " = { " v " };"
#define X4(w, x, y, z) \ #define X4(w, x, y, z) \
TEST_F(TrivialExpressionTest, nestedAttrsetMerge##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;")) " " \ auto v = eval( \
X_EXPAND_IF##y("a", X_EXPAND_IF##z("b", "d = 2;")) " }"); \ "{ a.b = { c = 1; d = 2; }; } == { " X_EXPAND_IF##w( \
ASSERT_THAT(v, IsTrue()); \ "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("{ " \ TEST_F(TrivialExpressionTest, nestedAttrsetMergeDup##w##x##y##z) \
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); \ ASSERT_THROW( \
}; \ eval( \
TEST_F(TrivialExpressionTest, nestedAttrsetMergeLet##w##x##y##z) { \ "{ " X_EXPAND_IF##w("a", X_EXPAND_IF##x("b", "c = 1;")) " " X_EXPAND_IF##y( \
auto v = eval("{ b = { c = 1; d = 2; }; } == (let " \ "a", X_EXPAND_IF##z("b", "c = 2;")) " }"), \
X_EXPAND_IF##w("a", X_EXPAND_IF##x("b", "c = 1;")) " " \ Error); \
X_EXPAND_IF##y("a", X_EXPAND_IF##z("b", "d = 2;")) " in a)"); \ }; \
ASSERT_THAT(v, IsTrue()); \ 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 X3(...) X4(__VA_ARGS__, 0) X4(__VA_ARGS__, 1)
#define X2(...) X3(__VA_ARGS__, 0) X3(__VA_ARGS__, 1) #define X2(...) X3(__VA_ARGS__, 0) X3(__VA_ARGS__, 1)
#define X1(...) X2(__VA_ARGS__, 0) X2(__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_IF0
#undef X_EXPAND_IF1 #undef X_EXPAND_IF1
#undef X1 #undef X1
@ -228,74 +255,88 @@ namespace nix {
#undef X3 #undef X3
#undef X4 #undef X4
TEST_F(TrivialExpressionTest, functor) { TEST_F(TrivialExpressionTest, functor)
auto v = eval("{ __functor = self: arg: self.v + arg; v = 10; } 5"); {
ASSERT_THAT(v, IsIntEq(15)); auto v = eval("{ __functor = self: arg: self.v + arg; v = 10; } 5");
} ASSERT_THAT(v, IsIntEq(15));
}
TEST_F(TrivialExpressionTest, forwardPipe) { TEST_F(TrivialExpressionTest, forwardPipe)
auto v = eval("1 |> builtins.add 2 |> builtins.mul 3"); {
ASSERT_THAT(v, IsIntEq(9)); auto v = eval("1 |> builtins.add 2 |> builtins.mul 3");
} ASSERT_THAT(v, IsIntEq(9));
}
TEST_F(TrivialExpressionTest, backwardPipe) { TEST_F(TrivialExpressionTest, backwardPipe)
auto v = eval("builtins.add 1 <| builtins.mul 2 <| 3"); {
ASSERT_THAT(v, IsIntEq(7)); auto v = eval("builtins.add 1 <| builtins.mul 2 <| 3");
} ASSERT_THAT(v, IsIntEq(7));
}
TEST_F(TrivialExpressionTest, forwardPipeEvaluationOrder) { TEST_F(TrivialExpressionTest, forwardPipeEvaluationOrder)
auto v = eval("1 |> null |> (x: 2)"); {
ASSERT_THAT(v, IsIntEq(2)); auto v = eval("1 |> null |> (x: 2)");
} ASSERT_THAT(v, IsIntEq(2));
}
TEST_F(TrivialExpressionTest, backwardPipeEvaluationOrder) { TEST_F(TrivialExpressionTest, backwardPipeEvaluationOrder)
auto v = eval("(x: 1) <| null <| 2"); {
ASSERT_THAT(v, IsIntEq(1)); auto v = eval("(x: 1) <| null <| 2");
} ASSERT_THAT(v, IsIntEq(1));
}
TEST_F(TrivialExpressionTest, differentPipeOperatorsDoNotAssociate) { TEST_F(TrivialExpressionTest, differentPipeOperatorsDoNotAssociate)
ASSERT_THROW(eval("(x: 1) <| 2 |> (x: 3)"), ParseError); {
} ASSERT_THROW(eval("(x: 1) <| 2 |> (x: 3)"), ParseError);
}
TEST_F(TrivialExpressionTest, differentPipeOperatorsParensLeft) { TEST_F(TrivialExpressionTest, differentPipeOperatorsParensLeft)
auto v = eval("((x: 1) <| 2) |> (x: 3)"); {
ASSERT_THAT(v, IsIntEq(3)); auto v = eval("((x: 1) <| 2) |> (x: 3)");
} ASSERT_THAT(v, IsIntEq(3));
}
TEST_F(TrivialExpressionTest, differentPipeOperatorsParensRight) { TEST_F(TrivialExpressionTest, differentPipeOperatorsParensRight)
auto v = eval("(x: 1) <| (2 |> (x: 3))"); {
ASSERT_THAT(v, IsIntEq(1)); auto v = eval("(x: 1) <| (2 |> (x: 3))");
} ASSERT_THAT(v, IsIntEq(1));
}
TEST_F(TrivialExpressionTest, forwardPipeLowestPrecedence) { TEST_F(TrivialExpressionTest, forwardPipeLowestPrecedence)
auto v = eval("false -> true |> (x: !x)"); {
ASSERT_THAT(v, IsFalse()); auto v = eval("false -> true |> (x: !x)");
} ASSERT_THAT(v, IsFalse());
}
TEST_F(TrivialExpressionTest, backwardPipeLowestPrecedence) { TEST_F(TrivialExpressionTest, backwardPipeLowestPrecedence)
auto v = eval("(x: !x) <| false -> true"); {
ASSERT_THAT(v, IsFalse()); auto v = eval("(x: !x) <| false -> true");
} ASSERT_THAT(v, IsFalse());
}
TEST_F(TrivialExpressionTest, forwardPipeStrongerThanElse) { TEST_F(TrivialExpressionTest, forwardPipeStrongerThanElse)
auto v = eval("if true then 1 else 2 |> 3"); {
ASSERT_THAT(v, IsIntEq(1)); auto v = eval("if true then 1 else 2 |> 3");
} ASSERT_THAT(v, IsIntEq(1));
}
TEST_F(TrivialExpressionTest, backwardPipeStrongerThanElse) { TEST_F(TrivialExpressionTest, backwardPipeStrongerThanElse)
auto v = eval("if true then 1 else 2 <| 3"); {
ASSERT_THAT(v, IsIntEq(1)); auto v = eval("if true then 1 else 2 <| 3");
} ASSERT_THAT(v, IsIntEq(1));
}
TEST_F(TrivialExpressionTest, bindOr) { TEST_F(TrivialExpressionTest, bindOr)
auto v = eval("{ or = 1; }"); {
ASSERT_THAT(v, IsAttrsOfSize(1)); auto v = eval("{ or = 1; }");
auto b = v.attrs()->find(createSymbol("or")); ASSERT_THAT(v, IsAttrsOfSize(1));
ASSERT_NE(b, nullptr); auto b = v.attrs()->find(createSymbol("or"));
ASSERT_THAT(*b->value, IsIntEq(1)); ASSERT_NE(b, nullptr);
} ASSERT_THAT(*b->value, IsIntEq(1));
}
TEST_F(TrivialExpressionTest, orCantBeUsed) { TEST_F(TrivialExpressionTest, orCantBeUsed)
ASSERT_THROW(eval("let or = 1; in or"), Error); {
} ASSERT_THROW(eval("let or = 1; in or"), Error);
}
} /* namespace nix */ } /* namespace nix */

View file

@ -10,46 +10,42 @@ namespace nix {
// Test a few cases of invalid string context elements. // Test a few cases of invalid string context elements.
TEST(NixStringContextElemTest, empty_invalid) { TEST(NixStringContextElemTest, empty_invalid)
EXPECT_THROW( {
NixStringContextElem::parse(""), EXPECT_THROW(NixStringContextElem::parse(""), BadNixStringContextElem);
BadNixStringContextElem);
} }
TEST(NixStringContextElemTest, single_bang_invalid) { TEST(NixStringContextElemTest, single_bang_invalid)
EXPECT_THROW( {
NixStringContextElem::parse("!"), EXPECT_THROW(NixStringContextElem::parse("!"), BadNixStringContextElem);
BadNixStringContextElem);
} }
TEST(NixStringContextElemTest, double_bang_invalid) { TEST(NixStringContextElemTest, double_bang_invalid)
EXPECT_THROW( {
NixStringContextElem::parse("!!/"), EXPECT_THROW(NixStringContextElem::parse("!!/"), BadStorePath);
BadStorePath);
} }
TEST(NixStringContextElemTest, eq_slash_invalid) { TEST(NixStringContextElemTest, eq_slash_invalid)
EXPECT_THROW( {
NixStringContextElem::parse("=/"), EXPECT_THROW(NixStringContextElem::parse("=/"), BadStorePath);
BadStorePath);
} }
TEST(NixStringContextElemTest, slash_invalid) { TEST(NixStringContextElemTest, slash_invalid)
EXPECT_THROW( {
NixStringContextElem::parse("/"), EXPECT_THROW(NixStringContextElem::parse("/"), BadStorePath);
BadStorePath);
} }
/** /**
* Round trip (string <-> data structure) test for * Round trip (string <-> data structure) test for
* `NixStringContextElem::Opaque`. * `NixStringContextElem::Opaque`.
*/ */
TEST(NixStringContextElemTest, opaque) { TEST(NixStringContextElemTest, opaque)
{
std::string_view opaque = "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x"; std::string_view opaque = "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x";
auto elem = NixStringContextElem::parse(opaque); auto elem = NixStringContextElem::parse(opaque);
auto * p = std::get_if<NixStringContextElem::Opaque>(&elem.raw); auto * p = std::get_if<NixStringContextElem::Opaque>(&elem.raw);
ASSERT_TRUE(p); ASSERT_TRUE(p);
ASSERT_EQ(p->path, StorePath { opaque }); ASSERT_EQ(p->path, StorePath{opaque});
ASSERT_EQ(elem.to_string(), opaque); ASSERT_EQ(elem.to_string(), opaque);
} }
@ -57,12 +53,13 @@ TEST(NixStringContextElemTest, opaque) {
* Round trip (string <-> data structure) test for * Round trip (string <-> data structure) test for
* `NixStringContextElem::DrvDeep`. * `NixStringContextElem::DrvDeep`.
*/ */
TEST(NixStringContextElemTest, drvDeep) { TEST(NixStringContextElemTest, drvDeep)
{
std::string_view drvDeep = "=g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv"; std::string_view drvDeep = "=g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv";
auto elem = NixStringContextElem::parse(drvDeep); auto elem = NixStringContextElem::parse(drvDeep);
auto * p = std::get_if<NixStringContextElem::DrvDeep>(&elem.raw); auto * p = std::get_if<NixStringContextElem::DrvDeep>(&elem.raw);
ASSERT_TRUE(p); 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); ASSERT_EQ(elem.to_string(), drvDeep);
} }
@ -70,15 +67,18 @@ TEST(NixStringContextElemTest, drvDeep) {
* Round trip (string <-> data structure) test for a simpler * Round trip (string <-> data structure) test for a simpler
* `NixStringContextElem::Built`. * `NixStringContextElem::Built`.
*/ */
TEST(NixStringContextElemTest, built_opaque) { TEST(NixStringContextElemTest, built_opaque)
{
std::string_view built = "!foo!g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv"; std::string_view built = "!foo!g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv";
auto elem = NixStringContextElem::parse(built); auto elem = NixStringContextElem::parse(built);
auto * p = std::get_if<NixStringContextElem::Built>(&elem.raw); auto * p = std::get_if<NixStringContextElem::Built>(&elem.raw);
ASSERT_TRUE(p); ASSERT_TRUE(p);
ASSERT_EQ(p->output, "foo"); ASSERT_EQ(p->output, "foo");
ASSERT_EQ(*p->drvPath, ((SingleDerivedPath) SingleDerivedPath::Opaque { ASSERT_EQ(
.path = StorePath { built.substr(5) }, *p->drvPath,
})); ((SingleDerivedPath) SingleDerivedPath::Opaque{
.path = StorePath{built.substr(5)},
}));
ASSERT_EQ(elem.to_string(), built); ASSERT_EQ(elem.to_string(), built);
} }
@ -86,7 +86,8 @@ TEST(NixStringContextElemTest, built_opaque) {
* Round trip (string <-> data structure) test for a more complex, * Round trip (string <-> data structure) test for a more complex,
* inductive `NixStringContextElem::Built`. * 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 * 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. * 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); auto * drvPath = std::get_if<SingleDerivedPath::Built>(&*p->drvPath);
ASSERT_TRUE(drvPath); ASSERT_TRUE(drvPath);
ASSERT_EQ(drvPath->output, "bar"); ASSERT_EQ(drvPath->output, "bar");
ASSERT_EQ(*drvPath->drvPath, ((SingleDerivedPath) SingleDerivedPath::Opaque { ASSERT_EQ(
.path = StorePath { built.substr(9) }, *drvPath->drvPath,
})); ((SingleDerivedPath) SingleDerivedPath::Opaque{
.path = StorePath{built.substr(9)},
}));
ASSERT_EQ(elem.to_string(), built); ASSERT_EQ(elem.to_string(), built);
} }
@ -112,17 +115,15 @@ TEST(NixStringContextElemTest, built_built) {
* Without the right experimental features enabled, we cannot parse a * Without the right experimental features enabled, we cannot parse a
* complex inductive string context element. * complex inductive string context element.
*/ */
TEST(NixStringContextElemTest, built_built_xp) { TEST(NixStringContextElemTest, built_built_xp)
{
ASSERT_THROW( ASSERT_THROW(
NixStringContextElem::parse("!foo!bar!g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv"), MissingExperimentalFeature); NixStringContextElem::parse("!foo!bar!g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv"), MissingExperimentalFeature);
} }
#ifndef COVERAGE #ifndef COVERAGE
RC_GTEST_PROP( RC_GTEST_PROP(NixStringContextElemTest, prop_round_rip, (const NixStringContextElem & o))
NixStringContextElemTest,
prop_round_rip,
(const NixStringContextElem & o))
{ {
ExperimentalFeatureSettings xpSettings; ExperimentalFeatureSettings xpSettings;
xpSettings.set("experimental-features", "dynamic-derivations"); xpSettings.set("experimental-features", "dynamic-derivations");
@ -131,4 +132,4 @@ RC_GTEST_PROP(
#endif #endif
} } // namespace nix

View file

@ -106,14 +106,11 @@ TEST_F(ValuePrintingTests, vApp)
TEST_F(ValuePrintingTests, vLambda) TEST_F(ValuePrintingTests, vLambda)
{ {
Env env { Env env{.up = nullptr, .values = {}};
.up = nullptr,
.values = { }
};
PosTable::Origin origin = state.positions.addOrigin(std::monostate(), 1); PosTable::Origin origin = state.positions.addOrigin(std::monostate(), 1);
auto posIdx = state.positions.add(origin, 0); auto posIdx = state.positions.add(origin, 0);
auto body = ExprInt(0); auto body = ExprInt(0);
auto formals = Formals {}; auto formals = Formals{};
ExprLambda eLambda(posIdx, createSymbol("a"), &formals, &body); ExprLambda eLambda(posIdx, createSymbol("a"), &formals, &body);
@ -130,9 +127,7 @@ TEST_F(ValuePrintingTests, vLambda)
TEST_F(ValuePrintingTests, vPrimOp) TEST_F(ValuePrintingTests, vPrimOp)
{ {
Value vPrimOp; Value vPrimOp;
PrimOp primOp{ PrimOp primOp{.name = "puppy"};
.name = "puppy"
};
vPrimOp.mkPrimOp(&primOp); vPrimOp.mkPrimOp(&primOp);
test(vPrimOp, "«primop puppy»"); test(vPrimOp, "«primop puppy»");
@ -140,9 +135,7 @@ TEST_F(ValuePrintingTests, vPrimOp)
TEST_F(ValuePrintingTests, vPrimOpApp) TEST_F(ValuePrintingTests, vPrimOpApp)
{ {
PrimOp primOp{ PrimOp primOp{.name = "puppy"};
.name = "puppy"
};
Value vPrimOp; Value vPrimOp;
vPrimOp.mkPrimOp(&primOp); vPrimOp.mkPrimOp(&primOp);
@ -161,16 +154,19 @@ TEST_F(ValuePrintingTests, vExternal)
{ {
return ""; return "";
} }
std::string typeOf() const override std::string typeOf() const override
{ {
return ""; return "";
} }
virtual std::ostream & print(std::ostream & str) const override virtual std::ostream & print(std::ostream & str) const override
{ {
str << "testing-external!"; str << "testing-external!";
return str; return str;
} }
} myExternal; } myExternal;
Value vExternal; Value vExternal;
vExternal.mkExternal(&myExternal); vExternal.mkExternal(&myExternal);
@ -220,10 +216,13 @@ TEST_F(ValuePrintingTests, depthAttrs)
Value vNested; Value vNested;
vNested.mkAttrs(builder2.finish()); vNested.mkAttrs(builder2.finish());
test(vNested, "{ nested = { ... }; one = 1; two = 2; }", PrintOptions { .maxDepth = 1 }); 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(
test(vNested, "{ nested = { nested = { }; one = 1; two = 2; }; one = 1; two = 2; }", PrintOptions { .maxDepth = 3 }); vNested,
test(vNested, "{ nested = { nested = { }; one = 1; two = 2; }; one = 1; two = 2; }", PrintOptions { .maxDepth = 4 }); "{ 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) TEST_F(ValuePrintingTests, depthList)
@ -256,11 +255,11 @@ TEST_F(ValuePrintingTests, depthList)
Value vList; Value vList;
vList.mkList(list); vList.mkList(list);
test(vList, "[ 1 2 { ... } ]", PrintOptions { .maxDepth = 1 }); 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; } ]", 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 = 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 = 4});
test(vList, "[ 1 2 { nested = { one = 1; two = 2; }; one = 1; two = 2; } ]", PrintOptions { .maxDepth = 5 }); test(vList, "[ 1 2 { nested = { one = 1; two = 2; }; one = 1; two = 2; } ]", PrintOptions{.maxDepth = 5});
} }
struct StringPrintingTests : LibExprTest struct StringPrintingTests : LibExprTest
@ -272,9 +271,7 @@ struct StringPrintingTests : LibExprTest
v.mkString(literal); v.mkString(literal);
std::stringstream out; std::stringstream out;
printValue(state, out, v, PrintOptions { printValue(state, out, v, PrintOptions{.maxStringLength = maxLength});
.maxStringLength = maxLength
});
ASSERT_EQ(out.str(), expected); ASSERT_EQ(out.str(), expected);
} }
}; };
@ -305,15 +302,9 @@ TEST_F(ValuePrintingTests, attrsTypeFirst)
Value vAttrs; Value vAttrs;
vAttrs.mkAttrs(builder.finish()); vAttrs.mkAttrs(builder.finish());
test(vAttrs, test(vAttrs, "{ type = \"puppy\"; apple = \"apple\"; }", PrintOptions{.maxAttrs = 100});
"{ type = \"puppy\"; apple = \"apple\"; }",
PrintOptions {
.maxAttrs = 100
});
test(vAttrs, test(vAttrs, "{ apple = \"apple\"; type = \"puppy\"; }", PrintOptions{});
"{ apple = \"apple\"; type = \"puppy\"; }",
PrintOptions { });
} }
TEST_F(ValuePrintingTests, ansiColorsInt) TEST_F(ValuePrintingTests, ansiColorsInt)
@ -321,11 +312,7 @@ TEST_F(ValuePrintingTests, ansiColorsInt)
Value v; Value v;
v.mkInt(10); v.mkInt(10);
test(v, test(v, ANSI_CYAN "10" ANSI_NORMAL, PrintOptions{.ansiColors = true});
ANSI_CYAN "10" ANSI_NORMAL,
PrintOptions {
.ansiColors = true
});
} }
TEST_F(ValuePrintingTests, ansiColorsFloat) TEST_F(ValuePrintingTests, ansiColorsFloat)
@ -333,11 +320,7 @@ TEST_F(ValuePrintingTests, ansiColorsFloat)
Value v; Value v;
v.mkFloat(1.6); v.mkFloat(1.6);
test(v, test(v, ANSI_CYAN "1.6" ANSI_NORMAL, PrintOptions{.ansiColors = true});
ANSI_CYAN "1.6" ANSI_NORMAL,
PrintOptions {
.ansiColors = true
});
} }
TEST_F(ValuePrintingTests, ansiColorsBool) TEST_F(ValuePrintingTests, ansiColorsBool)
@ -345,11 +328,7 @@ TEST_F(ValuePrintingTests, ansiColorsBool)
Value v; Value v;
v.mkBool(true); v.mkBool(true);
test(v, test(v, ANSI_CYAN "true" ANSI_NORMAL, PrintOptions{.ansiColors = true});
ANSI_CYAN "true" ANSI_NORMAL,
PrintOptions {
.ansiColors = true
});
} }
TEST_F(ValuePrintingTests, ansiColorsString) TEST_F(ValuePrintingTests, ansiColorsString)
@ -357,11 +336,7 @@ TEST_F(ValuePrintingTests, ansiColorsString)
Value v; Value v;
v.mkString("puppy"); v.mkString("puppy");
test(v, test(v, ANSI_MAGENTA "\"puppy\"" ANSI_NORMAL, PrintOptions{.ansiColors = true});
ANSI_MAGENTA "\"puppy\"" ANSI_NORMAL,
PrintOptions {
.ansiColors = true
});
} }
TEST_F(ValuePrintingTests, ansiColorsStringElided) TEST_F(ValuePrintingTests, ansiColorsStringElided)
@ -369,12 +344,10 @@ TEST_F(ValuePrintingTests, ansiColorsStringElided)
Value v; Value v;
v.mkString("puppy"); v.mkString("puppy");
test(v, test(
ANSI_MAGENTA "\"pup\" " ANSI_FAINT "«2 bytes elided»" ANSI_NORMAL, v,
PrintOptions { ANSI_MAGENTA "\"pup\" " ANSI_FAINT "«2 bytes elided»" ANSI_NORMAL,
.ansiColors = true, PrintOptions{.ansiColors = true, .maxStringLength = 3});
.maxStringLength = 3
});
} }
TEST_F(ValuePrintingTests, ansiColorsPath) TEST_F(ValuePrintingTests, ansiColorsPath)
@ -382,11 +355,7 @@ TEST_F(ValuePrintingTests, ansiColorsPath)
Value v; Value v;
v.mkPath(state.rootPath(CanonPath("puppy"))); v.mkPath(state.rootPath(CanonPath("puppy")));
test(v, test(v, ANSI_GREEN "/puppy" ANSI_NORMAL, PrintOptions{.ansiColors = true});
ANSI_GREEN "/puppy" ANSI_NORMAL,
PrintOptions {
.ansiColors = true
});
} }
TEST_F(ValuePrintingTests, ansiColorsNull) TEST_F(ValuePrintingTests, ansiColorsNull)
@ -394,11 +363,7 @@ TEST_F(ValuePrintingTests, ansiColorsNull)
Value v; Value v;
v.mkNull(); v.mkNull();
test(v, test(v, ANSI_CYAN "null" ANSI_NORMAL, PrintOptions{.ansiColors = true});
ANSI_CYAN "null" ANSI_NORMAL,
PrintOptions {
.ansiColors = true
});
} }
TEST_F(ValuePrintingTests, ansiColorsAttrs) TEST_F(ValuePrintingTests, ansiColorsAttrs)
@ -416,11 +381,10 @@ TEST_F(ValuePrintingTests, ansiColorsAttrs)
Value vAttrs; Value vAttrs;
vAttrs.mkAttrs(builder.finish()); vAttrs.mkAttrs(builder.finish());
test(vAttrs, test(
"{ one = " ANSI_CYAN "1" ANSI_NORMAL "; two = " ANSI_CYAN "2" ANSI_NORMAL "; }", vAttrs,
PrintOptions { "{ one = " ANSI_CYAN "1" ANSI_NORMAL "; two = " ANSI_CYAN "2" ANSI_NORMAL "; }",
.ansiColors = true PrintOptions{.ansiColors = true});
});
} }
TEST_F(ValuePrintingTests, ansiColorsDerivation) TEST_F(ValuePrintingTests, ansiColorsDerivation)
@ -434,20 +398,15 @@ TEST_F(ValuePrintingTests, ansiColorsDerivation)
Value vAttrs; Value vAttrs;
vAttrs.mkAttrs(builder.finish()); vAttrs.mkAttrs(builder.finish());
test(vAttrs, test(
ANSI_GREEN "«derivation»" ANSI_NORMAL, vAttrs,
PrintOptions { ANSI_GREEN "«derivation»" ANSI_NORMAL,
.ansiColors = true, PrintOptions{.ansiColors = true, .force = true, .derivationPaths = true});
.force = true,
.derivationPaths = true
});
test(vAttrs, test(
"{ type = " ANSI_MAGENTA "\"derivation\"" ANSI_NORMAL "; }", vAttrs,
PrintOptions { "{ type = " ANSI_MAGENTA "\"derivation\"" ANSI_NORMAL "; }",
.ansiColors = true, PrintOptions{.ansiColors = true, .force = true});
.force = true
});
} }
TEST_F(ValuePrintingTests, ansiColorsError) TEST_F(ValuePrintingTests, ansiColorsError)
@ -458,14 +417,13 @@ TEST_F(ValuePrintingTests, ansiColorsError)
Value vError; Value vError;
vError.mkApp(&throw_, &message); vError.mkApp(&throw_, &message);
test(vError, test(
ANSI_RED vError,
"«error: uh oh!»" ANSI_RED "«error: uh oh!»" ANSI_NORMAL,
ANSI_NORMAL, PrintOptions{
PrintOptions { .ansiColors = true,
.ansiColors = true, .force = true,
.force = true, });
});
} }
TEST_F(ValuePrintingTests, ansiColorsDerivationError) TEST_F(ValuePrintingTests, ansiColorsDerivationError)
@ -486,30 +444,20 @@ TEST_F(ValuePrintingTests, ansiColorsDerivationError)
Value vAttrs; Value vAttrs;
vAttrs.mkAttrs(builder.finish()); vAttrs.mkAttrs(builder.finish());
test(vAttrs, test(
"{ drvPath = " vAttrs,
ANSI_RED "{ drvPath = " ANSI_RED "«error: uh oh!»" ANSI_NORMAL "; type = " ANSI_MAGENTA "\"derivation\"" ANSI_NORMAL
"«error: uh oh!»" "; }",
ANSI_NORMAL PrintOptions{.ansiColors = true, .force = true});
"; type = "
ANSI_MAGENTA
"\"derivation\""
ANSI_NORMAL
"; }",
PrintOptions {
.ansiColors = true,
.force = true
});
test(vAttrs, test(
ANSI_RED vAttrs,
"«error: uh oh!»" ANSI_RED "«error: uh oh!»" ANSI_NORMAL,
ANSI_NORMAL, PrintOptions{
PrintOptions { .ansiColors = true,
.ansiColors = true, .force = true,
.force = true, .derivationPaths = true,
.derivationPaths = true, });
});
} }
TEST_F(ValuePrintingTests, ansiColorsAssert) TEST_F(ValuePrintingTests, ansiColorsAssert)
@ -523,12 +471,7 @@ TEST_F(ValuePrintingTests, ansiColorsAssert)
Value v; Value v;
state.mkThunk_(v, &expr); state.mkThunk_(v, &expr);
test(v, test(v, ANSI_RED "«error: assertion 'false' failed»" ANSI_NORMAL, PrintOptions{.ansiColors = true, .force = true});
ANSI_RED "«error: assertion 'false' failed»" ANSI_NORMAL,
PrintOptions {
.ansiColors = true,
.force = true
});
} }
TEST_F(ValuePrintingTests, ansiColorsList) TEST_F(ValuePrintingTests, ansiColorsList)
@ -545,77 +488,51 @@ TEST_F(ValuePrintingTests, ansiColorsList)
Value vList; Value vList;
vList.mkList(list); vList.mkList(list);
test(vList, test(
"[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_CYAN "2" ANSI_NORMAL " " ANSI_MAGENTA "«nullptr»" ANSI_NORMAL " ]", vList,
PrintOptions { "[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_CYAN "2" ANSI_NORMAL " " ANSI_MAGENTA "«nullptr»" ANSI_NORMAL " ]",
.ansiColors = true PrintOptions{.ansiColors = true});
});
} }
TEST_F(ValuePrintingTests, ansiColorsLambda) TEST_F(ValuePrintingTests, ansiColorsLambda)
{ {
Env env { Env env{.up = nullptr, .values = {}};
.up = nullptr,
.values = { }
};
PosTable::Origin origin = state.positions.addOrigin(std::monostate(), 1); PosTable::Origin origin = state.positions.addOrigin(std::monostate(), 1);
auto posIdx = state.positions.add(origin, 0); auto posIdx = state.positions.add(origin, 0);
auto body = ExprInt(0); auto body = ExprInt(0);
auto formals = Formals {}; auto formals = Formals{};
ExprLambda eLambda(posIdx, createSymbol("a"), &formals, &body); ExprLambda eLambda(posIdx, createSymbol("a"), &formals, &body);
Value vLambda; Value vLambda;
vLambda.mkLambda(&env, &eLambda); vLambda.mkLambda(&env, &eLambda);
test(vLambda, test(vLambda, ANSI_BLUE "«lambda @ «none»:1:1»" ANSI_NORMAL, PrintOptions{.ansiColors = true, .force = true});
ANSI_BLUE "«lambda @ «none»:1:1»" ANSI_NORMAL,
PrintOptions {
.ansiColors = true,
.force = true
});
eLambda.setName(createSymbol("puppy")); eLambda.setName(createSymbol("puppy"));
test(vLambda, test(vLambda, ANSI_BLUE "«lambda puppy @ «none»:1:1»" ANSI_NORMAL, PrintOptions{.ansiColors = true, .force = true});
ANSI_BLUE "«lambda puppy @ «none»:1:1»" ANSI_NORMAL,
PrintOptions {
.ansiColors = true,
.force = true
});
} }
TEST_F(ValuePrintingTests, ansiColorsPrimOp) TEST_F(ValuePrintingTests, ansiColorsPrimOp)
{ {
PrimOp primOp{ PrimOp primOp{.name = "puppy"};
.name = "puppy"
};
Value v; Value v;
v.mkPrimOp(&primOp); v.mkPrimOp(&primOp);
test(v, test(v, ANSI_BLUE "«primop puppy»" ANSI_NORMAL, PrintOptions{.ansiColors = true});
ANSI_BLUE "«primop puppy»" ANSI_NORMAL,
PrintOptions {
.ansiColors = true
});
} }
TEST_F(ValuePrintingTests, ansiColorsPrimOpApp) TEST_F(ValuePrintingTests, ansiColorsPrimOpApp)
{ {
PrimOp primOp{ PrimOp primOp{.name = "puppy"};
.name = "puppy"
};
Value vPrimOp; Value vPrimOp;
vPrimOp.mkPrimOp(&primOp); vPrimOp.mkPrimOp(&primOp);
Value v; Value v;
v.mkPrimOpApp(&vPrimOp, nullptr); v.mkPrimOpApp(&vPrimOp, nullptr);
test(v, test(v, ANSI_BLUE "«partially applied primop puppy»" ANSI_NORMAL, PrintOptions{.ansiColors = true});
ANSI_BLUE "«partially applied primop puppy»" ANSI_NORMAL,
PrintOptions {
.ansiColors = true
});
} }
TEST_F(ValuePrintingTests, ansiColorsThunk) TEST_F(ValuePrintingTests, ansiColorsThunk)
@ -623,11 +540,7 @@ TEST_F(ValuePrintingTests, ansiColorsThunk)
Value v; Value v;
v.mkThunk(nullptr, nullptr); v.mkThunk(nullptr, nullptr);
test(v, test(v, ANSI_MAGENTA "«thunk»" ANSI_NORMAL, PrintOptions{.ansiColors = true});
ANSI_MAGENTA "«thunk»" ANSI_NORMAL,
PrintOptions {
.ansiColors = true
});
} }
TEST_F(ValuePrintingTests, ansiColorsBlackhole) TEST_F(ValuePrintingTests, ansiColorsBlackhole)
@ -635,11 +548,7 @@ TEST_F(ValuePrintingTests, ansiColorsBlackhole)
Value v; Value v;
v.mkBlackhole(); v.mkBlackhole();
test(v, test(v, ANSI_RED "«potential infinite recursion»" ANSI_NORMAL, PrintOptions{.ansiColors = true});
ANSI_RED "«potential infinite recursion»" ANSI_NORMAL,
PrintOptions {
.ansiColors = true
});
} }
TEST_F(ValuePrintingTests, ansiColorsAttrsRepeated) TEST_F(ValuePrintingTests, ansiColorsAttrsRepeated)
@ -656,11 +565,7 @@ TEST_F(ValuePrintingTests, ansiColorsAttrsRepeated)
Value vAttrs; Value vAttrs;
vAttrs.mkAttrs(builder.finish()); vAttrs.mkAttrs(builder.finish());
test(vAttrs, test(vAttrs, "{ a = { }; b = " ANSI_MAGENTA "«repeated»" ANSI_NORMAL "; }", PrintOptions{.ansiColors = true});
"{ a = { }; b = " ANSI_MAGENTA "«repeated»" ANSI_NORMAL "; }",
PrintOptions {
.ansiColors = true
});
} }
TEST_F(ValuePrintingTests, ansiColorsListRepeated) TEST_F(ValuePrintingTests, ansiColorsListRepeated)
@ -676,11 +581,7 @@ TEST_F(ValuePrintingTests, ansiColorsListRepeated)
Value vList; Value vList;
vList.mkList(list); vList.mkList(list);
test(vList, test(vList, "[ { } " ANSI_MAGENTA "«repeated»" ANSI_NORMAL " ]", PrintOptions{.ansiColors = true});
"[ { } " ANSI_MAGENTA "«repeated»" ANSI_NORMAL " ]",
PrintOptions {
.ansiColors = true
});
} }
TEST_F(ValuePrintingTests, listRepeated) TEST_F(ValuePrintingTests, listRepeated)
@ -696,12 +597,8 @@ TEST_F(ValuePrintingTests, listRepeated)
Value vList; Value vList;
vList.mkList(list); vList.mkList(list);
test(vList, "[ { } «repeated» ]", PrintOptions { }); test(vList, "[ { } «repeated» ]", PrintOptions{});
test(vList, test(vList, "[ { } { } ]", PrintOptions{.trackRepeated = false});
"[ { } { } ]",
PrintOptions {
.trackRepeated = false
});
} }
TEST_F(ValuePrintingTests, ansiColorsAttrsElided) TEST_F(ValuePrintingTests, ansiColorsAttrsElided)
@ -719,12 +616,10 @@ TEST_F(ValuePrintingTests, ansiColorsAttrsElided)
Value vAttrs; Value vAttrs;
vAttrs.mkAttrs(builder.finish()); vAttrs.mkAttrs(builder.finish());
test(vAttrs, test(
"{ one = " ANSI_CYAN "1" ANSI_NORMAL "; " ANSI_FAINT "«1 attribute elided»" ANSI_NORMAL " }", vAttrs,
PrintOptions { "{ one = " ANSI_CYAN "1" ANSI_NORMAL "; " ANSI_FAINT "«1 attribute elided»" ANSI_NORMAL " }",
.ansiColors = true, PrintOptions{.ansiColors = true, .maxAttrs = 1});
.maxAttrs = 1
});
Value vThree; Value vThree;
vThree.mkInt(3); vThree.mkInt(3);
@ -732,12 +627,10 @@ TEST_F(ValuePrintingTests, ansiColorsAttrsElided)
builder.insert(state.symbols.create("three"), &vThree); builder.insert(state.symbols.create("three"), &vThree);
vAttrs.mkAttrs(builder.finish()); vAttrs.mkAttrs(builder.finish());
test(vAttrs, test(
"{ one = " ANSI_CYAN "1" ANSI_NORMAL "; " ANSI_FAINT "«2 attributes elided»" ANSI_NORMAL " }", vAttrs,
PrintOptions { "{ one = " ANSI_CYAN "1" ANSI_NORMAL "; " ANSI_FAINT "«2 attributes elided»" ANSI_NORMAL " }",
.ansiColors = true, PrintOptions{.ansiColors = true, .maxAttrs = 1});
.maxAttrs = 1
});
} }
TEST_F(ValuePrintingTests, ansiColorsListElided) TEST_F(ValuePrintingTests, ansiColorsListElided)
@ -751,37 +644,33 @@ TEST_F(ValuePrintingTests, ansiColorsListElided)
vTwo.mkInt(2); vTwo.mkInt(2);
{ {
auto list = state.buildList(2); auto list = state.buildList(2);
list.elems[0] = &vOne; list.elems[0] = &vOne;
list.elems[1] = &vTwo; list.elems[1] = &vTwo;
Value vList; Value vList;
vList.mkList(list); vList.mkList(list);
test(vList, test(
"[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_FAINT "«1 item elided»" ANSI_NORMAL " ]", vList,
PrintOptions { "[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_FAINT "«1 item elided»" ANSI_NORMAL " ]",
.ansiColors = true, PrintOptions{.ansiColors = true, .maxListItems = 1});
.maxListItems = 1
});
} }
Value vThree; Value vThree;
vThree.mkInt(3); vThree.mkInt(3);
{ {
auto list = state.buildList(3); auto list = state.buildList(3);
list.elems[0] = &vOne; list.elems[0] = &vOne;
list.elems[1] = &vTwo; list.elems[1] = &vTwo;
list.elems[2] = &vThree; list.elems[2] = &vThree;
Value vList; Value vList;
vList.mkList(list); vList.mkList(list);
test(vList, test(
"[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_FAINT "«2 items elided»" ANSI_NORMAL " ]", vList,
PrintOptions { "[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_FAINT "«2 items elided»" ANSI_NORMAL " ]",
.ansiColors = true, PrintOptions{.ansiColors = true, .maxListItems = 1});
.maxListItems = 1
});
} }
} }

View file

@ -1,10 +1,8 @@
#include "nix/expr/attr-path.hh" #include "nix/expr/attr-path.hh"
#include "nix/expr/eval-inline.hh" #include "nix/expr/eval-inline.hh"
namespace nix { namespace nix {
static Strings parseAttrPath(std::string_view s) static Strings parseAttrPath(std::string_view s)
{ {
Strings res; Strings res;
@ -19,18 +17,19 @@ static Strings parseAttrPath(std::string_view s)
while (1) { while (1) {
if (i == s.end()) if (i == s.end())
throw ParseError("missing closing quote in selection path '%1%'", s); throw ParseError("missing closing quote in selection path '%1%'", s);
if (*i == '"') break; if (*i == '"')
break;
cur.push_back(*i++); cur.push_back(*i++);
} }
} else } else
cur.push_back(*i); cur.push_back(*i);
++i; ++i;
} }
if (!cur.empty()) res.push_back(cur); if (!cur.empty())
res.push_back(cur);
return res; return res;
} }
std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s) std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s)
{ {
std::vector<Symbol> res; std::vector<Symbol> res;
@ -39,9 +38,8 @@ std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s)
return res; return res;
} }
std::pair<Value *, PosIdx>
std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::string & attrPath, findAlongAttrPath(EvalState & state, const std::string & attrPath, Bindings & autoArgs, Value & vIn)
Bindings & autoArgs, Value & vIn)
{ {
Strings tokens = parseAttrPath(attrPath); Strings tokens = parseAttrPath(attrPath);
@ -65,10 +63,12 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
if (!attrIndex) { if (!attrIndex) {
if (v->type() != nAttrs) if (v->type() != nAttrs)
state.error<TypeError>( state
"the expression selected by the selection path '%1%' should be a set but is %2%", .error<TypeError>(
attrPath, "the expression selected by the selection path '%1%' should be a set but is %2%",
showType(*v)).debugThrow(); attrPath,
showType(*v))
.debugThrow();
if (attr.empty()) if (attr.empty())
throw Error("empty attribute name in selection path '%1%'", attrPath); 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])); attrNames.insert(std::string(state.symbols[attr.name]));
auto suggestions = Suggestions::bestMatches(attrNames, attr); 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; v = &*a->value;
pos = a->pos; pos = a->pos;
@ -88,23 +89,23 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
else { else {
if (!v->isList()) if (!v->isList())
state.error<TypeError>( state
"the expression selected by the selection path '%1%' should be a list but is %2%", .error<TypeError>(
attrPath, "the expression selected by the selection path '%1%' should be a list but is %2%",
showType(*v)).debugThrow(); attrPath,
showType(*v))
.debugThrow();
if (*attrIndex >= v->listSize()) if (*attrIndex >= v->listSize())
throw AttrPathNotFound("list index %1% in selection path '%2%' is out of range", *attrIndex, attrPath); throw AttrPathNotFound("list index %1% in selection path '%2%' is out of range", *attrIndex, attrPath);
v = v->listElems()[*attrIndex]; v = v->listElems()[*attrIndex];
pos = noPos; pos = noPos;
} }
} }
return {v, pos}; return {v, pos};
} }
std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v, std::string what) std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v, std::string what)
{ {
Value * v2; Value * v2;
@ -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 // FIXME: is it possible to extract the Pos object instead of doing this
// toString + parsing? // toString + parsing?
NixStringContext context; 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 fn = path.path.abs();
auto fail = [fn]() { auto fail = [fn]() { throw ParseError("cannot parse 'meta.position' attribute '%s'", fn); };
throw ParseError("cannot parse 'meta.position' attribute '%s'", fn);
};
try { try {
auto colon = fn.rfind(':'); 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)); auto lineno = std::stoi(std::string(fn, colon + 1, std::string::npos));
return {SourcePath{path.accessor, CanonPath(fn.substr(0, colon))}, lineno}; return {SourcePath{path.accessor, CanonPath(fn.substr(0, colon))}, lineno};
} catch (std::invalid_argument & e) { } catch (std::invalid_argument & e) {
@ -137,5 +138,4 @@ std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v
} }
} }
} // namespace nix
}

View file

@ -3,11 +3,8 @@
#include <algorithm> #include <algorithm>
namespace nix { namespace nix {
/* Allocate a new array of attributes for an attribute set with a specific /* Allocate a new array of attributes for an attribute set with a specific
capacity. The space is implicitly reserved after the Bindings capacity. The space is implicitly reserved after the Bindings
structure. */ structure. */
@ -22,7 +19,6 @@ Bindings * EvalState::allocBindings(size_t capacity)
return new (allocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings((Bindings::size_t) capacity); return new (allocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings((Bindings::size_t) capacity);
} }
Value & BindingsBuilder::alloc(Symbol name, PosIdx pos) Value & BindingsBuilder::alloc(Symbol name, PosIdx pos)
{ {
auto value = state.allocValue(); auto value = state.allocValue();
@ -30,24 +26,21 @@ Value & BindingsBuilder::alloc(Symbol name, PosIdx pos)
return *value; return *value;
} }
Value & BindingsBuilder::alloc(std::string_view name, PosIdx pos) Value & BindingsBuilder::alloc(std::string_view name, PosIdx pos)
{ {
return alloc(state.symbols.create(name), pos); return alloc(state.symbols.create(name), pos);
} }
void Bindings::sort() void Bindings::sort()
{ {
if (size_) std::sort(begin(), end()); if (size_)
std::sort(begin(), end());
} }
Value & Value::mkAttrs(BindingsBuilder & bindings) Value & Value::mkAttrs(BindingsBuilder & bindings)
{ {
mkAttrs(bindings.finish()); mkAttrs(bindings.finish());
return *this; return *this;
} }
} // namespace nix
}

View file

@ -11,8 +11,10 @@ namespace nix::eval_cache {
CachedEvalError::CachedEvalError(ref<AttrCursor> cursor, Symbol attr) CachedEvalError::CachedEvalError(ref<AttrCursor> cursor, Symbol attr)
: EvalError(cursor->root->state, "cached failure of attribute '%s'", cursor->getAttrPathStr(attr)) : EvalError(cursor->root->state, "cached failure of attribute '%s'", cursor->getAttrPathStr(attr))
, cursor(cursor), attr(attr) , cursor(cursor)
{ } , attr(attr)
{
}
void CachedEvalError::force() void CachedEvalError::force()
{ {
@ -25,7 +27,8 @@ void CachedEvalError::force()
} }
// Shouldn't happen. // 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( static const char * schema = R"sql(
@ -59,10 +62,7 @@ struct AttrDb
SymbolTable & symbols; SymbolTable & symbols;
AttrDb( AttrDb(const StoreDirConfig & cfg, const Hash & fingerprint, SymbolTable & symbols)
const StoreDirConfig & cfg,
const Hash & fingerprint,
SymbolTable & symbols)
: cfg(cfg) : cfg(cfg)
, _state(std::make_unique<Sync<State>>()) , _state(std::make_unique<Sync<State>>())
, symbols(symbols) , symbols(symbols)
@ -78,17 +78,16 @@ struct AttrDb
state->db.isCache(); state->db.isCache();
state->db.exec(schema); state->db.exec(schema);
state->insertAttribute.create(state->db, state->insertAttribute.create(
"insert or replace into Attributes(parent, name, type, value) values (?, ?, ?, ?)"); state->db, "insert or replace into Attributes(parent, name, type, value) values (?, ?, ?, ?)");
state->insertAttributeWithContext.create(state->db, state->insertAttributeWithContext.create(
"insert or replace into Attributes(parent, name, type, value, context) values (?, ?, ?, ?, ?)"); state->db, "insert or replace into Attributes(parent, name, type, value, context) values (?, ?, ?, ?, ?)");
state->queryAttribute.create(state->db, state->queryAttribute.create(
"select rowid, type, value, context from Attributes where parent = ? and name = ?"); state->db, "select rowid, type, value, context from Attributes where parent = ? and name = ?");
state->queryAttributes.create(state->db, state->queryAttributes.create(state->db, "select name from Attributes where parent = ?");
"select name from Attributes where parent = ?");
state->txn = std::make_unique<SQLiteTxn>(state->db); state->txn = std::make_unique<SQLiteTxn>(state->db);
} }
@ -108,7 +107,8 @@ struct AttrDb
template<typename F> template<typename F>
AttrId doSQLite(F && fun) AttrId doSQLite(F && fun)
{ {
if (failed) return 0; if (failed)
return 0;
try { try {
return fun(); return fun();
} catch (SQLiteError &) { } catch (SQLiteError &) {
@ -118,116 +118,76 @@ struct AttrDb
} }
} }
AttrId setAttrs( AttrId setAttrs(AttrKey key, const std::vector<Symbol> & attrs)
AttrKey key,
const std::vector<Symbol> & attrs)
{ {
return doSQLite([&]() return doSQLite([&]() {
{
auto state(_state->lock()); auto state(_state->lock());
state->insertAttribute.use() state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::FullAttrs) (0, false).exec();
(key.first)
(symbols[key.second])
(AttrType::FullAttrs)
(0, false).exec();
AttrId rowId = state->db.getLastInsertedRowId(); AttrId rowId = state->db.getLastInsertedRowId();
assert(rowId); assert(rowId);
for (auto & attr : attrs) for (auto & attr : attrs)
state->insertAttribute.use() state->insertAttribute.use()(rowId)(symbols[attr])(AttrType::Placeholder) (0, false).exec();
(rowId)
(symbols[attr])
(AttrType::Placeholder)
(0, false).exec();
return rowId; return rowId;
}); });
} }
AttrId setString( AttrId setString(AttrKey key, std::string_view s, const char ** context = nullptr)
AttrKey key,
std::string_view s,
const char * * context = nullptr)
{ {
return doSQLite([&]() return doSQLite([&]() {
{
auto state(_state->lock()); auto state(_state->lock());
if (context) { if (context) {
std::string ctx; std::string ctx;
for (const char * * p = context; *p; ++p) { for (const char ** p = context; *p; ++p) {
if (p != context) ctx.push_back(' '); if (p != context)
ctx.push_back(' ');
ctx.append(*p); ctx.append(*p);
} }
state->insertAttributeWithContext.use() state->insertAttributeWithContext.use()(key.first)(symbols[key.second])(AttrType::String) (s) (ctx)
(key.first) .exec();
(symbols[key.second])
(AttrType::String)
(s)
(ctx).exec();
} else { } else {
state->insertAttribute.use() state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::String) (s).exec();
(key.first)
(symbols[key.second])
(AttrType::String)
(s).exec();
} }
return state->db.getLastInsertedRowId(); return state->db.getLastInsertedRowId();
}); });
} }
AttrId setBool( AttrId setBool(AttrKey key, bool b)
AttrKey key,
bool b)
{ {
return doSQLite([&]() return doSQLite([&]() {
{
auto state(_state->lock()); auto state(_state->lock());
state->insertAttribute.use() state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::Bool) (b ? 1 : 0).exec();
(key.first)
(symbols[key.second])
(AttrType::Bool)
(b ? 1 : 0).exec();
return state->db.getLastInsertedRowId(); return state->db.getLastInsertedRowId();
}); });
} }
AttrId setInt( AttrId setInt(AttrKey key, int n)
AttrKey key,
int n)
{ {
return doSQLite([&]() return doSQLite([&]() {
{
auto state(_state->lock()); auto state(_state->lock());
state->insertAttribute.use() state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::Int) (n).exec();
(key.first)
(symbols[key.second])
(AttrType::Int)
(n).exec();
return state->db.getLastInsertedRowId(); return state->db.getLastInsertedRowId();
}); });
} }
AttrId setListOfStrings( AttrId setListOfStrings(AttrKey key, const std::vector<std::string> & l)
AttrKey key,
const std::vector<std::string> & l)
{ {
return doSQLite([&]() return doSQLite([&]() {
{
auto state(_state->lock()); auto state(_state->lock());
state->insertAttribute.use() state->insertAttribute
(key.first) .use()(key.first)(symbols[key.second])(
(symbols[key.second]) AttrType::ListOfStrings) (dropEmptyInitThenConcatStringsSep("\t", l))
(AttrType::ListOfStrings) .exec();
(dropEmptyInitThenConcatStringsSep("\t", l)).exec();
return state->db.getLastInsertedRowId(); return state->db.getLastInsertedRowId();
}); });
@ -235,15 +195,10 @@ struct AttrDb
AttrId setPlaceholder(AttrKey key) AttrId setPlaceholder(AttrKey key)
{ {
return doSQLite([&]() return doSQLite([&]() {
{
auto state(_state->lock()); auto state(_state->lock());
state->insertAttribute.use() state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::Placeholder) (0, false).exec();
(key.first)
(symbols[key.second])
(AttrType::Placeholder)
(0, false).exec();
return state->db.getLastInsertedRowId(); return state->db.getLastInsertedRowId();
}); });
@ -251,15 +206,10 @@ struct AttrDb
AttrId setMissing(AttrKey key) AttrId setMissing(AttrKey key)
{ {
return doSQLite([&]() return doSQLite([&]() {
{
auto state(_state->lock()); auto state(_state->lock());
state->insertAttribute.use() state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::Missing) (0, false).exec();
(key.first)
(symbols[key.second])
(AttrType::Missing)
(0, false).exec();
return state->db.getLastInsertedRowId(); return state->db.getLastInsertedRowId();
}); });
@ -267,15 +217,10 @@ struct AttrDb
AttrId setMisc(AttrKey key) AttrId setMisc(AttrKey key)
{ {
return doSQLite([&]() return doSQLite([&]() {
{
auto state(_state->lock()); auto state(_state->lock());
state->insertAttribute.use() state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::Misc) (0, false).exec();
(key.first)
(symbols[key.second])
(AttrType::Misc)
(0, false).exec();
return state->db.getLastInsertedRowId(); return state->db.getLastInsertedRowId();
}); });
@ -283,15 +228,10 @@ struct AttrDb
AttrId setFailed(AttrKey key) AttrId setFailed(AttrKey key)
{ {
return doSQLite([&]() return doSQLite([&]() {
{
auto state(_state->lock()); auto state(_state->lock());
state->insertAttribute.use() state->insertAttribute.use()(key.first)(symbols[key.second])(AttrType::Failed) (0, false).exec();
(key.first)
(symbols[key.second])
(AttrType::Failed)
(0, false).exec();
return state->db.getLastInsertedRowId(); return state->db.getLastInsertedRowId();
}); });
@ -302,51 +242,49 @@ struct AttrDb
auto state(_state->lock()); auto state(_state->lock());
auto queryAttribute(state->queryAttribute.use()(key.first)(symbols[key.second])); 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 rowId = (AttrId) queryAttribute.getInt(0);
auto type = (AttrType) queryAttribute.getInt(1); auto type = (AttrType) queryAttribute.getInt(1);
switch (type) { switch (type) {
case AttrType::Placeholder: case AttrType::Placeholder:
return {{rowId, placeholder_t()}}; return {{rowId, placeholder_t()}};
case AttrType::FullAttrs: { case AttrType::FullAttrs: {
// FIXME: expensive, should separate this out. // FIXME: expensive, should separate this out.
std::vector<Symbol> attrs; std::vector<Symbol> attrs;
auto queryAttributes(state->queryAttributes.use()(rowId)); auto queryAttributes(state->queryAttributes.use()(rowId));
while (queryAttributes.next()) while (queryAttributes.next())
attrs.emplace_back(symbols.create(queryAttributes.getStr(0))); attrs.emplace_back(symbols.create(queryAttributes.getStr(0)));
return {{rowId, attrs}}; return {{rowId, attrs}};
} }
case AttrType::String: { case AttrType::String: {
NixStringContext context; NixStringContext context;
if (!queryAttribute.isNull(3)) if (!queryAttribute.isNull(3))
for (auto & s : tokenizeString<std::vector<std::string>>(queryAttribute.getStr(3), ";")) for (auto & s : tokenizeString<std::vector<std::string>>(queryAttribute.getStr(3), ";"))
context.insert(NixStringContextElem::parse(s)); context.insert(NixStringContextElem::parse(s));
return {{rowId, string_t{queryAttribute.getStr(2), context}}}; return {{rowId, string_t{queryAttribute.getStr(2), context}}};
} }
case AttrType::Bool: case AttrType::Bool:
return {{rowId, queryAttribute.getInt(2) != 0}}; return {{rowId, queryAttribute.getInt(2) != 0}};
case AttrType::Int: case AttrType::Int:
return {{rowId, int_t{NixInt{queryAttribute.getInt(2)}}}}; return {{rowId, int_t{NixInt{queryAttribute.getInt(2)}}}};
case AttrType::ListOfStrings: case AttrType::ListOfStrings:
return {{rowId, tokenizeString<std::vector<std::string>>(queryAttribute.getStr(2), "\t")}}; return {{rowId, tokenizeString<std::vector<std::string>>(queryAttribute.getStr(2), "\t")}};
case AttrType::Missing: case AttrType::Missing:
return {{rowId, missing_t()}}; return {{rowId, missing_t()}};
case AttrType::Misc: case AttrType::Misc:
return {{rowId, misc_t()}}; return {{rowId, misc_t()}};
case AttrType::Failed: case AttrType::Failed:
return {{rowId, failed_t()}}; return {{rowId, failed_t()}};
default: default:
throw Error("unexpected type in evaluation cache"); throw Error("unexpected type in evaluation cache");
} }
} }
}; };
static std::shared_ptr<AttrDb> makeAttrDb( static std::shared_ptr<AttrDb> makeAttrDb(const StoreDirConfig & cfg, const Hash & fingerprint, SymbolTable & symbols)
const StoreDirConfig & cfg,
const Hash & fingerprint,
SymbolTable & symbols)
{ {
try { try {
return std::make_shared<AttrDb>(cfg, fingerprint, symbols); return std::make_shared<AttrDb>(cfg, fingerprint, symbols);
@ -357,9 +295,7 @@ static std::shared_ptr<AttrDb> makeAttrDb(
} }
EvalCache::EvalCache( EvalCache::EvalCache(
std::optional<std::reference_wrapper<const Hash>> useCache, std::optional<std::reference_wrapper<const Hash>> useCache, EvalState & state, RootLoader rootLoader)
EvalState & state,
RootLoader rootLoader)
: db(useCache ? makeAttrDb(*state.store, *useCache, state.symbols) : nullptr) : db(useCache ? makeAttrDb(*state.store, *useCache, state.symbols) : nullptr)
, state(state) , state(state)
, rootLoader(rootLoader) , rootLoader(rootLoader)
@ -381,11 +317,10 @@ ref<AttrCursor> EvalCache::getRoot()
} }
AttrCursor::AttrCursor( AttrCursor::AttrCursor(
ref<EvalCache> root, ref<EvalCache> root, Parent parent, Value * value, std::optional<std::pair<AttrId, AttrValue>> && cachedValue)
Parent parent, : root(root)
Value * value, , parent(parent)
std::optional<std::pair<AttrId, AttrValue>> && cachedValue) , cachedValue(std::move(cachedValue))
: root(root), parent(parent), cachedValue(std::move(cachedValue))
{ {
if (value) if (value)
_value = allocRootValue(value); _value = allocRootValue(value);
@ -470,13 +405,11 @@ Value & AttrCursor::forceValue()
if (root->db && (!cachedValue || std::get_if<placeholder_t>(&cachedValue->second))) { if (root->db && (!cachedValue || std::get_if<placeholder_t>(&cachedValue->second))) {
if (v.type() == nString) if (v.type() == nString)
cachedValue = {root->db->setString(getKey(), v.c_str(), v.context()), cachedValue = {root->db->setString(getKey(), v.c_str(), v.context()), string_t{v.c_str(), {}}};
string_t{v.c_str(), {}}};
else if (v.type() == nPath) { else if (v.type() == nPath) {
auto path = v.path().path; auto path = v.path().path;
cachedValue = {root->db->setString(getKey(), path.abs()), string_t{path.abs(), {}}}; 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()}; cachedValue = {root->db->setBool(getKey(), v.boolean()), v.boolean()};
else if (v.type() == nInt) else if (v.type() == nInt)
cachedValue = {root->db->setInt(getKey(), v.integer().value), int_t{v.integer()}}; cachedValue = {root->db->setInt(getKey(), v.integer().value), int_t{v.integer()}};
@ -518,14 +451,14 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name)
else if (std::get_if<failed_t>(&attr->second)) else if (std::get_if<failed_t>(&attr->second))
throw CachedEvalError(ref(shared_from_this()), name); throw CachedEvalError(ref(shared_from_this()), name);
else else
return std::make_shared<AttrCursor>(root, return std::make_shared<AttrCursor>(
std::make_pair(ref(shared_from_this()), name), nullptr, std::move(attr)); root, std::make_pair(ref(shared_from_this()), name), nullptr, std::move(attr));
} }
// Incomplete attrset, so need to fall thru and // Incomplete attrset, so need to fall thru and
// evaluate to see whether 'name' exists // evaluate to see whether 'name' exists
} else } else
return nullptr; return nullptr;
//error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); // error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
} }
} }
@ -533,7 +466,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name)
if (v.type() != nAttrs) if (v.type() != nAttrs)
return nullptr; 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); auto attr = v.attrs()->get(name);
@ -618,17 +551,15 @@ string_t AttrCursor::getStringWithContext()
if (auto s = std::get_if<string_t>(&cachedValue->second)) { if (auto s = std::get_if<string_t>(&cachedValue->second)) {
bool valid = true; bool valid = true;
for (auto & c : s->second) { for (auto & c : s->second) {
const StorePath & path = std::visit(overloaded { const StorePath & path = std::visit(
[&](const NixStringContextElem::DrvDeep & d) -> const StorePath & { overloaded{
return d.drvPath; [&](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 & { c.raw);
return b.drvPath->getBaseStorePath();
},
[&](const NixStringContextElem::Opaque & o) -> const StorePath & {
return o.path;
},
}, c.raw);
if (!root->state.store->isValidPath(path)) { if (!root->state.store->isValidPath(path)) {
valid = false; valid = false;
break; break;
@ -649,8 +580,7 @@ string_t AttrCursor::getStringWithContext()
NixStringContext context; NixStringContext context;
copyContext(v, context); copyContext(v, context);
return {v.c_str(), std::move(context)}; return {v.c_str(), std::move(context)};
} } else if (v.type() == nPath)
else if (v.type() == nPath)
return {v.path().to_string(), {}}; return {v.path().to_string(), {}};
else else
root->state.error<TypeError>("'%s' is not a string but %s", getAttrPathStr(), showType(v)).debugThrow(); root->state.error<TypeError>("'%s' is not a string but %s", getAttrPathStr(), showType(v)).debugThrow();
@ -722,7 +652,8 @@ std::vector<std::string> AttrCursor::getListOfStrings()
std::vector<std::string> res; std::vector<std::string> res;
for (auto & elem : v.listItems()) for (auto & elem : v.listItems())
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) if (root->db)
cachedValue = {root->db->setListOfStrings(getKey(), res), res}; cachedValue = {root->db->setListOfStrings(getKey(), res), res};
@ -778,10 +709,10 @@ StorePath AttrCursor::forceDerivation()
been garbage-collected. So force it to be regenerated. */ been garbage-collected. So force it to be regenerated. */
aDrvPath->forceValue(); aDrvPath->forceValue();
if (!root->state.store->isValidPath(drvPath)) if (!root->state.store->isValidPath(drvPath))
throw Error("don't know how to recreate store derivation '%s'!", throw Error(
root->state.store->printStorePath(drvPath)); "don't know how to recreate store derivation '%s'!", root->state.store->printStorePath(drvPath));
} }
return 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. // NOTE: This is abusing side-effects.
// TODO: check compatibility with nested debugger calls. // TODO: check compatibility with nested debugger calls.
// TODO: What side-effects?? // TODO: What side-effects??
error.state.debugTraces.push_front(DebugTrace{ error.state.debugTraces.push_front(
.pos = expr.getPos(), DebugTrace{
.expr = expr, .pos = expr.getPos(),
.env = env, .expr = expr,
.hint = HintFmt("Fake frame for debugging purposes"), .env = env,
.isError = true}); .hint = HintFmt("Fake frame for debugging purposes"),
.isError = true});
return *this; return *this;
} }
@ -96,7 +97,8 @@ template<class T>
void EvalErrorBuilder<T>::panic() void EvalErrorBuilder<T>::panic()
{ {
logError(error.info()); 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(); abort();
} }
@ -112,4 +114,4 @@ template class EvalErrorBuilder<InfiniteRecursionError>;
template class EvalErrorBuilder<InvalidPathError>; template class EvalErrorBuilder<InvalidPathError>;
template class EvalErrorBuilder<IFDError>; template class EvalErrorBuilder<IFDError>;
} } // namespace nix

View file

@ -19,12 +19,14 @@ Strings EvalSettings::parseNixPath(const std::string & s)
auto start2 = p; auto start2 = p;
while (p != s.end() && *p != ':') { while (p != s.end() && *p != ':') {
if (*p == '=') start2 = p + 1; if (*p == '=')
start2 = p + 1;
++p; ++p;
} }
if (p == s.end()) { 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; break;
} }
@ -32,10 +34,12 @@ Strings EvalSettings::parseNixPath(const std::string & s)
auto prefix = std::string(start2, s.end()); auto prefix = std::string(start2, s.end());
if (EvalSettings::isPseudoUrl(prefix) || hasPrefix(prefix, "flake:")) { if (EvalSettings::isPseudoUrl(prefix) || hasPrefix(prefix, "flake:")) {
++p; ++p;
while (p != s.end() && *p != ':') ++p; while (p != s.end() && *p != ':')
++p;
} }
res.push_back(std::string(start, p)); res.push_back(std::string(start, p));
if (p == s.end()) break; if (p == s.end())
break;
} }
++p; ++p;
@ -75,11 +79,14 @@ Strings EvalSettings::getDefaultNixPath()
bool EvalSettings::isPseudoUrl(std::string_view s) 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("://"); 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); 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) std::string EvalSettings::resolvePseudoUrl(std::string_view url)
@ -98,9 +105,7 @@ const std::string & EvalSettings::getCurrentSystem() const
Path getNixDefExpr() Path getNixDefExpr()
{ {
return settings.useXDGBaseDirectories return settings.useXDGBaseDirectories ? getStateDir() + "/defexpr" : getHome() + "/.nix-defexpr";
? getStateDir() + "/defexpr"
: getHome() + "/.nix-defexpr";
} }
} // namespace nix } // namespace nix

File diff suppressed because it is too large Load diff

View file

@ -3,16 +3,19 @@
namespace nix { namespace nix {
FunctionCallTrace::FunctionCallTrace(const Pos & pos) : pos(pos) { FunctionCallTrace::FunctionCallTrace(const Pos & pos)
: pos(pos)
{
auto duration = std::chrono::high_resolution_clock::now().time_since_epoch(); auto duration = std::chrono::high_resolution_clock::now().time_since_epoch();
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration); auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
printMsg(lvlInfo, "function-trace entered %1% at %2%", pos, ns.count()); printMsg(lvlInfo, "function-trace entered %1% at %2%", pos, ns.count());
} }
FunctionCallTrace::~FunctionCallTrace() { FunctionCallTrace::~FunctionCallTrace()
{
auto duration = std::chrono::high_resolution_clock::now().time_since_epoch(); auto duration = std::chrono::high_resolution_clock::now().time_since_epoch();
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration); auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
printMsg(lvlInfo, "function-trace exited %1% at %2%", pos, ns.count()); printMsg(lvlInfo, "function-trace exited %1% at %2%", pos, ns.count());
} }
} } // namespace nix

View file

@ -7,18 +7,19 @@
#include <cstring> #include <cstring>
#include <regex> #include <regex>
namespace nix { namespace nix {
PackageInfo::PackageInfo(EvalState & state, std::string attrPath, const Bindings * attrs) 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) 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); auto [drvPath, selectedOutputs] = parsePathWithOutputs(*store, drvPathWithOutputs);
@ -31,10 +32,7 @@ PackageInfo::PackageInfo(EvalState & state, ref<Store> store, const std::string
if (selectedOutputs.size() > 1) if (selectedOutputs.size() > 1)
throw Error("building more than one derivation output is not supported, in '%s'", drvPathWithOutputs); throw Error("building more than one derivation output is not supported, in '%s'", drvPathWithOutputs);
outputName = outputName = selectedOutputs.empty() ? getOr(drv.env, "outputName", "out") : *selectedOutputs.begin();
selectedOutputs.empty()
? getOr(drv.env, "outputName", "out")
: *selectedOutputs.begin();
auto i = drv.outputs.find(outputName); auto i = drv.outputs.find(outputName);
if (i == drv.outputs.end()) if (i == drv.outputs.end())
@ -44,34 +42,36 @@ PackageInfo::PackageInfo(EvalState & state, ref<Store> store, const std::string
outPath = {output.path(*store, drv.name, outputName)}; outPath = {output.path(*store, drv.name, outputName)};
} }
std::string PackageInfo::queryName() const std::string PackageInfo::queryName() const
{ {
if (name == "" && attrs) { if (name == "" && attrs) {
auto i = attrs->find(state->sName); auto i = attrs->find(state->sName);
if (i == attrs->end()) state->error<TypeError>("derivation name missing").debugThrow(); if (i == attrs->end())
state->error<TypeError>("derivation name missing").debugThrow();
name = state->forceStringNoCtx(*i->value, noPos, "while evaluating the 'name' attribute of a derivation"); name = state->forceStringNoCtx(*i->value, noPos, "while evaluating the 'name' attribute of a derivation");
} }
return name; return name;
} }
std::string PackageInfo::querySystem() const std::string PackageInfo::querySystem() const
{ {
if (system == "" && attrs) { if (system == "" && attrs) {
auto i = attrs->find(state->sSystem); 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"); system =
i == attrs->end()
? "unknown"
: state->forceStringNoCtx(*i->value, i->pos, "while evaluating the 'system' attribute of a derivation");
} }
return system; return system;
} }
std::optional<StorePath> PackageInfo::queryDrvPath() const std::optional<StorePath> PackageInfo::queryDrvPath() const
{ {
if (!drvPath && attrs) { if (!drvPath && attrs) {
if (auto i = attrs->get(state->sDrvPath)) { if (auto i = attrs->get(state->sDrvPath)) {
NixStringContext context; 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 { try {
found.requireDerivation(); found.requireDerivation();
} catch (Error & e) { } catch (Error & e) {
@ -85,7 +85,6 @@ std::optional<StorePath> PackageInfo::queryDrvPath() const
return drvPath.value_or(std::nullopt); return drvPath.value_or(std::nullopt);
} }
StorePath PackageInfo::requireDrvPath() const StorePath PackageInfo::requireDrvPath() const
{ {
if (auto drvPath = queryDrvPath()) if (auto drvPath = queryDrvPath())
@ -93,21 +92,20 @@ StorePath PackageInfo::requireDrvPath() const
throw Error("derivation does not contain a 'drvPath' attribute"); throw Error("derivation does not contain a 'drvPath' attribute");
} }
StorePath PackageInfo::queryOutPath() const StorePath PackageInfo::queryOutPath() const
{ {
if (!outPath && attrs) { if (!outPath && attrs) {
auto i = attrs->find(state->sOutPath); auto i = attrs->find(state->sOutPath);
NixStringContext context; NixStringContext context;
if (i != attrs->end()) if (i != attrs->end())
outPath = state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the output path of a derivation"); outPath = state->coerceToStorePath(
i->pos, *i->value, context, "while evaluating the output path of a derivation");
} }
if (!outPath) if (!outPath)
throw UnimplementedError("CA derivations are not yet supported"); throw UnimplementedError("CA derivations are not yet supported");
return *outPath; return *outPath;
} }
PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall) PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall)
{ {
if (outputs.empty()) { if (outputs.empty()) {
@ -118,19 +116,25 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT
/* For each output... */ /* For each output... */
for (auto elem : i->value->listItems()) { for (auto elem : i->value->listItems()) {
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) { if (withPaths) {
/* Evaluate the corresponding set. */ /* Evaluate the corresponding set. */
auto out = attrs->get(state->symbols.create(output)); 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"); state->forceAttrs(*out->value, i->pos, "while evaluating an output of a derivation");
/* And evaluate its outPath attribute. */ /* And evaluate its outPath attribute. */
auto outPath = out->value->attrs()->get(state->sOutPath); auto outPath = out->value->attrs()->get(state->sOutPath);
if (!outPath) continue; // FIXME: throw error? if (!outPath)
continue; // FIXME: throw error?
NixStringContext context; 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 } else
outputs.emplace(output, std::nullopt); outputs.emplace(output, std::nullopt);
} }
@ -142,7 +146,8 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT
return outputs; return outputs;
const Attr * i; 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->sOutputSpecified))
&& state->forceBool(*i->value, i->pos, "while evaluating the 'outputSpecified' attribute of a derivation")) {
Outputs result; Outputs result;
auto out = outputs.find(queryOutputName()); auto out = outputs.find(queryOutputName());
if (out == outputs.end()) if (out == outputs.end())
@ -154,95 +159,103 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT
else { else {
/* Check for `meta.outputsToInstall` and return `outputs` reduced to that. */ /* Check for `meta.outputsToInstall` and return `outputs` reduced to that. */
const Value * outTI = queryMeta("outputsToInstall"); const Value * outTI = queryMeta("outputsToInstall");
if (!outTI) return outputs; if (!outTI)
return outputs;
auto errMsg = Error("this derivation has bad 'meta.outputsToInstall'"); auto errMsg = Error("this derivation has bad 'meta.outputsToInstall'");
/* ^ this shows during `nix-env -i` right under the bad derivation */ /* ^ this shows during `nix-env -i` right under the bad derivation */
if (!outTI->isList()) throw errMsg; if (!outTI->isList())
throw errMsg;
Outputs result; Outputs result;
for (auto elem : outTI->listItems()) { for (auto elem : outTI->listItems()) {
if (elem->type() != nString) throw errMsg; if (elem->type() != nString)
throw errMsg;
auto out = outputs.find(elem->c_str()); auto out = outputs.find(elem->c_str());
if (out == outputs.end()) throw errMsg; if (out == outputs.end())
throw errMsg;
result.insert(*out); result.insert(*out);
} }
return result; return result;
} }
} }
std::string PackageInfo::queryOutputName() const std::string PackageInfo::queryOutputName() const
{ {
if (outputName == "" && attrs) { if (outputName == "" && attrs) {
auto i = attrs->get(state->sOutputName); auto i = attrs->get(state->sOutputName);
outputName = i ? state->forceStringNoCtx(*i->value, noPos, "while evaluating the output name of a derivation") : ""; outputName =
i ? state->forceStringNoCtx(*i->value, noPos, "while evaluating the output name of a derivation") : "";
} }
return outputName; return outputName;
} }
const Bindings * PackageInfo::getMeta() const Bindings * PackageInfo::getMeta()
{ {
if (meta) return meta; if (meta)
if (!attrs) return 0; return meta;
if (!attrs)
return 0;
auto a = attrs->get(state->sMeta); auto a = attrs->get(state->sMeta);
if (!a) return 0; if (!a)
return 0;
state->forceAttrs(*a->value, a->pos, "while evaluating the 'meta' attribute of a derivation"); state->forceAttrs(*a->value, a->pos, "while evaluating the 'meta' attribute of a derivation");
meta = a->value->attrs(); meta = a->value->attrs();
return meta; return meta;
} }
StringSet PackageInfo::queryMetaNames() StringSet PackageInfo::queryMetaNames()
{ {
StringSet res; StringSet res;
if (!getMeta()) return res; if (!getMeta())
return res;
for (auto & i : *meta) for (auto & i : *meta)
res.emplace(state->symbols[i.name]); res.emplace(state->symbols[i.name]);
return res; return res;
} }
bool PackageInfo::checkMeta(Value & v) bool PackageInfo::checkMeta(Value & v)
{ {
state->forceValue(v, v.determinePos(noPos)); state->forceValue(v, v.determinePos(noPos));
if (v.type() == nList) { if (v.type() == nList) {
for (auto elem : v.listItems()) for (auto elem : v.listItems())
if (!checkMeta(*elem)) return false; if (!checkMeta(*elem))
return false;
return true; return true;
} } else if (v.type() == nAttrs) {
else if (v.type() == nAttrs) { if (v.attrs()->get(state->sOutPath))
if (v.attrs()->get(state->sOutPath)) return false; return false;
for (auto & i : *v.attrs()) for (auto & i : *v.attrs())
if (!checkMeta(*i.value)) return false; if (!checkMeta(*i.value))
return false;
return true; return true;
} } else
else return v.type() == nInt || v.type() == nBool || v.type() == nString || return v.type() == nInt || v.type() == nBool || v.type() == nString || v.type() == nFloat;
v.type() == nFloat;
} }
Value * PackageInfo::queryMeta(const std::string & name) Value * PackageInfo::queryMeta(const std::string & name)
{ {
if (!getMeta()) return 0; if (!getMeta())
return 0;
auto a = meta->get(state->symbols.create(name)); 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; return a->value;
} }
std::string PackageInfo::queryMetaString(const std::string & name) std::string PackageInfo::queryMetaString(const std::string & name)
{ {
Value * v = queryMeta(name); Value * v = queryMeta(name);
if (!v || v->type() != nString) return ""; if (!v || v->type() != nString)
return "";
return v->c_str(); return v->c_str();
} }
NixInt PackageInfo::queryMetaInt(const std::string & name, NixInt def) NixInt PackageInfo::queryMetaInt(const std::string & name, NixInt def)
{ {
Value * v = queryMeta(name); Value * v = queryMeta(name);
if (!v) return def; if (!v)
if (v->type() == nInt) return v->integer(); return def;
if (v->type() == nInt)
return v->integer();
if (v->type() == nString) { if (v->type() == nString) {
/* Backwards compatibility with before we had support for /* Backwards compatibility with before we had support for
integer meta fields. */ integer meta fields. */
@ -255,8 +268,10 @@ NixInt PackageInfo::queryMetaInt(const std::string & name, NixInt def)
NixFloat PackageInfo::queryMetaFloat(const std::string & name, NixFloat def) NixFloat PackageInfo::queryMetaFloat(const std::string & name, NixFloat def)
{ {
Value * v = queryMeta(name); Value * v = queryMeta(name);
if (!v) return def; if (!v)
if (v->type() == nFloat) return v->fpoint(); return def;
if (v->type() == nFloat)
return v->fpoint();
if (v->type() == nString) { if (v->type() == nString) {
/* Backwards compatibility with before we had support for /* Backwards compatibility with before we had support for
float meta fields. */ float meta fields. */
@ -266,22 +281,24 @@ NixFloat PackageInfo::queryMetaFloat(const std::string & name, NixFloat def)
return def; return def;
} }
bool PackageInfo::queryMetaBool(const std::string & name, bool def) bool PackageInfo::queryMetaBool(const std::string & name, bool def)
{ {
Value * v = queryMeta(name); Value * v = queryMeta(name);
if (!v) return def; if (!v)
if (v->type() == nBool) return v->boolean(); return def;
if (v->type() == nBool)
return v->boolean();
if (v->type() == nString) { if (v->type() == nString) {
/* Backwards compatibility with before we had support for /* Backwards compatibility with before we had support for
Boolean meta fields. */ Boolean meta fields. */
if (v->string_view() == "true") return true; if (v->string_view() == "true")
if (v->string_view() == "false") return false; return true;
if (v->string_view() == "false")
return false;
} }
return def; return def;
} }
void PackageInfo::setMeta(const std::string & name, Value * v) void PackageInfo::setMeta(const std::string & name, Value * v)
{ {
getMeta(); getMeta();
@ -291,30 +308,35 @@ void PackageInfo::setMeta(const std::string & name, Value * v)
for (auto i : *meta) for (auto i : *meta)
if (i.name != sym) if (i.name != sym)
attrs.insert(i); attrs.insert(i);
if (v) attrs.insert(sym, v); if (v)
attrs.insert(sym, v);
meta = attrs.finish(); meta = attrs.finish();
} }
/* Cache for already considered attrsets. */ /* Cache for already considered attrsets. */
typedef std::set<const Bindings *> Done; typedef std::set<const Bindings *> Done;
/* Evaluate value `v'. If it evaluates to a set of type `derivation', /* 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'). then put information about it in `drvs' (unless it's already in `done').
The result boolean indicates whether it makes sense The result boolean indicates whether it makes sense
for the caller to recursively search for derivations in `v'. */ for the caller to recursively search for derivations in `v'. */
static bool getDerivation(EvalState & state, Value & v, static bool getDerivation(
const std::string & attrPath, PackageInfos & drvs, Done & done, EvalState & state,
Value & v,
const std::string & attrPath,
PackageInfos & drvs,
Done & done,
bool ignoreAssertionFailures) bool ignoreAssertionFailures)
{ {
try { try {
state.forceValue(v, v.determinePos(noPos)); 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 = /* Remove spurious duplicates (e.g., a set like `rec { x =
derivation {...}; y = 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()); PackageInfo drv(state, attrPath, v.attrs());
@ -325,42 +347,44 @@ static bool getDerivation(EvalState & state, Value & v,
return false; return false;
} catch (AssertionError & e) { } catch (AssertionError & e) {
if (ignoreAssertionFailures) return false; if (ignoreAssertionFailures)
return false;
throw; throw;
} }
} }
std::optional<PackageInfo> getDerivation(EvalState & state, Value & v, bool ignoreAssertionFailures)
std::optional<PackageInfo> getDerivation(EvalState & state, Value & v,
bool ignoreAssertionFailures)
{ {
Done done; Done done;
PackageInfos drvs; PackageInfos drvs;
getDerivation(state, v, "", drvs, done, ignoreAssertionFailures); getDerivation(state, v, "", drvs, done, ignoreAssertionFailures);
if (drvs.size() != 1) return {}; if (drvs.size() != 1)
return {};
return std::move(drvs.front()); return std::move(drvs.front());
} }
static std::string addToPath(const std::string & s1, std::string_view s2) static std::string addToPath(const std::string & s1, std::string_view s2)
{ {
return s1.empty() ? std::string(s2) : s1 + "." + s2; return s1.empty() ? std::string(s2) : s1 + "." + s2;
} }
static std::regex attrRegex("[A-Za-z_][A-Za-z0-9-_+]*"); static std::regex attrRegex("[A-Za-z_][A-Za-z0-9-_+]*");
static void getDerivations(
static void getDerivations(EvalState & state, Value & vIn, EvalState & state,
const std::string & pathPrefix, Bindings & autoArgs, Value & vIn,
PackageInfos & drvs, Done & done, const std::string & pathPrefix,
Bindings & autoArgs,
PackageInfos & drvs,
Done & done,
bool ignoreAssertionFailures) bool ignoreAssertionFailures)
{ {
Value v; Value v;
state.autoCallFunction(autoArgs, vIn, v); state.autoCallFunction(autoArgs, vIn, v);
/* Process the expression. */ /* Process the expression. */
if (!getDerivation(state, v, pathPrefix, drvs, done, ignoreAssertionFailures)) ; if (!getDerivation(state, v, pathPrefix, drvs, done, ignoreAssertionFailures))
;
else if (v.type() == nAttrs) { else if (v.type() == nAttrs) {
@ -388,8 +412,11 @@ static void getDerivations(EvalState & state, Value & vIn,
`recurseForDerivations = true' attribute. */ `recurseForDerivations = true' attribute. */
if (i->value->type() == nAttrs) { if (i->value->type() == nAttrs) {
auto j = i->value->attrs()->get(state.sRecurseForDerivations); auto j = i->value->attrs()->get(state.sRecurseForDerivations);
if (j && state.forceBool(*j->value, j->pos, "while evaluating the attribute `recurseForDerivations`")) if (j
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); && state.forceBool(
*j->value, j->pos, "while evaluating the attribute `recurseForDerivations`"))
getDerivations(
state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
} }
} }
} catch (Error & e) { } catch (Error & e) {
@ -411,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(); state.error<TypeError>("expression does not evaluate to a derivation (or a set or list of those)").debugThrow();
} }
void getDerivations(
void getDerivations(EvalState & state, Value & v, const std::string & pathPrefix, EvalState & state,
Bindings & autoArgs, PackageInfos & drvs, bool ignoreAssertionFailures) Value & v,
const std::string & pathPrefix,
Bindings & autoArgs,
PackageInfos & drvs,
bool ignoreAssertionFailures)
{ {
Done done; Done done;
getDerivations(state, v, pathPrefix, autoArgs, drvs, done, ignoreAssertionFailures); getDerivations(state, v, pathPrefix, autoArgs, drvs, done, ignoreAssertionFailures);
} }
} // namespace nix
}

View file

@ -11,11 +11,8 @@ namespace nix {
MakeError(AttrPathNotFound, Error); MakeError(AttrPathNotFound, Error);
MakeError(NoPositionInfo, Error); MakeError(NoPositionInfo, Error);
std::pair<Value *, PosIdx> findAlongAttrPath( std::pair<Value *, PosIdx>
EvalState & state, findAlongAttrPath(EvalState & state, const std::string & attrPath, Bindings & autoArgs, Value & vIn);
const std::string & attrPath,
Bindings & autoArgs,
Value & vIn);
/** /**
* Heuristic to find the filename and lineno or a nix value. * 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); std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s);
} } // namespace nix

View file

@ -8,7 +8,6 @@
namespace nix { namespace nix {
class EvalState; class EvalState;
struct Value; struct Value;
@ -25,15 +24,19 @@ struct Attr
PosIdx pos; PosIdx pos;
Value * value; Value * value;
Attr(Symbol name, Value * value, PosIdx pos = noPos) Attr(Symbol name, Value * value, PosIdx pos = noPos)
: name(name), pos(pos), value(value) { }; : name(name)
Attr() { }; , pos(pos)
auto operator <=> (const Attr & a) const , value(value) {};
Attr() {};
auto operator<=>(const Attr & a) const
{ {
return name <=> a.name; 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. " "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 " "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."); "introduce new fields that need not be present for almost every instance.");
@ -54,13 +57,24 @@ private:
size_t size_, capacity_; size_t size_, capacity_;
Attr attrs[0]; Attr attrs[0];
Bindings(size_t capacity) : size_(0), capacity_(capacity) { } Bindings(size_t capacity)
: size_(0)
, capacity_(capacity)
{
}
Bindings(const Bindings & bindings) = delete; Bindings(const Bindings & bindings) = delete;
public: 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; typedef Attr * iterator;
@ -76,7 +90,8 @@ public:
{ {
Attr key(name, 0); Attr key(name, 0);
const_iterator i = std::lower_bound(begin(), end(), key); const_iterator i = std::lower_bound(begin(), end(), key);
if (i != end() && i->name == name) return i; if (i != end() && i->name == name)
return i;
return end(); return end();
} }
@ -84,15 +99,30 @@ public:
{ {
Attr key(name, 0); Attr key(name, 0);
const_iterator i = std::lower_bound(begin(), end(), key); const_iterator i = std::lower_bound(begin(), end(), key);
if (i != end() && i->name == name) return &*i; if (i != end() && i->name == name)
return &*i;
return nullptr; return nullptr;
} }
iterator begin() { return &attrs[0]; } iterator begin()
iterator end() { return &attrs[size_]; } {
return &attrs[0];
}
const_iterator begin() const { return &attrs[0]; } iterator end()
const_iterator end() const { return &attrs[size_]; } {
return &attrs[size_];
}
const_iterator begin() const
{
return &attrs[0];
}
const_iterator end() const
{
return &attrs[size_];
}
Attr & operator[](size_t pos) Attr & operator[](size_t pos)
{ {
@ -106,7 +136,10 @@ public:
void sort(); void sort();
size_t capacity() const { return capacity_; } size_t capacity() const
{
return capacity_;
}
/** /**
* Returns the attributes in lexicographically sorted order. * Returns the attributes in lexicographically sorted order.
@ -143,8 +176,10 @@ public:
EvalState & state; EvalState & state;
BindingsBuilder(EvalState & state, Bindings * bindings) BindingsBuilder(EvalState & state, Bindings * bindings)
: bindings(bindings), state(state) : bindings(bindings)
{ } , state(state)
{
}
void insert(Symbol name, Value * value, PosIdx pos = noPos) void insert(Symbol name, Value * value, PosIdx pos = noPos)
{ {
@ -191,4 +226,4 @@ public:
friend struct ExprAttrs; friend struct ExprAttrs;
}; };
} } // namespace nix

View file

@ -43,10 +43,7 @@ class EvalCache : public std::enable_shared_from_this<EvalCache>
public: public:
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);
ref<AttrCursor> getRoot(); ref<AttrCursor> getRoot();
}; };
@ -63,11 +60,23 @@ enum AttrType {
Int = 8, Int = 8,
}; };
struct placeholder_t {}; struct placeholder_t
struct missing_t {}; {};
struct misc_t {};
struct failed_t {}; struct missing_t
struct int_t { NixInt x; }; {};
struct misc_t
{};
struct failed_t
{};
struct int_t
{
NixInt x;
};
typedef uint64_t AttrId; typedef uint64_t AttrId;
typedef std::pair<AttrId, Symbol> AttrKey; typedef std::pair<AttrId, Symbol> AttrKey;
typedef std::pair<std::string, NixStringContext> string_t; typedef std::pair<std::string, NixStringContext> string_t;
@ -81,8 +90,8 @@ typedef std::variant<
failed_t, failed_t,
bool, bool,
int_t, int_t,
std::vector<std::string> std::vector<std::string>>
> AttrValue; AttrValue;
class AttrCursor : public std::enable_shared_from_this<AttrCursor> class AttrCursor : public std::enable_shared_from_this<AttrCursor>
{ {
@ -161,4 +170,4 @@ public:
StorePath forceDerivation(); StorePath forceDerivation();
}; };
} } // namespace nix::eval_cache

View file

@ -60,6 +60,7 @@ struct InvalidPathError : public EvalError
{ {
public: public:
Path path; Path path;
InvalidPathError(EvalState & state, const Path & path) InvalidPathError(EvalState & state, const Path & path)
: EvalError(state, "path '%s' is not valid", path) : EvalError(state, "path '%s' is not valid", path)
{ {
@ -119,4 +120,4 @@ public:
[[gnu::noinline, gnu::noreturn]] void panic(); [[gnu::noinline, gnu::noreturn]] void panic();
}; };
} } // namespace nix

View file

@ -23,11 +23,11 @@ inline void * allocBytes(size_t n)
#else #else
p = calloc(n, 1); p = calloc(n, 1);
#endif #endif
if (!p) throw std::bad_alloc(); if (!p)
throw std::bad_alloc();
return p; return p;
} }
[[gnu::always_inline]] [[gnu::always_inline]]
Value * EvalState::allocValue() Value * EvalState::allocValue()
{ {
@ -38,7 +38,8 @@ Value * EvalState::allocValue()
have to explicitly clear the first word of every object we take. */ have to explicitly clear the first word of every object we take. */
if (!*valueAllocCache) { if (!*valueAllocCache) {
*valueAllocCache = GC_malloc_many(sizeof(Value)); *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. /* GC_NEXT is a convenience macro for accessing the first word of an object.
@ -54,7 +55,6 @@ Value * EvalState::allocValue()
return (Value *) p; return (Value *) p;
} }
[[gnu::always_inline]] [[gnu::always_inline]]
Env & EvalState::allocEnv(size_t size) Env & EvalState::allocEnv(size_t size)
{ {
@ -68,7 +68,8 @@ Env & EvalState::allocEnv(size_t size)
/* see allocValue for explanations. */ /* see allocValue for explanations. */
if (!*env1AllocCache) { if (!*env1AllocCache) {
*env1AllocCache = GC_malloc_many(sizeof(Env) + sizeof(Value *)); *env1AllocCache = GC_malloc_many(sizeof(Env) + sizeof(Value *));
if (!*env1AllocCache) throw std::bad_alloc(); if (!*env1AllocCache)
throw std::bad_alloc();
} }
void * p = *env1AllocCache; void * p = *env1AllocCache;
@ -84,7 +85,6 @@ Env & EvalState::allocEnv(size_t size)
return *env; return *env;
} }
[[gnu::always_inline]] [[gnu::always_inline]]
void EvalState::forceValue(Value & v, const PosIdx pos) void EvalState::forceValue(Value & v, const PosIdx pos)
{ {
@ -94,7 +94,7 @@ void EvalState::forceValue(Value & v, const PosIdx pos)
Expr * expr = v.payload.thunk.expr; Expr * expr = v.payload.thunk.expr;
try { try {
v.mkBlackhole(); v.mkBlackhole();
//checkInterrupt(); // checkInterrupt();
if (env) [[likely]] if (env) [[likely]]
expr->eval(*this, *env, v); expr->eval(*this, *env, v);
else else
@ -104,54 +104,47 @@ void EvalState::forceValue(Value & v, const PosIdx pos)
tryFixupBlackHolePos(v, pos); tryFixupBlackHolePos(v, pos);
throw; throw;
} }
} } else if (v.isApp())
else if (v.isApp())
callFunction(*v.payload.app.left, *v.payload.app.right, v, pos); callFunction(*v.payload.app.left, *v.payload.app.right, v, pos);
} }
[[gnu::always_inline]] [[gnu::always_inline]]
inline void EvalState::forceAttrs(Value & v, const PosIdx pos, std::string_view errorCtx) inline void EvalState::forceAttrs(Value & v, const PosIdx pos, std::string_view errorCtx)
{ {
forceAttrs(v, [&]() { return pos; }, errorCtx); forceAttrs(v, [&]() { return pos; }, errorCtx);
} }
template<typename Callable>
template <typename Callable>
[[gnu::always_inline]] [[gnu::always_inline]]
inline void EvalState::forceAttrs(Value & v, Callable getPos, std::string_view errorCtx) inline void EvalState::forceAttrs(Value & v, Callable getPos, std::string_view errorCtx)
{ {
PosIdx pos = getPos(); PosIdx pos = getPos();
forceValue(v, pos); forceValue(v, pos);
if (v.type() != nAttrs) { if (v.type() != nAttrs) {
error<TypeError>( error<TypeError>("expected a set but found %1%: %2%", showType(v), ValuePrinter(*this, v, errorPrintOptions))
"expected a set but found %1%: %2%", .withTrace(pos, errorCtx)
showType(v), .debugThrow();
ValuePrinter(*this, v, errorPrintOptions)
).withTrace(pos, errorCtx).debugThrow();
} }
} }
[[gnu::always_inline]] [[gnu::always_inline]]
inline void EvalState::forceList(Value & v, const PosIdx pos, std::string_view errorCtx) inline void EvalState::forceList(Value & v, const PosIdx pos, std::string_view errorCtx)
{ {
forceValue(v, pos); forceValue(v, pos);
if (!v.isList()) { if (!v.isList()) {
error<TypeError>( error<TypeError>("expected a list but found %1%: %2%", showType(v), ValuePrinter(*this, v, errorPrintOptions))
"expected a list but found %1%: %2%", .withTrace(pos, errorCtx)
showType(v), .debugThrow();
ValuePrinter(*this, v, errorPrintOptions)
).withTrace(pos, errorCtx).debugThrow();
} }
} }
[[gnu::always_inline]] [[gnu::always_inline]]
inline CallDepth EvalState::addCallDepth(const PosIdx pos) { inline CallDepth EvalState::addCallDepth(const PosIdx pos)
{
if (callDepth > settings.maxCallDepth) if (callDepth > settings.maxCallDepth)
error<EvalBaseError>("stack overflow; max-call-depth exceeded").atPos(pos).debugThrow(); error<EvalBaseError>("stack overflow; max-call-depth exceeded").atPos(pos).debugThrow();
return CallDepth(callDepth); return CallDepth(callDepth);
}; };
} } // namespace nix

View file

@ -73,7 +73,9 @@ struct EvalSettings : Config
)"}; )"};
Setting<Strings> nixPath{ Setting<Strings> nixPath{
this, {}, "nix-path", this,
{},
"nix-path",
R"( R"(
List of search paths to use for [lookup path](@docroot@/language/constructs/lookup-path.md) resolution. List of search paths to use for [lookup path](@docroot@/language/constructs/lookup-path.md) resolution.
This setting determines the value of This setting determines the value of
@ -106,10 +108,14 @@ struct EvalSettings : Config
> If [restricted evaluation](@docroot@/command-ref/conf-file.md#conf-restrict-eval) is enabled, the default value is empty. > 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 `[ ]`. > If [pure evaluation](#conf-pure-eval) is enabled, `builtins.nixPath` *always* evaluates to the empty list `[ ]`.
)", {}, false}; )",
{},
false};
Setting<std::string> currentSystem{ Setting<std::string> currentSystem{
this, "", "eval-system", this,
"",
"eval-system",
R"( R"(
This option defines This option defines
[`builtins.currentSystem`](@docroot@/language/builtins.md#builtins-currentSystem) [`builtins.currentSystem`](@docroot@/language/builtins.md#builtins-currentSystem)
@ -129,7 +135,9 @@ struct EvalSettings : Config
const std::string & getCurrentSystem() const; const std::string & getCurrentSystem() const;
Setting<bool> restrictEval{ Setting<bool> restrictEval{
this, false, "restrict-eval", this,
false,
"restrict-eval",
R"( R"(
If set to `true`, the Nix evaluator will not allow access to any If set to `true`, the Nix evaluator will not allow access to any
files outside of files outside of
@ -138,7 +146,10 @@ struct EvalSettings : Config
[`allowed-uris`](@docroot@/command-ref/conf-file.md#conf-allowed-uris). [`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"( R"(
Pure evaluation mode ensures that the result of Nix expressions is fully determined by explicitly declared inputs, and not influenced by external state: Pure evaluation mode ensures that the result of Nix expressions is fully determined by explicitly declared inputs, and not influenced by external state:
@ -148,11 +159,12 @@ struct EvalSettings : Config
- [`builtins.currentTime`](@docroot@/language/builtins.md#builtins-currentTime) - [`builtins.currentTime`](@docroot@/language/builtins.md#builtins-currentTime)
- [`builtins.nixPath`](@docroot@/language/builtins.md#builtins-nixPath) - [`builtins.nixPath`](@docroot@/language/builtins.md#builtins-nixPath)
- [`builtins.storePath`](@docroot@/language/builtins.md#builtins-storePath) - [`builtins.storePath`](@docroot@/language/builtins.md#builtins-storePath)
)" )"};
};
Setting<bool> enableImportFromDerivation{ Setting<bool> enableImportFromDerivation{
this, true, "allow-import-from-derivation", this,
true,
"allow-import-from-derivation",
R"( R"(
By default, Nix allows [Import from Derivation](@docroot@/language/import-from-derivation.md). By default, Nix allows [Import from Derivation](@docroot@/language/import-from-derivation.md).
@ -162,7 +174,10 @@ struct EvalSettings : Config
regardless of the state of the store. regardless of the state of the store.
)"}; )"};
Setting<Strings> allowedUris{this, {}, "allowed-uris", Setting<Strings> allowedUris{
this,
{},
"allowed-uris",
R"( R"(
A list of URI prefixes to which access is allowed in restricted A list of URI prefixes to which access is allowed in restricted
evaluation mode. For example, when set to evaluation mode. For example, when set to
@ -175,7 +190,10 @@ struct EvalSettings : Config
- or the prefix is a URI scheme ended by a colon `:` and the URI has the same scheme. - 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"( R"(
If set to `true`, the Nix evaluator will trace every function call. If set to `true`, the Nix evaluator will trace every function call.
Nix will print a log message at the "vomit" level for every function Nix will print a log message at the "vomit" level for every function
@ -193,26 +211,38 @@ struct EvalSettings : Config
`flamegraph.pl`. `flamegraph.pl`.
)"}; )"};
Setting<bool> useEvalCache{this, true, "eval-cache", Setting<bool> useEvalCache{
this,
true,
"eval-cache",
R"( R"(
Whether to use the flake evaluation cache. 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. 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. Intermediate results are not cached.
)"}; )"};
Setting<bool> ignoreExceptionsDuringTry{this, false, "ignore-try", Setting<bool> ignoreExceptionsDuringTry{
this,
false,
"ignore-try",
R"( R"(
If set to true, ignore exceptions inside 'tryEval' calls when evaluating nix expressions in If set to true, ignore exceptions inside 'tryEval' calls when evaluating nix expressions in
debug mode (using the --debugger flag). By default the debugger will pause on all exceptions. debug mode (using the --debugger flag). By default the debugger will pause 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."}; "Whether `builtins.traceVerbose` should trace its first argument when evaluated."};
Setting<unsigned int> maxCallDepth{this, 10000, "max-call-depth", Setting<unsigned int> maxCallDepth{
"The maximum function call depth to allow before erroring."}; 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"( R"(
If set to true and the `--debugger` flag is given, the following functions If set to true and the `--debugger` flag is given, the following functions
will enter the debugger like [`builtins.break`](@docroot@/language/builtins.md#builtins-break). will enter the debugger like [`builtins.break`](@docroot@/language/builtins.md#builtins-break).
@ -225,7 +255,10 @@ struct EvalSettings : Config
This is useful for debugging warnings in third-party Nix code. 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"( R"(
If set to true and the `--debugger` flag is given, [`builtins.warn`](@docroot@/language/builtins.md#builtins-warn) 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). will enter the debugger like [`builtins.break`](@docroot@/language/builtins.md#builtins-break).
@ -235,7 +268,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). 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"( R"(
If set to true, [`builtins.warn`](@docroot@/language/builtins.md#builtins-warn) will throw an error when logging a warning. If set to true, [`builtins.warn`](@docroot@/language/builtins.md#builtins-warn) will throw an error when logging a warning.
@ -254,4 +290,4 @@ struct EvalSettings : Config
*/ */
Path getNixDefExpr(); Path getNixDefExpr();
} } // namespace nix

View file

@ -33,39 +33,46 @@ namespace nix {
constexpr size_t maxPrimOpArity = 8; constexpr size_t maxPrimOpArity = 8;
class Store; class Store;
namespace fetchers { namespace fetchers {
struct Settings; struct Settings;
struct InputCache; struct InputCache;
} } // namespace fetchers
struct EvalSettings; struct EvalSettings;
class EvalState; class EvalState;
class StorePath; class StorePath;
struct SingleDerivedPath; struct SingleDerivedPath;
enum RepairFlag : bool; enum RepairFlag : bool;
struct MemorySourceAccessor; struct MemorySourceAccessor;
namespace eval_cache { namespace eval_cache {
class EvalCache; class EvalCache;
} }
/** /**
* Increments a count on construction and decrements on destruction. * Increments a count on construction and decrements on destruction.
*/ */
class CallDepth { class CallDepth
size_t & count; {
size_t & count;
public: public:
CallDepth(size_t & count) : count(count) { CallDepth(size_t & count)
++count; : count(count)
} {
~CallDepth() { ++count;
--count; }
}
~CallDepth()
{
--count;
}
}; };
/** /**
* Function that implements a primop. * 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 * Info about a primitive operation, and its implementation
@ -150,7 +157,9 @@ struct Constant
bool impureOnly = false; 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 std::unordered_map<PosIdx, DocComment> DocCommentMap;
@ -160,23 +169,25 @@ struct Env
Value * values[0]; 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); 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); 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::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; struct RegexCache;
std::shared_ptr<RegexCache> makeRegexCache(); std::shared_ptr<RegexCache> makeRegexCache();
struct DebugTrace { struct DebugTrace
{
/* WARNING: Converting PosIdx -> Pos should be done with extra care. This is /* WARNING: Converting PosIdx -> Pos should be done with extra care. This is
due to the fact that operator[] of PosTable is incredibly expensive. */ due to the fact that operator[] of PosTable is incredibly expensive. */
std::variant<Pos, PosIdx> pos; std::variant<Pos, PosIdx> pos;
@ -209,19 +220,11 @@ public:
SymbolTable symbols; SymbolTable symbols;
PosTable positions; PosTable positions;
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue, const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue, sSystem, sOverrides, sOutputs, sOutputName,
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls, sIgnoreNulls, sFile, sLine, sColumn, sFunctor, sToString, sRight, sWrong, sStructuredAttrs, sAllowedReferences,
sFile, sLine, sColumn, sFunctor, sToString, sAllowedRequisites, sDisallowedReferences, sDisallowedRequisites, sMaxSize, sMaxClosureSize, sBuilder, sArgs,
sRight, sWrong, sStructuredAttrs, sContentAddressed, sImpure, sOutputHash, sOutputHashAlgo, sOutputHashMode, sRecurseForDerivations, sDescription,
sAllowedReferences, sAllowedRequisites, sDisallowedReferences, sDisallowedRequisites, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath, sPrefix, sOutputSpecified;
sMaxSize, sMaxClosureSize,
sBuilder, sArgs,
sContentAddressed, sImpure,
sOutputHash, sOutputHashAlgo, sOutputHashMode,
sRecurseForDerivations,
sDescription, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath,
sPrefix,
sOutputSpecified;
const Expr::AstSymbols exprSymbols; const Expr::AstSymbols exprSymbols;
@ -308,19 +311,21 @@ public:
/** /**
* Debugger * Debugger
*/ */
ReplExitStatus (* debugRepl)(ref<EvalState> es, const ValMap & extraEnv); ReplExitStatus (*debugRepl)(ref<EvalState> es, const ValMap & extraEnv);
bool debugStop; bool debugStop;
bool inDebugger = false; bool inDebugger = false;
int trylevel; int trylevel;
std::list<DebugTrace> debugTraces; std::list<DebugTrace> debugTraces;
std::map<const Expr*, const std::shared_ptr<const StaticEnv>> exprEnvs; std::map<const Expr *, const std::shared_ptr<const StaticEnv>> exprEnvs;
const std::shared_ptr<const StaticEnv> getStaticEnv(const Expr & expr) const const std::shared_ptr<const StaticEnv> getStaticEnv(const Expr & expr) const
{ {
auto i = exprEnvs.find(&expr); auto i = exprEnvs.find(&expr);
if (i != exprEnvs.end()) if (i != exprEnvs.end())
return i->second; return i->second;
else 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. */ /** Whether a debug repl can be started. If `false`, `runDebugRepl(error)` will return without starting a repl. */
@ -339,7 +344,8 @@ public:
template<class T, typename... Args> template<class T, typename... Args>
[[nodiscard, gnu::noinline]] [[nodiscard, gnu::noinline]]
EvalErrorBuilder<T> & error(const Args & ... args) { EvalErrorBuilder<T> & error(const Args &... args)
{
// `EvalErrorBuilder::debugThrow` performs the corresponding `delete`. // `EvalErrorBuilder::debugThrow` performs the corresponding `delete`.
return *new EvalErrorBuilder<T>(*this, args...); return *new EvalErrorBuilder<T>(*this, args...);
} }
@ -358,13 +364,25 @@ private:
/** /**
* A cache from path names to parse trees. * 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 std::unordered_map<
SourcePath,
Expr *,
std::hash<SourcePath>,
std::equal_to<SourcePath>,
traceable_allocator<std::pair<const SourcePath, Expr *>>>
FileParseCache;
FileParseCache fileParseCache; FileParseCache fileParseCache;
/** /**
* A cache from path names to values. * 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 std::unordered_map<
SourcePath,
Value,
std::hash<SourcePath>,
std::equal_to<SourcePath>,
traceable_allocator<std::pair<const SourcePath, Value>>>
FileEvalCache;
FileEvalCache fileEvalCache; FileEvalCache fileEvalCache;
/** /**
@ -404,7 +422,10 @@ public:
std::shared_ptr<Store> buildStore = nullptr); std::shared_ptr<Store> buildStore = nullptr);
~EvalState(); ~EvalState();
LookupPath getLookupPath() { return lookupPath; } LookupPath getLookupPath()
{
return lookupPath;
}
/** /**
* Return a `SourcePath` that refers to `path` in the root * Return a `SourcePath` that refers to `path` in the root
@ -485,9 +506,7 @@ public:
* *
* If it is not found, return `std::nullopt`. * If it is not found, return `std::nullopt`.
*/ */
std::optional<SourcePath> resolveLookupPathPath( std::optional<SourcePath> resolveLookupPathPath(const LookupPath::Path & elem, bool initAccessControl = false);
const LookupPath::Path & elem,
bool initAccessControl = false);
/** /**
* Evaluate an expression to normal form * Evaluate an expression to normal form
@ -529,7 +548,7 @@ public:
void forceAttrs(Value & v, const PosIdx pos, std::string_view errorCtx); 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 forceAttrs(Value & v, Callable getPos, std::string_view errorCtx);
inline void forceList(Value & v, const PosIdx pos, std::string_view errorCtx); inline void forceList(Value & v, const PosIdx pos, std::string_view errorCtx);
@ -538,15 +557,20 @@ public:
*/ */
void forceFunction(Value & v, const PosIdx pos, std::string_view errorCtx); 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, 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); std::string_view forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx);
template<typename... Args> template<typename... Args>
[[gnu::noinline]] [[gnu::noinline]]
void addErrorTrace(Error & e, const Args & ... formatArgs) const; void addErrorTrace(Error & e, const Args &... formatArgs) const;
template<typename... Args> template<typename... Args>
[[gnu::noinline]] [[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: public:
/** /**
@ -555,8 +579,8 @@ public:
*/ */
bool isDerivation(Value & v); bool isDerivation(Value & v);
std::optional<std::string> tryAttrsToString(const PosIdx pos, Value & v, std::optional<std::string> tryAttrsToString(
NixStringContext & context, bool coerceMore = false, bool copyToStore = true); const PosIdx pos, Value & v, NixStringContext & context, bool coerceMore = false, bool copyToStore = true);
/** /**
* String coercion. * String coercion.
@ -566,9 +590,13 @@ public:
* booleans and lists to a string. If `copyToStore` is set, * booleans and lists to a string. If `copyToStore` is set,
* referenced paths are copied to the Nix store as a side effect. * referenced paths are copied to the Nix store as a side effect.
*/ */
BackedStringView coerceToString(const PosIdx pos, Value & v, NixStringContext & context, BackedStringView coerceToString(
const PosIdx pos,
Value & v,
NixStringContext & context,
std::string_view errorCtx, std::string_view errorCtx,
bool coerceMore = false, bool copyToStore = true, bool coerceMore = false,
bool copyToStore = true,
bool canonicalizePath = true); bool canonicalizePath = true);
StorePath copyPathToStore(NixStringContext & context, const SourcePath & path); StorePath copyPathToStore(NixStringContext & context, const SourcePath & path);
@ -590,7 +618,11 @@ public:
/** /**
* Part of `coerceToSingleDerivedPath()` without any store IO which is exposed for unit testing only. * 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`. * Coerce to `SingleDerivedPath`.
@ -630,7 +662,13 @@ public:
/** /**
* Internal primops not exposed to the user. * 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; std::unordered_map<
std::string,
Value *,
std::hash<std::string>,
std::equal_to<std::string>,
traceable_allocator<std::pair<const std::string, Value *>>>
internalPrimOps;
/** /**
* Name and documentation about every constant. * Name and documentation about every constant.
@ -704,7 +742,8 @@ private:
std::shared_ptr<StaticEnv> & staticEnv); 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; size_t callDepth = 0;
@ -767,7 +806,7 @@ public:
/** /**
* Return a boolean `Value *` without allocating. * Return a boolean `Value *` without allocating.
*/ */
Value *getBool(bool b); Value * getBool(bool b);
void mkThunk_(Value & v, Expr * expr); void mkThunk_(Value & v, Expr * expr);
void mkPos(Value & v, PosIdx pos); void mkPos(Value & v, PosIdx pos);
@ -811,9 +850,7 @@ public:
* *
* A combination of `mkStorePathString` and `mkOutputString`. * A combination of `mkStorePathString` and `mkOutputString`.
*/ */
void mkSingleDerivedPathString( void mkSingleDerivedPathString(const SingleDerivedPath & p, Value & v);
const SingleDerivedPath & p,
Value & v);
void concatLists(Value & v, size_t nrLists, Value * const * lists, const PosIdx pos, std::string_view errorCtx); void concatLists(Value & v, size_t nrLists, Value * const * lists, const PosIdx pos, std::string_view errorCtx);
@ -844,22 +881,22 @@ public:
* @param[out] maybePaths if not nullptr, all built or referenced store paths will be added to this set * @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. * @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[in] str the string to realise
* @param[out] paths all referenced store paths will be added to this set * @param[out] paths all referenced store paths will be added to this set
* @return the realised string * @return the realised string
* @throw EvalError if the value is not a string, path or derivation (see `coerceToString`) * @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. */ /* Call the binary path filter predicate used builtins.path etc. */
bool callPathFilter( bool callPathFilter(Value * filterFun, const SourcePath & path, PosIdx pos);
Value * filterFun,
const SourcePath & path,
PosIdx pos);
DocComment getDocCommentForPos(PosIdx pos); DocComment getDocCommentForPos(PosIdx pos);
@ -878,8 +915,7 @@ private:
* Like `mkSingleDerivedPathStringRaw` but just creates a raw string * Like `mkSingleDerivedPathStringRaw` but just creates a raw string
* Value, which would also have a string context. * Value, which would also have a string context.
*/ */
std::string mkSingleDerivedPathStringRaw( std::string mkSingleDerivedPathStringRaw(const SingleDerivedPath & p);
const SingleDerivedPath & p);
unsigned long nrEnvs = 0; unsigned long nrEnvs = 0;
unsigned long nrValuesInEnvs = 0; unsigned long nrValuesInEnvs = 0;
@ -916,20 +952,23 @@ private:
friend struct ExprFloat; friend struct ExprFloat;
friend struct ExprPath; friend struct ExprPath;
friend struct ExprSelect; friend struct ExprSelect;
friend void prim_getAttr(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_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_split(EvalState & state, const PosIdx pos, Value ** args, Value & v);
friend struct Value; friend struct Value;
friend class ListBuilder; friend class ListBuilder;
}; };
struct DebugTraceStacker { struct DebugTraceStacker
{
DebugTraceStacker(EvalState & evalState, DebugTrace t); DebugTraceStacker(EvalState & evalState, DebugTrace t);
~DebugTraceStacker() ~DebugTraceStacker()
{ {
evalState.debugTraces.pop_front(); evalState.debugTraces.pop_front();
} }
EvalState & evalState; EvalState & evalState;
DebugTrace trace; DebugTrace trace;
}; };
@ -955,6 +994,6 @@ SourcePath resolveExprPath(SourcePath path, bool addDefaultNix = true);
*/ */
bool isAllowedURI(std::string_view uri, const Strings & allowedPaths); bool isAllowedURI(std::string_view uri, const Strings & allowedPaths);
} } // namespace nix
#include "nix/expr/eval-inline.hh" #include "nix/expr/eval-inline.hh"

View file

@ -13,4 +13,4 @@ struct FunctionCallTrace
FunctionCallTrace(const Pos & pos); FunctionCallTrace(const Pos & pos);
~FunctionCallTrace(); ~FunctionCallTrace();
}; };
} } // 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. * 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>>; using SmallVector = boost::container::small_vector<T, nItems, traceable_allocator<T>>;
/** /**
* A vector of value pointers. See `SmallVector`. * A vector of value pointers. See `SmallVector`.
*/ */
template <size_t nItems> template<size_t nItems>
using SmallValueVector = SmallVector<Value *, nItems>; using SmallValueVector = SmallVector<Value *, nItems>;
/** /**
@ -23,7 +23,7 @@ using SmallValueVector = SmallVector<Value *, nItems>;
* *
* See also `SmallValueVector`. * See also `SmallValueVector`.
*/ */
template <size_t nItems> template<size_t nItems>
using SmallTemporaryValueVector = SmallVector<Value, nItems>; using SmallTemporaryValueVector = SmallVector<Value, nItems>;
} } // namespace nix

View file

@ -7,7 +7,6 @@
#include <string> #include <string>
#include <map> #include <map>
namespace nix { namespace nix {
/** /**
@ -33,7 +32,7 @@ private:
*/ */
bool failed = false; bool failed = false;
const Bindings * attrs = nullptr, * meta = nullptr; const Bindings *attrs = nullptr, *meta = nullptr;
const Bindings * getMeta(); const Bindings * getMeta();
@ -45,7 +44,8 @@ public:
*/ */
std::string attrPath; 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, std::string attrPath, const Bindings * attrs);
PackageInfo(EvalState & state, ref<Store> store, const std::string & drvPathWithOutputs); PackageInfo(EvalState & state, ref<Store> store, const std::string & drvPathWithOutputs);
@ -74,28 +74,46 @@ public:
MetaValue queryMetaInfo(EvalState & state, const string & name) const; MetaValue queryMetaInfo(EvalState & state, const string & name) const;
*/ */
void setName(const std::string & s) { name = s; } void setName(const std::string & s)
void setDrvPath(StorePath path) { drvPath = {{std::move(path)}}; } {
void setOutPath(StorePath path) { outPath = {{std::move(path)}}; } name = s;
}
void setFailed() { failed = true; }; void setDrvPath(StorePath path)
bool hasFailed() { return failed; }; {
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; typedef std::list<PackageInfo, traceable_allocator<PackageInfo>> PackageInfos;
/** /**
* If value `v` denotes a derivation, return a PackageInfo object * If value `v` denotes a derivation, return a PackageInfo object
* describing it. Otherwise return nothing. * describing it. Otherwise return nothing.
*/ */
std::optional<PackageInfo> getDerivation(EvalState & state, std::optional<PackageInfo> getDerivation(EvalState & state, Value & v, bool ignoreAssertionFailures);
Value & v, bool ignoreAssertionFailures);
void getDerivations(EvalState & state, Value & v, const std::string & pathPrefix, void getDerivations(
Bindings & autoArgs, PackageInfos & drvs, EvalState & state,
Value & v,
const std::string & pathPrefix,
Bindings & autoArgs,
PackageInfos & drvs,
bool ignoreAssertionFailures); bool ignoreAssertionFailures);
} // namespace nix
}

View file

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

View file

@ -19,7 +19,8 @@ struct StaticEnv;
struct Value; 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: * Note that this does not implement the following:
* - argument attribute names ("formals"): TBD * - argument attribute names ("formals"): TBD
@ -34,7 +35,8 @@ struct Value;
* `f: g: final: prev: <...>`. The parameters `final` and `prev` are part * `f: g: final: prev: <...>`. The parameters `final` and `prev` are part
* of the overlay concept, while distracting from the function's purpose. * of the overlay concept, while distracting from the function's purpose.
*/ */
struct DocComment { struct DocComment
{
/** /**
* Start of the comment, including the opening, ie `/` and `**`. * 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 * therefore baking optionality into it is also useful, to avoiding the memory
* overhead of `std::optional`. * 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; std::string getInnerText(const PosTable & positions) const;
}; };
/** /**
@ -66,52 +70,69 @@ struct AttrName
{ {
Symbol symbol; Symbol symbol;
Expr * expr = nullptr; Expr * expr = nullptr;
AttrName(Symbol s) : symbol(s) {}; AttrName(Symbol s)
AttrName(Expr * e) : expr(e) {}; : symbol(s) {};
AttrName(Expr * e)
: expr(e) {};
}; };
typedef std::vector<AttrName> AttrPath; typedef std::vector<AttrName> AttrPath;
std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath); std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath);
/* Abstract syntax of Nix expressions. */ /* Abstract syntax of Nix expressions. */
struct Expr struct Expr
{ {
struct AstSymbols { struct AstSymbols
{
Symbol sub, lessThan, mul, div, or_, findFile, nixPath, body; Symbol sub, lessThan, mul, div, or_, findFile, nixPath, body;
}; };
static unsigned long nrExprs; static unsigned long nrExprs;
Expr() {
Expr()
{
nrExprs++; nrExprs++;
} }
virtual ~Expr() { };
virtual ~Expr() {};
virtual void show(const SymbolTable & symbols, std::ostream & str) const; virtual void show(const SymbolTable & symbols, std::ostream & str) const;
virtual void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env); virtual void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env);
virtual void eval(EvalState & state, Env & env, Value & v); virtual void eval(EvalState & state, Env & env, Value & v);
virtual Value * maybeThunk(EvalState & state, Env & env); virtual Value * maybeThunk(EvalState & state, Env & env);
virtual void setName(Symbol name); virtual void setName(Symbol name);
virtual void setDocComment(DocComment docComment) { }; virtual void setDocComment(DocComment docComment) {};
virtual PosIdx getPos() const { return noPos; }
virtual PosIdx getPos() const
{
return noPos;
}
// These are temporary methods to be used only in parser.y // These are temporary methods to be used only in parser.y
virtual void resetCursedOr() { }; virtual void resetCursedOr() {};
virtual void warnIfCursedOr(const SymbolTable & symbols, const PosTable & positions) { }; 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 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; void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) override;
struct ExprInt : Expr struct ExprInt : Expr
{ {
Value v; 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; Value * maybeThunk(EvalState & state, Env & env) override;
COMMON_METHODS COMMON_METHODS
}; };
@ -119,7 +140,12 @@ struct ExprInt : Expr
struct ExprFloat : Expr struct ExprFloat : Expr
{ {
Value v; Value v;
ExprFloat(NixFloat nf) { v.mkFloat(nf); };
ExprFloat(NixFloat nf)
{
v.mkFloat(nf);
};
Value * maybeThunk(EvalState & state, Env & env) override; Value * maybeThunk(EvalState & state, Env & env) override;
COMMON_METHODS COMMON_METHODS
}; };
@ -128,7 +154,13 @@ struct ExprString : Expr
{ {
std::string s; std::string s;
Value v; Value v;
ExprString(std::string &&s) : s(std::move(s)) { v.mkString(this->s.data()); };
ExprString(std::string && s)
: s(std::move(s))
{
v.mkString(this->s.data());
};
Value * maybeThunk(EvalState & state, Env & env) override; Value * maybeThunk(EvalState & state, Env & env) override;
COMMON_METHODS COMMON_METHODS
}; };
@ -138,10 +170,14 @@ struct ExprPath : Expr
ref<SourceAccessor> accessor; ref<SourceAccessor> accessor;
std::string s; std::string s;
Value v; 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()); v.mkPath(&*accessor, this->s.c_str());
} }
Value * maybeThunk(EvalState & state, Env & env) override; Value * maybeThunk(EvalState & state, Env & env) override;
COMMON_METHODS COMMON_METHODS
}; };
@ -170,10 +206,18 @@ struct ExprVar : Expr
Level level = 0; Level level = 0;
Displacement displ = 0; Displacement displ = 0;
ExprVar(Symbol name) : name(name) { }; ExprVar(Symbol name)
ExprVar(const PosIdx & pos, Symbol name) : pos(pos), name(name) { }; : name(name) {};
ExprVar(const PosIdx & pos, Symbol name)
: pos(pos)
, name(name) {};
Value * maybeThunk(EvalState & state, Env & env) override; Value * maybeThunk(EvalState & state, Env & env) override;
PosIdx getPos() const override { return pos; }
PosIdx getPos() const override
{
return pos;
}
COMMON_METHODS COMMON_METHODS
}; };
@ -184,7 +228,8 @@ struct ExprVar : Expr
*/ */
struct ExprInheritFrom : ExprVar struct ExprInheritFrom : ExprVar
{ {
ExprInheritFrom(PosIdx pos, Displacement displ): ExprVar(pos, {}) ExprInheritFrom(PosIdx pos, Displacement displ)
: ExprVar(pos, {})
{ {
this->level = 0; this->level = 0;
this->displ = displ; this->displ = displ;
@ -197,11 +242,26 @@ struct ExprInheritFrom : ExprVar
struct ExprSelect : Expr struct ExprSelect : Expr
{ {
PosIdx pos; PosIdx pos;
Expr * e, * def; Expr *e, *def;
AttrPath attrPath; 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, AttrPath attrPath, Expr * def)
ExprSelect(const PosIdx & pos, Expr * e, Symbol name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); }; : pos(pos)
PosIdx getPos() const override { return 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. * 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). * @param[out] attrs The attribute set that should contain the last attribute name (if it exists).
* @return The last attribute name in `attrPath` * @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); Symbol evalExceptFinalSelect(EvalState & state, Env & env, Value & attrs);
@ -220,8 +281,15 @@ struct ExprOpHasAttr : Expr
{ {
Expr * e; Expr * e;
AttrPath attrPath; AttrPath attrPath;
ExprOpHasAttr(Expr * e, AttrPath attrPath) : e(e), attrPath(std::move(attrPath)) { }; ExprOpHasAttr(Expr * e, AttrPath attrPath)
PosIdx getPos() const override { return e->getPos(); } : e(e)
, attrPath(std::move(attrPath)) {};
PosIdx getPos() const override
{
return e->getPos();
}
COMMON_METHODS COMMON_METHODS
}; };
@ -229,7 +297,9 @@ struct ExprAttrs : Expr
{ {
bool recursive; bool recursive;
PosIdx pos; PosIdx pos;
struct AttrDef {
struct AttrDef
{
enum class Kind { enum class Kind {
/** `attr = expr;` */ /** `attr = expr;` */
Plain, Plain,
@ -244,8 +314,10 @@ struct ExprAttrs : Expr
PosIdx pos; PosIdx pos;
Displacement displ = 0; // displacement Displacement displ = 0; // displacement
AttrDef(Expr * e, const PosIdx & pos, Kind kind = Kind::Plain) AttrDef(Expr * e, const PosIdx & pos, Kind kind = Kind::Plain)
: kind(kind), e(e), pos(pos) { }; : kind(kind)
AttrDef() { }; , e(e)
, pos(pos) {};
AttrDef() {};
template<typename T> template<typename T>
const T & chooseByKind(const T & plain, const T & inherited, const T & inheritedFrom) const 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; typedef std::map<Symbol, AttrDef> AttrDefs;
AttrDefs attrs; AttrDefs attrs;
std::unique_ptr<std::vector<Expr *>> inheritFromExprs; std::unique_ptr<std::vector<Expr *>> inheritFromExprs;
struct DynamicAttrDef {
Expr * nameExpr, * valueExpr; struct DynamicAttrDef
{
Expr *nameExpr, *valueExpr;
PosIdx pos; PosIdx pos;
DynamicAttrDef(Expr * nameExpr, Expr * valueExpr, const 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; typedef std::vector<DynamicAttrDef> DynamicAttrDefs;
DynamicAttrDefs dynamicAttrs; DynamicAttrDefs dynamicAttrs;
ExprAttrs(const PosIdx &pos) : recursive(false), pos(pos) { }; ExprAttrs(const PosIdx & pos)
ExprAttrs() : recursive(false) { }; : recursive(false)
PosIdx getPos() const override { return pos; } , pos(pos) {};
ExprAttrs()
: recursive(false) {};
PosIdx getPos() const override
{
return pos;
}
COMMON_METHODS COMMON_METHODS
std::shared_ptr<const StaticEnv> bindInheritSources( std::shared_ptr<const StaticEnv> bindInheritSources(EvalState & es, const std::shared_ptr<const StaticEnv> & env);
EvalState & es, const std::shared_ptr<const StaticEnv> & env);
Env * buildInheritFromEnv(EvalState & state, Env & up); Env * buildInheritFromEnv(EvalState & state, Env & up);
void showBindings(const SymbolTable & symbols, std::ostream & str) const; void showBindings(const SymbolTable & symbols, std::ostream & str) const;
}; };
@ -286,7 +371,7 @@ struct ExprAttrs : Expr
struct ExprList : Expr struct ExprList : Expr
{ {
std::vector<Expr *> elems; std::vector<Expr *> elems;
ExprList() { }; ExprList() {};
COMMON_METHODS COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env) override; Value * maybeThunk(EvalState & state, Env & env) override;
@ -311,19 +396,18 @@ struct Formals
bool has(Symbol arg) const bool has(Symbol arg) const
{ {
auto it = std::lower_bound(formals.begin(), formals.end(), arg, auto it = std::lower_bound(
[] (const Formal & f, const Symbol & sym) { return f.name < sym; }); formals.begin(), formals.end(), arg, [](const Formal & f, const Symbol & sym) { return f.name < sym; });
return it != formals.end() && it->name == arg; return it != formals.end() && it->name == arg;
} }
std::vector<Formal> lexicographicOrder(const SymbolTable & symbols) const std::vector<Formal> lexicographicOrder(const SymbolTable & symbols) const
{ {
std::vector<Formal> result(formals.begin(), formals.end()); std::vector<Formal> result(formals.begin(), formals.end());
std::sort(result.begin(), result.end(), std::sort(result.begin(), result.end(), [&](const Formal & a, const Formal & b) {
[&] (const Formal & a, const Formal & b) { std::string_view sa = symbols[a.name], sb = symbols[b.name];
std::string_view sa = symbols[a.name], sb = symbols[b.name]; return sa < sb;
return sa < sb; });
});
return result; return result;
} }
}; };
@ -338,17 +422,31 @@ struct ExprLambda : Expr
DocComment docComment; DocComment docComment;
ExprLambda(PosIdx pos, Symbol arg, Formals * formals, Expr * body) 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) ExprLambda(PosIdx pos, Formals * formals, Expr * body)
: pos(pos), formals(formals), body(body) : pos(pos)
, formals(formals)
, body(body)
{ {
} }
void setName(Symbol name) override; void setName(Symbol name) override;
std::string showNamePos(const EvalState & state) const; 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; virtual void setDocComment(DocComment docComment) override;
COMMON_METHODS COMMON_METHODS
}; };
@ -359,13 +457,28 @@ struct ExprCall : Expr
std::vector<Expr *> args; std::vector<Expr *> args;
PosIdx pos; PosIdx pos;
std::optional<PosIdx> cursedOrEndPos; // used during parsing to warn about https://github.com/NixOS/nix/issues/11118 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) 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) ExprCall(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args, PosIdx && cursedOrEndPos)
: fun(fun), args(args), pos(pos), cursedOrEndPos(cursedOrEndPos) : fun(fun)
{ } , args(args)
PosIdx getPos() const override { return pos; } , pos(pos)
, cursedOrEndPos(cursedOrEndPos)
{
}
PosIdx getPos() const override
{
return pos;
}
virtual void resetCursedOr() override; virtual void resetCursedOr() override;
virtual void warnIfCursedOr(const SymbolTable & symbols, const PosTable & positions) override; virtual void warnIfCursedOr(const SymbolTable & symbols, const PosTable & positions) override;
COMMON_METHODS COMMON_METHODS
@ -375,90 +488,144 @@ struct ExprLet : Expr
{ {
ExprAttrs * attrs; ExprAttrs * attrs;
Expr * body; Expr * body;
ExprLet(ExprAttrs * attrs, Expr * body) : attrs(attrs), body(body) { }; ExprLet(ExprAttrs * attrs, Expr * body)
: attrs(attrs)
, body(body) {};
COMMON_METHODS COMMON_METHODS
}; };
struct ExprWith : Expr struct ExprWith : Expr
{ {
PosIdx pos; PosIdx pos;
Expr * attrs, * body; Expr *attrs, *body;
size_t prevWith; size_t prevWith;
ExprWith * parentWith; ExprWith * parentWith;
ExprWith(const PosIdx & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { }; ExprWith(const PosIdx & pos, Expr * attrs, Expr * body)
PosIdx getPos() const override { return pos; } : pos(pos)
, attrs(attrs)
, body(body) {};
PosIdx getPos() const override
{
return pos;
}
COMMON_METHODS COMMON_METHODS
}; };
struct ExprIf : Expr struct ExprIf : Expr
{ {
PosIdx pos; PosIdx pos;
Expr * cond, * then, * else_; Expr *cond, *then, *else_;
ExprIf(const PosIdx & pos, Expr * cond, Expr * then, Expr * else_) : pos(pos), cond(cond), then(then), else_(else_) { }; ExprIf(const PosIdx & pos, Expr * cond, Expr * then, Expr * else_)
PosIdx getPos() const override { return pos; } : pos(pos)
, cond(cond)
, then(then)
, else_(else_) {};
PosIdx getPos() const override
{
return pos;
}
COMMON_METHODS COMMON_METHODS
}; };
struct ExprAssert : Expr struct ExprAssert : Expr
{ {
PosIdx pos; PosIdx pos;
Expr * cond, * body; Expr *cond, *body;
ExprAssert(const PosIdx & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { }; ExprAssert(const PosIdx & pos, Expr * cond, Expr * body)
PosIdx getPos() const override { return pos; } : pos(pos)
, cond(cond)
, body(body) {};
PosIdx getPos() const override
{
return pos;
}
COMMON_METHODS COMMON_METHODS
}; };
struct ExprOpNot : Expr struct ExprOpNot : Expr
{ {
Expr * e; Expr * e;
ExprOpNot(Expr * e) : e(e) { }; ExprOpNot(Expr * e)
PosIdx getPos() const override { return e->getPos(); } : e(e) {};
PosIdx getPos() const override
{
return e->getPos();
}
COMMON_METHODS COMMON_METHODS
}; };
#define MakeBinOp(name, s) \ #define MakeBinOp(name, s) \
struct name : Expr \ struct name : Expr \
{ \ { \
PosIdx pos; \ PosIdx pos; \
Expr * e1, * e2; \ Expr *e1, *e2; \
name(Expr * e1, Expr * e2) : e1(e1), e2(e2) { }; \ name(Expr * e1, Expr * e2) \
name(const PosIdx & pos, Expr * e1, Expr * e2) : pos(pos), e1(e1), e2(e2) { }; \ : e1(e1) \
void show(const SymbolTable & symbols, std::ostream & str) const override \ , e2(e2) {}; \
{ \ name(const PosIdx & pos, Expr * e1, Expr * e2) \
str << "("; e1->show(symbols, str); str << " " s " "; e2->show(symbols, str); str << ")"; \ : 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 \ void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) override \
{ \ { \
e1->bindVars(es, env); e2->bindVars(es, env); \ e1->bindVars(es, env); \
} \ e2->bindVars(es, env); \
void eval(EvalState & state, Env & env, Value & v) override; \ } \
PosIdx getPos() const override { return pos; } \ void eval(EvalState & state, Env & env, Value & v) override; \
PosIdx getPos() const override \
{ \
return pos; \
} \
}; };
MakeBinOp(ExprOpEq, "==") MakeBinOp(ExprOpEq, "==") MakeBinOp(ExprOpNEq, "!=") MakeBinOp(ExprOpAnd, "&&") MakeBinOp(ExprOpOr, "||")
MakeBinOp(ExprOpNEq, "!=") MakeBinOp(ExprOpImpl, "->") MakeBinOp(ExprOpUpdate, "//") MakeBinOp(ExprOpConcatLists, "++")
MakeBinOp(ExprOpAnd, "&&")
MakeBinOp(ExprOpOr, "||")
MakeBinOp(ExprOpImpl, "->")
MakeBinOp(ExprOpUpdate, "//")
MakeBinOp(ExprOpConcatLists, "++")
struct ExprConcatStrings : Expr struct ExprConcatStrings : Expr
{ {
PosIdx pos; PosIdx pos;
bool forceString; bool forceString;
std::vector<std::pair<PosIdx, Expr *>> * es; std::vector<std::pair<PosIdx, Expr *>> * es;
ExprConcatStrings(const PosIdx & pos, 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) { }; : pos(pos)
PosIdx getPos() const override { return pos; } , forceString(forceString)
, es(es) {};
PosIdx getPos() const override
{
return pos;
}
COMMON_METHODS COMMON_METHODS
}; };
struct ExprPos : Expr struct ExprPos : Expr
{ {
PosIdx pos; PosIdx pos;
ExprPos(const PosIdx & pos) : pos(pos) { }; ExprPos(const PosIdx & pos)
PosIdx getPos() const override { return pos; } : pos(pos) {};
PosIdx getPos() const override
{
return pos;
}
COMMON_METHODS COMMON_METHODS
}; };
@ -466,14 +633,16 @@ struct ExprPos : Expr
struct ExprBlackHole : Expr struct ExprBlackHole : Expr
{ {
void show(const SymbolTable & symbols, std::ostream & str) const override {} 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 {} void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) override {}
[[noreturn]] static void throwInfiniteRecursionError(EvalState & state, Value & v); [[noreturn]] static void throwInfiniteRecursionError(EvalState & state, Value & v);
}; };
extern ExprBlackHole eBlackHole; extern ExprBlackHole eBlackHole;
/* Static environments are used to map variable names onto (level, /* Static environments are used to map variable names onto (level,
displacement) pairs used to obtain the value of the variable at displacement) pairs used to obtain the value of the variable at
runtime. */ runtime. */
@ -495,8 +664,9 @@ struct StaticEnv
void sort() void sort()
{ {
std::stable_sort(vars.begin(), vars.end(), std::stable_sort(vars.begin(), vars.end(), [](const Vars::value_type & a, const Vars::value_type & b) {
[](const Vars::value_type & a, const Vars::value_type & b) { return a.first < b.first; }); return a.first < b.first;
});
} }
void deduplicate() void deduplicate()
@ -504,7 +674,8 @@ struct StaticEnv
auto it = vars.begin(), jt = it, end = vars.end(); auto it = vars.begin(), jt = it, end = vars.end();
while (jt != end) { while (jt != end) {
*it = *jt++; *it = *jt++;
while (jt != end && it->first == jt->first) *it = *jt++; while (jt != end && it->first == jt->first)
*it = *jt++;
it++; it++;
} }
vars.erase(it, end); vars.erase(it, end);
@ -514,10 +685,10 @@ struct StaticEnv
{ {
Vars::value_type key(name, 0); Vars::value_type key(name, 0);
auto i = std::lower_bound(vars.begin(), vars.end(), key); 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(); return vars.end();
} }
}; };
} // namespace nix
}

View file

@ -17,7 +17,11 @@ struct StringToken
const char * p; const char * p;
size_t l; size_t l;
bool hasIndentation; 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. // 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) // backup to recover from yyless(0)
int stashedBeginOffset, stashedEndOffset; int stashedBeginOffset, stashedEndOffset;
void stash() { void stash()
{
stashedBeginOffset = beginOffset; stashedBeginOffset = beginOffset;
stashedEndOffset = endOffset; stashedEndOffset = endOffset;
} }
void unstash() { void unstash()
{
beginOffset = stashedBeginOffset; beginOffset = stashedBeginOffset;
endOffset = stashedEndOffset; endOffset = stashedEndOffset;
} }
@ -87,32 +93,30 @@ struct ParserState
void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos); void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos);
void dupAttr(Symbol attr, 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); void addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def);
Formals * validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {}); Formals * validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {});
Expr * stripIndentation(const PosIdx pos, Expr * stripIndentation(const PosIdx pos, std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es);
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es);
PosIdx at(const ParserLocation & loc); PosIdx at(const ParserLocation & loc);
}; };
inline void ParserState::dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos) inline void ParserState::dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos)
{ {
throw ParseError({ throw ParseError(
.msg = HintFmt("attribute '%1%' already defined at %2%", {.msg = HintFmt("attribute '%1%' already defined at %2%", showAttrPath(symbols, attrPath), positions[prevPos]),
showAttrPath(symbols, attrPath), positions[prevPos]), .pos = positions[pos]});
.pos = positions[pos]
});
} }
inline void ParserState::dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos) inline void ParserState::dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos)
{ {
throw ParseError({ throw ParseError(
.msg = HintFmt("attribute '%1%' already defined at %2%", symbols[attr], positions[prevPos]), {.msg = HintFmt("attribute '%1%' already defined at %2%", symbols[attr], positions[prevPos]),
.pos = positions[pos] .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; AttrPath::iterator i;
// All attrpaths have at least one attr // 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 * Precondition: attrPath is used for error messages and should already contain
* symbol as its last element. * 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); ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(symbol);
if (j != attrs->attrs.end()) { if (j != attrs->attrs.end()) {
@ -189,12 +194,14 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const S
attrPath.pop_back(); attrPath.pop_back();
} }
ae->attrs.clear(); 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.begin()),
std::make_move_iterator(ae->dynamicAttrs.end())); std::make_move_iterator(ae->dynamicAttrs.end()));
ae->dynamicAttrs.clear(); ae->dynamicAttrs.clear();
if (ae->inheritFromExprs) { 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->begin()),
std::make_move_iterator(ae->inheritFromExprs->end())); std::make_move_iterator(ae->inheritFromExprs->end()));
ae->inheritFromExprs = nullptr; 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) inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Symbol arg)
{ {
std::sort(formals->formals.begin(), formals->formals.end(), std::sort(formals->formals.begin(), formals->formals.end(), [](const auto & a, const auto & b) {
[] (const auto & a, const auto & b) { return std::tie(a.name, a.pos) < std::tie(b.name, b.pos);
return std::tie(a.name, a.pos) < std::tie(b.name, b.pos); });
});
std::optional<std::pair<Symbol, PosIdx>> duplicate; std::optional<std::pair<Symbol, PosIdx>> duplicate;
for (size_t i = 0; i + 1 < formals->formals.size(); i++) { 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)); duplicate = std::min(thisDup, duplicate.value_or(thisDup));
} }
if (duplicate) if (duplicate)
throw ParseError({ throw ParseError(
.msg = HintFmt("duplicate formal function argument '%1%'", symbols[duplicate->first]), {.msg = HintFmt("duplicate formal function argument '%1%'", symbols[duplicate->first]),
.pos = positions[duplicate->second] .pos = positions[duplicate->second]});
});
if (arg && formals->has(arg)) if (arg && formals->has(arg))
throw ParseError({ throw ParseError(
.msg = HintFmt("duplicate formal function argument '%1%'", symbols[arg]), {.msg = HintFmt("duplicate formal function argument '%1%'", symbols[arg]), .pos = positions[pos]});
.pos = positions[pos]
});
return formals; return formals;
} }
inline Expr * ParserState::stripIndentation(const PosIdx pos, inline Expr *
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es) 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 /* Figure out the minimum indentation. Note that by design
whitespace-only final lines are not taken into account. (So whitespace-only final lines are not taken into account. (So
@ -255,7 +259,8 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos,
/* Anti-quotations and escaped characters end the current start-of-line whitespace. */ /* Anti-quotations and escaped characters end the current start-of-line whitespace. */
if (atStartOfLine) { if (atStartOfLine) {
atStartOfLine = false; atStartOfLine = false;
if (curIndent < minIndent) minIndent = curIndent; if (curIndent < minIndent)
minIndent = curIndent;
} }
continue; continue;
} }
@ -269,7 +274,8 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos,
curIndent = 0; curIndent = 0;
} else { } else {
atStartOfLine = false; atStartOfLine = false;
if (curIndent < minIndent) minIndent = curIndent; if (curIndent < minIndent)
minIndent = curIndent;
} }
} else if (str->p[j] == '\n') { } else if (str->p[j] == '\n') {
atStartOfLine = true; atStartOfLine = true;
@ -284,20 +290,19 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos,
size_t curDropped = 0; size_t curDropped = 0;
size_t n = es.size(); size_t n = es.size();
auto i = es.begin(); auto i = es.begin();
const auto trimExpr = [&] (Expr * e) { const auto trimExpr = [&](Expr * e) {
atStartOfLine = false; atStartOfLine = false;
curDropped = 0; curDropped = 0;
es2->emplace_back(i->first, e); es2->emplace_back(i->first, e);
}; };
const auto trimString = [&] (const StringToken & t) { const auto trimString = [&](const StringToken & t) {
std::string s2; std::string s2;
for (size_t j = 0; j < t.l; ++j) { for (size_t j = 0; j < t.l; ++j) {
if (atStartOfLine) { if (atStartOfLine) {
if (t.p[j] == ' ') { if (t.p[j] == ' ') {
if (curDropped++ >= minIndent) if (curDropped++ >= minIndent)
s2 += t.p[j]; s2 += t.p[j];
} } else if (t.p[j] == '\n') {
else if (t.p[j] == '\n') {
curDropped = 0; curDropped = 0;
s2 += t.p[j]; s2 += t.p[j];
} else { } else {
@ -307,7 +312,8 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos,
} }
} else { } else {
s2 += t.p[j]; 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) { 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. // 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. // This also ensures that equivalent empty strings result in the same ast, which is helpful when testing formatters.
if (es2->size() == 0) { if (es2->size() == 0) {
auto *const result = new ExprString(""); auto * const result = new ExprString("");
delete es2; delete es2;
return result; return result;
} }
/* If this is a single string, then don't do a concatenation. */ /* If this is a single string, then don't do a concatenation. */
if (es2->size() == 1 && dynamic_cast<ExprString *>((*es2)[0].second)) { if (es2->size() == 1 && dynamic_cast<ExprString *>((*es2)[0].second)) {
auto *const result = (*es2)[0].second; auto * const result = (*es2)[0].second;
delete es2; delete es2;
return result; return result;
} }
@ -355,4 +361,4 @@ inline PosIdx ParserState::at(const ParserLocation & loc)
return positions.add(origin, loc.beginOffset); 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 * 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 * 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); void makePositionThunks(EvalState & state, const PosIdx pos, Value & line, Value & column);
} } // namespace nix

View file

@ -15,10 +15,6 @@ namespace nix {
* See: https://github.com/NixOS/nix/issues/9730 * See: https://github.com/NixOS/nix/issues/9730
*/ */
void printAmbiguous( void printAmbiguous(
Value &v, Value & v, const SymbolTable & symbols, std::ostream & str, std::set<const void *> * seen, int depth);
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, * `PrintOptions` for unknown and therefore potentially large values in error messages,
* to avoid printing "too much" output. * to avoid printing "too much" output.
*/ */
static PrintOptions errorPrintOptions = PrintOptions { static PrintOptions errorPrintOptions = PrintOptions{
.ansiColors = true, .ansiColors = true,
.maxDepth = 10, .maxDepth = 10,
.maxAttrs = 10, .maxAttrs = 10,
@ -118,4 +118,4 @@ static PrintOptions errorPrintOptions = PrintOptions {
.maxStringLength = 1024, .maxStringLength = 1024,
}; };
} } // namespace nix

View file

@ -26,10 +26,14 @@ struct Value;
* @param s The logical string * @param s The logical string
*/ */
std::ostream & printLiteralString(std::ostream & o, std::string_view s); 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)); 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)); 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); 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 `<<` * A partially-applied form of `printValue` which can be formatted using `<<`
* without allocating an intermediate string. * without allocating an intermediate string.
*/ */
class ValuePrinter { class ValuePrinter
friend std::ostream & operator << (std::ostream & output, const ValuePrinter & printer); {
friend std::ostream & operator<<(std::ostream & output, const ValuePrinter & printer);
private: private:
EvalState & state; EvalState & state;
Value & value; Value & value;
PrintOptions options; PrintOptions options;
public: public:
ValuePrinter(EvalState & state, Value & value, PrintOptions options = PrintOptions {}) ValuePrinter(EvalState & state, Value & value, PrintOptions options = PrintOptions{})
: state(state), value(value), options(options) { } : state(state)
, value(value)
, options(options)
{
}
}; };
std::ostream & operator<<(std::ostream & output, const ValuePrinter & printer); std::ostream & operator<<(std::ostream & output, const ValuePrinter & printer);
/** /**
* `ValuePrinter` does its own ANSI formatting, so we don't color it * `ValuePrinter` does its own ANSI formatting, so we don't color it
* magenta. * magenta.
@ -88,4 +96,4 @@ std::ostream & operator<<(std::ostream & output, const ValuePrinter & printer);
template<> template<>
HintFmt & HintFmt::operator%(const ValuePrinter & value); HintFmt & HintFmt::operator%(const ValuePrinter & value);
} } // namespace nix

View file

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

View file

@ -105,4 +105,4 @@ struct LookupPath::Elem
static LookupPath::Elem parse(std::string_view rawElem); static LookupPath::Elem parse(std::string_view rawElem);
}; };
} } // namespace nix

View file

@ -23,10 +23,13 @@ class SymbolStr
private: private:
const std::string * s; const std::string * s;
explicit SymbolStr(const std::string & symbol): s(&symbol) {} explicit SymbolStr(const std::string & symbol)
: s(&symbol)
{
}
public: public:
bool operator == (std::string_view s2) const bool operator==(std::string_view s2) const
{ {
return *s == s2; return *s == s2;
} }
@ -36,12 +39,12 @@ public:
return s->c_str(); return s->c_str();
} }
operator const std::string_view () const operator const std::string_view() const
{ {
return *s; return *s;
} }
friend std::ostream & operator <<(std::ostream & os, const SymbolStr & symbol); friend std::ostream & operator<<(std::ostream & os, const SymbolStr & symbol);
bool empty() const bool empty() const
{ {
@ -61,15 +64,31 @@ class Symbol
private: private:
uint32_t id; uint32_t id;
explicit Symbol(uint32_t id): id(id) {} explicit Symbol(uint32_t id)
: id(id)
{
}
public: public:
Symbol() : id(0) {} Symbol()
: id(0)
{
}
explicit operator bool() const { return id > 0; } explicit operator bool() const
{
return id > 0;
}
auto operator<=>(const Symbol other) const { return id <=> other.id; } auto operator<=>(const Symbol other) const
bool operator==(const Symbol other) const { return id == other.id; } {
return id <=> other.id;
}
bool operator==(const Symbol other) const
{
return id == other.id;
}
friend class std::hash<Symbol>; friend class std::hash<Symbol>;
}; };
@ -137,7 +156,7 @@ public:
} }
}; };
} } // namespace nix
template<> template<>
struct std::hash<nix::Symbol> struct std::hash<nix::Symbol>

View file

@ -10,13 +10,18 @@
namespace nix { namespace nix {
nlohmann::json printValueAsJSON(EvalState & state, bool strict, nlohmann::json printValueAsJSON(
Value & v, const PosIdx pos, NixStringContext & context, bool copyToStore = true); EvalState & state, bool strict, Value & v, const PosIdx pos, NixStringContext & context, bool copyToStore = true);
void printValueAsJSON(EvalState & state, bool strict,
Value & v, const PosIdx pos, std::ostream & str, NixStringContext & context, bool copyToStore = true);
void printValueAsJSON(
EvalState & state,
bool strict,
Value & v,
const PosIdx pos,
std::ostream & str,
NixStringContext & context,
bool copyToStore = true);
MakeError(JSONSerializationError, Error); MakeError(JSONSerializationError, Error);
} } // namespace nix

View file

@ -9,7 +9,13 @@
namespace nix { namespace nix {
void printValueAsXML(EvalState & state, bool strict, bool location, void printValueAsXML(
Value & v, std::ostream & out, NixStringContext & context, const PosIdx pos); EvalState & state,
bool strict,
bool location,
Value & v,
std::ostream & out,
NixStringContext & context,
const PosIdx pos);
} }

View file

@ -18,7 +18,6 @@ namespace nix {
struct Value; struct Value;
class BindingsBuilder; class BindingsBuilder;
typedef enum { typedef enum {
tUninitialized = 0, tUninitialized = 0,
tInt = 1, tInt = 1,
@ -44,19 +43,7 @@ typedef enum {
* grouping together implementation details like tList*, different function * grouping together implementation details like tList*, different function
* types, and types in non-normal form (so thunks and co.) * types, and types in non-normal form (so thunks and co.)
*/ */
typedef enum { typedef enum { nThunk, nInt, nFloat, nBool, nString, nPath, nNull, nAttrs, nList, nFunction, nExternal } ValueType;
nThunk,
nInt,
nFloat,
nBool,
nString,
nPath,
nNull,
nAttrs,
nList,
nFunction,
nExternal
} ValueType;
class Bindings; class Bindings;
struct Env; struct Env;
@ -81,15 +68,15 @@ using NixFloat = double;
*/ */
class ExternalValueBase class ExternalValueBase
{ {
friend std::ostream & operator << (std::ostream & str, const ExternalValueBase & v); friend std::ostream & operator<<(std::ostream & str, const ExternalValueBase & v);
friend class Printer; friend class Printer;
protected: protected:
/** /**
* Print out the value * Print out the value
*/ */
virtual std::ostream & print(std::ostream & str) const = 0; virtual std::ostream & print(std::ostream & str) const = 0;
public: public:
/** /**
* Return a simple string describing the type * Return a simple string describing the type
*/ */
@ -104,41 +91,44 @@ class ExternalValueBase
* Coerce the value to a string. Defaults to uncoercable, i.e. throws an * Coerce the value to a string. Defaults to uncoercable, i.e. throws an
* error. * error.
*/ */
virtual std::string coerceToString(EvalState & state, const PosIdx & pos, NixStringContext & context, bool copyMore, bool copyToStore) const; virtual std::string coerceToString(
EvalState & state, const PosIdx & pos, NixStringContext & context, bool copyMore, bool copyToStore) const;
/** /**
* Compare to another value of the same type. Defaults to uncomparable, * Compare to another value of the same type. Defaults to uncomparable,
* i.e. always false. * i.e. always false.
*/ */
virtual bool operator ==(const ExternalValueBase & b) const noexcept; virtual bool operator==(const ExternalValueBase & b) const noexcept;
/** /**
* Print the value as JSON. Defaults to unconvertable, i.e. throws an error * Print the value as JSON. Defaults to unconvertable, i.e. throws an error
*/ */
virtual nlohmann::json printValueAsJSON(EvalState & state, bool strict, virtual nlohmann::json
NixStringContext & context, bool copyToStore = true) const; printValueAsJSON(EvalState & state, bool strict, NixStringContext & context, bool copyToStore = true) const;
/** /**
* Print the value as XML. Defaults to unevaluated * Print the value as XML. Defaults to unevaluated
*/ */
virtual void printValueAsXML(EvalState & state, bool strict, bool location, virtual void printValueAsXML(
XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen, EvalState & state,
bool strict,
bool location,
XMLWriter & doc,
NixStringContext & context,
PathSet & drvsSeen,
const PosIdx pos) const; const PosIdx pos) const;
virtual ~ExternalValueBase() virtual ~ExternalValueBase() {};
{
};
}; };
std::ostream & operator << (std::ostream & str, const ExternalValueBase & v); std::ostream & operator<<(std::ostream & str, const ExternalValueBase & v);
class ListBuilder class ListBuilder
{ {
const size_t size; const size_t size;
Value * inlineElems[2] = {nullptr, nullptr}; Value * inlineElems[2] = {nullptr, nullptr};
public: public:
Value * * elems; Value ** elems;
ListBuilder(EvalState & state, size_t size); ListBuilder(EvalState & state, size_t size);
// NOTE: Can be noexcept because we are just copying integral values and // NOTE: Can be noexcept because we are just copying integral values and
@ -147,22 +137,29 @@ public:
: size(x.size) : size(x.size)
, inlineElems{x.inlineElems[0], x.inlineElems[1]} , inlineElems{x.inlineElems[0], x.inlineElems[1]}
, elems(size <= 2 ? inlineElems : x.elems) , elems(size <= 2 ? inlineElems : x.elems)
{ } {
}
Value * & operator [](size_t n) Value *& operator[](size_t n)
{ {
return elems[n]; return elems[n];
} }
typedef Value * * iterator; typedef Value ** iterator;
iterator begin() { return &elems[0]; } iterator begin()
iterator end() { return &elems[size]; } {
return &elems[0];
}
iterator end()
{
return &elems[size];
}
friend struct Value; friend struct Value;
}; };
struct Value struct Value
{ {
private: private:
@ -172,21 +169,40 @@ private:
public: public:
void print(EvalState &state, std::ostream &str, PrintOptions options = PrintOptions {}); void print(EvalState & state, std::ostream & str, PrintOptions options = PrintOptions{});
// Functions needed to distinguish the type // Functions needed to distinguish the type
// These should be removed eventually, by putting the functionality that's // These should be removed eventually, by putting the functionality that's
// needed by callers into methods of this type // needed by callers into methods of this type
// type() == nThunk // type() == nThunk
inline bool isThunk() const { return internalType == tThunk; }; inline bool isThunk() const
inline bool isApp() const { return internalType == tApp; }; {
return internalType == tThunk;
};
inline bool isApp() const
{
return internalType == tApp;
};
inline bool isBlackhole() const; inline bool isBlackhole() const;
// type() == nFunction // type() == nFunction
inline bool isLambda() const { return internalType == tLambda; }; inline bool isLambda() const
inline bool isPrimOp() const { return internalType == tPrimOp; }; {
inline bool isPrimOpApp() const { return internalType == tPrimOpApp; }; return internalType == tLambda;
};
inline bool isPrimOp() const
{
return internalType == tPrimOp;
};
inline bool isPrimOpApp() const
{
return internalType == tPrimOpApp;
};
/** /**
* Strings in the evaluator carry a so-called `context` which * Strings in the evaluator carry a so-called `context` which
@ -210,26 +226,31 @@ public:
* For canonicity, the store paths should be in sorted order. * For canonicity, the store paths should be in sorted order.
*/ */
struct StringWithContext { struct StringWithContext
{
const char * c_str; const char * c_str;
const char * * context; // must be in sorted order const char ** context; // must be in sorted order
}; };
struct Path { struct Path
{
SourceAccessor * accessor; SourceAccessor * accessor;
const char * path; const char * path;
}; };
struct ClosureThunk { struct ClosureThunk
{
Env * env; Env * env;
Expr * expr; Expr * expr;
}; };
struct FunctionApplicationThunk { struct FunctionApplicationThunk
Value * left, * right; {
Value *left, *right;
}; };
struct Lambda { struct Lambda
{
Env * env; Env * env;
ExprLambda * fun; ExprLambda * fun;
}; };
@ -244,10 +265,13 @@ public:
Path path; Path path;
Bindings * attrs; Bindings * attrs;
struct {
struct
{
size_t size; size_t size;
Value * const * elems; Value * const * elems;
} bigList; } bigList;
Value * smallList[2]; Value * smallList[2];
ClosureThunk thunk; ClosureThunk thunk;
FunctionApplicationThunk app; FunctionApplicationThunk app;
@ -270,18 +294,35 @@ public:
inline ValueType type(bool invalidIsThunk = false) const inline ValueType type(bool invalidIsThunk = false) const
{ {
switch (internalType) { switch (internalType) {
case tUninitialized: break; case tUninitialized:
case tInt: return nInt; break;
case tBool: return nBool; case tInt:
case tString: return nString; return nInt;
case tPath: return nPath; case tBool:
case tNull: return nNull; return nBool;
case tAttrs: return nAttrs; case tString:
case tList1: case tList2: case tListN: return nList; return nString;
case tLambda: case tPrimOp: case tPrimOpApp: return nFunction; case tPath:
case tExternal: return nExternal; return nPath;
case tFloat: return nFloat; case tNull:
case tThunk: case tApp: return nThunk; return nNull;
case tAttrs:
return nAttrs;
case tList1:
case tList2:
case tListN:
return nList;
case tLambda:
case tPrimOp:
case tPrimOpApp:
return nFunction;
case tExternal:
return nExternal;
case tFloat:
return nFloat;
case tThunk:
case tApp:
return nThunk;
} }
if (invalidIsThunk) if (invalidIsThunk)
return nThunk; return nThunk;
@ -312,17 +353,17 @@ public:
inline void mkInt(NixInt n) inline void mkInt(NixInt n)
{ {
finishValue(tInt, { .integer = n }); finishValue(tInt, {.integer = n});
} }
inline void mkBool(bool b) inline void mkBool(bool b)
{ {
finishValue(tBool, { .boolean = b }); finishValue(tBool, {.boolean = b});
} }
inline void mkString(const char * s, const char * * context = 0) inline void mkString(const char * s, const char ** context = 0)
{ {
finishValue(tString, { .string = { .c_str = s, .context = context } }); finishValue(tString, {.string = {.c_str = s, .context = context}});
} }
void mkString(std::string_view s); void mkString(std::string_view s);
@ -341,7 +382,7 @@ public:
inline void mkPath(SourceAccessor * accessor, const char * path) inline void mkPath(SourceAccessor * accessor, const char * path)
{ {
finishValue(tPath, { .path = { .accessor = accessor, .path = path } }); finishValue(tPath, {.path = {.accessor = accessor, .path = path}});
} }
inline void mkNull() inline void mkNull()
@ -351,7 +392,7 @@ public:
inline void mkAttrs(Bindings * a) inline void mkAttrs(Bindings * a)
{ {
finishValue(tAttrs, { .attrs = a }); finishValue(tAttrs, {.attrs = a});
} }
Value & mkAttrs(BindingsBuilder & bindings); Value & mkAttrs(BindingsBuilder & bindings);
@ -359,26 +400,26 @@ public:
void mkList(const ListBuilder & builder) void mkList(const ListBuilder & builder)
{ {
if (builder.size == 1) if (builder.size == 1)
finishValue(tList1, { .smallList = { builder.inlineElems[0] } }); finishValue(tList1, {.smallList = {builder.inlineElems[0]}});
else if (builder.size == 2) else if (builder.size == 2)
finishValue(tList2, { .smallList = { builder.inlineElems[0], builder.inlineElems[1] } }); finishValue(tList2, {.smallList = {builder.inlineElems[0], builder.inlineElems[1]}});
else else
finishValue(tListN, { .bigList = { .size = builder.size, .elems = builder.elems } }); finishValue(tListN, {.bigList = {.size = builder.size, .elems = builder.elems}});
} }
inline void mkThunk(Env * e, Expr * ex) inline void mkThunk(Env * e, Expr * ex)
{ {
finishValue(tThunk, { .thunk = { .env = e, .expr = ex } }); finishValue(tThunk, {.thunk = {.env = e, .expr = ex}});
} }
inline void mkApp(Value * l, Value * r) inline void mkApp(Value * l, Value * r)
{ {
finishValue(tApp, { .app = { .left = l, .right = r } }); finishValue(tApp, {.app = {.left = l, .right = r}});
} }
inline void mkLambda(Env * e, ExprLambda * f) inline void mkLambda(Env * e, ExprLambda * f)
{ {
finishValue(tLambda, { .lambda = { .env = e, .fun = f } }); finishValue(tLambda, {.lambda = {.env = e, .fun = f}});
} }
inline void mkBlackhole(); inline void mkBlackhole();
@ -387,7 +428,7 @@ public:
inline void mkPrimOpApp(Value * l, Value * r) inline void mkPrimOpApp(Value * l, Value * r)
{ {
finishValue(tPrimOpApp, { .primOpApp = { .left = l, .right = r } }); finishValue(tPrimOpApp, {.primOpApp = {.left = l, .right = r}});
} }
/** /**
@ -397,12 +438,12 @@ public:
inline void mkExternal(ExternalValueBase * e) inline void mkExternal(ExternalValueBase * e)
{ {
finishValue(tExternal, { .external = e }); finishValue(tExternal, {.external = e});
} }
inline void mkFloat(NixFloat n) inline void mkFloat(NixFloat n)
{ {
finishValue(tFloat, { .fpoint = n }); finishValue(tFloat, {.fpoint = n});
} }
bool isList() const bool isList() const
@ -444,8 +485,7 @@ public:
{ {
assert(internalType == tPath); assert(internalType == tPath);
return SourcePath( return SourcePath(
ref(payload.path.accessor->shared_from_this()), ref(payload.path.accessor->shared_from_this()), CanonPath(CanonPath::unchecked_t(), payload.path.path));
CanonPath(CanonPath::unchecked_t(), payload.path.path));
} }
std::string_view string_view() const std::string_view string_view() const
@ -460,36 +500,47 @@ public:
return payload.string.c_str; return payload.string.c_str;
} }
const char * * context() const const char ** context() const
{ {
return payload.string.context; return payload.string.context;
} }
ExternalValueBase * external() const ExternalValueBase * external() const
{ return payload.external; } {
return payload.external;
}
const Bindings * attrs() const const Bindings * attrs() const
{ return payload.attrs; } {
return payload.attrs;
}
const PrimOp * primOp() const const PrimOp * primOp() const
{ return payload.primOp; } {
return payload.primOp;
}
bool boolean() const bool boolean() const
{ return payload.boolean; } {
return payload.boolean;
}
NixInt integer() const NixInt integer() const
{ return payload.integer; } {
return payload.integer;
}
NixFloat fpoint() const NixFloat fpoint() const
{ return payload.fpoint; } {
return payload.fpoint;
}
}; };
extern ExprBlackHole eBlackHole; extern ExprBlackHole eBlackHole;
bool Value::isBlackhole() const bool Value::isBlackhole() const
{ {
return internalType == tThunk && payload.thunk.expr == (Expr*) &eBlackHole; return internalType == tThunk && payload.thunk.expr == (Expr *) &eBlackHole;
} }
void Value::mkBlackhole() void Value::mkBlackhole()
@ -497,11 +548,16 @@ void Value::mkBlackhole()
mkThunk(nullptr, (Expr *) &eBlackHole); mkThunk(nullptr, (Expr *) &eBlackHole);
} }
typedef std::vector<Value *, traceable_allocator<Value *>> ValueVector; typedef std::vector<Value *, traceable_allocator<Value *>> ValueVector;
typedef std::unordered_map<Symbol, Value *, std::hash<Symbol>, std::equal_to<Symbol>, traceable_allocator<std::pair<const Symbol, Value *>>> ValueMap; typedef std::unordered_map<
typedef std::map<Symbol, ValueVector, std::less<Symbol>, traceable_allocator<std::pair<const Symbol, ValueVector>>> ValueVectorMap; Symbol,
Value *,
std::hash<Symbol>,
std::equal_to<Symbol>,
traceable_allocator<std::pair<const Symbol, Value *>>>
ValueMap;
typedef std::map<Symbol, ValueVector, std::less<Symbol>, traceable_allocator<std::pair<const Symbol, ValueVector>>>
ValueVectorMap;
/** /**
* A value allocated in traceable memory. * A value allocated in traceable memory.
@ -512,4 +568,4 @@ RootValue allocRootValue(Value * v);
void forceNoNullByte(std::string_view s, std::function<Pos()> = nullptr); void forceNoNullByte(std::string_view s, std::function<Pos()> = nullptr);
} } // namespace nix

View file

@ -15,7 +15,7 @@ public:
std::string_view raw; std::string_view raw;
template<typename... Args> template<typename... Args>
BadNixStringContextElem(std::string_view raw_, const Args & ... args) BadNixStringContextElem(std::string_view raw_, const Args &... args)
: Error("") : Error("")
{ {
raw = raw_; raw = raw_;
@ -24,7 +24,8 @@ public:
} }
}; };
struct NixStringContextElem { struct NixStringContextElem
{
/** /**
* Plain opaque path to some store object. * Plain opaque path to some store object.
* *
@ -41,7 +42,8 @@ struct NixStringContextElem {
* *
* Encoded in the form `=<drvPath>`. * Encoded in the form `=<drvPath>`.
*/ */
struct DrvDeep { struct DrvDeep
{
StorePath drvPath; StorePath drvPath;
GENERATE_CMP(DrvDeep, me->drvPath); GENERATE_CMP(DrvDeep, me->drvPath);
@ -54,11 +56,7 @@ struct NixStringContextElem {
*/ */
using Built = SingleDerivedPath::Built; using Built = SingleDerivedPath::Built;
using Raw = std::variant< using Raw = std::variant<Opaque, DrvDeep, Built>;
Opaque,
DrvDeep,
Built
>;
Raw raw; Raw raw;
@ -74,12 +72,11 @@ struct NixStringContextElem {
* *
* @param xpSettings Stop-gap to avoid globals during unit tests. * @param xpSettings Stop-gap to avoid globals during unit tests.
*/ */
static NixStringContextElem parse( static NixStringContextElem
std::string_view s, parse(std::string_view s, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
std::string to_string() const; std::string to_string() const;
}; };
typedef std::set<NixStringContextElem> NixStringContext; typedef std::set<NixStringContextElem> NixStringContext;
} } // namespace nix

View file

@ -12,8 +12,10 @@ namespace nix {
// for more information, refer to // for more information, refer to
// https://github.com/nlohmann/json/blob/master/include/nlohmann/detail/input/json_sax.hpp // https://github.com/nlohmann/json/blob/master/include/nlohmann/detail/input/json_sax.hpp
class JSONSax : nlohmann::json_sax<json> { class JSONSax : nlohmann::json_sax<json>
class JSONState { {
class JSONState
{
protected: protected:
std::unique_ptr<JSONState> parent; std::unique_ptr<JSONState> parent;
RootValue v; RootValue v;
@ -22,22 +24,36 @@ class JSONSax : nlohmann::json_sax<json> {
{ {
throw std::logic_error("tried to close toplevel json parser state"); throw std::logic_error("tried to close toplevel json parser state");
} }
explicit JSONState(std::unique_ptr<JSONState> && p) : parent(std::move(p)) {}
explicit JSONState(Value * v) : v(allocRootValue(v)) {} explicit JSONState(std::unique_ptr<JSONState> && p)
: parent(std::move(p))
{
}
explicit JSONState(Value * v)
: v(allocRootValue(v))
{
}
JSONState(JSONState & p) = delete; JSONState(JSONState & p) = delete;
Value & value(EvalState & state) Value & value(EvalState & state)
{ {
if (!v) if (!v)
v = allocRootValue(state.allocValue()); v = allocRootValue(state.allocValue());
return **v; return **v;
} }
virtual ~JSONState() {} virtual ~JSONState() {}
virtual void add() {} virtual void add() {}
}; };
class JSONObjectState : public JSONState { class JSONObjectState : public JSONState
{
using JSONState::JSONState; using JSONState::JSONState;
ValueMap attrs; ValueMap attrs;
std::unique_ptr<JSONState> resolve(EvalState & state) override std::unique_ptr<JSONState> resolve(EvalState & state) override
{ {
auto attrs2 = state.buildBindings(attrs.size()); auto attrs2 = state.buildBindings(attrs.size());
@ -46,7 +62,11 @@ class JSONSax : nlohmann::json_sax<json> {
parent->value(state).mkAttrs(attrs2); parent->value(state).mkAttrs(attrs2);
return std::move(parent); return std::move(parent);
} }
void add() override { v = nullptr; }
void add() override
{
v = nullptr;
}
public: public:
void key(string_t & name, EvalState & state) void key(string_t & name, EvalState & state)
{ {
@ -55,8 +75,10 @@ class JSONSax : nlohmann::json_sax<json> {
} }
}; };
class JSONListState : public JSONState { class JSONListState : public JSONState
{
ValueVector values; ValueVector values;
std::unique_ptr<JSONState> resolve(EvalState & state) override std::unique_ptr<JSONState> resolve(EvalState & state) override
{ {
auto list = state.buildList(values.size()); auto list = state.buildList(values.size());
@ -65,12 +87,15 @@ class JSONSax : nlohmann::json_sax<json> {
parent->value(state).mkList(list); parent->value(state).mkList(list);
return std::move(parent); return std::move(parent);
} }
void add() override {
void add() override
{
values.push_back(*v); values.push_back(*v);
v = nullptr; v = nullptr;
} }
public: public:
JSONListState(std::unique_ptr<JSONState> && p, std::size_t reserve) : JSONState(std::move(p)) JSONListState(std::unique_ptr<JSONState> && p, std::size_t reserve)
: JSONState(std::move(p))
{ {
values.reserve(reserve); values.reserve(reserve);
} }
@ -80,7 +105,9 @@ class JSONSax : nlohmann::json_sax<json> {
std::unique_ptr<JSONState> rs; std::unique_ptr<JSONState> rs;
public: public:
JSONSax(EvalState & state, Value & v) : state(state), rs(new JSONState(&v)) {}; JSONSax(EvalState & state, Value & v)
: state(state)
, rs(new JSONState(&v)) {};
bool null() override bool null() override
{ {
@ -130,7 +157,7 @@ public:
} }
#if NLOHMANN_JSON_VERSION_MAJOR >= 3 && NLOHMANN_JSON_VERSION_MINOR >= 8 #if NLOHMANN_JSON_VERSION_MAJOR >= 3 && NLOHMANN_JSON_VERSION_MINOR >= 8
bool binary(binary_t&) override bool binary(binary_t &) override
{ {
// This function ought to be unreachable // This function ought to be unreachable
assert(false); assert(false);
@ -146,27 +173,30 @@ public:
bool key(string_t & name) override bool key(string_t & name) override
{ {
dynamic_cast<JSONObjectState*>(rs.get())->key(name, state); dynamic_cast<JSONObjectState *>(rs.get())->key(name, state);
return true; return true;
} }
bool end_object() override { bool end_object() override
{
rs = rs->resolve(state); rs = rs->resolve(state);
rs->add(); rs->add();
return true; return true;
} }
bool end_array() override { bool end_array() override
{
return end_object(); return end_object();
} }
bool start_array(size_t len) override { bool start_array(size_t len) override
rs = std::make_unique<JSONListState>(std::move(rs), {
len != std::numeric_limits<size_t>::max() ? len : 128); rs = std::make_unique<JSONListState>(std::move(rs), len != std::numeric_limits<size_t>::max() ? len : 128);
return true; return true;
} }
bool parse_error(std::size_t, const std::string&, const nlohmann::detail::exception& ex) override { bool parse_error(std::size_t, const std::string &, const nlohmann::detail::exception & ex) override
{
throw JSONParseError("%s", ex.what()); throw JSONParseError("%s", ex.what());
} }
}; };
@ -179,4 +209,4 @@ void parseJSON(EvalState & state, const std::string_view & s_, Value & v)
throw JSONParseError("Invalid JSON Value"); throw JSONParseError("Invalid JSON Value");
} }
} } // namespace nix

View file

@ -14,4 +14,4 @@ void initLoc(YYLTYPE * loc);
void adjustLoc(yyscan_t yyscanner, YYLTYPE * loc, const char * s, size_t len); void adjustLoc(yyscan_t yyscanner, YYLTYPE * loc, const char * s, size_t len);
} // namespace nix::lexer } // namespace nix::lexer::internal

View file

@ -17,7 +17,7 @@ ExprBlackHole eBlackHole;
// FIXME: remove, because *symbols* are abstract and do not have a single // FIXME: remove, because *symbols* are abstract and do not have a single
// textual representation; see printIdentifier() // textual representation; see printIdentifier()
std::ostream & operator <<(std::ostream & str, const SymbolStr & symbol) std::ostream & operator<<(std::ostream & str, const SymbolStr & symbol)
{ {
std::string_view s = symbol; std::string_view s = symbol;
return printIdentifier(str, s); return printIdentifier(str, s);
@ -76,7 +76,8 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co
{ {
typedef const decltype(attrs)::value_type * Attr; typedef const decltype(attrs)::value_type * Attr;
std::vector<Attr> sorted; std::vector<Attr> sorted;
for (auto & i : attrs) sorted.push_back(&i); for (auto & i : attrs)
sorted.push_back(&i);
std::sort(sorted.begin(), sorted.end(), [&](Attr a, Attr b) { std::sort(sorted.begin(), sorted.end(), [&](Attr a, Attr b) {
std::string_view sa = symbols[a->first], sb = symbols[b->first]; std::string_view sa = symbols[a->first], sb = symbols[b->first];
return sa < sb; return sa < sb;
@ -102,14 +103,16 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co
} }
if (!inherits.empty()) { if (!inherits.empty()) {
str << "inherit"; str << "inherit";
for (auto sym : inherits) str << " " << symbols[sym]; for (auto sym : inherits)
str << " " << symbols[sym];
str << "; "; str << "; ";
} }
for (const auto & [from, syms] : inheritsFrom) { for (const auto & [from, syms] : inheritsFrom) {
str << "inherit ("; str << "inherit (";
(*inheritFromExprs)[from]->show(symbols, str); (*inheritFromExprs)[from]->show(symbols, str);
str << ")"; str << ")";
for (auto sym : syms) str << " " << symbols[sym]; for (auto sym : syms)
str << " " << symbols[sym];
str << "; "; str << "; ";
} }
for (auto & i : sorted) { for (auto & i : sorted) {
@ -130,7 +133,8 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co
void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const
{ {
if (recursive) str << "rec "; if (recursive)
str << "rec ";
str << "{ "; str << "{ ";
showBindings(symbols, str); showBindings(symbols, str);
str << "}"; str << "}";
@ -157,7 +161,10 @@ void ExprLambda::show(const SymbolTable & symbols, std::ostream & str) const
// same expression being printed in two different ways depending on its // same expression being printed in two different ways depending on its
// context. always use lexicographic ordering to avoid this. // context. always use lexicographic ordering to avoid this.
for (auto & i : formals->lexicographicOrder(symbols)) { for (auto & i : formals->lexicographicOrder(symbols)) {
if (first) first = false; else str << ", "; if (first)
first = false;
else
str << ", ";
str << symbols[i.name]; str << symbols[i.name];
if (i.def) { if (i.def) {
str << " ? "; str << " ? ";
@ -165,13 +172,16 @@ void ExprLambda::show(const SymbolTable & symbols, std::ostream & str) const
} }
} }
if (formals->ellipsis) { if (formals->ellipsis) {
if (!first) str << ", "; if (!first)
str << ", ";
str << "..."; str << "...";
} }
str << " }"; str << " }";
if (arg) str << " @ "; if (arg)
str << " @ ";
} }
if (arg) str << symbols[arg]; if (arg)
str << symbols[arg];
str << ": "; str << ": ";
body->show(symbols, str); body->show(symbols, str);
str << ")"; str << ")";
@ -182,7 +192,7 @@ void ExprCall::show(const SymbolTable & symbols, std::ostream & str) const
str << '('; str << '(';
fun->show(symbols, str); fun->show(symbols, str);
for (auto e : args) { for (auto e : args) {
str << ' '; str << ' ';
e->show(symbols, str); e->show(symbols, str);
} }
str << ')'; str << ')';
@ -237,7 +247,10 @@ void ExprConcatStrings::show(const SymbolTable & symbols, std::ostream & str) co
bool first = true; bool first = true;
str << "("; str << "(";
for (auto & i : *es) { for (auto & i : *es) {
if (first) first = false; else str << " + "; if (first)
first = false;
else
str << " + ";
i.second->show(symbols, str); i.second->show(symbols, str);
} }
str << ")"; str << ")";
@ -248,13 +261,15 @@ void ExprPos::show(const SymbolTable & symbols, std::ostream & str) const
str << "__curPos"; str << "__curPos";
} }
std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath) std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath)
{ {
std::ostringstream out; std::ostringstream out;
bool first = true; bool first = true;
for (auto & i : attrPath) { for (auto & i : attrPath) {
if (!first) out << '.'; else first = false; if (!first)
out << '.';
else
first = false;
if (i.symbol) if (i.symbol)
out << symbols[i.symbol]; out << symbols[i.symbol];
else { else {
@ -266,7 +281,6 @@ std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath)
return out.str(); return out.str();
} }
/* Computing levels/displacements for variables. */ /* Computing levels/displacements for variables. */
void Expr::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) void Expr::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
@ -312,7 +326,8 @@ void ExprVar::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
int withLevel = -1; int withLevel = -1;
for (curEnv = env.get(), level = 0; curEnv; curEnv = curEnv->up.get(), level++) { for (curEnv = env.get(), level = 0; curEnv; curEnv = curEnv->up.get(), level++) {
if (curEnv->isWith) { if (curEnv->isWith) {
if (withLevel == -1) withLevel = level; if (withLevel == -1)
withLevel = level;
} else { } else {
auto i = curEnv->find(name); auto i = curEnv->find(name);
if (i != curEnv->vars.end()) { if (i != curEnv->vars.end()) {
@ -327,10 +342,7 @@ void ExprVar::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
enclosing `with'. If there is no `with', then we can issue an enclosing `with'. If there is no `with', then we can issue an
"undefined variable" error now. */ "undefined variable" error now. */
if (withLevel == -1) if (withLevel == -1)
es.error<UndefinedVarError>( es.error<UndefinedVarError>("undefined variable '%1%'", es.symbols[name]).atPos(pos).debugThrow();
"undefined variable '%1%'",
es.symbols[name]
).atPos(pos).debugThrow();
for (auto * e = env.get(); e && !fromWith; e = e->up.get()) for (auto * e = env.get(); e && !fromWith; e = e->up.get())
fromWith = e->isWith; fromWith = e->isWith;
this->level = withLevel; this->level = withLevel;
@ -348,7 +360,8 @@ void ExprSelect::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
es.exprEnvs.insert(std::make_pair(this, env)); es.exprEnvs.insert(std::make_pair(this, env));
e->bindVars(es, env); e->bindVars(es, env);
if (def) def->bindVars(es, env); if (def)
def->bindVars(es, env);
for (auto & i : attrPath) for (auto & i : attrPath)
if (!i.symbol) if (!i.symbol)
i.expr->bindVars(es, env); i.expr->bindVars(es, env);
@ -365,8 +378,8 @@ void ExprOpHasAttr::bindVars(EvalState & es, const std::shared_ptr<const StaticE
i.expr->bindVars(es, env); i.expr->bindVars(es, env);
} }
std::shared_ptr<const StaticEnv> ExprAttrs::bindInheritSources( std::shared_ptr<const StaticEnv>
EvalState & es, const std::shared_ptr<const StaticEnv> & env) ExprAttrs::bindInheritSources(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{ {
if (!inheritFromExprs) if (!inheritFromExprs)
return nullptr; return nullptr;
@ -392,7 +405,7 @@ void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
es.exprEnvs.insert(std::make_pair(this, env)); es.exprEnvs.insert(std::make_pair(this, env));
if (recursive) { if (recursive) {
auto newEnv = [&] () -> std::shared_ptr<const StaticEnv> { auto newEnv = [&]() -> std::shared_ptr<const StaticEnv> {
auto newEnv = std::make_shared<StaticEnv>(nullptr, env, attrs.size()); auto newEnv = std::make_shared<StaticEnv>(nullptr, env, attrs.size());
Displacement displ = 0; Displacement displ = 0;
@ -411,8 +424,7 @@ void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
i.nameExpr->bindVars(es, newEnv); i.nameExpr->bindVars(es, newEnv);
i.valueExpr->bindVars(es, newEnv); i.valueExpr->bindVars(es, newEnv);
} }
} } else {
else {
auto inheritFromEnv = bindInheritSources(es, env); auto inheritFromEnv = bindInheritSources(es, env);
for (auto & i : attrs) for (auto & i : attrs)
@ -439,14 +451,13 @@ void ExprLambda::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
if (es.debugRepl) if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env)); es.exprEnvs.insert(std::make_pair(this, env));
auto newEnv = std::make_shared<StaticEnv>( auto newEnv =
nullptr, env, std::make_shared<StaticEnv>(nullptr, env, (hasFormals() ? formals->formals.size() : 0) + (!arg ? 0 : 1));
(hasFormals() ? formals->formals.size() : 0) +
(!arg ? 0 : 1));
Displacement displ = 0; Displacement displ = 0;
if (arg) newEnv->vars.emplace_back(arg, displ++); if (arg)
newEnv->vars.emplace_back(arg, displ++);
if (hasFormals()) { if (hasFormals()) {
for (auto & i : formals->formals) for (auto & i : formals->formals)
@ -455,7 +466,8 @@ void ExprLambda::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
newEnv->sort(); newEnv->sort();
for (auto & i : formals->formals) for (auto & i : formals->formals)
if (i.def) i.def->bindVars(es, newEnv); if (i.def)
i.def->bindVars(es, newEnv);
} }
body->bindVars(es, newEnv); body->bindVars(es, newEnv);
@ -473,7 +485,7 @@ void ExprCall::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
void ExprLet::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) void ExprLet::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{ {
auto newEnv = [&] () -> std::shared_ptr<const StaticEnv> { auto newEnv = [&]() -> std::shared_ptr<const StaticEnv> {
auto newEnv = std::make_shared<StaticEnv>(nullptr, env, attrs->attrs.size()); auto newEnv = std::make_shared<StaticEnv>(nullptr, env, attrs->attrs.size());
Displacement displ = 0; Displacement displ = 0;
@ -562,13 +574,9 @@ void ExprPos::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
es.exprEnvs.insert(std::make_pair(this, env)); es.exprEnvs.insert(std::make_pair(this, env));
} }
/* Storing function names. */ /* Storing function names. */
void Expr::setName(Symbol name) void Expr::setName(Symbol name) {}
{
}
void ExprLambda::setName(Symbol name) void ExprLambda::setName(Symbol name)
{ {
@ -576,16 +584,14 @@ void ExprLambda::setName(Symbol name)
body->setName(name); body->setName(name);
} }
std::string ExprLambda::showNamePos(const EvalState & state) const std::string ExprLambda::showNamePos(const EvalState & state) const
{ {
std::string id(name std::string id(name ? concatStrings("'", state.symbols[name], "'") : "anonymous function");
? concatStrings("'", state.symbols[name], "'")
: "anonymous function");
return fmt("%1% at %2%", id, state.positions[pos]); return fmt("%1% at %2%", id, state.positions[pos]);
} }
void ExprLambda::setDocComment(DocComment docComment) { void ExprLambda::setDocComment(DocComment docComment)
{
// RFC 145 specifies that the innermost doc comment wins. // RFC 145 specifies that the innermost doc comment wins.
// See https://github.com/NixOS/rfcs/blob/master/rfcs/0145-doc-strings.md#ambiguous-placement // See https://github.com/NixOS/rfcs/blob/master/rfcs/0145-doc-strings.md#ambiguous-placement
if (!this->docComment) { if (!this->docComment) {
@ -606,11 +612,12 @@ void ExprLambda::setDocComment(DocComment docComment) {
size_t SymbolTable::totalSize() const size_t SymbolTable::totalSize() const
{ {
size_t n = 0; size_t n = 0;
dump([&] (const std::string & s) { n += s.size(); }); dump([&](const std::string & s) { n += s.size(); });
return n; return n;
} }
std::string DocComment::getInnerText(const PosTable & positions) const { std::string DocComment::getInnerText(const PosTable & positions) const
{
auto beginPos = positions[begin]; auto beginPos = positions[begin];
auto endPos = positions[end]; auto endPos = positions[end];
auto docCommentStr = beginPos.getSnippetUpTo(endPos).value_or(""); auto docCommentStr = beginPos.getSnippetUpTo(endPos).value_or("");
@ -628,8 +635,6 @@ std::string DocComment::getInnerText(const PosTable & positions) const {
return docStr; return docStr;
} }
/* Cursed or handling. /* Cursed or handling.
* *
* In parser.y, every use of expr_select in a production must call one of the * In parser.y, every use of expr_select in a production must call one of the
@ -647,13 +652,16 @@ void ExprCall::warnIfCursedOr(const SymbolTable & symbols, const PosTable & posi
{ {
if (cursedOrEndPos.has_value()) { if (cursedOrEndPos.has_value()) {
std::ostringstream out; std::ostringstream out;
out << "at " << positions[pos] << ": " out << "at " << positions[pos]
<< ": "
"This expression uses `or` as an identifier in a way that will change in a future Nix release.\n" "This expression uses `or` as an identifier in a way that will change in a future Nix release.\n"
"Wrap this entire expression in parentheses to preserve its current meaning:\n" "Wrap this entire expression in parentheses to preserve its current meaning:\n"
" (" << positions[pos].getSnippetUpTo(positions[*cursedOrEndPos]).value_or("could not read expression") << ")\n" " ("
<< positions[pos].getSnippetUpTo(positions[*cursedOrEndPos]).value_or("could not read expression")
<< ")\n"
"Give feedback at https://github.com/NixOS/nix/pull/11121"; "Give feedback at https://github.com/NixOS/nix/pull/11121";
warn(out.str()); warn(out.str());
} }
} }
} } // namespace nix

View file

@ -18,4 +18,4 @@ SourcePath EvalState::storePath(const StorePath & path)
return {rootFS, CanonPath{store->printStorePath(path)}}; return {rootFS, CanonPath{store->printStorePath(path)}};
} }
} } // namespace nix

File diff suppressed because it is too large Load diff

View file

@ -5,10 +5,11 @@
namespace nix { namespace nix {
static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{ {
NixStringContext context; NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardStringContext"); auto s = state.coerceToString(
pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardStringContext");
v.mkString(*s); v.mkString(*s);
} }
@ -21,18 +22,17 @@ static RegisterPrimOp primop_unsafeDiscardStringContext({
.fun = prim_unsafeDiscardStringContext, .fun = prim_unsafeDiscardStringContext,
}); });
static void prim_hasContext(EvalState & state, const PosIdx pos, Value ** args, Value & v)
static void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NixStringContext context; NixStringContext context;
state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.hasContext"); state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.hasContext");
v.mkBool(!context.empty()); v.mkBool(!context.empty());
} }
static RegisterPrimOp primop_hasContext({ static RegisterPrimOp primop_hasContext(
.name = "__hasContext", {.name = "__hasContext",
.args = {"s"}, .args = {"s"},
.doc = R"( .doc = R"(
Return `true` if string *s* has a non-empty context. Return `true` if string *s* has a non-empty context.
The context can be obtained with The context can be obtained with
[`getContext`](#builtins-getContext). [`getContext`](#builtins-getContext).
@ -50,21 +50,18 @@ static RegisterPrimOp primop_hasContext({
> else { ${name} = meta; } > else { ${name} = meta; }
> ``` > ```
)", )",
.fun = prim_hasContext .fun = prim_hasContext});
});
static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value ** args, Value & v)
static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NixStringContext context; NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardOutputDependency"); auto s = state.coerceToString(
pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardOutputDependency");
NixStringContext context2; NixStringContext context2;
for (auto && c : context) { for (auto && c : context) {
if (auto * ptr = std::get_if<NixStringContextElem::DrvDeep>(&c.raw)) { if (auto * ptr = std::get_if<NixStringContextElem::DrvDeep>(&c.raw)) {
context2.emplace(NixStringContextElem::Opaque { context2.emplace(NixStringContextElem::Opaque{.path = ptr->drvPath});
.path = ptr->drvPath
});
} else { } else {
/* Can reuse original item */ /* Can reuse original item */
context2.emplace(std::move(c).raw); context2.emplace(std::move(c).raw);
@ -74,10 +71,10 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx p
v.mkString(*s, context2); v.mkString(*s, context2);
} }
static RegisterPrimOp primop_unsafeDiscardOutputDependency({ static RegisterPrimOp primop_unsafeDiscardOutputDependency(
.name = "__unsafeDiscardOutputDependency", {.name = "__unsafeDiscardOutputDependency",
.args = {"s"}, .args = {"s"},
.doc = R"( .doc = R"(
Create a copy of the given string where every Create a copy of the given string where every
[derivation deep](@docroot@/language/string-context.md#string-context-element-derivation-deep) [derivation deep](@docroot@/language/string-context.md#string-context-element-derivation-deep)
string context element is turned into a string context element is turned into a
@ -94,58 +91,58 @@ static RegisterPrimOp primop_unsafeDiscardOutputDependency({
[`builtins.addDrvOutputDependencies`]: #builtins-addDrvOutputDependencies [`builtins.addDrvOutputDependencies`]: #builtins-addDrvOutputDependencies
)", )",
.fun = prim_unsafeDiscardOutputDependency .fun = prim_unsafeDiscardOutputDependency});
});
static void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, Value ** args, Value & v)
static void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NixStringContext context; NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.addDrvOutputDependencies"); auto s = state.coerceToString(
pos, *args[0], context, "while evaluating the argument passed to builtins.addDrvOutputDependencies");
auto contextSize = context.size(); auto contextSize = context.size();
if (contextSize != 1) { if (contextSize != 1) {
state.error<EvalError>( state.error<EvalError>("context of string '%s' must have exactly one element, but has %d", *s, contextSize)
"context of string '%s' must have exactly one element, but has %d", .atPos(pos)
*s, .debugThrow();
contextSize
).atPos(pos).debugThrow();
} }
NixStringContext context2 { NixStringContext context2{
(NixStringContextElem { std::visit(overloaded { (NixStringContextElem{std::visit(
[&](const NixStringContextElem::Opaque & c) -> NixStringContextElem::DrvDeep { overloaded{
if (!c.path.isDerivation()) { [&](const NixStringContextElem::Opaque & c) -> NixStringContextElem::DrvDeep {
state.error<EvalError>( if (!c.path.isDerivation()) {
"path '%s' is not a derivation", state.error<EvalError>("path '%s' is not a derivation", state.store->printStorePath(c.path))
state.store->printStorePath(c.path) .atPos(pos)
).atPos(pos).debugThrow(); .debugThrow();
} }
return NixStringContextElem::DrvDeep { return NixStringContextElem::DrvDeep{
.drvPath = c.path, .drvPath = c.path,
}; };
},
[&](const NixStringContextElem::Built & c) -> NixStringContextElem::DrvDeep {
state
.error<EvalError>(
"`addDrvOutputDependencies` can only act on derivations, not on a derivation output such as '%1%'",
c.output)
.atPos(pos)
.debugThrow();
},
[&](const NixStringContextElem::DrvDeep & c) -> NixStringContextElem::DrvDeep {
/* Reuse original item because we want this to be idempotent. */
/* FIXME: Suspicious move out of const. This is actually a copy, so the comment
above does not make much sense. */
return std::move(c);
},
}, },
[&](const NixStringContextElem::Built & c) -> NixStringContextElem::DrvDeep { context.begin()->raw)}),
state.error<EvalError>(
"`addDrvOutputDependencies` can only act on derivations, not on a derivation output such as '%1%'",
c.output
).atPos(pos).debugThrow();
},
[&](const NixStringContextElem::DrvDeep & c) -> NixStringContextElem::DrvDeep {
/* Reuse original item because we want this to be idempotent. */
/* FIXME: Suspicious move out of const. This is actually a copy, so the comment
above does not make much sense. */
return std::move(c);
},
}, context.begin()->raw) }),
}; };
v.mkString(*s, context2); v.mkString(*s, context2);
} }
static RegisterPrimOp primop_addDrvOutputDependencies({ static RegisterPrimOp primop_addDrvOutputDependencies(
.name = "__addDrvOutputDependencies", {.name = "__addDrvOutputDependencies",
.args = {"s"}, .args = {"s"},
.doc = R"( .doc = R"(
Create a copy of the given string where a single Create a copy of the given string where a single
[constant](@docroot@/language/string-context.md#string-context-element-constant) [constant](@docroot@/language/string-context.md#string-context-element-constant)
string context element is turned into a string context element is turned into a
@ -159,9 +156,7 @@ static RegisterPrimOp primop_addDrvOutputDependencies({
This is the opposite of [`builtins.unsafeDiscardOutputDependency`](#builtins-unsafeDiscardOutputDependency). This is the opposite of [`builtins.unsafeDiscardOutputDependency`](#builtins-unsafeDiscardOutputDependency).
)", )",
.fun = prim_addDrvOutputDependencies .fun = prim_addDrvOutputDependencies});
});
/* Extract the context of a string as a structured Nix value. /* Extract the context of a string as a structured Nix value.
@ -182,31 +177,31 @@ static RegisterPrimOp primop_addDrvOutputDependencies({
Note that for a given path any combination of the above attributes Note that for a given path any combination of the above attributes
may be present. may be present.
*/ */
static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_getContext(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{ {
struct ContextInfo { struct ContextInfo
{
bool path = false; bool path = false;
bool allOutputs = false; bool allOutputs = false;
Strings outputs; Strings outputs;
}; };
NixStringContext context; NixStringContext context;
state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.getContext"); state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.getContext");
auto contextInfos = std::map<StorePath, ContextInfo>(); auto contextInfos = std::map<StorePath, ContextInfo>();
for (auto && i : context) { for (auto && i : context) {
std::visit(overloaded { std::visit(
[&](NixStringContextElem::DrvDeep && d) { overloaded{
contextInfos[std::move(d.drvPath)].allOutputs = true; [&](NixStringContextElem::DrvDeep && d) { contextInfos[std::move(d.drvPath)].allOutputs = true; },
[&](NixStringContextElem::Built && b) {
// FIXME should eventually show string context as is, no
// resolving here.
auto drvPath = resolveDerivedPath(*state.store, *b.drvPath);
contextInfos[std::move(drvPath)].outputs.emplace_back(std::move(b.output));
},
[&](NixStringContextElem::Opaque && o) { contextInfos[std::move(o.path)].path = true; },
}, },
[&](NixStringContextElem::Built && b) { ((NixStringContextElem &&) i).raw);
// FIXME should eventually show string context as is, no
// resolving here.
auto drvPath = resolveDerivedPath(*state.store, *b.drvPath);
contextInfos[std::move(drvPath)].outputs.emplace_back(std::move(b.output));
},
[&](NixStringContextElem::Opaque && o) {
contextInfos[std::move(o.path)].path = true;
},
}, ((NixStringContextElem &&) i).raw);
} }
auto attrs = state.buildBindings(contextInfos.size()); auto attrs = state.buildBindings(contextInfos.size());
@ -231,10 +226,10 @@ static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args,
v.mkAttrs(attrs); v.mkAttrs(attrs);
} }
static RegisterPrimOp primop_getContext({ static RegisterPrimOp primop_getContext(
.name = "__getContext", {.name = "__getContext",
.args = {"s"}, .args = {"s"},
.doc = R"( .doc = R"(
Return the string context of *s*. Return the string context of *s*.
The string context tracks references to derivations within a string. The string context tracks references to derivations within a string.
@ -253,19 +248,18 @@ static RegisterPrimOp primop_getContext({
{ "/nix/store/arhvjaf6zmlyn8vh8fgn55rpwnxq0n7l-a.drv" = { outputs = [ "out" ]; }; } { "/nix/store/arhvjaf6zmlyn8vh8fgn55rpwnxq0n7l-a.drv" = { outputs = [ "out" ]; }; }
``` ```
)", )",
.fun = prim_getContext .fun = prim_getContext});
});
/* Append the given context to a given string. /* Append the given context to a given string.
See the commentary above getContext for details of the See the commentary above getContext for details of the
context representation. context representation.
*/ */
static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_appendContext(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{ {
NixStringContext context; NixStringContext context;
auto orig = state.forceString(*args[0], context, noPos, "while evaluating the first argument passed to builtins.appendContext"); auto orig = state.forceString(
*args[0], context, noPos, "while evaluating the first argument passed to builtins.appendContext");
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.appendContext"); state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.appendContext");
@ -274,10 +268,7 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
for (auto & i : *args[1]->attrs()) { for (auto & i : *args[1]->attrs()) {
const auto & name = state.symbols[i.name]; const auto & name = state.symbols[i.name];
if (!state.store->isStorePath(name)) if (!state.store->isStorePath(name))
state.error<EvalError>( state.error<EvalError>("context key '%s' is not a store path", name).atPos(i.pos).debugThrow();
"context key '%s' is not a store path",
name
).atPos(i.pos).debugThrow();
auto namePath = state.store->parseStorePath(name); auto namePath = state.store->parseStorePath(name);
if (!settings.readOnlyMode) if (!settings.readOnlyMode)
state.store->ensurePath(namePath); state.store->ensurePath(namePath);
@ -285,39 +276,46 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
if (auto attr = i.value->attrs()->get(sPath)) { if (auto attr = i.value->attrs()->get(sPath)) {
if (state.forceBool(*attr->value, attr->pos, "while evaluating the `path` attribute of a string context")) if (state.forceBool(*attr->value, attr->pos, "while evaluating the `path` attribute of a string context"))
context.emplace(NixStringContextElem::Opaque { context.emplace(
.path = namePath, NixStringContextElem::Opaque{
}); .path = namePath,
});
} }
if (auto attr = i.value->attrs()->get(sAllOutputs)) { if (auto attr = i.value->attrs()->get(sAllOutputs)) {
if (state.forceBool(*attr->value, attr->pos, "while evaluating the `allOutputs` attribute of a string context")) { if (state.forceBool(
*attr->value, attr->pos, "while evaluating the `allOutputs` attribute of a string context")) {
if (!isDerivation(name)) { if (!isDerivation(name)) {
state.error<EvalError>( state
"tried to add all-outputs context of %s, which is not a derivation, to a string", .error<EvalError>(
name "tried to add all-outputs context of %s, which is not a derivation, to a string", name)
).atPos(i.pos).debugThrow(); .atPos(i.pos)
.debugThrow();
} }
context.emplace(NixStringContextElem::DrvDeep { context.emplace(
.drvPath = namePath, NixStringContextElem::DrvDeep{
}); .drvPath = namePath,
});
} }
} }
if (auto attr = i.value->attrs()->get(state.sOutputs)) { if (auto attr = i.value->attrs()->get(state.sOutputs)) {
state.forceList(*attr->value, attr->pos, "while evaluating the `outputs` attribute of a string context"); state.forceList(*attr->value, attr->pos, "while evaluating the `outputs` attribute of a string context");
if (attr->value->listSize() && !isDerivation(name)) { if (attr->value->listSize() && !isDerivation(name)) {
state.error<EvalError>( state
"tried to add derivation output context of %s, which is not a derivation, to a string", .error<EvalError>(
name "tried to add derivation output context of %s, which is not a derivation, to a string", name)
).atPos(i.pos).debugThrow(); .atPos(i.pos)
.debugThrow();
} }
for (auto elem : attr->value->listItems()) { for (auto elem : attr->value->listItems()) {
auto outputName = state.forceStringNoCtx(*elem, attr->pos, "while evaluating an output name within a string context"); auto outputName =
context.emplace(NixStringContextElem::Built { state.forceStringNoCtx(*elem, attr->pos, "while evaluating an output name within a string context");
.drvPath = makeConstantStorePathRef(namePath), context.emplace(
.output = std::string { outputName }, NixStringContextElem::Built{
}); .drvPath = makeConstantStorePathRef(namePath),
.output = std::string{outputName},
});
} }
} }
} }
@ -325,10 +323,6 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
v.mkString(orig, context); v.mkString(orig, context);
} }
static RegisterPrimOp primop_appendContext({ static RegisterPrimOp primop_appendContext({.name = "__appendContext", .arity = 2, .fun = prim_appendContext});
.name = "__appendContext",
.arity = 2,
.fun = prim_appendContext
});
} } // namespace nix

View file

@ -15,29 +15,35 @@ namespace nix {
* @param toPathMaybe Path to write the rewritten path to. If empty, the error shows the actual path. * @param toPathMaybe Path to write the rewritten path to. If empty, the error shows the actual path.
* @param v Return `Value` * @param v Return `Value`
*/ */
static void runFetchClosureWithRewrite(EvalState & state, const PosIdx pos, Store & fromStore, const StorePath & fromPath, const std::optional<StorePath> & toPathMaybe, Value &v) { static void runFetchClosureWithRewrite(
EvalState & state,
const PosIdx pos,
Store & fromStore,
const StorePath & fromPath,
const std::optional<StorePath> & toPathMaybe,
Value & v)
{
// establish toPath or throw // establish toPath or throw
if (!toPathMaybe || !state.store->isValidPath(*toPathMaybe)) { if (!toPathMaybe || !state.store->isValidPath(*toPathMaybe)) {
auto rewrittenPath = makeContentAddressed(fromStore, *state.store, fromPath); auto rewrittenPath = makeContentAddressed(fromStore, *state.store, fromPath);
if (toPathMaybe && *toPathMaybe != rewrittenPath) if (toPathMaybe && *toPathMaybe != rewrittenPath)
throw Error({ throw Error(
.msg = HintFmt("rewriting '%s' to content-addressed form yielded '%s', while '%s' was expected", {.msg = HintFmt(
state.store->printStorePath(fromPath), "rewriting '%s' to content-addressed form yielded '%s', while '%s' was expected",
state.store->printStorePath(rewrittenPath), state.store->printStorePath(fromPath),
state.store->printStorePath(*toPathMaybe)), state.store->printStorePath(rewrittenPath),
.pos = state.positions[pos] state.store->printStorePath(*toPathMaybe)),
}); .pos = state.positions[pos]});
if (!toPathMaybe) if (!toPathMaybe)
throw Error({ throw Error(
.msg = HintFmt( {.msg = HintFmt(
"rewriting '%s' to content-addressed form yielded '%s'\n" "rewriting '%s' to content-addressed form yielded '%s'\n"
"Use this value for the 'toPath' attribute passed to 'fetchClosure'", "Use this value for the 'toPath' attribute passed to 'fetchClosure'",
state.store->printStorePath(fromPath), state.store->printStorePath(fromPath),
state.store->printStorePath(rewrittenPath)), state.store->printStorePath(rewrittenPath)),
.pos = state.positions[pos] .pos = state.positions[pos]});
});
} }
const auto & toPath = *toPathMaybe; const auto & toPath = *toPathMaybe;
@ -49,13 +55,12 @@ static void runFetchClosureWithRewrite(EvalState & state, const PosIdx pos, Stor
if (!resultInfo->isContentAddressed(*state.store)) { if (!resultInfo->isContentAddressed(*state.store)) {
// We don't perform the rewriting when outPath already exists, as an optimisation. // We don't perform the rewriting when outPath already exists, as an optimisation.
// However, we can quickly detect a mistake if the toPath is input addressed. // However, we can quickly detect a mistake if the toPath is input addressed.
throw Error({ throw Error(
.msg = HintFmt( {.msg = HintFmt(
"The 'toPath' value '%s' is input-addressed, so it can't possibly be the result of rewriting to a content-addressed path.\n\n" "The 'toPath' value '%s' is input-addressed, so it can't possibly be the result of rewriting to a content-addressed path.\n\n"
"Set 'toPath' to an empty string to make Nix report the correct content-addressed path.", "Set 'toPath' to an empty string to make Nix report the correct content-addressed path.",
state.store->printStorePath(toPath)), state.store->printStorePath(toPath)),
.pos = state.positions[pos] .pos = state.positions[pos]});
});
} }
state.mkStorePathString(toPath, v); state.mkStorePathString(toPath, v);
@ -64,24 +69,25 @@ static void runFetchClosureWithRewrite(EvalState & state, const PosIdx pos, Stor
/** /**
* Fetch the closure and make sure it's content addressed. * Fetch the closure and make sure it's content addressed.
*/ */
static void runFetchClosureWithContentAddressedPath(EvalState & state, const PosIdx pos, Store & fromStore, const StorePath & fromPath, Value & v) { static void runFetchClosureWithContentAddressedPath(
EvalState & state, const PosIdx pos, Store & fromStore, const StorePath & fromPath, Value & v)
{
if (!state.store->isValidPath(fromPath)) if (!state.store->isValidPath(fromPath))
copyClosure(fromStore, *state.store, RealisedPath::Set { fromPath }); copyClosure(fromStore, *state.store, RealisedPath::Set{fromPath});
auto info = state.store->queryPathInfo(fromPath); auto info = state.store->queryPathInfo(fromPath);
if (!info->isContentAddressed(*state.store)) { if (!info->isContentAddressed(*state.store)) {
throw Error({ throw Error(
.msg = HintFmt( {.msg = HintFmt(
"The 'fromPath' value '%s' is input-addressed, but 'inputAddressed' is set to 'false' (default).\n\n" "The 'fromPath' value '%s' is input-addressed, but 'inputAddressed' is set to 'false' (default).\n\n"
"If you do intend to fetch an input-addressed store path, add\n\n" "If you do intend to fetch an input-addressed store path, add\n\n"
" inputAddressed = true;\n\n" " inputAddressed = true;\n\n"
"to the 'fetchClosure' arguments.\n\n" "to the 'fetchClosure' arguments.\n\n"
"Note that to ensure authenticity input-addressed store paths, users must configure a trusted binary cache public key on their systems. This is not needed for content-addressed paths.", "Note that to ensure authenticity input-addressed store paths, users must configure a trusted binary cache public key on their systems. This is not needed for content-addressed paths.",
state.store->printStorePath(fromPath)), state.store->printStorePath(fromPath)),
.pos = state.positions[pos] .pos = state.positions[pos]});
});
} }
state.mkStorePathString(fromPath, v); state.mkStorePathString(fromPath, v);
@ -90,21 +96,22 @@ static void runFetchClosureWithContentAddressedPath(EvalState & state, const Pos
/** /**
* Fetch the closure and make sure it's input addressed. * Fetch the closure and make sure it's input addressed.
*/ */
static void runFetchClosureWithInputAddressedPath(EvalState & state, const PosIdx pos, Store & fromStore, const StorePath & fromPath, Value & v) { static void runFetchClosureWithInputAddressedPath(
EvalState & state, const PosIdx pos, Store & fromStore, const StorePath & fromPath, Value & v)
{
if (!state.store->isValidPath(fromPath)) if (!state.store->isValidPath(fromPath))
copyClosure(fromStore, *state.store, RealisedPath::Set { fromPath }); copyClosure(fromStore, *state.store, RealisedPath::Set{fromPath});
auto info = state.store->queryPathInfo(fromPath); auto info = state.store->queryPathInfo(fromPath);
if (info->isContentAddressed(*state.store)) { if (info->isContentAddressed(*state.store)) {
throw Error({ throw Error(
.msg = HintFmt( {.msg = HintFmt(
"The store object referred to by 'fromPath' at '%s' is not input-addressed, but 'inputAddressed' is set to 'true'.\n\n" "The store object referred to by 'fromPath' at '%s' is not input-addressed, but 'inputAddressed' is set to 'true'.\n\n"
"Remove the 'inputAddressed' attribute (it defaults to 'false') to expect 'fromPath' to be content-addressed", "Remove the 'inputAddressed' attribute (it defaults to 'false') to expect 'fromPath' to be content-addressed",
state.store->printStorePath(fromPath)), state.store->printStorePath(fromPath)),
.pos = state.positions[pos] .pos = state.positions[pos]});
});
} }
state.mkStorePathString(fromPath, v); state.mkStorePathString(fromPath, v);
@ -112,7 +119,7 @@ static void runFetchClosureWithInputAddressedPath(EvalState & state, const PosId
typedef std::optional<StorePath> StorePathOrGap; typedef std::optional<StorePath> StorePathOrGap;
static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{ {
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.fetchClosure"); state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.fetchClosure");
@ -136,67 +143,58 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg
state.forceValue(*attr.value, attr.pos); state.forceValue(*attr.value, attr.pos);
bool isEmptyString = attr.value->type() == nString && attr.value->string_view() == ""; bool isEmptyString = attr.value->type() == nString && attr.value->string_view() == "";
if (isEmptyString) { if (isEmptyString) {
toPath = StorePathOrGap {}; toPath = StorePathOrGap{};
} } else {
else {
NixStringContext context; NixStringContext context;
toPath = state.coerceToStorePath(attr.pos, *attr.value, context, attrHint()); toPath = state.coerceToStorePath(attr.pos, *attr.value, context, attrHint());
} }
} }
else if (attrName == "fromStore") else if (attrName == "fromStore")
fromStoreUrl = state.forceStringNoCtx(*attr.value, attr.pos, fromStoreUrl = state.forceStringNoCtx(*attr.value, attr.pos, attrHint());
attrHint());
else if (attrName == "inputAddressed") else if (attrName == "inputAddressed")
inputAddressedMaybe = state.forceBool(*attr.value, attr.pos, attrHint()); inputAddressedMaybe = state.forceBool(*attr.value, attr.pos, attrHint());
else else
throw Error({ throw Error(
.msg = HintFmt("attribute '%s' isn't supported in call to 'fetchClosure'", attrName), {.msg = HintFmt("attribute '%s' isn't supported in call to 'fetchClosure'", attrName),
.pos = state.positions[pos] .pos = state.positions[pos]});
});
} }
if (!fromPath) if (!fromPath)
throw Error({ throw Error(
.msg = HintFmt("attribute '%s' is missing in call to 'fetchClosure'", "fromPath"), {.msg = HintFmt("attribute '%s' is missing in call to 'fetchClosure'", "fromPath"),
.pos = state.positions[pos] .pos = state.positions[pos]});
});
bool inputAddressed = inputAddressedMaybe.value_or(false); bool inputAddressed = inputAddressedMaybe.value_or(false);
if (inputAddressed) { if (inputAddressed) {
if (toPath) if (toPath)
throw Error({ throw Error(
.msg = HintFmt("attribute '%s' is set to true, but '%s' is also set. Please remove one of them", {.msg = HintFmt(
"inputAddressed", "attribute '%s' is set to true, but '%s' is also set. Please remove one of them",
"toPath"), "inputAddressed",
.pos = state.positions[pos] "toPath"),
}); .pos = state.positions[pos]});
} }
if (!fromStoreUrl) if (!fromStoreUrl)
throw Error({ throw Error(
.msg = HintFmt("attribute '%s' is missing in call to 'fetchClosure'", "fromStore"), {.msg = HintFmt("attribute '%s' is missing in call to 'fetchClosure'", "fromStore"),
.pos = state.positions[pos] .pos = state.positions[pos]});
});
auto parsedURL = parseURL(*fromStoreUrl); auto parsedURL = parseURL(*fromStoreUrl);
if (parsedURL.scheme != "http" && if (parsedURL.scheme != "http" && parsedURL.scheme != "https"
parsedURL.scheme != "https" && && !(getEnv("_NIX_IN_TEST").has_value() && parsedURL.scheme == "file"))
!(getEnv("_NIX_IN_TEST").has_value() && parsedURL.scheme == "file")) throw Error(
throw Error({ {.msg = HintFmt("'fetchClosure' only supports http:// and https:// stores"), .pos = state.positions[pos]});
.msg = HintFmt("'fetchClosure' only supports http:// and https:// stores"),
.pos = state.positions[pos]
});
if (!parsedURL.query.empty()) if (!parsedURL.query.empty())
throw Error({ throw Error(
.msg = HintFmt("'fetchClosure' does not support URL query parameters (in '%s')", *fromStoreUrl), {.msg = HintFmt("'fetchClosure' does not support URL query parameters (in '%s')", *fromStoreUrl),
.pos = state.positions[pos] .pos = state.positions[pos]});
});
auto fromStore = openStore(parsedURL.to_string()); auto fromStore = openStore(parsedURL.to_string());
@ -284,4 +282,4 @@ static RegisterPrimOp primop_fetchClosure({
.experimentalFeature = Xp::FetchClosure, .experimentalFeature = Xp::FetchClosure,
}); });
} } // namespace nix

View file

@ -8,7 +8,7 @@
namespace nix { namespace nix {
static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{ {
std::string url; std::string url;
std::optional<Hash> rev; std::optional<Hash> rev;
@ -23,31 +23,46 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
for (auto & attr : *args[0]->attrs()) { for (auto & attr : *args[0]->attrs()) {
std::string_view n(state.symbols[attr.name]); std::string_view n(state.symbols[attr.name]);
if (n == "url") if (n == "url")
url = state.coerceToString(attr.pos, *attr.value, context, url = state
"while evaluating the `url` attribute passed to builtins.fetchMercurial", .coerceToString(
false, false).toOwned(); attr.pos,
*attr.value,
context,
"while evaluating the `url` attribute passed to builtins.fetchMercurial",
false,
false)
.toOwned();
else if (n == "rev") { else if (n == "rev") {
// Ugly: unlike fetchGit, here the "rev" attribute can // Ugly: unlike fetchGit, here the "rev" attribute can
// be both a revision or a branch/tag name. // be both a revision or a branch/tag name.
auto value = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `rev` attribute passed to builtins.fetchMercurial"); auto value = state.forceStringNoCtx(
*attr.value, attr.pos, "while evaluating the `rev` attribute passed to builtins.fetchMercurial");
if (std::regex_match(value.begin(), value.end(), revRegex)) if (std::regex_match(value.begin(), value.end(), revRegex))
rev = Hash::parseAny(value, HashAlgorithm::SHA1); rev = Hash::parseAny(value, HashAlgorithm::SHA1);
else else
ref = value; ref = value;
} } else if (n == "name")
else if (n == "name") name = state.forceStringNoCtx(
name = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `name` attribute passed to builtins.fetchMercurial"); *attr.value, attr.pos, "while evaluating the `name` attribute passed to builtins.fetchMercurial");
else else
state.error<EvalError>("unsupported argument '%s' to 'fetchMercurial'", state.symbols[attr.name]).atPos(attr.pos).debugThrow(); state.error<EvalError>("unsupported argument '%s' to 'fetchMercurial'", state.symbols[attr.name])
.atPos(attr.pos)
.debugThrow();
} }
if (url.empty()) if (url.empty())
state.error<EvalError>("'url' argument required").atPos(pos).debugThrow(); state.error<EvalError>("'url' argument required").atPos(pos).debugThrow();
} else } else
url = state.coerceToString(pos, *args[0], context, url = state
"while evaluating the first argument passed to builtins.fetchMercurial", .coerceToString(
false, false).toOwned(); pos,
*args[0],
context,
"while evaluating the first argument passed to builtins.fetchMercurial",
false,
false)
.toOwned();
// FIXME: git externals probably can be used to bypass the URI // FIXME: git externals probably can be used to bypass the URI
// whitelist. Ah well. // whitelist. Ah well.
@ -60,8 +75,10 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
attrs.insert_or_assign("type", "hg"); attrs.insert_or_assign("type", "hg");
attrs.insert_or_assign("url", url.find("://") != std::string::npos ? url : "file://" + url); attrs.insert_or_assign("url", url.find("://") != std::string::npos ? url : "file://" + url);
attrs.insert_or_assign("name", std::string(name)); attrs.insert_or_assign("name", std::string(name));
if (ref) attrs.insert_or_assign("ref", *ref); if (ref)
if (rev) attrs.insert_or_assign("rev", rev->gitRev()); attrs.insert_or_assign("ref", *ref);
if (rev)
attrs.insert_or_assign("rev", rev->gitRev());
auto input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs)); auto input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs));
auto [storePath, input2] = input.fetchToStore(state.store); auto [storePath, input2] = input.fetchToStore(state.store);
@ -82,10 +99,6 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
state.allowPath(storePath); state.allowPath(storePath);
} }
static RegisterPrimOp r_fetchMercurial({ static RegisterPrimOp r_fetchMercurial({.name = "fetchMercurial", .arity = 1, .fun = prim_fetchMercurial});
.name = "fetchMercurial",
.arity = 1,
.fun = prim_fetchMercurial
});
} } // namespace nix

View file

@ -37,8 +37,7 @@ void emitTreeAttrs(
attrs.alloc("narHash").mkString(narHash->to_string(HashFormat::SRI, true)); attrs.alloc("narHash").mkString(narHash->to_string(HashFormat::SRI, true));
if (input.getType() == "git") if (input.getType() == "git")
attrs.alloc("submodules").mkBool( attrs.alloc("submodules").mkBool(fetchers::maybeGetBoolAttr(input.attrs, "submodules").value_or(false));
fetchers::maybeGetBoolAttr(input.attrs, "submodules").value_or(false));
if (!forceDirty) { if (!forceDirty) {
@ -56,7 +55,6 @@ void emitTreeAttrs(
attrs.alloc("revCount").mkInt(*revCount); attrs.alloc("revCount").mkInt(*revCount);
else if (emptyRevFallback) else if (emptyRevFallback)
attrs.alloc("revCount").mkInt(0); attrs.alloc("revCount").mkInt(0);
} }
if (auto dirtyRev = fetchers::maybeGetStrAttr(input.attrs, "dirtyRev")) { if (auto dirtyRev = fetchers::maybeGetStrAttr(input.attrs, "dirtyRev")) {
@ -66,14 +64,14 @@ void emitTreeAttrs(
if (auto lastModified = input.getLastModified()) { if (auto lastModified = input.getLastModified()) {
attrs.alloc("lastModified").mkInt(*lastModified); attrs.alloc("lastModified").mkInt(*lastModified);
attrs.alloc("lastModifiedDate").mkString( attrs.alloc("lastModifiedDate").mkString(fmt("%s", std::put_time(std::gmtime(&*lastModified), "%Y%m%d%H%M%S")));
fmt("%s", std::put_time(std::gmtime(&*lastModified), "%Y%m%d%H%M%S")));
} }
v.mkAttrs(attrs); v.mkAttrs(attrs);
} }
struct FetchTreeParams { struct FetchTreeParams
{
bool emptyRevFallback = false; bool emptyRevFallback = false;
bool allowNameArgument = false; bool allowNameArgument = false;
bool isFetchGit = false; bool isFetchGit = false;
@ -81,17 +79,14 @@ struct FetchTreeParams {
}; };
static void fetchTree( static void fetchTree(
EvalState & state, EvalState & state, const PosIdx pos, Value ** args, Value & v, const FetchTreeParams & params = FetchTreeParams{})
const PosIdx pos, {
Value * * args, fetchers::Input input{state.fetchSettings};
Value & v,
const FetchTreeParams & params = FetchTreeParams{}
) {
fetchers::Input input { state.fetchSettings };
NixStringContext context; NixStringContext context;
std::optional<std::string> type; std::optional<std::string> type;
auto fetcher = params.isFetchGit ? "fetchGit" : "fetchTree"; auto fetcher = params.isFetchGit ? "fetchGit" : "fetchTree";
if (params.isFetchGit) type = "git"; if (params.isFetchGit)
type = "git";
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
@ -102,47 +97,55 @@ static void fetchTree(
if (auto aType = args[0]->attrs()->get(state.sType)) { if (auto aType = args[0]->attrs()->get(state.sType)) {
if (type) if (type)
state.error<EvalError>( state.error<EvalError>("unexpected argument 'type'").atPos(pos).debugThrow();
"unexpected argument 'type'" type = state.forceStringNoCtx(
).atPos(pos).debugThrow(); *aType->value, aType->pos, fmt("while evaluating the `type` argument passed to '%s'", fetcher));
type = state.forceStringNoCtx(*aType->value, aType->pos,
fmt("while evaluating the `type` argument passed to '%s'", fetcher));
} else if (!type) } else if (!type)
state.error<EvalError>( state.error<EvalError>("argument 'type' is missing in call to '%s'", fetcher).atPos(pos).debugThrow();
"argument 'type' is missing in call to '%s'", fetcher
).atPos(pos).debugThrow();
attrs.emplace("type", type.value()); attrs.emplace("type", type.value());
for (auto & attr : *args[0]->attrs()) { for (auto & attr : *args[0]->attrs()) {
if (attr.name == state.sType) continue; if (attr.name == state.sType)
continue;
state.forceValue(*attr.value, attr.pos); state.forceValue(*attr.value, attr.pos);
if (attr.value->type() == nPath || attr.value->type() == nString) { if (attr.value->type() == nPath || attr.value->type() == nString) {
auto s = state.coerceToString(attr.pos, *attr.value, context, "", false, false).toOwned(); auto s = state.coerceToString(attr.pos, *attr.value, context, "", false, false).toOwned();
attrs.emplace(state.symbols[attr.name], attrs.emplace(
params.isFetchGit && state.symbols[attr.name] == "url" state.symbols[attr.name],
? fixGitURL(s) params.isFetchGit && state.symbols[attr.name] == "url" ? fixGitURL(s) : s);
: s); } else if (attr.value->type() == nBool)
}
else if (attr.value->type() == nBool)
attrs.emplace(state.symbols[attr.name], Explicit<bool>{attr.value->boolean()}); attrs.emplace(state.symbols[attr.name], Explicit<bool>{attr.value->boolean()});
else if (attr.value->type() == nInt) { else if (attr.value->type() == nInt) {
auto intValue = attr.value->integer().value; auto intValue = attr.value->integer().value;
if (intValue < 0) if (intValue < 0)
state.error<EvalError>("negative value given for '%s' argument '%s': %d", fetcher, state.symbols[attr.name], intValue).atPos(pos).debugThrow(); state
.error<EvalError>(
"negative value given for '%s' argument '%s': %d",
fetcher,
state.symbols[attr.name],
intValue)
.atPos(pos)
.debugThrow();
attrs.emplace(state.symbols[attr.name], uint64_t(intValue)); attrs.emplace(state.symbols[attr.name], uint64_t(intValue));
} else if (state.symbols[attr.name] == "publicKeys") { } else if (state.symbols[attr.name] == "publicKeys") {
experimentalFeatureSettings.require(Xp::VerifiedFetches); experimentalFeatureSettings.require(Xp::VerifiedFetches);
attrs.emplace(state.symbols[attr.name], printValueAsJSON(state, true, *attr.value, pos, context).dump()); attrs.emplace(
} state.symbols[attr.name], printValueAsJSON(state, true, *attr.value, pos, context).dump());
else } else
state.error<TypeError>("argument '%s' to '%s' is %s while a string, Boolean or integer is expected", state
state.symbols[attr.name], fetcher, showType(*attr.value)).debugThrow(); .error<TypeError>(
"argument '%s' to '%s' is %s while a string, Boolean or integer is expected",
state.symbols[attr.name],
fetcher,
showType(*attr.value))
.debugThrow();
} }
if (params.isFetchGit && !attrs.contains("exportIgnore") && (!attrs.contains("submodules") || !*fetchers::maybeGetBoolAttr(attrs, "submodules"))) { if (params.isFetchGit && !attrs.contains("exportIgnore")
&& (!attrs.contains("submodules") || !*fetchers::maybeGetBoolAttr(attrs, "submodules"))) {
attrs.emplace("exportIgnore", Explicit<bool>{true}); attrs.emplace("exportIgnore", Explicit<bool>{true});
} }
@ -153,29 +156,38 @@ static void fetchTree(
if (!params.allowNameArgument) if (!params.allowNameArgument)
if (auto nameIter = attrs.find("name"); nameIter != attrs.end()) if (auto nameIter = attrs.find("name"); nameIter != attrs.end())
state.error<EvalError>( state.error<EvalError>("argument 'name' isnt supported in call to '%s'", fetcher)
"argument 'name' isnt supported in call to '%s'", fetcher .atPos(pos)
).atPos(pos).debugThrow(); .debugThrow();
input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs)); input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs));
} else { } else {
auto url = state.coerceToString(pos, *args[0], context, auto url = state
fmt("while evaluating the first argument passed to '%s'", fetcher), .coerceToString(
false, false).toOwned(); pos,
*args[0],
context,
fmt("while evaluating the first argument passed to '%s'", fetcher),
false,
false)
.toOwned();
if (params.isFetchGit) { if (params.isFetchGit) {
fetchers::Attrs attrs; fetchers::Attrs attrs;
attrs.emplace("type", "git"); attrs.emplace("type", "git");
attrs.emplace("url", fixGitURL(url)); attrs.emplace("url", fixGitURL(url));
if (!attrs.contains("exportIgnore") && (!attrs.contains("submodules") || !*fetchers::maybeGetBoolAttr(attrs, "submodules"))) { if (!attrs.contains("exportIgnore")
&& (!attrs.contains("submodules") || !*fetchers::maybeGetBoolAttr(attrs, "submodules"))) {
attrs.emplace("exportIgnore", Explicit<bool>{true}); attrs.emplace("exportIgnore", Explicit<bool>{true});
} }
input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs)); input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs));
} else { } else {
if (!experimentalFeatureSettings.isEnabled(Xp::Flakes)) if (!experimentalFeatureSettings.isEnabled(Xp::Flakes))
state.error<EvalError>( state
"passing a string argument to '%s' requires the 'flakes' experimental feature", fetcher .error<EvalError>(
).atPos(pos).debugThrow(); "passing a string argument to '%s' requires the 'flakes' experimental feature", fetcher)
.atPos(pos)
.debugThrow();
input = fetchers::Input::fromURL(state.fetchSettings, url); input = fetchers::Input::fromURL(state.fetchSettings, url);
} }
} }
@ -190,9 +202,11 @@ static void fetchTree(
"This is deprecated since such inputs are verifiable but may not be reproducible.", "This is deprecated since such inputs are verifiable but may not be reproducible.",
input.to_string()); input.to_string());
else else
state.error<EvalError>( state
"in pure evaluation mode, '%s' will not fetch unlocked input '%s'", .error<EvalError>(
fetcher, input.to_string()).atPos(pos).debugThrow(); "in pure evaluation mode, '%s' will not fetch unlocked input '%s'", fetcher, input.to_string())
.atPos(pos)
.debugThrow();
} }
state.checkURI(input.toURLString()); state.checkURI(input.toURLString());
@ -211,9 +225,9 @@ static void fetchTree(
emitTreeAttrs(state, storePath, input2, v, params.emptyRevFallback, false); emitTreeAttrs(state, storePath, input2, v, params.emptyRevFallback, false);
} }
static void prim_fetchTree(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_fetchTree(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{ {
fetchTree(state, pos, args, v, { }); fetchTree(state, pos, args, v, {});
} }
static RegisterPrimOp primop_fetchTree({ static RegisterPrimOp primop_fetchTree({
@ -446,7 +460,7 @@ static RegisterPrimOp primop_fetchTree({
.experimentalFeature = Xp::FetchTree, .experimentalFeature = Xp::FetchTree,
}); });
void prim_fetchFinalTree(EvalState & state, const PosIdx pos, Value * * args, Value & v) void prim_fetchFinalTree(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{ {
fetchTree(state, pos, args, v, {.isFinal = true}); fetchTree(state, pos, args, v, {.isFinal = true});
} }
@ -458,8 +472,14 @@ static RegisterPrimOp primop_fetchFinalTree({
.internal = true, .internal = true,
}); });
static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v, static void fetch(
const std::string & who, bool unpack, std::string name) EvalState & state,
const PosIdx pos,
Value ** args,
Value & v,
const std::string & who,
bool unpack,
std::string name)
{ {
std::optional<std::string> url; std::optional<std::string> url;
std::optional<Hash> expectedHash; std::optional<Hash> expectedHash;
@ -476,19 +496,20 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
if (n == "url") if (n == "url")
url = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the url we should fetch"); url = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the url we should fetch");
else if (n == "sha256") else if (n == "sha256")
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the sha256 of the content we should fetch"), HashAlgorithm::SHA256); expectedHash = newHashAllowEmpty(
state.forceStringNoCtx(
*attr.value, attr.pos, "while evaluating the sha256 of the content we should fetch"),
HashAlgorithm::SHA256);
else if (n == "name") { else if (n == "name") {
nameAttrPassed = true; nameAttrPassed = true;
name = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the name of the content we should fetch"); name = state.forceStringNoCtx(
} *attr.value, attr.pos, "while evaluating the name of the content we should fetch");
else } else
state.error<EvalError>("unsupported argument '%s' to '%s'", n, who) state.error<EvalError>("unsupported argument '%s' to '%s'", n, who).atPos(pos).debugThrow();
.atPos(pos).debugThrow();
} }
if (!url) if (!url)
state.error<EvalError>( state.error<EvalError>("'url' argument required").atPos(pos).debugThrow();
"'url' argument required").atPos(pos).debugThrow();
} else } else
url = state.forceStringNoCtx(*args[0], pos, "while evaluating the url we should fetch"); url = state.forceStringNoCtx(*args[0], pos, "while evaluating the url we should fetch");
@ -504,27 +525,41 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
checkName(name); checkName(name);
} catch (BadStorePathName & e) { } catch (BadStorePathName & e) {
auto resolution = auto resolution =
nameAttrPassed ? HintFmt("Please change the value for the 'name' attribute passed to '%s', so that it can create a valid store path.", who) : nameAttrPassed
isArgAttrs ? HintFmt("Please add a valid 'name' attribute to the argument for '%s', so that it can create a valid store path.", who) : ? HintFmt(
HintFmt("Please pass an attribute set with 'url' and 'name' attributes to '%s', so that it can create a valid store path.", who); "Please change the value for the 'name' attribute passed to '%s', so that it can create a valid store path.",
who)
: isArgAttrs
? HintFmt(
"Please add a valid 'name' attribute to the argument for '%s', so that it can create a valid store path.",
who)
: HintFmt(
"Please pass an attribute set with 'url' and 'name' attributes to '%s', so that it can create a valid store path.",
who);
state.error<EvalError>( state
std::string("invalid store path name when fetching URL '%s': %s. %s"), *url, Uncolored(e.message()), Uncolored(resolution.str())) .error<EvalError>(
.atPos(pos).debugThrow(); std::string("invalid store path name when fetching URL '%s': %s. %s"),
*url,
Uncolored(e.message()),
Uncolored(resolution.str()))
.atPos(pos)
.debugThrow();
} }
if (state.settings.pureEval && !expectedHash) if (state.settings.pureEval && !expectedHash)
state.error<EvalError>("in pure evaluation mode, '%s' requires a 'sha256' argument", who).atPos(pos).debugThrow(); state.error<EvalError>("in pure evaluation mode, '%s' requires a 'sha256' argument", who)
.atPos(pos)
.debugThrow();
// early exit if pinned and already in the store // early exit if pinned and already in the store
if (expectedHash && expectedHash->algo == HashAlgorithm::SHA256) { if (expectedHash && expectedHash->algo == HashAlgorithm::SHA256) {
auto expectedPath = state.store->makeFixedOutputPath( auto expectedPath = state.store->makeFixedOutputPath(
name, name,
FixedOutputInfo { FixedOutputInfo{
.method = unpack ? FileIngestionMethod::NixArchive : FileIngestionMethod::Flat, .method = unpack ? FileIngestionMethod::NixArchive : FileIngestionMethod::Flat,
.hash = *expectedHash, .hash = *expectedHash,
.references = {} .references = {}});
});
if (state.store->isValidPath(expectedPath)) { if (state.store->isValidPath(expectedPath)) {
state.allowAndSetStorePathString(expectedPath, v); state.allowAndSetStorePathString(expectedPath, v);
@ -534,34 +569,32 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
// TODO: fetching may fail, yet the path may be substitutable. // TODO: fetching may fail, yet the path may be substitutable.
// https://github.com/NixOS/nix/issues/4313 // https://github.com/NixOS/nix/issues/4313
auto storePath = auto storePath = unpack ? fetchToStore(
unpack *state.store,
? fetchToStore( fetchers::downloadTarball(state.store, state.fetchSettings, *url),
*state.store, FetchMode::Copy,
fetchers::downloadTarball(state.store, state.fetchSettings, *url), name)
FetchMode::Copy, : fetchers::downloadFile(state.store, *url, name).storePath;
name)
: fetchers::downloadFile(state.store, *url, name).storePath;
if (expectedHash) { if (expectedHash) {
auto hash = unpack auto hash = unpack ? state.store->queryPathInfo(storePath)->narHash
? state.store->queryPathInfo(storePath)->narHash : hashFile(HashAlgorithm::SHA256, state.store->toRealPath(storePath));
: hashFile(HashAlgorithm::SHA256, state.store->toRealPath(storePath));
if (hash != *expectedHash) { if (hash != *expectedHash) {
state.error<EvalError>( state
"hash mismatch in file downloaded from '%s':\n specified: %s\n got: %s", .error<EvalError>(
*url, "hash mismatch in file downloaded from '%s':\n specified: %s\n got: %s",
expectedHash->to_string(HashFormat::Nix32, true), *url,
hash.to_string(HashFormat::Nix32, true) expectedHash->to_string(HashFormat::Nix32, true),
).withExitStatus(102) hash.to_string(HashFormat::Nix32, true))
.debugThrow(); .withExitStatus(102)
.debugThrow();
} }
} }
state.allowAndSetStorePathString(storePath, v); state.allowAndSetStorePathString(storePath, v);
} }
static void prim_fetchurl(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_fetchurl(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{ {
fetch(state, pos, args, v, "fetchurl", false, ""); fetch(state, pos, args, v, "fetchurl", false, "");
} }
@ -587,7 +620,7 @@ static RegisterPrimOp primop_fetchurl({
.fun = prim_fetchurl, .fun = prim_fetchurl,
}); });
static void prim_fetchTarball(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_fetchTarball(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{ {
fetch(state, pos, args, v, "fetchTarball", true, "source"); fetch(state, pos, args, v, "fetchTarball", true, "source");
} }
@ -637,14 +670,10 @@ static RegisterPrimOp primop_fetchTarball({
.fun = prim_fetchTarball, .fun = prim_fetchTarball,
}); });
static void prim_fetchGit(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_fetchGit(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{ {
fetchTree(state, pos, args, v, fetchTree(
FetchTreeParams { state, pos, args, v, FetchTreeParams{.emptyRevFallback = true, .allowNameArgument = true, .isFetchGit = true});
.emptyRevFallback = true,
.allowNameArgument = true,
.isFetchGit = true
});
} }
static RegisterPrimOp primop_fetchGit({ static RegisterPrimOp primop_fetchGit({
@ -857,4 +886,4 @@ static RegisterPrimOp primop_fetchGit({
.fun = prim_fetchGit, .fun = prim_fetchGit,
}); });
} } // namespace nix

View file

@ -7,7 +7,7 @@
namespace nix { namespace nix {
static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value & val) static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Value & val)
{ {
auto toml = state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.fromTOML"); auto toml = state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.fromTOML");
@ -16,75 +16,75 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, V
std::function<void(Value &, toml::value)> visit; std::function<void(Value &, toml::value)> visit;
visit = [&](Value & v, toml::value t) { visit = [&](Value & v, toml::value t) {
switch (t.type()) {
case toml::value_t::table: {
auto table = toml::get<toml::table>(t);
switch(t.type()) size_t size = 0;
{ for (auto & i : table) {
case toml::value_t::table: (void) i;
{ size++;
auto table = toml::get<toml::table>(t); }
size_t size = 0; auto attrs = state.buildBindings(size);
for (auto & i : table) { (void) i; size++; }
auto attrs = state.buildBindings(size); for (auto & elem : table) {
forceNoNullByte(elem.first);
visit(attrs.alloc(elem.first), elem.second);
}
for(auto & elem : table) { v.mkAttrs(attrs);
forceNoNullByte(elem.first); } break;
visit(attrs.alloc(elem.first), elem.second); ;
} case toml::value_t::array: {
auto array = toml::get<std::vector<toml::value>>(t);
v.mkAttrs(attrs);
}
break;;
case toml::value_t::array:
{
auto array = toml::get<std::vector<toml::value>>(t);
auto list = state.buildList(array.size());
for (const auto & [n, v] : enumerate(list))
visit(*(v = state.allocValue()), array[n]);
v.mkList(list);
}
break;;
case toml::value_t::boolean:
v.mkBool(toml::get<bool>(t));
break;;
case toml::value_t::integer:
v.mkInt(toml::get<int64_t>(t));
break;;
case toml::value_t::floating:
v.mkFloat(toml::get<NixFloat>(t));
break;;
case toml::value_t::string:
{
auto s = toml::get<std::string_view>(t);
forceNoNullByte(s);
v.mkString(s);
}
break;;
case toml::value_t::local_datetime:
case toml::value_t::offset_datetime:
case toml::value_t::local_date:
case toml::value_t::local_time:
{
if (experimentalFeatureSettings.isEnabled(Xp::ParseTomlTimestamps)) {
auto attrs = state.buildBindings(2);
attrs.alloc("_type").mkString("timestamp");
std::ostringstream s;
s << t;
auto str = toView(s);
forceNoNullByte(str);
attrs.alloc("value").mkString(str);
v.mkAttrs(attrs);
} else {
throw std::runtime_error("Dates and times are not supported");
}
}
break;;
case toml::value_t::empty:
v.mkNull();
break;;
auto list = state.buildList(array.size());
for (const auto & [n, v] : enumerate(list))
visit(*(v = state.allocValue()), array[n]);
v.mkList(list);
} break;
;
case toml::value_t::boolean:
v.mkBool(toml::get<bool>(t));
break;
;
case toml::value_t::integer:
v.mkInt(toml::get<int64_t>(t));
break;
;
case toml::value_t::floating:
v.mkFloat(toml::get<NixFloat>(t));
break;
;
case toml::value_t::string: {
auto s = toml::get<std::string_view>(t);
forceNoNullByte(s);
v.mkString(s);
} break;
;
case toml::value_t::local_datetime:
case toml::value_t::offset_datetime:
case toml::value_t::local_date:
case toml::value_t::local_time: {
if (experimentalFeatureSettings.isEnabled(Xp::ParseTomlTimestamps)) {
auto attrs = state.buildBindings(2);
attrs.alloc("_type").mkString("timestamp");
std::ostringstream s;
s << t;
auto str = toView(s);
forceNoNullByte(str);
attrs.alloc("value").mkString(str);
v.mkAttrs(attrs);
} else {
throw std::runtime_error("Dates and times are not supported");
}
} break;
;
case toml::value_t::empty:
v.mkNull();
break;
;
} }
}; };
@ -95,10 +95,10 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, V
} }
} }
static RegisterPrimOp primop_fromTOML({ static RegisterPrimOp primop_fromTOML(
.name = "fromTOML", {.name = "fromTOML",
.args = {"e"}, .args = {"e"},
.doc = R"( .doc = R"(
Convert a TOML string to a Nix value. For example, Convert a TOML string to a Nix value. For example,
```nix ```nix
@ -112,7 +112,6 @@ static RegisterPrimOp primop_fromTOML({
returns the value `{ s = "a"; table = { y = 2; }; x = 1; }`. returns the value `{ s = "a"; table = { y = 2; }; x = 1; }`.
)", )",
.fun = prim_fromTOML .fun = prim_fromTOML});
});
} } // namespace nix

View file

@ -7,11 +7,7 @@ namespace nix {
// See: https://github.com/NixOS/nix/issues/9730 // See: https://github.com/NixOS/nix/issues/9730
void printAmbiguous( void printAmbiguous(
Value &v, Value & v, const SymbolTable & symbols, std::ostream & str, std::set<const void *> * seen, int depth)
const SymbolTable &symbols,
std::ostream &str,
std::set<const void *> *seen,
int depth)
{ {
checkInterrupt(); checkInterrupt();
@ -98,4 +94,4 @@ void printAmbiguous(
} }
} }
} } // namespace nix

View file

@ -28,9 +28,7 @@ void printElided(
output << ANSI_NORMAL; output << ANSI_NORMAL;
} }
std::ostream & printLiteralString(std::ostream & str, const std::string_view string, size_t maxLength, bool ansiColors)
std::ostream &
printLiteralString(std::ostream & str, const std::string_view string, size_t maxLength, bool ansiColors)
{ {
size_t charsPrinted = 0; size_t charsPrinted = 0;
if (ansiColors) if (ansiColors)
@ -43,12 +41,18 @@ printLiteralString(std::ostream & str, const std::string_view string, size_t max
return str; return str;
} }
if (*i == '\"' || *i == '\\') str << "\\" << *i; if (*i == '\"' || *i == '\\')
else if (*i == '\n') str << "\\n"; str << "\\" << *i;
else if (*i == '\r') str << "\\r"; else if (*i == '\n')
else if (*i == '\t') str << "\\t"; str << "\\n";
else if (*i == '$' && *(i+1) == '{') str << "\\" << *i; else if (*i == '\r')
else str << *i; str << "\\r";
else if (*i == '\t')
str << "\\t";
else if (*i == '$' && *(i + 1) == '{')
str << "\\" << *i;
else
str << *i;
charsPrinted++; charsPrinted++;
} }
str << "\""; str << "\"";
@ -57,14 +61,12 @@ printLiteralString(std::ostream & str, const std::string_view string, size_t max
return str; return str;
} }
std::ostream & std::ostream & printLiteralString(std::ostream & str, const std::string_view string)
printLiteralString(std::ostream & str, const std::string_view string)
{ {
return printLiteralString(str, string, std::numeric_limits<size_t>::max(), false); return printLiteralString(str, string, std::numeric_limits<size_t>::max(), false);
} }
std::ostream & std::ostream & printLiteralBool(std::ostream & str, bool boolean)
printLiteralBool(std::ostream & str, bool boolean)
{ {
str << (boolean ? "true" : "false"); str << (boolean ? "true" : "false");
return str; return str;
@ -80,13 +82,12 @@ printLiteralBool(std::ostream & str, bool boolean)
bool isReservedKeyword(const std::string_view str) bool isReservedKeyword(const std::string_view str)
{ {
static const std::unordered_set<std::string_view> reservedKeywords = { static const std::unordered_set<std::string_view> reservedKeywords = {
"if", "then", "else", "assert", "with", "let", "in", "rec", "inherit" "if", "then", "else", "assert", "with", "let", "in", "rec", "inherit"};
};
return reservedKeywords.contains(str); return reservedKeywords.contains(str);
} }
std::ostream & std::ostream & printIdentifier(std::ostream & str, std::string_view s)
printIdentifier(std::ostream & str, std::string_view s) { {
if (s.empty()) if (s.empty())
str << "\"\""; str << "\"\"";
else if (isReservedKeyword(s)) else if (isReservedKeyword(s))
@ -98,10 +99,8 @@ printIdentifier(std::ostream & str, std::string_view s) {
return str; return str;
} }
for (auto c : s) for (auto c : s)
if (!((c >= 'a' && c <= 'z') || if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '\''
(c >= 'A' && c <= 'Z') || || c == '-')) {
(c >= '0' && c <= '9') ||
c == '_' || c == '\'' || c == '-')) {
printLiteralString(str, s); printLiteralString(str, s);
return str; return str;
} }
@ -112,21 +111,22 @@ printIdentifier(std::ostream & str, std::string_view s) {
static bool isVarName(std::string_view s) static bool isVarName(std::string_view s)
{ {
if (s.size() == 0) return false; if (s.size() == 0)
if (isReservedKeyword(s)) return false; return false;
if (isReservedKeyword(s))
return false;
char c = s[0]; 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) for (auto & i : s)
if (!((i >= 'a' && i <= 'z') || if (!((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || (i >= '0' && i <= '9') || i == '_' || i == '-'
(i >= 'A' && i <= 'Z') || || i == '\''))
(i >= '0' && i <= '9') ||
i == '_' || i == '-' || i == '\''))
return false; return false;
return true; return true;
} }
std::ostream & std::ostream & printAttributeName(std::ostream & str, std::string_view name)
printAttributeName(std::ostream & str, std::string_view name) { {
if (isVarName(name)) if (isVarName(name))
str << name; str << name;
else else
@ -134,7 +134,7 @@ printAttributeName(std::ostream & str, std::string_view name) {
return str; return str;
} }
bool isImportantAttrName(const std::string& attrName) bool isImportantAttrName(const std::string & attrName)
{ {
return attrName == "type" || attrName == "_type"; return attrName == "type" || attrName == "_type";
} }
@ -144,12 +144,11 @@ typedef std::pair<std::string, Value *> AttrPair;
struct ImportantFirstAttrNameCmp struct ImportantFirstAttrNameCmp
{ {
bool operator()(const AttrPair& lhs, const AttrPair& rhs) const bool operator()(const AttrPair & lhs, const AttrPair & rhs) const
{ {
auto lhsIsImportant = isImportantAttrName(lhs.first); auto lhsIsImportant = isImportantAttrName(lhs.first);
auto rhsIsImportant = isImportantAttrName(rhs.first); auto rhsIsImportant = isImportantAttrName(rhs.first);
return std::forward_as_tuple(!lhsIsImportant, lhs.first) return std::forward_as_tuple(!lhsIsImportant, lhs.first) < std::forward_as_tuple(!rhsIsImportant, rhs.first);
< std::forward_as_tuple(!rhsIsImportant, rhs.first);
} }
}; };
@ -275,7 +274,8 @@ private:
std::optional<StorePath> storePath; std::optional<StorePath> storePath;
if (auto i = v.attrs()->get(state.sDrvPath)) { if (auto i = v.attrs()->get(state.sDrvPath)) {
NixStringContext context; NixStringContext context;
storePath = state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation"); storePath =
state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation");
} }
/* This unfortunately breaks printing nested values because of /* This unfortunately breaks printing nested values because of
@ -499,10 +499,10 @@ private:
output << ANSI_NORMAL; output << ANSI_NORMAL;
} else if (v.isThunk() || v.isApp()) { } else if (v.isThunk() || v.isApp()) {
if (options.ansiColors) if (options.ansiColors)
output << ANSI_MAGENTA; output << ANSI_MAGENTA;
output << "«thunk»"; output << "«thunk»";
if (options.ansiColors) if (options.ansiColors)
output << ANSI_NORMAL; output << ANSI_NORMAL;
} else { } else {
unreachable(); unreachable();
} }
@ -593,8 +593,7 @@ private:
} }
} catch (Error & e) { } catch (Error & e) {
if (options.errors == ErrorPrintBehavior::Throw if (options.errors == ErrorPrintBehavior::Throw
|| (options.errors == ErrorPrintBehavior::ThrowTopLevel || (options.errors == ErrorPrintBehavior::ThrowTopLevel && depth == 0)) {
&& depth == 0)) {
throw; throw;
} }
printError_(e); printError_(e);
@ -603,7 +602,11 @@ private:
public: public:
Printer(std::ostream & output, EvalState & state, PrintOptions options) Printer(std::ostream & output, EvalState & state, PrintOptions options)
: output(output), state(state), options(options) { } : output(output)
, state(state)
, options(options)
{
}
void print(Value & v) void print(Value & v)
{ {
@ -636,8 +639,8 @@ std::ostream & operator<<(std::ostream & output, const ValuePrinter & printer)
template<> template<>
HintFmt & HintFmt::operator%(const ValuePrinter & value) HintFmt & HintFmt::operator%(const ValuePrinter & value)
{ {
fmt % value; fmt % value;
return *this; return *this;
} }
} } // namespace nix

View file

@ -2,8 +2,7 @@
namespace nix { namespace nix {
std::optional<std::string_view> LookupPath::Prefix::suffixIfPotentialMatch( std::optional<std::string_view> LookupPath::Prefix::suffixIfPotentialMatch(std::string_view path) const
std::string_view path) const
{ {
auto n = s.size(); auto n = s.size();
@ -21,29 +20,25 @@ std::optional<std::string_view> LookupPath::Prefix::suffixIfPotentialMatch(
} }
/* Skip next path separator. */ /* Skip next path separator. */
return { return {path.substr(needSeparator ? n + 1 : n)};
path.substr(needSeparator ? n + 1 : n)
};
} }
LookupPath::Elem LookupPath::Elem::parse(std::string_view rawElem) LookupPath::Elem LookupPath::Elem::parse(std::string_view rawElem)
{ {
size_t pos = rawElem.find('='); size_t pos = rawElem.find('=');
return LookupPath::Elem { return LookupPath::Elem{
.prefix = Prefix { .prefix =
.s = pos == std::string::npos Prefix{
? std::string { "" } .s = pos == std::string::npos ? std::string{""} : std::string{rawElem.substr(0, pos)},
: std::string { rawElem.substr(0, pos) }, },
}, .path =
.path = Path { Path{
.s = std::string { rawElem.substr(pos + 1) }, .s = std::string{rawElem.substr(pos + 1)},
}, },
}; };
} }
LookupPath LookupPath::parse(const Strings & rawElems) LookupPath LookupPath::parse(const Strings & rawElems)
{ {
LookupPath res; LookupPath res;
@ -52,4 +47,4 @@ LookupPath LookupPath::parse(const Strings & rawElems)
return res; return res;
} }
} } // namespace nix

View file

@ -7,107 +7,109 @@
#include <iomanip> #include <iomanip>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
namespace nix { namespace nix {
using json = nlohmann::json; using json = nlohmann::json;
// TODO: rename. It doesn't print. // TODO: rename. It doesn't print.
json printValueAsJSON(EvalState & state, bool strict, json printValueAsJSON(
Value & v, const PosIdx pos, NixStringContext & context, bool copyToStore) EvalState & state, bool strict, Value & v, const PosIdx pos, NixStringContext & context, bool copyToStore)
{ {
checkInterrupt(); checkInterrupt();
if (strict) state.forceValue(v, pos); if (strict)
state.forceValue(v, pos);
json out; json out;
switch (v.type()) { switch (v.type()) {
case nInt: case nInt:
out = v.integer().value; out = v.integer().value;
break; break;
case nBool: case nBool:
out = v.boolean(); out = v.boolean();
break; break;
case nString: case nString:
copyContext(v, context); copyContext(v, context);
out = v.c_str(); out = v.c_str();
break; break;
case nPath: case nPath:
if (copyToStore) if (copyToStore)
out = state.store->printStorePath( out = state.store->printStorePath(state.copyPathToStore(context, v.path()));
state.copyPathToStore(context, v.path())); else
else out = v.path().path.abs();
out = v.path().path.abs(); break;
break;
case nNull: case nNull:
// already initialized as null // already initialized as null
break; break;
case nAttrs: { case nAttrs: {
auto maybeString = state.tryAttrsToString(pos, v, context, false, false); auto maybeString = state.tryAttrsToString(pos, v, context, false, false);
if (maybeString) { if (maybeString) {
out = *maybeString; out = *maybeString;
break;
}
if (auto i = v.attrs()->get(state.sOutPath))
return printValueAsJSON(state, strict, *i->value, i->pos, context, copyToStore);
else {
out = json::object();
for (auto & a : v.attrs()->lexicographicOrder(state.symbols)) {
try {
out.emplace(state.symbols[a->name], printValueAsJSON(state, strict, *a->value, a->pos, context, copyToStore));
} catch (Error & e) {
e.addTrace(state.positions[a->pos],
HintFmt("while evaluating attribute '%1%'", state.symbols[a->name]));
throw;
}
}
}
break; break;
} }
if (auto i = v.attrs()->get(state.sOutPath))
case nList: { return printValueAsJSON(state, strict, *i->value, i->pos, context, copyToStore);
out = json::array(); else {
int i = 0; out = json::object();
for (auto elem : v.listItems()) { for (auto & a : v.attrs()->lexicographicOrder(state.symbols)) {
try { try {
out.push_back(printValueAsJSON(state, strict, *elem, pos, context, copyToStore)); out.emplace(
state.symbols[a->name],
printValueAsJSON(state, strict, *a->value, a->pos, context, copyToStore));
} catch (Error & e) { } catch (Error & e) {
e.addTrace(state.positions[pos], e.addTrace(
HintFmt("while evaluating list element at index %1%", i)); state.positions[a->pos], HintFmt("while evaluating attribute '%1%'", state.symbols[a->name]));
throw; throw;
} }
i++;
} }
break;
} }
break;
}
case nExternal: case nList: {
return v.external()->printValueAsJSON(state, strict, context, copyToStore); out = json::array();
break; int i = 0;
for (auto elem : v.listItems()) {
try {
out.push_back(printValueAsJSON(state, strict, *elem, pos, context, copyToStore));
} catch (Error & e) {
e.addTrace(state.positions[pos], HintFmt("while evaluating list element at index %1%", i));
throw;
}
i++;
}
break;
}
case nFloat: case nExternal:
out = v.fpoint(); return v.external()->printValueAsJSON(state, strict, context, copyToStore);
break; break;
case nThunk: case nFloat:
case nFunction: out = v.fpoint();
state.error<TypeError>( break;
"cannot convert %1% to JSON",
showType(v) case nThunk:
) case nFunction:
.atPos(v.determinePos(pos)) state.error<TypeError>("cannot convert %1% to JSON", showType(v)).atPos(v.determinePos(pos)).debugThrow();
.debugThrow();
} }
return out; return out;
} }
void printValueAsJSON(EvalState & state, bool strict, void printValueAsJSON(
Value & v, const PosIdx pos, std::ostream & str, NixStringContext & context, bool copyToStore) EvalState & state,
bool strict,
Value & v,
const PosIdx pos,
std::ostream & str,
NixStringContext & context,
bool copyToStore)
{ {
try { try {
str << printValueAsJSON(state, strict, v, pos, context, copyToStore); str << printValueAsJSON(state, strict, v, pos, context, copyToStore);
@ -116,12 +118,10 @@ void printValueAsJSON(EvalState & state, bool strict,
} }
} }
json ExternalValueBase::printValueAsJSON(EvalState & state, bool strict, json ExternalValueBase::printValueAsJSON(
NixStringContext & context, bool copyToStore) const EvalState & state, bool strict, NixStringContext & context, bool copyToStore) const
{ {
state.error<TypeError>("cannot convert %1% to JSON", showType()) state.error<TypeError>("cannot convert %1% to JSON", showType()).debugThrow();
.debugThrow();
} }
} // namespace nix
}

View file

@ -5,10 +5,8 @@
#include <cstdlib> #include <cstdlib>
namespace nix { namespace nix {
static XMLAttrs singletonAttrs(const std::string & name, std::string_view value) static XMLAttrs singletonAttrs(const std::string & name, std::string_view value)
{ {
XMLAttrs attrs; XMLAttrs attrs;
@ -16,12 +14,16 @@ static XMLAttrs singletonAttrs(const std::string & name, std::string_view value)
return attrs; return attrs;
} }
static void printValueAsXML(
static void printValueAsXML(EvalState & state, bool strict, bool location, EvalState & state,
Value & v, XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen, bool strict,
bool location,
Value & v,
XMLWriter & doc,
NixStringContext & context,
PathSet & drvsSeen,
const PosIdx pos); const PosIdx pos);
static void posToXML(EvalState & state, XMLAttrs & xmlAttrs, const Pos & pos) static void posToXML(EvalState & state, XMLAttrs & xmlAttrs, const Pos & pos)
{ {
if (auto path = std::get_if<SourcePath>(&pos.origin)) if (auto path = std::get_if<SourcePath>(&pos.origin))
@ -30,142 +32,167 @@ static void posToXML(EvalState & state, XMLAttrs & xmlAttrs, const Pos & pos)
xmlAttrs["column"] = fmt("%1%", pos.column); xmlAttrs["column"] = fmt("%1%", pos.column);
} }
static void showAttrs(
static void showAttrs(EvalState & state, bool strict, bool location, EvalState & state,
const Bindings & attrs, XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen) bool strict,
bool location,
const Bindings & attrs,
XMLWriter & doc,
NixStringContext & context,
PathSet & drvsSeen)
{ {
StringSet names; StringSet names;
for (auto & a : attrs.lexicographicOrder(state.symbols)) { for (auto & a : attrs.lexicographicOrder(state.symbols)) {
XMLAttrs xmlAttrs; XMLAttrs xmlAttrs;
xmlAttrs["name"] = state.symbols[a->name]; xmlAttrs["name"] = state.symbols[a->name];
if (location && a->pos) posToXML(state, xmlAttrs, state.positions[a->pos]); if (location && a->pos)
posToXML(state, xmlAttrs, state.positions[a->pos]);
XMLOpenElement _(doc, "attr", xmlAttrs); XMLOpenElement _(doc, "attr", xmlAttrs);
printValueAsXML(state, strict, location, printValueAsXML(state, strict, location, *a->value, doc, context, drvsSeen, a->pos);
*a->value, doc, context, drvsSeen, a->pos);
} }
} }
static void printValueAsXML(
static void printValueAsXML(EvalState & state, bool strict, bool location, EvalState & state,
Value & v, XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen, bool strict,
bool location,
Value & v,
XMLWriter & doc,
NixStringContext & context,
PathSet & drvsSeen,
const PosIdx pos) const PosIdx pos)
{ {
checkInterrupt(); checkInterrupt();
if (strict) state.forceValue(v, pos); if (strict)
state.forceValue(v, pos);
switch (v.type()) { switch (v.type()) {
case nInt: case nInt:
doc.writeEmptyElement("int", singletonAttrs("value", fmt("%1%", v.integer()))); doc.writeEmptyElement("int", singletonAttrs("value", fmt("%1%", v.integer())));
break; break;
case nBool: case nBool:
doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean() ? "true" : "false")); doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean() ? "true" : "false"));
break; break;
case nString: case nString:
/* !!! show the context? */ /* !!! show the context? */
copyContext(v, context); copyContext(v, context);
doc.writeEmptyElement("string", singletonAttrs("value", v.c_str())); doc.writeEmptyElement("string", singletonAttrs("value", v.c_str()));
break; break;
case nPath: case nPath:
doc.writeEmptyElement("path", singletonAttrs("value", v.path().to_string())); doc.writeEmptyElement("path", singletonAttrs("value", v.path().to_string()));
break; break;
case nNull: case nNull:
doc.writeEmptyElement("null"); doc.writeEmptyElement("null");
break; break;
case nAttrs: case nAttrs:
if (state.isDerivation(v)) { if (state.isDerivation(v)) {
XMLAttrs xmlAttrs;
Path drvPath;
if (auto a = v.attrs()->get(state.sDrvPath)) {
if (strict) state.forceValue(*a->value, a->pos);
if (a->value->type() == nString)
xmlAttrs["drvPath"] = drvPath = a->value->c_str();
}
if (auto a = v.attrs()->get(state.sOutPath)) {
if (strict) state.forceValue(*a->value, a->pos);
if (a->value->type() == nString)
xmlAttrs["outPath"] = a->value->c_str();
}
XMLOpenElement _(doc, "derivation", xmlAttrs);
if (drvPath != "" && drvsSeen.insert(drvPath).second)
showAttrs(state, strict, location, *v.attrs(), doc, context, drvsSeen);
else
doc.writeEmptyElement("repeated");
}
else {
XMLOpenElement _(doc, "attrs");
showAttrs(state, strict, location, *v.attrs(), doc, context, drvsSeen);
}
break;
case nList: {
XMLOpenElement _(doc, "list");
for (auto v2 : v.listItems())
printValueAsXML(state, strict, location, *v2, doc, context, drvsSeen, pos);
break;
}
case nFunction: {
if (!v.isLambda()) {
// FIXME: Serialize primops and primopapps
doc.writeEmptyElement("unevaluated");
break;
}
XMLAttrs xmlAttrs; XMLAttrs xmlAttrs;
if (location) posToXML(state, xmlAttrs, state.positions[v.payload.lambda.fun->pos]);
XMLOpenElement _(doc, "function", xmlAttrs);
if (v.payload.lambda.fun->hasFormals()) { Path drvPath;
XMLAttrs attrs; if (auto a = v.attrs()->get(state.sDrvPath)) {
if (v.payload.lambda.fun->arg) attrs["name"] = state.symbols[v.payload.lambda.fun->arg]; if (strict)
if (v.payload.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1"; state.forceValue(*a->value, a->pos);
XMLOpenElement _(doc, "attrspat", attrs); if (a->value->type() == nString)
for (auto & i : v.payload.lambda.fun->formals->lexicographicOrder(state.symbols)) xmlAttrs["drvPath"] = drvPath = a->value->c_str();
doc.writeEmptyElement("attr", singletonAttrs("name", state.symbols[i.name])); }
} else
doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.payload.lambda.fun->arg]));
break; if (auto a = v.attrs()->get(state.sOutPath)) {
if (strict)
state.forceValue(*a->value, a->pos);
if (a->value->type() == nString)
xmlAttrs["outPath"] = a->value->c_str();
}
XMLOpenElement _(doc, "derivation", xmlAttrs);
if (drvPath != "" && drvsSeen.insert(drvPath).second)
showAttrs(state, strict, location, *v.attrs(), doc, context, drvsSeen);
else
doc.writeEmptyElement("repeated");
} }
case nExternal: else {
v.external()->printValueAsXML(state, strict, location, doc, context, drvsSeen, pos); XMLOpenElement _(doc, "attrs");
break; showAttrs(state, strict, location, *v.attrs(), doc, context, drvsSeen);
}
case nFloat: break;
doc.writeEmptyElement("float", singletonAttrs("value", fmt("%1%", v.fpoint())));
break;
case nThunk: case nList: {
XMLOpenElement _(doc, "list");
for (auto v2 : v.listItems())
printValueAsXML(state, strict, location, *v2, doc, context, drvsSeen, pos);
break;
}
case nFunction: {
if (!v.isLambda()) {
// FIXME: Serialize primops and primopapps
doc.writeEmptyElement("unevaluated"); doc.writeEmptyElement("unevaluated");
break;
}
XMLAttrs xmlAttrs;
if (location)
posToXML(state, xmlAttrs, state.positions[v.payload.lambda.fun->pos]);
XMLOpenElement _(doc, "function", xmlAttrs);
if (v.payload.lambda.fun->hasFormals()) {
XMLAttrs attrs;
if (v.payload.lambda.fun->arg)
attrs["name"] = state.symbols[v.payload.lambda.fun->arg];
if (v.payload.lambda.fun->formals->ellipsis)
attrs["ellipsis"] = "1";
XMLOpenElement _(doc, "attrspat", attrs);
for (auto & i : v.payload.lambda.fun->formals->lexicographicOrder(state.symbols))
doc.writeEmptyElement("attr", singletonAttrs("name", state.symbols[i.name]));
} else
doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.payload.lambda.fun->arg]));
break;
}
case nExternal:
v.external()->printValueAsXML(state, strict, location, doc, context, drvsSeen, pos);
break;
case nFloat:
doc.writeEmptyElement("float", singletonAttrs("value", fmt("%1%", v.fpoint())));
break;
case nThunk:
doc.writeEmptyElement("unevaluated");
} }
} }
void ExternalValueBase::printValueAsXML(
void ExternalValueBase::printValueAsXML(EvalState & state, bool strict, EvalState & state,
bool location, XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen, bool strict,
bool location,
XMLWriter & doc,
NixStringContext & context,
PathSet & drvsSeen,
const PosIdx pos) const const PosIdx pos) const
{ {
doc.writeEmptyElement("unevaluated"); doc.writeEmptyElement("unevaluated");
} }
void printValueAsXML(
void printValueAsXML(EvalState & state, bool strict, bool location, EvalState & state,
Value & v, std::ostream & out, NixStringContext & context, const PosIdx pos) bool strict,
bool location,
Value & v,
std::ostream & out,
NixStringContext & context,
const PosIdx pos)
{ {
XMLWriter doc(true, out); XMLWriter doc(true, out);
XMLOpenElement root(doc, "expr"); XMLOpenElement root(doc, "expr");
@ -173,5 +200,4 @@ void printValueAsXML(EvalState & state, bool strict, bool location,
printValueAsXML(state, strict, location, v, doc, context, drvsSeen, pos); printValueAsXML(state, strict, location, v, doc, context, drvsSeen, pos);
} }
} // namespace nix
}

View file

@ -5,9 +5,7 @@
namespace nix { namespace nix {
NixStringContextElem NixStringContextElem::parse( NixStringContextElem NixStringContextElem::parse(std::string_view s0, const ExperimentalFeatureSettings & xpSettings)
std::string_view s0,
const ExperimentalFeatureSettings & xpSettings)
{ {
std::string_view s = s0; std::string_view s = s0;
@ -16,16 +14,16 @@ NixStringContextElem NixStringContextElem::parse(
// Case on whether there is a '!' // Case on whether there is a '!'
size_t index = s.find("!"); size_t index = s.find("!");
if (index == std::string_view::npos) { if (index == std::string_view::npos) {
return SingleDerivedPath::Opaque { return SingleDerivedPath::Opaque{
.path = StorePath { s }, .path = StorePath{s},
}; };
} else { } else {
std::string output { s.substr(0, index) }; std::string output{s.substr(0, index)};
// Advance string to parse after the '!' // Advance string to parse after the '!'
s = s.substr(index + 1); s = s.substr(index + 1);
auto drv = make_ref<SingleDerivedPath>(parseRest()); auto drv = make_ref<SingleDerivedPath>(parseRest());
drvRequireExperiment(*drv, xpSettings); drvRequireExperiment(*drv, xpSettings);
return SingleDerivedPath::Built { return SingleDerivedPath::Built{
.drvPath = std::move(drv), .drvPath = std::move(drv),
.output = std::move(output), .output = std::move(output),
}; };
@ -33,8 +31,7 @@ NixStringContextElem NixStringContextElem::parse(
}; };
if (s.size() == 0) { if (s.size() == 0) {
throw BadNixStringContextElem(s0, throw BadNixStringContextElem(s0, "String context element should never be an empty string");
"String context element should never be an empty string");
} }
switch (s.at(0)) { switch (s.at(0)) {
@ -44,28 +41,23 @@ NixStringContextElem NixStringContextElem::parse(
// Find *second* '!' // Find *second* '!'
if (s.find("!") == std::string_view::npos) { if (s.find("!") == std::string_view::npos) {
throw BadNixStringContextElem(s0, throw BadNixStringContextElem(s0, "String content element beginning with '!' should have a second '!'");
"String content element beginning with '!' should have a second '!'");
} }
return std::visit( return std::visit([&](auto x) -> NixStringContextElem { return std::move(x); }, parseRest());
[&](auto x) -> NixStringContextElem { return std::move(x); },
parseRest());
} }
case '=': { case '=': {
return NixStringContextElem::DrvDeep { return NixStringContextElem::DrvDeep{
.drvPath = StorePath { s.substr(1) }, .drvPath = StorePath{s.substr(1)},
}; };
} }
default: { default: {
// Ensure no '!' // Ensure no '!'
if (s.find("!") != std::string_view::npos) { if (s.find("!") != std::string_view::npos) {
throw BadNixStringContextElem(s0, throw BadNixStringContextElem(
"String content element not beginning with '!' should not have a second '!'"); s0, "String content element not beginning with '!' should not have a second '!'");
} }
return std::visit( return std::visit([&](auto x) -> NixStringContextElem { return std::move(x); }, parseRest());
[&](auto x) -> NixStringContextElem { return std::move(x); },
parseRest());
} }
} }
} }
@ -76,33 +68,33 @@ std::string NixStringContextElem::to_string() const
std::function<void(const SingleDerivedPath &)> toStringRest; std::function<void(const SingleDerivedPath &)> toStringRest;
toStringRest = [&](auto & p) { toStringRest = [&](auto & p) {
std::visit(overloaded { std::visit(
[&](const SingleDerivedPath::Opaque & o) { overloaded{
res += o.path.to_string(); [&](const SingleDerivedPath::Opaque & o) { res += o.path.to_string(); },
[&](const SingleDerivedPath::Built & o) {
res += o.output;
res += '!';
toStringRest(*o.drvPath);
},
}, },
[&](const SingleDerivedPath::Built & o) { p.raw());
res += o.output;
res += '!';
toStringRest(*o.drvPath);
},
}, p.raw());
}; };
std::visit(overloaded { std::visit(
[&](const NixStringContextElem::Built & b) { overloaded{
res += '!'; [&](const NixStringContextElem::Built & b) {
toStringRest(b); res += '!';
toStringRest(b);
},
[&](const NixStringContextElem::Opaque & o) { toStringRest(o); },
[&](const NixStringContextElem::DrvDeep & d) {
res += '=';
res += d.drvPath.to_string();
},
}, },
[&](const NixStringContextElem::Opaque & o) { raw);
toStringRest(o);
},
[&](const NixStringContextElem::DrvDeep & d) {
res += '=';
res += d.drvPath.to_string();
},
}, raw);
return res; return res;
} }
} } // namespace nix

Some files were not shown because too many files have changed in this diff Show more