1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-29 13:41:00 +01:00

Merge remote-tracking branch 'origin/master' into lazy-trees

This commit is contained in:
Eelco Dolstra 2023-02-21 13:32:59 +01:00
commit 5d1e5a09d1
36 changed files with 1150 additions and 740 deletions

View file

@ -4,6 +4,7 @@
#include "derivations.hh"
#include "nixexpr.hh"
#include "profiles.hh"
#include "repl.hh"
#include <nlohmann/json.hpp>
@ -121,7 +122,7 @@ ref<EvalState> EvalCommand::getEvalState()
;
if (startReplOnEvalErrors) {
evalState->debugRepl = &runRepl;
evalState->debugRepl = &AbstractNixRepl::runSimple;
};
}
return ref<EvalState>(evalState);
@ -218,23 +219,6 @@ void StorePathCommand::run(ref<Store> store, std::vector<StorePath> && storePath
run(store, *storePaths.begin());
}
Strings editorFor(const SourcePath & file, uint32_t line)
{
auto path = file.getPhysicalPath();
if (!path)
throw Error("cannot open '%s' in an editor because it has no physical path", file);
auto editor = getEnv("EDITOR").value_or("cat");
auto args = tokenizeString<Strings>(editor);
if (line > 0 && (
editor.find("emacs") != std::string::npos ||
editor.find("nano") != std::string::npos ||
editor.find("vim") != std::string::npos ||
editor.find("kak") != std::string::npos))
args.push_back(fmt("+%d", line));
args.push_back(path->abs());
return args;
}
MixProfile::MixProfile()
{
addFlag({

View file

@ -231,10 +231,6 @@ static RegisterCommand registerCommand2(std::vector<std::string> && name)
return RegisterCommand(std::move(name), [](){ return make_ref<T>(); });
}
/* Helper function to generate args that invoke $EDITOR on
filename:lineno. */
Strings editorFor(const SourcePath & file, uint32_t line);
struct MixProfile : virtual StoreCommand
{
std::optional<Path> profile;
@ -284,8 +280,4 @@ void printClosureDiff(
const StorePath & afterPath,
std::string_view indent);
void runRepl(
ref<EvalState> evalState,
const ValMap & extraEnv);
}

23
src/libcmd/editor-for.cc Normal file
View file

@ -0,0 +1,23 @@
#include "util.hh"
#include "editor-for.hh"
namespace nix {
Strings editorFor(const SourcePath & file, uint32_t line)
{
auto path = file.getPhysicalPath();
if (!path)
throw Error("cannot open '%s' in an editor because it has no physical path", file);
auto editor = getEnv("EDITOR").value_or("cat");
auto args = tokenizeString<Strings>(editor);
if (line > 0 && (
editor.find("emacs") != std::string::npos ||
editor.find("nano") != std::string::npos ||
editor.find("vim") != std::string::npos ||
editor.find("kak") != std::string::npos))
args.push_back(fmt("+%d", line));
args.push_back(path->abs());
return args;
}
}

12
src/libcmd/editor-for.hh Normal file
View file

@ -0,0 +1,12 @@
#pragma once
#include "types.hh"
#include "input-accessor.hh"
namespace nix {
/* Helper function to generate args that invoke $EDITOR on
filename:lineno. */
Strings editorFor(const SourcePath & file, uint32_t line);
}

View file

@ -0,0 +1,109 @@
#include "globals.hh"
#include "installable-attr-path.hh"
#include "outputs-spec.hh"
#include "util.hh"
#include "command.hh"
#include "attr-path.hh"
#include "common-eval-args.hh"
#include "derivations.hh"
#include "eval-inline.hh"
#include "eval.hh"
#include "get-drvs.hh"
#include "store-api.hh"
#include "shared.hh"
#include "flake/flake.hh"
#include "eval-cache.hh"
#include "url.hh"
#include "registry.hh"
#include "build-result.hh"
#include <regex>
#include <queue>
#include <nlohmann/json.hpp>
namespace nix {
InstallableAttrPath::InstallableAttrPath(
ref<EvalState> state,
SourceExprCommand & cmd,
Value * v,
const std::string & attrPath,
ExtendedOutputsSpec extendedOutputsSpec)
: InstallableValue(state)
, cmd(cmd)
, v(allocRootValue(v))
, attrPath(attrPath)
, extendedOutputsSpec(std::move(extendedOutputsSpec))
{ }
std::pair<Value *, PosIdx> InstallableAttrPath::toValue(EvalState & state)
{
auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v);
state.forceValue(*vRes, pos);
return {vRes, pos};
}
DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths()
{
auto v = toValue(*state).first;
Bindings & autoArgs = *cmd.getAutoArgs(*state);
DrvInfos drvInfos;
getDerivations(*state, *v, "", autoArgs, drvInfos, false);
// Backward compatibility hack: group results by drvPath. This
// helps keep .all output together.
std::map<StorePath, OutputsSpec> byDrvPath;
for (auto & drvInfo : drvInfos) {
auto drvPath = drvInfo.queryDrvPath();
if (!drvPath)
throw Error("'%s' is not a derivation", what());
auto newOutputs = std::visit(overloaded {
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
std::set<std::string> outputsToInstall;
for (auto & output : drvInfo.queryOutputs(false, true))
outputsToInstall.insert(output.first);
return OutputsSpec::Names { std::move(outputsToInstall) };
},
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec {
return e;
},
}, extendedOutputsSpec.raw());
auto [iter, didInsert] = byDrvPath.emplace(*drvPath, newOutputs);
if (!didInsert)
iter->second = iter->second.union_(newOutputs);
}
DerivedPathsWithInfo res;
for (auto & [drvPath, outputs] : byDrvPath)
res.push_back({
.path = DerivedPath::Built {
.drvPath = drvPath,
.outputs = outputs,
},
});
return res;
}
InstallableAttrPath InstallableAttrPath::parse(
ref<EvalState> state,
SourceExprCommand & cmd,
Value * v,
std::string_view prefix,
ExtendedOutputsSpec extendedOutputsSpec)
{
return {
state, cmd, v,
prefix == "." ? "" : std::string { prefix },
extendedOutputsSpec
};
}
}

View file

@ -0,0 +1,56 @@
#include "globals.hh"
#include "installable-value.hh"
#include "outputs-spec.hh"
#include "util.hh"
#include "command.hh"
#include "attr-path.hh"
#include "common-eval-args.hh"
#include "derivations.hh"
#include "eval-inline.hh"
#include "eval.hh"
#include "get-drvs.hh"
#include "store-api.hh"
#include "shared.hh"
#include "eval-cache.hh"
#include "url.hh"
#include "registry.hh"
#include "build-result.hh"
#include <regex>
#include <queue>
#include <nlohmann/json.hpp>
namespace nix {
class InstallableAttrPath : public InstallableValue
{
SourceExprCommand & cmd;
RootValue v;
std::string attrPath;
ExtendedOutputsSpec extendedOutputsSpec;
InstallableAttrPath(
ref<EvalState> state,
SourceExprCommand & cmd,
Value * v,
const std::string & attrPath,
ExtendedOutputsSpec extendedOutputsSpec);
std::string what() const override { return attrPath; };
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
DerivedPathsWithInfo toDerivedPaths() override;
public:
static InstallableAttrPath parse(
ref<EvalState> state,
SourceExprCommand & cmd,
Value * v,
std::string_view prefix,
ExtendedOutputsSpec extendedOutputsSpec);
};
}

View file

@ -0,0 +1,235 @@
#include "globals.hh"
#include "installable-flake.hh"
#include "installable-derived-path.hh"
#include "outputs-spec.hh"
#include "util.hh"
#include "command.hh"
#include "attr-path.hh"
#include "common-eval-args.hh"
#include "derivations.hh"
#include "eval-inline.hh"
#include "eval.hh"
#include "get-drvs.hh"
#include "store-api.hh"
#include "shared.hh"
#include "flake/flake.hh"
#include "eval-cache.hh"
#include "url.hh"
#include "registry.hh"
#include "build-result.hh"
#include <regex>
#include <queue>
#include <nlohmann/json.hpp>
namespace nix {
std::vector<std::string> InstallableFlake::getActualAttrPaths()
{
std::vector<std::string> res;
for (auto & prefix : prefixes)
res.push_back(prefix + *attrPaths.begin());
for (auto & s : attrPaths)
res.push_back(s);
return res;
}
Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake)
{
auto vFlake = state.allocValue();
callFlake(state, lockedFlake, *vFlake);
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
assert(aOutputs);
state.forceValue(*aOutputs->value, [&]() { return aOutputs->value->determinePos(noPos); });
return aOutputs->value;
}
static std::string showAttrPaths(const std::vector<std::string> & paths)
{
std::string s;
for (const auto & [n, i] : enumerate(paths)) {
if (n > 0) s += n + 1 == paths.size() ? " or " : ", ";
s += '\''; s += i; s += '\'';
}
return s;
}
InstallableFlake::InstallableFlake(
SourceExprCommand * cmd,
ref<EvalState> state,
FlakeRef && flakeRef,
std::string_view fragment,
ExtendedOutputsSpec extendedOutputsSpec,
Strings attrPaths,
Strings prefixes,
const flake::LockFlags & lockFlags)
: InstallableValue(state),
flakeRef(flakeRef),
attrPaths(fragment == "" ? attrPaths : Strings{(std::string) fragment}),
prefixes(fragment == "" ? Strings{} : prefixes),
extendedOutputsSpec(std::move(extendedOutputsSpec)),
lockFlags(lockFlags)
{
if (cmd && cmd->getAutoArgs(*state)->size())
throw UsageError("'--arg' and '--argstr' are incompatible with flakes");
}
DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
{
Activity act(*logger, lvlTalkative, actUnknown, fmt("evaluating derivation '%s'", what()));
auto attr = getCursor(*state);
auto attrPath = attr->getAttrPathStr();
if (!attr->isDerivation()) {
// FIXME: use eval cache?
auto v = attr->forceValue();
if (v.type() == nPath) {
auto storePath = v.path().fetchToStore(state->store);
return {{
.path = DerivedPath::Opaque {
.path = std::move(storePath),
}
}};
}
else if (v.type() == nString) {
PathSet context;
auto s = state->forceString(v, context, noPos, fmt("while evaluating the flake output attribute '%s'", attrPath));
auto storePath = state->store->maybeParseStorePath(s);
if (storePath && context.count(std::string(s))) {
return {{
.path = DerivedPath::Opaque {
.path = std::move(*storePath),
}
}};
} else
throw Error("flake output attribute '%s' evaluates to the string '%s' which is not a store path", attrPath, s);
}
else
throw Error("flake output attribute '%s' is not a derivation or path", attrPath);
}
auto drvPath = attr->forceDerivation();
std::optional<NixInt> priority;
if (attr->maybeGetAttr(state->sOutputSpecified)) {
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
if (auto aPriority = aMeta->maybeGetAttr("priority"))
priority = aPriority->getInt();
}
return {{
.path = DerivedPath::Built {
.drvPath = std::move(drvPath),
.outputs = std::visit(overloaded {
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
std::set<std::string> outputsToInstall;
if (auto aOutputSpecified = attr->maybeGetAttr(state->sOutputSpecified)) {
if (aOutputSpecified->getBool()) {
if (auto aOutputName = attr->maybeGetAttr("outputName"))
outputsToInstall = { aOutputName->getString() };
}
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
if (auto aOutputsToInstall = aMeta->maybeGetAttr("outputsToInstall"))
for (auto & s : aOutputsToInstall->getListOfStrings())
outputsToInstall.insert(s);
}
if (outputsToInstall.empty())
outputsToInstall.insert("out");
return OutputsSpec::Names { std::move(outputsToInstall) };
},
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec {
return e;
},
}, extendedOutputsSpec.raw()),
},
.info = {
.priority = priority,
.originalRef = flakeRef,
.resolvedRef = getLockedFlake()->flake.lockedRef,
.attrPath = attrPath,
.extendedOutputsSpec = extendedOutputsSpec,
}
}};
}
std::pair<Value *, PosIdx> InstallableFlake::toValue(EvalState & state)
{
return {&getCursor(state)->forceValue(), noPos};
}
std::vector<ref<eval_cache::AttrCursor>>
InstallableFlake::getCursors(EvalState & state)
{
auto evalCache = openEvalCache(state,
std::make_shared<flake::LockedFlake>(lockFlake(state, flakeRef, lockFlags)));
auto root = evalCache->getRoot();
std::vector<ref<eval_cache::AttrCursor>> res;
Suggestions suggestions;
auto attrPaths = getActualAttrPaths();
for (auto & attrPath : attrPaths) {
debug("trying flake output attribute '%s'", attrPath);
auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath));
if (attr) {
res.push_back(ref(*attr));
} else {
suggestions += attr.getSuggestions();
}
}
if (res.size() == 0)
throw Error(
suggestions,
"flake '%s' does not provide attribute %s",
flakeRef,
showAttrPaths(attrPaths));
return res;
}
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
{
if (!_lockedFlake) {
flake::LockFlags lockFlagsApplyConfig = lockFlags;
lockFlagsApplyConfig.applyNixConfig = true;
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlagsApplyConfig));
}
return _lockedFlake;
}
FlakeRef InstallableFlake::nixpkgsFlakeRef() const
{
auto lockedFlake = getLockedFlake();
if (auto nixpkgsInput = lockedFlake->lockFile.findInput({"nixpkgs"})) {
if (auto lockedNode = std::dynamic_pointer_cast<const flake::LockedNode>(nixpkgsInput)) {
debug("using nixpkgs flake '%s'", lockedNode->lockedRef);
return std::move(lockedNode->lockedRef);
}
}
return Installable::nixpkgsFlakeRef();
}
}

View file

@ -0,0 +1,50 @@
#pragma once
#include "installable-value.hh"
namespace nix {
struct InstallableFlake : InstallableValue
{
FlakeRef flakeRef;
Strings attrPaths;
Strings prefixes;
ExtendedOutputsSpec extendedOutputsSpec;
const flake::LockFlags & lockFlags;
mutable std::shared_ptr<flake::LockedFlake> _lockedFlake;
InstallableFlake(
SourceExprCommand * cmd,
ref<EvalState> state,
FlakeRef && flakeRef,
std::string_view fragment,
ExtendedOutputsSpec extendedOutputsSpec,
Strings attrPaths,
Strings prefixes,
const flake::LockFlags & lockFlags);
std::string what() const override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }
std::vector<std::string> getActualAttrPaths();
Value * getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake);
DerivedPathsWithInfo toDerivedPaths() override;
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
/* Get a cursor to every attrpath in getActualAttrPaths()
that exists. However if none exists, throw an exception. */
std::vector<ref<eval_cache::AttrCursor>>
getCursors(EvalState & state) override;
std::shared_ptr<flake::LockedFlake> getLockedFlake() const;
FlakeRef nixpkgsFlakeRef() const override;
};
ref<eval_cache::EvalCache> openEvalCache(
EvalState & state,
std::shared_ptr<flake::LockedFlake> lockedFlake);
}

View file

@ -0,0 +1,14 @@
#pragma once
#include "installables.hh"
namespace nix {
struct InstallableValue : Installable
{
ref<EvalState> state;
InstallableValue(ref<EvalState> state) : state(state) {}
};
}

View file

@ -1,6 +1,8 @@
#include "globals.hh"
#include "installables.hh"
#include "installable-derived-path.hh"
#include "installable-attr-path.hh"
#include "installable-flake.hh"
#include "outputs-spec.hh"
#include "util.hh"
#include "command.hh"
@ -391,111 +393,6 @@ static StorePath getDeriver(
return *derivers.begin();
}
struct InstallableAttrPath : InstallableValue
{
SourceExprCommand & cmd;
RootValue v;
std::string attrPath;
ExtendedOutputsSpec extendedOutputsSpec;
InstallableAttrPath(
ref<EvalState> state,
SourceExprCommand & cmd,
Value * v,
const std::string & attrPath,
ExtendedOutputsSpec extendedOutputsSpec)
: InstallableValue(state)
, cmd(cmd)
, v(allocRootValue(v))
, attrPath(attrPath)
, extendedOutputsSpec(std::move(extendedOutputsSpec))
{ }
std::string what() const override { return attrPath; }
std::pair<Value *, PosIdx> toValue(EvalState & state) override
{
auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v);
state.forceValue(*vRes, pos);
return {vRes, pos};
}
DerivedPathsWithInfo toDerivedPaths() override
{
auto v = toValue(*state).first;
Bindings & autoArgs = *cmd.getAutoArgs(*state);
DrvInfos drvInfos;
getDerivations(*state, *v, "", autoArgs, drvInfos, false);
// Backward compatibility hack: group results by drvPath. This
// helps keep .all output together.
std::map<StorePath, OutputsSpec> byDrvPath;
for (auto & drvInfo : drvInfos) {
auto drvPath = drvInfo.queryDrvPath();
if (!drvPath)
throw Error("'%s' is not a derivation", what());
auto newOutputs = std::visit(overloaded {
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
std::set<std::string> outputsToInstall;
for (auto & output : drvInfo.queryOutputs(false, true))
outputsToInstall.insert(output.first);
return OutputsSpec::Names { std::move(outputsToInstall) };
},
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec {
return e;
},
}, extendedOutputsSpec.raw());
auto [iter, didInsert] = byDrvPath.emplace(*drvPath, newOutputs);
if (!didInsert)
iter->second = iter->second.union_(newOutputs);
}
DerivedPathsWithInfo res;
for (auto & [drvPath, outputs] : byDrvPath)
res.push_back({
.path = DerivedPath::Built {
.drvPath = drvPath,
.outputs = outputs,
},
});
return res;
}
};
std::vector<std::string> InstallableFlake::getActualAttrPaths()
{
std::vector<std::string> res;
for (auto & prefix : prefixes)
res.push_back(prefix + *attrPaths.begin());
for (auto & s : attrPaths)
res.push_back(s);
return res;
}
Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake)
{
auto vFlake = state.allocValue();
callFlake(state, lockedFlake, *vFlake);
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
assert(aOutputs);
state.forceValue(*aOutputs->value, [&]() { return aOutputs->value->determinePos(noPos); });
return aOutputs->value;
}
ref<eval_cache::EvalCache> openEvalCache(
EvalState & state,
std::shared_ptr<flake::LockedFlake> lockedFlake)
@ -525,186 +422,6 @@ ref<eval_cache::EvalCache> openEvalCache(
});
}
static std::string showAttrPaths(const std::vector<std::string> & paths)
{
std::string s;
for (const auto & [n, i] : enumerate(paths)) {
if (n > 0) s += n + 1 == paths.size() ? " or " : ", ";
s += '\''; s += i; s += '\'';
}
return s;
}
InstallableFlake::InstallableFlake(
SourceExprCommand * cmd,
ref<EvalState> state,
FlakeRef && flakeRef,
std::string_view fragment,
ExtendedOutputsSpec extendedOutputsSpec,
Strings attrPaths,
Strings prefixes,
const flake::LockFlags & lockFlags)
: InstallableValue(state),
flakeRef(flakeRef),
attrPaths(fragment == "" ? attrPaths : Strings{(std::string) fragment}),
prefixes(fragment == "" ? Strings{} : prefixes),
extendedOutputsSpec(std::move(extendedOutputsSpec)),
lockFlags(lockFlags)
{
if (cmd && cmd->getAutoArgs(*state)->size())
throw UsageError("'--arg' and '--argstr' are incompatible with flakes");
}
DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
{
Activity act(*logger, lvlTalkative, actUnknown, fmt("evaluating derivation '%s'", what()));
auto attr = getCursor(*state);
auto attrPath = attr->getAttrPathStr();
if (!attr->isDerivation()) {
// FIXME: use eval cache?
auto v = attr->forceValue();
if (v.type() == nPath) {
auto storePath = v.path().fetchToStore(state->store);
return {{
.path = DerivedPath::Opaque {
.path = std::move(storePath),
}
}};
}
else if (v.type() == nString) {
PathSet context;
auto s = state->forceString(v, context, noPos, fmt("while evaluating the flake output attribute '%s'", attrPath));
auto storePath = state->store->maybeParseStorePath(s);
if (storePath && context.count(std::string(s))) {
return {{
.path = DerivedPath::Opaque {
.path = std::move(*storePath),
}
}};
} else
throw Error("flake output attribute '%s' evaluates to the string '%s' which is not a store path", attrPath, s);
}
else
throw Error("flake output attribute '%s' is not a derivation or path", attrPath);
}
auto drvPath = attr->forceDerivation();
std::optional<NixInt> priority;
if (attr->maybeGetAttr(state->sOutputSpecified)) {
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
if (auto aPriority = aMeta->maybeGetAttr("priority"))
priority = aPriority->getInt();
}
return {{
.path = DerivedPath::Built {
.drvPath = std::move(drvPath),
.outputs = std::visit(overloaded {
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
std::set<std::string> outputsToInstall;
if (auto aOutputSpecified = attr->maybeGetAttr(state->sOutputSpecified)) {
if (aOutputSpecified->getBool()) {
if (auto aOutputName = attr->maybeGetAttr("outputName"))
outputsToInstall = { aOutputName->getString() };
}
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
if (auto aOutputsToInstall = aMeta->maybeGetAttr("outputsToInstall"))
for (auto & s : aOutputsToInstall->getListOfStrings())
outputsToInstall.insert(s);
}
if (outputsToInstall.empty())
outputsToInstall.insert("out");
return OutputsSpec::Names { std::move(outputsToInstall) };
},
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec {
return e;
},
}, extendedOutputsSpec.raw()),
},
.info = {
.priority = priority,
.originalRef = flakeRef,
.resolvedRef = getLockedFlake()->flake.lockedRef,
.attrPath = attrPath,
.extendedOutputsSpec = extendedOutputsSpec,
}
}};
}
std::pair<Value *, PosIdx> InstallableFlake::toValue(EvalState & state)
{
return {&getCursor(state)->forceValue(), noPos};
}
std::vector<ref<eval_cache::AttrCursor>>
InstallableFlake::getCursors(EvalState & state)
{
auto evalCache = openEvalCache(state,
std::make_shared<flake::LockedFlake>(lockFlake(state, flakeRef, lockFlags)));
auto root = evalCache->getRoot();
std::vector<ref<eval_cache::AttrCursor>> res;
Suggestions suggestions;
auto attrPaths = getActualAttrPaths();
for (auto & attrPath : attrPaths) {
debug("trying flake output attribute '%s'", attrPath);
auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath));
if (attr) {
res.push_back(ref(*attr));
} else {
suggestions += attr.getSuggestions();
}
}
if (res.size() == 0)
throw Error(
suggestions,
"flake '%s' does not provide attribute %s",
flakeRef,
showAttrPaths(attrPaths));
return res;
}
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
{
if (!_lockedFlake) {
flake::LockFlags lockFlagsApplyConfig = lockFlags;
lockFlagsApplyConfig.applyNixConfig = true;
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlagsApplyConfig));
}
return _lockedFlake;
}
FlakeRef InstallableFlake::nixpkgsFlakeRef() const
{
auto lockedFlake = getLockedFlake();
if (auto nixpkgsInput = lockedFlake->lockFile.findInput({"nixpkgs"})) {
if (auto lockedNode = std::dynamic_pointer_cast<const flake::LockedNode>(nixpkgsInput)) {
debug("using nixpkgs flake '%s'", lockedNode->lockedRef);
return std::move(lockedNode->lockedRef);
}
}
return Installable::nixpkgsFlakeRef();
}
std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
ref<Store> store, std::vector<std::string> ss)
{
@ -739,9 +456,8 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(s);
result.push_back(
std::make_shared<InstallableAttrPath>(
state, *this, vFile,
prefix == "." ? "" : std::string { prefix },
extendedOutputsSpec));
InstallableAttrPath::parse(
state, *this, vFile, prefix, extendedOutputsSpec)));
}
} else {
@ -993,8 +709,8 @@ void InstallablesCommand::prepare()
installables = load();
}
Installables InstallablesCommand::load() {
Installables installables;
Installables InstallablesCommand::load()
{
if (_installables.empty() && useDefaultInstallables())
// FIXME: commands like "nix profile install" should not have a
// default, probably.
@ -1004,11 +720,8 @@ Installables InstallablesCommand::load() {
std::vector<std::string> InstallablesCommand::getFlakesForCompletion()
{
if (_installables.empty()) {
if (useDefaultInstallables())
return {"."};
return {};
}
if (_installables.empty() && useDefaultInstallables())
return {"."};
return _installables;
}

View file

@ -161,54 +161,4 @@ struct Installable
typedef std::vector<std::shared_ptr<Installable>> Installables;
struct InstallableValue : Installable
{
ref<EvalState> state;
InstallableValue(ref<EvalState> state) : state(state) {}
};
struct InstallableFlake : InstallableValue
{
FlakeRef flakeRef;
Strings attrPaths;
Strings prefixes;
ExtendedOutputsSpec extendedOutputsSpec;
const flake::LockFlags & lockFlags;
mutable std::shared_ptr<flake::LockedFlake> _lockedFlake;
InstallableFlake(
SourceExprCommand * cmd,
ref<EvalState> state,
FlakeRef && flakeRef,
std::string_view fragment,
ExtendedOutputsSpec extendedOutputsSpec,
Strings attrPaths,
Strings prefixes,
const flake::LockFlags & lockFlags);
std::string what() const override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }
std::vector<std::string> getActualAttrPaths();
Value * getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake);
DerivedPathsWithInfo toDerivedPaths() override;
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
/* Get a cursor to every attrpath in getActualAttrPaths()
that exists. However if none exists, throw an exception. */
std::vector<ref<eval_cache::AttrCursor>>
getCursors(EvalState & state) override;
std::shared_ptr<flake::LockedFlake> getLockedFlake() const;
FlakeRef nixpkgsFlakeRef() const override;
};
ref<eval_cache::EvalCache> openEvalCache(
EvalState & state,
std::shared_ptr<flake::LockedFlake> lockedFlake);
}

View file

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

View file

@ -19,6 +19,8 @@ extern "C" {
}
#endif
#include "repl.hh"
#include "ansicolor.hh"
#include "shared.hh"
#include "eval.hh"
@ -31,7 +33,9 @@ extern "C" {
#include "get-drvs.hh"
#include "derivations.hh"
#include "globals.hh"
#include "command.hh"
#include "flake/flake.hh"
#include "flake/lockfile.hh"
#include "editor-for.hh"
#include "finally.hh"
#include "markdown.hh"
#include "local-fs-store.hh"
@ -45,18 +49,16 @@ extern "C" {
namespace nix {
struct NixRepl
: AbstractNixRepl,
#if HAVE_BOEHMGC
: gc
gc
#endif
{
std::string curDir;
ref<EvalState> state;
Bindings * autoArgs;
size_t debugTraceIndex;
Strings loadedFiles;
typedef std::vector<std::pair<Value*,std::string>> AnnotatedValues;
std::function<AnnotatedValues()> getValues;
const static int envSize = 32768;
@ -69,8 +71,11 @@ struct NixRepl
NixRepl(const Strings & searchPath, nix::ref<Store> store,ref<EvalState> state,
std::function<AnnotatedValues()> getValues);
~NixRepl();
void mainLoop();
virtual ~NixRepl();
void mainLoop() override;
void initEnv() override;
StringSet completePrefix(const std::string & prefix);
bool getLine(std::string & input, const std::string & prompt);
StorePath getDerivationPath(Value & v);
@ -78,7 +83,6 @@ struct NixRepl
void loadFile(const Path & path);
void loadFlake(const std::string & flakeRef);
void initEnv();
void loadFiles();
void reloadFiles();
void addAttrsToScope(Value & attrs);
@ -92,7 +96,6 @@ struct NixRepl
std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen);
};
std::string removeWhitespace(std::string s)
{
s = chomp(s);
@ -104,7 +107,7 @@ std::string removeWhitespace(std::string s)
NixRepl::NixRepl(const Strings & searchPath, nix::ref<Store> store, ref<EvalState> state,
std::function<NixRepl::AnnotatedValues()> getValues)
: state(state)
: AbstractNixRepl(state)
, debugTraceIndex(0)
, getValues(getValues)
, staticEnv(new StaticEnv(false, state->staticBaseEnv.get()))
@ -1029,8 +1032,22 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
return str;
}
void runRepl(
ref<EvalState>evalState,
std::unique_ptr<AbstractNixRepl> AbstractNixRepl::create(
const Strings & searchPath, nix::ref<Store> store, ref<EvalState> state,
std::function<AnnotatedValues()> getValues)
{
return std::make_unique<NixRepl>(
searchPath,
openStore(),
state,
getValues
);
}
void AbstractNixRepl::runSimple(
ref<EvalState> evalState,
const ValMap & extraEnv)
{
auto getValues = [&]()->NixRepl::AnnotatedValues{
@ -1054,91 +1071,4 @@ void runRepl(
repl->mainLoop();
}
struct CmdRepl : InstallablesCommand
{
CmdRepl() {
evalSettings.pureEval = false;
}
void prepare() override
{
if (!settings.isExperimentalFeatureEnabled(Xp::ReplFlake) && !(file) && this->_installables.size() >= 1) {
warn("future versions of Nix will require using `--file` to load a file");
if (this->_installables.size() > 1)
warn("more than one input file is not currently supported");
auto filePath = this->_installables[0].data();
file = std::optional(filePath);
_installables.front() = _installables.back();
_installables.pop_back();
}
installables = InstallablesCommand::load();
}
std::vector<std::string> files;
Strings getDefaultFlakeAttrPaths() override
{
return {""};
}
bool useDefaultInstallables() override
{
return file.has_value() or expr.has_value();
}
bool forceImpureByDefault() override
{
return true;
}
std::string description() override
{
return "start an interactive environment for evaluating Nix expressions";
}
std::string doc() override
{
return
#include "repl.md"
;
}
void run(ref<Store> store) override
{
auto state = getEvalState();
auto getValues = [&]()->NixRepl::AnnotatedValues{
auto installables = load();
NixRepl::AnnotatedValues values;
for (auto & installable: installables){
auto what = installable->what();
if (file){
auto [val, pos] = installable->toValue(*state);
auto what = installable->what();
state->forceValue(*val, pos);
auto autoArgs = getAutoArgs(*state);
auto valPost = state->allocValue();
state->autoCallFunction(*autoArgs, *val, *valPost);
state->forceValue(*valPost, pos);
values.push_back( {valPost, what });
} else {
auto [val, pos] = installable->toValue(*state);
values.push_back( {val, what} );
}
}
return values;
};
auto repl = std::make_unique<NixRepl>(
searchPath,
openStore(),
state,
getValues
);
repl->autoArgs = getAutoArgs(*repl->state);
repl->initEnv();
repl->mainLoop();
}
};
static auto rCmdRepl = registerCommand<CmdRepl>("repl");
}

39
src/libcmd/repl.hh Normal file
View file

@ -0,0 +1,39 @@
#pragma once
#include "eval.hh"
#if HAVE_BOEHMGC
#define GC_INCLUDE_NEW
#include <gc/gc_cpp.h>
#endif
namespace nix {
struct AbstractNixRepl
{
ref<EvalState> state;
Bindings * autoArgs;
AbstractNixRepl(ref<EvalState> state)
: state(state)
{ }
virtual ~AbstractNixRepl()
{ }
typedef std::vector<std::pair<Value*,std::string>> AnnotatedValues;
static std::unique_ptr<AbstractNixRepl> create(
const Strings & searchPath, nix::ref<Store> store, ref<EvalState> state,
std::function<AnnotatedValues()> getValues);
static void runSimple(
ref<EvalState> evalState,
const ValMap & extraEnv);
virtual void initEnv() = 0;
virtual void mainLoop() = 0;
};
}

View file

@ -3057,9 +3057,9 @@ static RegisterPrimOp primop_foldlStrict({
.doc = R"(
Reduce a list by applying a binary operator, from left to right,
e.g. `foldl' op nul [x0 x1 x2 ...] = op (op (op nul x0) x1) x2)
...`. The operator is applied strictly, i.e., its arguments are
evaluated first. For example, `foldl' (x: y: x + y) 0 [1 2 3]`
evaluates to 6.
...`. For example, `foldl' (x: y: x + y) 0 [1 2 3]` evaluates to 6.
The return value of each application of `op` is evaluated immediately,
even for intermediate values.
)",
.fun = prim_foldlStrict,
});

View file

@ -971,10 +971,6 @@ void LocalDerivationGoal::startBuilder()
"nobody:x:65534:65534:Nobody:/:/noshell\n",
sandboxUid(), sandboxGid(), settings.sandboxBuildDir));
/* Make /etc unwritable */
if (!parsedDrv->useUidRange())
chmod_(chrootRootDir + "/etc", 0555);
/* Save the mount- and user namespace of the child. We have to do this
*before* the child does a chroot. */
sandboxMountNamespace = open(fmt("/proc/%d/ns/mnt", (pid_t) pid).c_str(), O_RDONLY);
@ -1855,6 +1851,10 @@ void LocalDerivationGoal::runChild()
}
}
/* Make /etc unwritable */
if (!parsedDrv->useUidRange())
chmod_(chrootRootDir + "/etc", 0555);
/* Unshare this mount namespace. This is necessary because
pivot_root() below changes the root of the mount
namespace. This means that the call to setns() in

View file

@ -5,6 +5,7 @@
#include "worker-protocol.hh"
#include "fs-accessor.hh"
#include <boost/container/small_vector.hpp>
#include <nlohmann/json.hpp>
namespace nix {
@ -890,4 +891,63 @@ std::optional<BasicDerivation> Derivation::tryResolve(
const Hash impureOutputHash = hashString(htSHA256, "impure");
nlohmann::json DerivationOutput::toJSON(
const Store & store, std::string_view drvName, std::string_view outputName) const
{
nlohmann::json res = nlohmann::json::object();
std::visit(overloaded {
[&](const DerivationOutput::InputAddressed & doi) {
res["path"] = store.printStorePath(doi.path);
},
[&](const DerivationOutput::CAFixed & dof) {
res["path"] = store.printStorePath(dof.path(store, drvName, outputName));
res["hashAlgo"] = dof.hash.printMethodAlgo();
res["hash"] = dof.hash.hash.to_string(Base16, false);
},
[&](const DerivationOutput::CAFloating & dof) {
res["hashAlgo"] = makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType);
},
[&](const DerivationOutput::Deferred &) {},
[&](const DerivationOutput::Impure & doi) {
res["hashAlgo"] = makeFileIngestionPrefix(doi.method) + printHashType(doi.hashType);
res["impure"] = true;
},
}, raw());
return res;
}
nlohmann::json Derivation::toJSON(const Store & store) const
{
nlohmann::json res = nlohmann::json::object();
{
nlohmann::json & outputsObj = res["outputs"];
outputsObj = nlohmann::json::object();
for (auto & [outputName, output] : outputs) {
outputsObj[outputName] = output.toJSON(store, name, outputName);
}
}
{
auto& inputsList = res["inputSrcs"];
inputsList = nlohmann::json ::array();
for (auto & input : inputSrcs)
inputsList.emplace_back(store.printStorePath(input));
}
{
auto& inputDrvsObj = res["inputDrvs"];
inputDrvsObj = nlohmann::json ::object();
for (auto & input : inputDrvs)
inputDrvsObj[store.printStorePath(input.first)] = input.second;
}
res["system"] = platform;
res["builder"] = builder;
res["args"] = args;
res["env"] = env;
return res;
}
}

View file

@ -83,6 +83,11 @@ struct DerivationOutput : _DerivationOutputRaw
inline const Raw & raw() const {
return static_cast<const Raw &>(*this);
}
nlohmann::json toJSON(
const Store & store,
std::string_view drvName,
std::string_view outputName) const;
};
typedef std::map<std::string, DerivationOutput> DerivationOutputs;
@ -210,6 +215,8 @@ struct Derivation : BasicDerivation
Derivation() = default;
Derivation(const BasicDerivation & bd) : BasicDerivation(bd) { }
Derivation(BasicDerivation && bd) : BasicDerivation(std::move(bd)) { }
nlohmann::json toJSON(const Store & store) const;
};

View file

@ -0,0 +1,124 @@
#include <nlohmann/json.hpp>
#include <gtest/gtest.h>
#include "derivations.hh"
#include "tests/libstore.hh"
namespace nix {
class DerivationTest : public LibStoreTest
{
};
#define TEST_JSON(TYPE, NAME, STR, VAL, ...) \
TEST_F(DerivationTest, TYPE ## _ ## NAME ## _to_json) { \
using nlohmann::literals::operator "" _json; \
ASSERT_EQ( \
STR ## _json, \
(TYPE { VAL }).toJSON(*store __VA_OPT__(,) __VA_ARGS__)); \
}
TEST_JSON(DerivationOutput, inputAddressed,
R"({
"path": "/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-drv-name-output-name"
})",
(DerivationOutput::InputAddressed {
.path = store->parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-drv-name-output-name"),
}),
"drv-name", "output-name")
TEST_JSON(DerivationOutput, caFixed,
R"({
"hashAlgo": "r:sha256",
"hash": "894517c9163c896ec31a2adbd33c0681fd5f45b2c0ef08a64c92a03fb97f390f",
"path": "/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-drv-name-output-name"
})",
(DerivationOutput::CAFixed {
.hash = {
.method = FileIngestionMethod::Recursive,
.hash = Hash::parseAnyPrefixed("sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="),
},
}),
"drv-name", "output-name")
TEST_JSON(DerivationOutput, caFloating,
R"({
"hashAlgo": "r:sha256"
})",
(DerivationOutput::CAFloating {
.method = FileIngestionMethod::Recursive,
.hashType = htSHA256,
}),
"drv-name", "output-name")
TEST_JSON(DerivationOutput, deferred,
R"({ })",
DerivationOutput::Deferred { },
"drv-name", "output-name")
TEST_JSON(DerivationOutput, impure,
R"({
"hashAlgo": "r:sha256",
"impure": true
})",
(DerivationOutput::Impure {
.method = FileIngestionMethod::Recursive,
.hashType = htSHA256,
}),
"drv-name", "output-name")
TEST_JSON(Derivation, impure,
R"({
"inputSrcs": [
"/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"
],
"inputDrvs": {
"/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv": [
"cat",
"dog"
]
},
"system": "wasm-sel4",
"builder": "foo",
"args": [
"bar",
"baz"
],
"env": {
"BIG_BAD": "WOLF"
},
"outputs": {}
})",
({
Derivation drv;
drv.inputSrcs = {
store->parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"),
};
drv.inputDrvs = {
{
store->parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv"),
{
"cat",
"dog",
},
}
};
drv.platform = "wasm-sel4";
drv.builder = "foo";
drv.args = {
"bar",
"baz",
};
drv.env = {
{
"BIG_BAD",
"WOLF",
},
};
drv;
}))
#undef TEST_JSON
}

View file

@ -1,4 +1,5 @@
#include "command.hh"
#include "installable-flake.hh"
#include "common-args.hh"
#include "shared.hh"
#include "store-api.hh"

View file

@ -1,5 +1,6 @@
#include "eval.hh"
#include "command.hh"
#include "installable-flake.hh"
#include "common-args.hh"
#include "shared.hh"
#include "store-api.hh"

View file

@ -3,6 +3,7 @@
#include "eval.hh"
#include "attr-path.hh"
#include "progress-bar.hh"
#include "editor-for.hh"
#include <unistd.h>

View file

@ -1,4 +1,5 @@
#include "command.hh"
#include "installable-flake.hh"
#include "common-args.hh"
#include "shared.hh"
#include "eval.hh"

View file

@ -1,4 +1,5 @@
#include "command.hh"
#include "installable-flake.hh"
#include "common-args.hh"
#include "shared.hh"
#include "store-api.hh"

95
src/nix/repl.cc Normal file
View file

@ -0,0 +1,95 @@
#include "eval.hh"
#include "globals.hh"
#include "command.hh"
#include "repl.hh"
namespace nix {
struct CmdRepl : InstallablesCommand
{
CmdRepl() {
evalSettings.pureEval = false;
}
void prepare() override
{
if (!settings.isExperimentalFeatureEnabled(Xp::ReplFlake) && !(file) && this->_installables.size() >= 1) {
warn("future versions of Nix will require using `--file` to load a file");
if (this->_installables.size() > 1)
warn("more than one input file is not currently supported");
auto filePath = this->_installables[0].data();
file = std::optional(filePath);
_installables.front() = _installables.back();
_installables.pop_back();
}
installables = InstallablesCommand::load();
}
std::vector<std::string> files;
Strings getDefaultFlakeAttrPaths() override
{
return {""};
}
bool useDefaultInstallables() override
{
return file.has_value() or expr.has_value();
}
bool forceImpureByDefault() override
{
return true;
}
std::string description() override
{
return "start an interactive environment for evaluating Nix expressions";
}
std::string doc() override
{
return
#include "repl.md"
;
}
void run(ref<Store> store) override
{
auto state = getEvalState();
auto getValues = [&]()->AbstractNixRepl::AnnotatedValues{
auto installables = load();
AbstractNixRepl::AnnotatedValues values;
for (auto & installable: installables){
auto what = installable->what();
if (file){
auto [val, pos] = installable->toValue(*state);
auto what = installable->what();
state->forceValue(*val, pos);
auto autoArgs = getAutoArgs(*state);
auto valPost = state->allocValue();
state->autoCallFunction(*autoArgs, *val, *valPost);
state->forceValue(*valPost, pos);
values.push_back( {valPost, what });
} else {
auto [val, pos] = installable->toValue(*state);
values.push_back( {val, what} );
}
}
return values;
};
auto repl = AbstractNixRepl::create(
searchPath,
openStore(),
state,
getValues
);
repl->autoArgs = getAutoArgs(*repl->state);
repl->initEnv();
repl->mainLoop();
}
};
static auto rCmdRepl = registerCommand<CmdRepl>("repl");
}

View file

@ -54,56 +54,8 @@ struct CmdShowDerivation : InstallablesCommand
for (auto & drvPath : drvPaths) {
if (!drvPath.isDerivation()) continue;
json& drvObj = jsonRoot[store->printStorePath(drvPath)];
auto drv = store->readDerivation(drvPath);
{
json& outputsObj = drvObj["outputs"];
outputsObj = json::object();
for (auto & [_outputName, output] : drv.outputs) {
auto & outputName = _outputName; // work around clang bug
auto& outputObj = outputsObj[outputName];
outputObj = json::object();
std::visit(overloaded {
[&](const DerivationOutput::InputAddressed & doi) {
outputObj["path"] = store->printStorePath(doi.path);
},
[&](const DerivationOutput::CAFixed & dof) {
outputObj["path"] = store->printStorePath(dof.path(*store, drv.name, outputName));
outputObj["hashAlgo"] = dof.hash.printMethodAlgo();
outputObj["hash"] = dof.hash.hash.to_string(Base16, false);
},
[&](const DerivationOutput::CAFloating & dof) {
outputObj["hashAlgo"] = makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType);
},
[&](const DerivationOutput::Deferred &) {},
[&](const DerivationOutput::Impure & doi) {
outputObj["hashAlgo"] = makeFileIngestionPrefix(doi.method) + printHashType(doi.hashType);
outputObj["impure"] = true;
},
}, output.raw());
}
}
{
auto& inputsList = drvObj["inputSrcs"];
inputsList = json::array();
for (auto & input : drv.inputSrcs)
inputsList.emplace_back(store->printStorePath(input));
}
{
auto& inputDrvsObj = drvObj["inputDrvs"];
inputDrvsObj = json::object();
for (auto & input : drv.inputDrvs)
inputDrvsObj[store->printStorePath(input.first)] = input.second;
}
drvObj["system"] = drv.platform;
drvObj["builder"] = drv.builder;
drvObj["args"] = drv.args;
drvObj["env"] = drv.env;
jsonRoot[store->printStorePath(drvPath)] =
store->readDerivation(drvPath).toJSON(*store);
}
std::cout << jsonRoot.dump(2) << std::endl;
}