aboutsummaryrefslogtreecommitdiff
path: root/src/nix
diff options
context:
space:
mode:
Diffstat (limited to 'src/nix')
-rw-r--r--src/nix/app.cc89
-rw-r--r--src/nix/build.cc18
-rw-r--r--src/nix/build.md2
-rw-r--r--src/nix/bundle.cc7
-rw-r--r--src/nix/copy.cc20
-rw-r--r--src/nix/daemon.cc6
-rw-r--r--src/nix/develop.cc325
-rw-r--r--src/nix/develop.md12
-rw-r--r--src/nix/diff-closures.cc4
-rw-r--r--src/nix/edit.cc5
-rw-r--r--src/nix/flake-check.md7
-rw-r--r--src/nix/flake-init.md2
-rw-r--r--src/nix/flake-list-inputs.md23
-rw-r--r--src/nix/flake-metadata.md (renamed from src/nix/flake-info.md)27
-rw-r--r--src/nix/flake-show.md3
-rw-r--r--src/nix/flake.cc351
-rw-r--r--src/nix/flake.md12
-rw-r--r--src/nix/get-env.sh119
-rw-r--r--src/nix/local.mk4
-rw-r--r--src/nix/log.cc8
-rw-r--r--src/nix/main.cc70
-rw-r--r--src/nix/make-content-addressable.cc2
-rw-r--r--src/nix/path-info.cc2
-rw-r--r--src/nix/path-info.md2
-rw-r--r--src/nix/prefetch.cc24
-rw-r--r--src/nix/print-dev-env.md37
-rw-r--r--src/nix/profile-history.md4
-rw-r--r--src/nix/profile-remove.md1
-rw-r--r--src/nix/profile-rollback.md26
-rw-r--r--src/nix/profile-upgrade.md2
-rw-r--r--src/nix/profile-wipe-history.md20
-rw-r--r--src/nix/profile.cc111
-rw-r--r--src/nix/realisation.cc85
-rw-r--r--src/nix/realisation/info.md15
-rw-r--r--src/nix/registry-add.md7
-rw-r--r--src/nix/registry-pin.md7
-rw-r--r--src/nix/registry-remove.md6
-rw-r--r--src/nix/registry.cc87
-rw-r--r--src/nix/registry.md2
-rw-r--r--src/nix/repl.cc111
-rw-r--r--src/nix/run.cc73
-rw-r--r--src/nix/run.hh11
-rw-r--r--src/nix/search.cc1
-rw-r--r--src/nix/show-config.cc5
-rw-r--r--src/nix/show-derivation.cc8
-rw-r--r--src/nix/sigs.cc4
-rw-r--r--src/nix/store-delete.cc2
-rw-r--r--src/nix/store-prefetch-file.md2
-rw-r--r--src/nix/store-repair.cc2
-rw-r--r--src/nix/verify.cc12
-rw-r--r--src/nix/why-depends.cc4
51 files changed, 1246 insertions, 543 deletions
diff --git a/src/nix/app.cc b/src/nix/app.cc
index cf147c631..9719a65dd 100644
--- a/src/nix/app.cc
+++ b/src/nix/app.cc
@@ -3,34 +3,79 @@
#include "eval-inline.hh"
#include "eval-cache.hh"
#include "names.hh"
+#include "command.hh"
namespace nix {
-App Installable::toApp(EvalState & state)
+struct InstallableDerivedPath : Installable
{
- auto [cursor, attrPath] = getCursor(state);
+ ref<Store> store;
+ const DerivedPath derivedPath;
- auto type = cursor->getAttr("type")->getString();
+ InstallableDerivedPath(ref<Store> store, const DerivedPath & derivedPath)
+ : store(store)
+ , derivedPath(derivedPath)
+ {
+ }
+
+
+ std::string what() override { return derivedPath.to_string(*store); }
+
+ DerivedPaths toDerivedPaths() override
+ {
+ return {derivedPath};
+ }
- auto checkProgram = [&](const Path & program)
+ std::optional<StorePath> getStorePath() override
{
- if (!state.store->isInStore(program))
- throw Error("app program '%s' is not in the Nix store", program);
- };
+ return std::nullopt;
+ }
+};
+
+/**
+ * Return the rewrites that are needed to resolve a string whose context is
+ * included in `dependencies`
+ */
+StringPairs resolveRewrites(Store & store, const BuiltPaths dependencies)
+{
+ StringPairs res;
+ for (auto & dep : dependencies)
+ if (auto drvDep = std::get_if<BuiltPathBuilt>(&dep))
+ for (auto & [ outputName, outputPath ] : drvDep->outputs)
+ res.emplace(
+ downstreamPlaceholder(store, drvDep->drvPath, outputName),
+ store.printStorePath(outputPath)
+ );
+ return res;
+}
+
+/**
+ * Resolve the given string assuming the given context
+ */
+std::string resolveString(Store & store, const std::string & toResolve, const BuiltPaths dependencies)
+{
+ auto rewrites = resolveRewrites(store, dependencies);
+ return rewriteStrings(toResolve, rewrites);
+}
+
+UnresolvedApp Installable::toApp(EvalState & state)
+{
+ auto [cursor, attrPath] = getCursor(state);
+
+ auto type = cursor->getAttr("type")->getString();
if (type == "app") {
auto [program, context] = cursor->getAttr("program")->getStringWithContext();
- checkProgram(program);
std::vector<StorePathWithOutputs> context2;
for (auto & [path, name] : context)
context2.push_back({state.store->parseStorePath(path), {name}});
- return App {
+ return UnresolvedApp{App {
.context = std::move(context2),
.program = program,
- };
+ }};
}
else if (type == "derivation") {
@@ -45,15 +90,33 @@ App Installable::toApp(EvalState & state)
? aMainProgram->getString()
: DrvName(name).name;
auto program = outPath + "/bin/" + mainProgram;
- checkProgram(program);
- return App {
+ return UnresolvedApp { App {
.context = { { drvPath, {outputName} } },
.program = program,
- };
+ }};
}
else
throw Error("attribute '%s' has unsupported type '%s'", attrPath, type);
}
+// FIXME: move to libcmd
+App UnresolvedApp::resolve(ref<Store> evalStore, ref<Store> store)
+{
+ auto res = unresolved;
+
+ std::vector<std::shared_ptr<Installable>> installableContext;
+
+ for (auto & ctxElt : unresolved.context)
+ installableContext.push_back(
+ std::make_shared<InstallableDerivedPath>(store, ctxElt.toDerivedPath()));
+
+ auto builtContext = build(evalStore, store, Realise::Outputs, installableContext);
+ res.program = resolveString(*store, unresolved.program, builtContext);
+ if (!store->isInStore(res.program))
+ throw Error("app program '%s' is not in the Nix store", res.program);
+
+ return res;
+}
+
}
diff --git a/src/nix/build.cc b/src/nix/build.cc
index 724ce9d79..6e31757a2 100644
--- a/src/nix/build.cc
+++ b/src/nix/build.cc
@@ -52,7 +52,12 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
void run(ref<Store> store) override
{
- auto buildables = build(store, dryRun ? Realise::Nothing : Realise::Outputs, installables, buildMode);
+ auto buildables = build(
+ getEvalStore(), store,
+ dryRun ? Realise::Derivation : Realise::Outputs,
+ installables, buildMode);
+
+ if (json) logger->cout("%s", derivedPathsWithHintsToJSON(buildables, store).dump());
if (dryRun) return;
@@ -61,26 +66,23 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
for (const auto & [_i, buildable] : enumerate(buildables)) {
auto i = _i;
std::visit(overloaded {
- [&](BuildableOpaque bo) {
+ [&](const BuiltPath::Opaque & bo) {
std::string symlink = outLink;
if (i) symlink += fmt("-%d", i);
store2->addPermRoot(bo.path, absPath(symlink));
},
- [&](BuildableFromDrv bfd) {
- auto builtOutputs = store->queryDerivationOutputMap(bfd.drvPath);
- for (auto & output : builtOutputs) {
+ [&](const BuiltPath::Built & bfd) {
+ for (auto & output : bfd.outputs) {
std::string symlink = outLink;
if (i) symlink += fmt("-%d", i);
if (output.first != "out") symlink += fmt("-%s", output.first);
store2->addPermRoot(output.second, absPath(symlink));
}
},
- }, buildable);
+ }, buildable.raw());
}
updateProfile(buildables);
-
- if (json) logger->cout("%s", buildablesToJSON(buildables, store).dump());
}
};
diff --git a/src/nix/build.md b/src/nix/build.md
index c2f3e387a..20138b7e0 100644
--- a/src/nix/build.md
+++ b/src/nix/build.md
@@ -81,7 +81,7 @@ path installables are substituted.
Unless `--no-link` is specified, after a successful build, it creates
symlinks to the store paths of the installables. These symlinks have
-the prefix `./result` by default; this can be overriden using the
+the prefix `./result` by default; this can be overridden using the
`--out-link` option. Each symlink has a suffix `-<N>-<outname>`, where
*N* is the index of the installable (with the left-most installable
having index 0), and *outname* is the symbolic derivation output name
diff --git a/src/nix/bundle.cc b/src/nix/bundle.cc
index 48f4eb6e3..aca024bca 100644
--- a/src/nix/bundle.cc
+++ b/src/nix/bundle.cc
@@ -59,7 +59,7 @@ struct CmdBundle : InstallableCommand
Strings getDefaultFlakeAttrPathPrefixes() override
{
- Strings res{"apps." + settings.thisSystem.get() + ".", "packages"};
+ Strings res{"apps." + settings.thisSystem.get() + "."};
for (auto & s : SourceExprCommand::getDefaultFlakeAttrPathPrefixes())
res.push_back(s);
return res;
@@ -69,8 +69,7 @@ struct CmdBundle : InstallableCommand
{
auto evalState = getEvalState();
- auto app = installable->toApp(*evalState);
- store->buildPaths(app.context);
+ auto app = installable->toApp(*evalState).resolve(getEvalStore(), store);
auto [bundlerFlakeRef, bundlerName] = parseFlakeRefWithFragment(bundler, absPath("."));
const flake::LockFlags lockFlags{ .writeLockFile = false };
@@ -110,7 +109,7 @@ struct CmdBundle : InstallableCommand
StorePath outPath = store->parseStorePath(evalState->coerceToPath(*attr2->pos, *attr2->value, context2));
- store->buildPaths({{drvPath}});
+ store->buildPaths({ DerivedPath::Built { drvPath } });
auto outPathS = store->printStorePath(outPath);
diff --git a/src/nix/copy.cc b/src/nix/copy.cc
index f59f7c76b..197c85316 100644
--- a/src/nix/copy.cc
+++ b/src/nix/copy.cc
@@ -8,7 +8,7 @@
using namespace nix;
-struct CmdCopy : RealisedPathsCommand
+struct CmdCopy : BuiltPathsCommand
{
std::string srcUri, dstUri;
@@ -16,10 +16,10 @@ struct CmdCopy : RealisedPathsCommand
SubstituteFlag substitute = NoSubstitute;
- using RealisedPathsCommand::run;
+ using BuiltPathsCommand::run;
CmdCopy()
- : RealisedPathsCommand(true)
+ : BuiltPathsCommand(true)
{
addFlag({
.longName = "from",
@@ -75,16 +75,22 @@ struct CmdCopy : RealisedPathsCommand
if (srcUri.empty() && dstUri.empty())
throw UsageError("you must pass '--from' and/or '--to'");
- RealisedPathsCommand::run(store);
+ BuiltPathsCommand::run(store);
}
- void run(ref<Store> srcStore, std::vector<RealisedPath> paths) override
+ void run(ref<Store> srcStore, BuiltPaths && paths) override
{
ref<Store> dstStore = dstUri.empty() ? openStore() : openStore(dstUri);
+ RealisedPath::Set stuffToCopy;
+
+ for (auto & builtPath : paths) {
+ auto theseRealisations = builtPath.toRealisedPaths(*srcStore);
+ stuffToCopy.insert(theseRealisations.begin(), theseRealisations.end());
+ }
+
copyPaths(
- srcStore, dstStore, RealisedPath::Set(paths.begin(), paths.end()),
- NoRepair, checkSigs, substitute);
+ *srcStore, *dstStore, stuffToCopy, NoRepair, checkSigs, substitute);
}
};
diff --git a/src/nix/daemon.cc b/src/nix/daemon.cc
index 86c6fe252..da8d91029 100644
--- a/src/nix/daemon.cc
+++ b/src/nix/daemon.cc
@@ -156,9 +156,6 @@ static void daemonLoop()
if (chdir("/") == -1)
throw SysError("cannot change current directory");
- // Get rid of children automatically; don't let them become zombies.
- setSigChldAction(true);
-
AutoCloseFD fdSocket;
// Handle socket-based activation by systemd.
@@ -176,6 +173,9 @@ static void daemonLoop()
fdSocket = createUnixDomainSocket(settings.nixDaemonSocketFile, 0666);
}
+ // Get rid of children automatically; don't let them become zombies.
+ setSigChldAction(true);
+
// Loop accepting connections.
while (1) {
diff --git a/src/nix/develop.cc b/src/nix/develop.cc
index d0b140570..f22335023 100644
--- a/src/nix/develop.cc
+++ b/src/nix/develop.cc
@@ -3,11 +3,14 @@
#include "common-args.hh"
#include "shared.hh"
#include "store-api.hh"
+#include "path-with-outputs.hh"
#include "derivations.hh"
#include "affinity.hh"
#include "progress-bar.hh"
+#include "run.hh"
-#include <regex>
+#include <memory>
+#include <nlohmann/json.hpp>
using namespace nix;
@@ -24,94 +27,142 @@ static DevelopSettings developSettings;
static GlobalConfig::Register rDevelopSettings(&developSettings);
-struct Var
-{
- bool exported = true;
- bool associative = false;
- std::string quoted; // quoted string or array
-};
-
struct BuildEnvironment
{
- std::map<std::string, Var> env;
- std::string bashFunctions;
-};
+ struct String
+ {
+ bool exported;
+ std::string value;
-BuildEnvironment readEnvironment(const Path & path)
-{
- BuildEnvironment res;
+ bool operator == (const String & other) const
+ {
+ return exported == other.exported && value == other.value;
+ }
+ };
- std::set<std::string> exported;
+ using Array = std::vector<std::string>;
- debug("reading environment file '%s'", path);
+ using Associative = std::map<std::string, std::string>;
- auto file = readFile(path);
+ using Value = std::variant<String, Array, Associative>;
- auto pos = file.cbegin();
+ std::map<std::string, Value> vars;
+ std::map<std::string, std::string> bashFunctions;
- static std::string varNameRegex =
- R"re((?:[a-zA-Z_][a-zA-Z0-9_]*))re";
+ static BuildEnvironment fromJSON(std::string_view in)
+ {
+ BuildEnvironment res;
- static std::string simpleStringRegex =
- R"re((?:[a-zA-Z0-9_/:\.\-\+=]*))re";
+ std::set<std::string> exported;
- static std::string dquotedStringRegex =
- R"re((?:\$?"(?:[^"\\]|\\[$`"\\\n])*"))re";
+ auto json = nlohmann::json::parse(in);
- static std::string squotedStringRegex =
- R"re((?:\$?(?:'(?:[^'\\]|\\[abeEfnrtv\\'"?])*'|\\')+))re";
+ for (auto & [name, info] : json["variables"].items()) {
+ std::string type = info["type"];
+ if (type == "var" || type == "exported")
+ res.vars.insert({name, BuildEnvironment::String { .exported = type == "exported", .value = info["value"] }});
+ else if (type == "array")
+ res.vars.insert({name, (Array) info["value"]});
+ else if (type == "associative")
+ res.vars.insert({name, (Associative) info["value"]});
+ }
- static std::string indexedArrayRegex =
- R"re((?:\(( *\[[0-9]+\]="(?:[^"\\]|\\.)*")*\)))re";
+ for (auto & [name, def] : json["bashFunctions"].items()) {
+ res.bashFunctions.insert({name, def});
+ }
- static std::regex declareRegex(
- "^declare -a?x (" + varNameRegex + ")(=(" +
- dquotedStringRegex + "|" + indexedArrayRegex + "))?\n");
+ return res;
+ }
- static std::regex varRegex(
- "^(" + varNameRegex + ")=(" + simpleStringRegex + "|" + squotedStringRegex + "|" + indexedArrayRegex + ")\n");
+ std::string toJSON() const
+ {
+ auto res = nlohmann::json::object();
+
+ auto vars2 = nlohmann::json::object();
+ for (auto & [name, value] : vars) {
+ auto info = nlohmann::json::object();
+ if (auto str = std::get_if<String>(&value)) {
+ info["type"] = str->exported ? "exported" : "var";
+ info["value"] = str->value;
+ }
+ else if (auto arr = std::get_if<Array>(&value)) {
+ info["type"] = "array";
+ info["value"] = *arr;
+ }
+ else if (auto arr = std::get_if<Associative>(&value)) {
+ info["type"] = "associative";
+ info["value"] = *arr;
+ }
+ vars2[name] = std::move(info);
+ }
+ res["variables"] = std::move(vars2);
- /* Note: we distinguish between an indexed and associative array
- using the space before the closing parenthesis. Will
- undoubtedly regret this some day. */
- static std::regex assocArrayRegex(
- "^(" + varNameRegex + ")=" + R"re((?:\(( *\[[^\]]+\]="(?:[^"\\]|\\.)*")* *\)))re" + "\n");
+ res["bashFunctions"] = bashFunctions;
- static std::regex functionRegex(
- "^" + varNameRegex + " \\(\\) *\n");
+ auto json = res.dump();
- while (pos != file.end()) {
+ assert(BuildEnvironment::fromJSON(json) == *this);
- std::smatch match;
+ return json;
+ }
- if (std::regex_search(pos, file.cend(), match, declareRegex, std::regex_constants::match_continuous)) {
- pos = match[0].second;
- exported.insert(match[1]);
+ void toBash(std::ostream & out, const std::set<std::string> & ignoreVars) const
+ {
+ for (auto & [name, value] : vars) {
+ if (!ignoreVars.count(name)) {
+ if (auto str = std::get_if<String>(&value)) {
+ out << fmt("%s=%s\n", name, shellEscape(str->value));
+ if (str->exported)
+ out << fmt("export %s\n", name);
+ }
+ else if (auto arr = std::get_if<Array>(&value)) {
+ out << "declare -a " << name << "=(";
+ for (auto & s : *arr)
+ out << shellEscape(s) << " ";
+ out << ")\n";
+ }
+ else if (auto arr = std::get_if<Associative>(&value)) {
+ out << "declare -A " << name << "=(";
+ for (auto & [n, v] : *arr)
+ out << "[" << shellEscape(n) << "]=" << shellEscape(v) << " ";
+ out << ")\n";
+ }
+ }
}
- else if (std::regex_search(pos, file.cend(), match, varRegex, std::regex_constants::match_continuous)) {
- pos = match[0].second;
- res.env.insert({match[1], Var { .exported = exported.count(match[1]) > 0, .quoted = match[2] }});
+ for (auto & [name, def] : bashFunctions) {
+ out << name << " ()\n{\n" << def << "}\n";
}
+ }
- else if (std::regex_search(pos, file.cend(), match, assocArrayRegex, std::regex_constants::match_continuous)) {
- pos = match[0].second;
- res.env.insert({match[1], Var { .associative = true, .quoted = match[2] }});
- }
+ static std::string getString(const Value & value)
+ {
+ if (auto str = std::get_if<String>(&value))
+ return str->value;
+ else
+ throw Error("bash variable is not a string");
+ }
- else if (std::regex_search(pos, file.cend(), match, functionRegex, std::regex_constants::match_continuous)) {
- res.bashFunctions = std::string(pos, file.cend());
- break;
+ static Array getStrings(const Value & value)
+ {
+ if (auto str = std::get_if<String>(&value))
+ return tokenizeString<Array>(str->value);
+ else if (auto arr = std::get_if<Array>(&value)) {
+ return *arr;
+ } else if (auto assoc = std::get_if<Associative>(&value)) {
+ Array assocKeys;
+ std::for_each(assoc->begin(), assoc->end(), [&](auto & n) { assocKeys.push_back(n.first); });
+ return assocKeys;
}
-
- else throw Error("shell environment '%s' has unexpected line '%s'",
- path, file.substr(pos - file.cbegin(), 60));
+ else
+ throw Error("bash variable is not a string or array");
}
- res.env.erase("__output");
-
- return res;
-}
+ bool operator == (const BuildEnvironment & other) const
+ {
+ return vars == other.vars && bashFunctions == other.bashFunctions;
+ }
+};
const static std::string getEnvSh =
#include "get-env.sh.gen.hh"
@@ -122,15 +173,15 @@ const static std::string getEnvSh =
modified derivation with the same dependencies and nearly the same
initial environment variables, that just writes the resulting
environment to a file and exits. */
-StorePath getDerivationEnvironment(ref<Store> store, const StorePath & drvPath)
+static StorePath getDerivationEnvironment(ref<Store> store, ref<Store> evalStore, const StorePath & drvPath)
{
- auto drv = store->derivationFromPath(drvPath);
+ auto drv = evalStore->derivationFromPath(drvPath);
auto builder = baseNameOf(drv.builder);
if (builder != "bash")
throw Error("'nix develop' only works on derivations that use 'bash' as their builder");
- auto getEnvShPath = store->addTextToStore("get-env.sh", getEnvSh, {});
+ auto getEnvShPath = evalStore->addTextToStore("get-env.sh", getEnvSh, {});
drv.args = {store->printStorePath(getEnvShPath)};
@@ -143,26 +194,34 @@ StorePath getDerivationEnvironment(ref<Store> store, const StorePath & drvPath)
/* Rehash and write the derivation. FIXME: would be nice to use
'buildDerivation', but that's privileged. */
drv.name += "-env";
- for (auto & output : drv.outputs) {
- output.second = { .output = DerivationOutputInputAddressed { .path = StorePath::dummy } };
- drv.env[output.first] = "";
- }
drv.inputSrcs.insert(std::move(getEnvShPath));
- Hash h = std::get<0>(hashDerivationModulo(*store, drv, true));
+ if (settings.isExperimentalFeatureEnabled("ca-derivations")) {
+ for (auto & output : drv.outputs) {
+ output.second = {
+ .output = DerivationOutputDeferred{},
+ };
+ drv.env[output.first] = hashPlaceholder(output.first);
+ }
+ } else {
+ for (auto & output : drv.outputs) {
+ output.second = { .output = DerivationOutputInputAddressed { .path = StorePath::dummy } };
+ drv.env[output.first] = "";
+ }
+ Hash h = std::get<0>(hashDerivationModulo(*evalStore, drv, true));
- for (auto & output : drv.outputs) {
- auto outPath = store->makeOutputPath(output.first, h, drv.name);
- output.second = { .output = DerivationOutputInputAddressed { .path = outPath } };
- drv.env[output.first] = store->printStorePath(outPath);
+ for (auto & output : drv.outputs) {
+ auto outPath = store->makeOutputPath(output.first, h, drv.name);
+ output.second = { .output = DerivationOutputInputAddressed { .path = outPath } };
+ drv.env[output.first] = store->printStorePath(outPath);
+ }
}
- auto shellDrvPath = writeDerivation(*store, drv);
+ auto shellDrvPath = writeDerivation(*evalStore, drv);
/* Build the derivation. */
- store->buildPaths({{shellDrvPath}});
+ store->buildPaths({DerivedPath::Built{shellDrvPath}}, bmNormal, evalStore);
- for (auto & [_0, outputAndOptPath] : drv.outputsAndOptPaths(*store)) {
- auto & [_1, optPath] = outputAndOptPath;
+ for (auto & [_0, optPath] : evalStore->queryPartialDerivationOutputMap(shellDrvPath)) {
assert(optPath);
auto & outPath = *optPath;
assert(store->isValidPath(outPath));
@@ -176,18 +235,15 @@ StorePath getDerivationEnvironment(ref<Store> store, const StorePath & drvPath)
struct Common : InstallableCommand, MixProfile
{
- std::set<string> ignoreVars{
+ std::set<std::string> ignoreVars{
"BASHOPTS",
- "EUID",
"HOME", // FIXME: don't ignore in pure mode?
- "HOSTNAME",
"NIX_BUILD_TOP",
"NIX_ENFORCE_PURITY",
"NIX_LOG_FD",
+ "NIX_REMOTE",
"PPID",
- "PWD",
"SHELLOPTS",
- "SHLVL",
"SSL_CERT_FILE", // FIXME: only want to ignore /no-cert-file.crt
"TEMP",
"TEMPDIR",
@@ -223,22 +279,10 @@ struct Common : InstallableCommand, MixProfile
out << "nix_saved_PATH=\"$PATH\"\n";
- for (auto & i : buildEnvironment.env) {
- if (!ignoreVars.count(i.first) && !hasPrefix(i.first, "BASH_")) {
- if (i.second.associative)
- out << fmt("declare -A %s=(%s)\n", i.first, i.second.quoted);
- else {
- out << fmt("%s=%s\n", i.first, i.second.quoted);
- if (i.second.exported)
- out << fmt("export %s\n", i.first);
- }
- }
- }
+ buildEnvironment.toBash(out, ignoreVars);
out << "PATH=\"$PATH:$nix_saved_PATH\"\n";
- out << buildEnvironment.bashFunctions << "\n";
-
out << "export NIX_BUILD_TOP=\"$(mktemp -d -t nix-shell.XXXXXX)\"\n";
for (auto & i : {"TMP", "TMPDIR", "TEMP", "TEMPDIR"})
out << fmt("export %s=\"$NIX_BUILD_TOP\"\n", i);
@@ -248,25 +292,25 @@ struct Common : InstallableCommand, MixProfile
auto script = out.str();
/* Substitute occurrences of output paths. */
- auto outputs = buildEnvironment.env.find("outputs");
- assert(outputs != buildEnvironment.env.end());
+ auto outputs = buildEnvironment.vars.find("outputs");
+ assert(outputs != buildEnvironment.vars.end());
// FIXME: properly unquote 'outputs'.
StringMap rewrites;
- for (auto & outputName : tokenizeString<std::vector<std::string>>(replaceStrings(outputs->second.quoted, "'", ""))) {
- auto from = buildEnvironment.env.find(outputName);
- assert(from != buildEnvironment.env.end());
+ for (auto & outputName : BuildEnvironment::getStrings(outputs->second)) {
+ auto from = buildEnvironment.vars.find(outputName);
+ assert(from != buildEnvironment.vars.end());
// FIXME: unquote
- rewrites.insert({from->second.quoted, outputsDir + "/" + outputName});
+ rewrites.insert({BuildEnvironment::getString(from->second), outputsDir + "/" + outputName});
}
/* Substitute redirects. */
for (auto & [installable_, dir_] : redirects) {
auto dir = absPath(dir_);
auto installable = parseInstallable(store, installable_);
- auto buildable = installable->toBuildable();
- auto doRedirect = [&](const StorePath & path)
- {
+ auto builtPaths = toStorePaths(
+ getEvalStore(), store, Realise::Nothing, OperateOn::Output, {installable});
+ for (auto & path: builtPaths) {
auto from = store->printStorePath(path);
if (script.find(from) == std::string::npos)
warn("'%s' (path '%s') is not used by this build environment", installable->what(), from);
@@ -274,16 +318,7 @@ struct Common : InstallableCommand, MixProfile
printInfo("redirecting '%s' to '%s'", from, dir);
rewrites.insert({from, dir});
}
- };
- std::visit(overloaded {
- [&](const BuildableOpaque & bo) {
- doRedirect(bo.path);
- },
- [&](const BuildableFromDrv & bfd) {
- for (auto & [outputName, path] : bfd.outputs)
- if (path) doRedirect(*path);
- },
- }, buildable);
+ }
}
return rewriteStrings(script, rewrites);
@@ -293,6 +328,12 @@ struct Common : InstallableCommand, MixProfile
{
return {"devShell." + settings.thisSystem.get(), "defaultPackage." + settings.thisSystem.get()};
}
+ Strings getDefaultFlakeAttrPathPrefixes() override
+ {
+ auto res = SourceExprCommand::getDefaultFlakeAttrPathPrefixes();
+ res.emplace_front("devShells." + settings.thisSystem.get() + ".");
+ return res;
+ }
StorePath getShellOutPath(ref<Store> store)
{
@@ -308,7 +349,7 @@ struct Common : InstallableCommand, MixProfile
auto & drvPath = *drvs.begin();
- return getDerivationEnvironment(store, drvPath);
+ return getDerivationEnvironment(store, getEvalStore(), drvPath);
}
}
@@ -320,7 +361,9 @@ struct Common : InstallableCommand, MixProfile
updateProfile(shellOutPath);
- return {readEnvironment(strPath), strPath};
+ debug("reading environment file '%s'", strPath);
+
+ return {BuildEnvironment::fromJSON(readFile(store->toRealPath(shellOutPath))), strPath};
}
};
@@ -350,6 +393,12 @@ struct CmdDevelop : Common, MixEnvironment
});
addFlag({
+ .longName = "unpack",
+ .description = "Run the `unpack` phase.",
+ .handler = {&phase, {"unpack"}},
+ });
+
+ addFlag({
.longName = "configure",
.description = "Run the `configure` phase.",
.handler = {&phase, {"configure"}},
@@ -403,7 +452,7 @@ struct CmdDevelop : Common, MixEnvironment
if (verbosity >= lvlDebug)
script += "set -x\n";
- script += fmt("rm -f '%s'\n", rcFilePath);
+ script += fmt("command rm -f '%s'\n", rcFilePath);
if (phase) {
if (!command.empty())
@@ -422,7 +471,7 @@ struct CmdDevelop : Common, MixEnvironment
}
else {
- script += "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc;\n";
+ script = "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc;\n" + script;
if (developSettings.bashPrompt != "")
script += fmt("[ -n \"$PS1\" ] && PS1=%s;\n", shellEscape(developSettings.bashPrompt));
if (developSettings.bashPromptSuffix != "")
@@ -431,8 +480,6 @@ struct CmdDevelop : Common, MixEnvironment
writeFull(rcFileFd.get(), script);
- stopProgressBar();
-
setEnviron();
// prevent garbage collection until shell exits
setenv("NIX_GCROOT", gcroot.data(), 1);
@@ -442,16 +489,20 @@ struct CmdDevelop : Common, MixEnvironment
try {
auto state = getEvalState();
+ auto nixpkgsLockFlags = lockFlags;
+ nixpkgsLockFlags.inputOverrides = {};
+ nixpkgsLockFlags.inputUpdates = {};
+
auto bashInstallable = std::make_shared<InstallableFlake>(
this,
state,
installable->nixpkgsFlakeRef(),
Strings{"bashInteractive"},
Strings{"legacyPackages." + settings.thisSystem.get() + "."},
- lockFlags);
+ nixpkgsLockFlags);
- shell = state->store->printStorePath(
- toStorePath(state->store, Realise::Outputs, OperateOn::Output, bashInstallable)) + "/bin/bash";
+ shell = store->printStorePath(
+ toStorePath(getEvalStore(), store, Realise::Outputs, OperateOn::Output, bashInstallable)) + "/bin/bash";
} catch (Error &) {
ignoreException();
}
@@ -461,16 +512,25 @@ struct CmdDevelop : Common, MixEnvironment
auto args = phase || !command.empty() ? Strings{std::string(baseNameOf(shell)), rcFilePath}
: Strings{std::string(baseNameOf(shell)), "--rcfile", rcFilePath};
- restoreAffinity();
- restoreSignals();
-
- execvp(shell.c_str(), stringsToCharPtrs(args).data());
+ // Need to chdir since phases assume in flake directory
+ if (phase) {
+ // chdir if installable is a flake of type git+file or path
+ auto installableFlake = std::dynamic_pointer_cast<InstallableFlake>(installable);
+ if (installableFlake) {
+ auto sourcePath = installableFlake->getLockedFlake()->flake.resolvedRef.input.getSourcePath();
+ if (sourcePath) {
+ if (chdir(sourcePath->c_str()) == -1) {
+ throw SysError("chdir to '%s' failed", *sourcePath);
+ }
+ }
+ }
+ }
- throw SysError("executing shell '%s'", shell);
+ runProgramInStore(store, shell, args);
}
};
-struct CmdPrintDevEnv : Common
+struct CmdPrintDevEnv : Common, MixJSON
{
std::string description() override
{
@@ -492,7 +552,10 @@ struct CmdPrintDevEnv : Common
stopProgressBar();
- std::cout << makeRcScript(store, buildEnvironment);
+ logger->writeToStdout(
+ json
+ ? buildEnvironment.toJSON()
+ : makeRcScript(store, buildEnvironment));
}
};
diff --git a/src/nix/develop.md b/src/nix/develop.md
index e71d9f8aa..1f214966a 100644
--- a/src/nix/develop.md
+++ b/src/nix/develop.md
@@ -29,6 +29,7 @@ R""(
* Run a particular build phase directly:
```console
+ # nix develop --unpack
# nix develop --configure
# nix develop --build
# nix develop --check
@@ -84,11 +85,20 @@ the flake's `nixConfig` attribute.
# Flake output attributes
-If no flake output attribute is given, `nix run` tries the following
+If no flake output attribute is given, `nix develop` tries the following
flake output attributes:
* `devShell.<system>`
* `defaultPackage.<system>`
+If a flake output *name* is given, `nix develop` tries the following flake
+output attributes:
+
+* `devShells.<system>.<name>`
+
+* `packages.<system>.<name>`
+
+* `legacyPackages.<system>.<name>`
+
)""
diff --git a/src/nix/diff-closures.cc b/src/nix/diff-closures.cc
index 0c7d531c1..734c41e0e 100644
--- a/src/nix/diff-closures.cc
+++ b/src/nix/diff-closures.cc
@@ -131,9 +131,9 @@ struct CmdDiffClosures : SourceExprCommand
void run(ref<Store> store) override
{
auto before = parseInstallable(store, _before);
- auto beforePath = toStorePath(store, Realise::Outputs, operateOn, before);
+ auto beforePath = toStorePath(getEvalStore(), store, Realise::Outputs, operateOn, before);
auto after = parseInstallable(store, _after);
- auto afterPath = toStorePath(store, Realise::Outputs, operateOn, after);
+ auto afterPath = toStorePath(getEvalStore(), store, Realise::Outputs, operateOn, after);
printClosureDiff(store, beforePath, afterPath, "");
}
};
diff --git a/src/nix/edit.cc b/src/nix/edit.cc
index 6472dd27a..fc48db0d7 100644
--- a/src/nix/edit.cc
+++ b/src/nix/edit.cc
@@ -31,7 +31,7 @@ struct CmdEdit : InstallableCommand
auto [v, pos] = installable->toValue(*state);
try {
- pos = findDerivationFilename(*state, *v, installable->what());
+ pos = findPackageFilename(*state, *v, installable->what());
} catch (NoPositionInfo &) {
}
@@ -42,7 +42,8 @@ struct CmdEdit : InstallableCommand
auto args = editorFor(pos);
- restoreSignals();
+ restoreProcessContext();
+
execvp(args.front().c_str(), stringsToCharPtrs(args).data());
std::string command;
diff --git a/src/nix/flake-check.md b/src/nix/flake-check.md
index dc079ba0c..d995d6274 100644
--- a/src/nix/flake-check.md
+++ b/src/nix/flake-check.md
@@ -22,13 +22,18 @@ This command verifies that the flake specified by flake reference
that the derivations specified by the flake's `checks` output can be
built successfully.
+If the `keep-going` option is set to `true`, Nix will keep evaluating as much
+as it can and report the errors as it encounters them. Otherwise it will stop
+at the first error.
+
# Evaluation checks
-This following flake output attributes must be derivations:
+The following flake output attributes must be derivations:
* `checks.`*system*`.`*name*
* `defaultPackage.`*system*`
* `devShell.`*system*`
+* `devShells.`*system*`.`*name*`
* `nixosConfigurations.`*name*`.config.system.build.toplevel
* `packages.`*system*`.`*name*
diff --git a/src/nix/flake-init.md b/src/nix/flake-init.md
index c66154ad5..890038016 100644
--- a/src/nix/flake-init.md
+++ b/src/nix/flake-init.md
@@ -24,7 +24,7 @@ R""(
This command creates a flake in the current directory by copying the
files of a template. It will not overwrite existing files. The default
-template is `templates#defaultTemplate`, but this can be overriden
+template is `templates#defaultTemplate`, but this can be overridden
using `-t`.
# Template definitions
diff --git a/src/nix/flake-list-inputs.md b/src/nix/flake-list-inputs.md
deleted file mode 100644
index 250e13be0..000000000
--- a/src/nix/flake-list-inputs.md
+++ /dev/null
@@ -1,23 +0,0 @@
-R""(
-
-# Examples
-
-* Show the inputs of the `hydra` flake:
-
- ```console
- # nix flake list-inputs github:NixOS/hydra
- github:NixOS/hydra/bde8d81876dfc02143e5070e42c78d8f0d83d6f7
- ├───nix: github:NixOS/nix/79aa7d95183cbe6c0d786965f0dbff414fd1aa67
- │ ├───lowdown-src: github:kristapsdz/lowdown/1705b4a26fbf065d9574dce47a94e8c7c79e052f
- │ └───nixpkgs: github:NixOS/nixpkgs/ad0d20345219790533ebe06571f82ed6b034db31
- └───nixpkgs follows input 'nix/nixpkgs'
- ```
-
-# Description
-
-This command shows the inputs of the flake specified by the flake
-referenced *flake-url*. Since it prints the locked inputs that result
-from generating or updating the lock file, this command essentially
-displays the contents of the flake's lock file in human-readable form.
-
-)""
diff --git a/src/nix/flake-info.md b/src/nix/flake-metadata.md
index fda3171db..5a009409b 100644
--- a/src/nix/flake-info.md
+++ b/src/nix/flake-metadata.md
@@ -5,19 +5,24 @@ R""(
* Show what `nixpkgs` resolves to:
```console
- # nix flake info nixpkgs
- Resolved URL: github:NixOS/nixpkgs
- Locked URL: github:NixOS/nixpkgs/b67ba0bfcc714453cdeb8d713e35751eb8b4c8f4
- Description: A collection of packages for the Nix package manager
- Path: /nix/store/23qapccs6cfmwwrlq8kr41vz5vdmns3r-source
- Revision: b67ba0bfcc714453cdeb8d713e35751eb8b4c8f4
- Last modified: 2020-12-23 12:36:12
+ # nix flake metadata nixpkgs
+ Resolved URL: github:edolstra/dwarffs
+ Locked URL: github:edolstra/dwarffs/f691e2c991e75edb22836f1dbe632c40324215c5
+ Description: A filesystem that fetches DWARF debug info from the Internet on demand
+ Path: /nix/store/769s05vjydmc2lcf6b02az28wsa9ixh1-source
+ Revision: f691e2c991e75edb22836f1dbe632c40324215c5
+ Last modified: 2021-01-21 15:41:26
+ Inputs:
+ ├───nix: github:NixOS/nix/6254b1f5d298ff73127d7b0f0da48f142bdc753c
+ │ ├───lowdown-src: github:kristapsdz/lowdown/1705b4a26fbf065d9574dce47a94e8c7c79e052f
+ │ └───nixpkgs: github:NixOS/nixpkgs/ad0d20345219790533ebe06571f82ed6b034db31
+ └───nixpkgs follows input 'nix/nixpkgs'
```
* Show information about `dwarffs` in JSON format:
```console
- # nix flake info dwarffs --json | jq .
+ # nix flake metadata dwarffs --json | jq .
{
"description": "A filesystem that fetches DWARF debug info from the Internet on demand",
"lastModified": 1597153508,
@@ -29,6 +34,7 @@ R""(
"rev": "d181d714fd36eb06f4992a1997cd5601e26db8f5",
"type": "github"
},
+ "locks": { ... },
"original": {
"id": "dwarffs",
"type": "indirect"
@@ -75,6 +81,9 @@ data. This includes:
time of the commit of the locked flake; for tarball flakes, it's the
most recent timestamp of any file inside the tarball.
+* `Inputs`: The flake inputs with their corresponding lock file
+ entries.
+
With `--json`, the output is a JSON object with the following fields:
* `original` and `originalUrl`: The flake reference specified by the
@@ -96,4 +105,6 @@ With `--json`, the output is a JSON object with the following fields:
* `lastModified`: See `Last modified` above.
+* `locks`: The contents of `flake.lock`.
+
)""
diff --git a/src/nix/flake-show.md b/src/nix/flake-show.md
index 1a42c44a0..e484cf47e 100644
--- a/src/nix/flake-show.md
+++ b/src/nix/flake-show.md
@@ -35,4 +35,7 @@ specified by flake reference *flake-url*. These are the top-level
attributes in the `outputs` of the flake, as well as lower-level
attributes for some standard outputs (e.g. `packages` or `checks`).
+With `--json`, the output is in a JSON representation suitable for automatic
+processing by other tools.
+
)""
diff --git a/src/nix/flake.cc b/src/nix/flake.cc
index 2f0c468a8..7e4d23f6e 100644
--- a/src/nix/flake.cc
+++ b/src/nix/flake.cc
@@ -7,6 +7,7 @@
#include "get-drvs.hh"
#include "store-api.hh"
#include "derivations.hh"
+#include "path-with-outputs.hh"
#include "attr-path.hh"
#include "fetchers.hh"
#include "registry.hh"
@@ -43,12 +44,6 @@ public:
return parseFlakeRef(flakeUrl, absPath(".")); //FIXME
}
- Flake getFlake()
- {
- auto evalState = getEvalState();
- return flake::getFlake(*evalState, getFlakeRef(), lockFlags.useRegistries);
- }
-
LockedFlake lockFlake()
{
return flake::lockFlake(*getEvalState(), getFlakeRef(), lockFlags);
@@ -60,43 +55,6 @@ public:
}
};
-static void printFlakeInfo(const Store & store, const Flake & flake)
-{
- logger->cout("Resolved URL: %s", flake.resolvedRef.to_string());
- logger->cout("Locked URL: %s", flake.lockedRef.to_string());
- if (flake.description)
- logger->cout("Description: %s", *flake.description);
- logger->cout("Path: %s", store.printStorePath(flake.sourceInfo->storePath));
- if (auto rev = flake.lockedRef.input.getRev())
- logger->cout("Revision: %s", rev->to_string(Base16, false));
- if (auto revCount = flake.lockedRef.input.getRevCount())
- logger->cout("Revisions: %s", *revCount);
- if (auto lastModified = flake.lockedRef.input.getLastModified())
- logger->cout("Last modified: %s",
- std::put_time(std::localtime(&*lastModified), "%F %T"));
-}
-
-static nlohmann::json flakeToJSON(const Store & store, const Flake & flake)
-{
- nlohmann::json j;
- if (flake.description)
- j["description"] = *flake.description;
- j["originalUrl"] = flake.originalRef.to_string();
- j["original"] = fetchers::attrsToJSON(flake.originalRef.toAttrs());
- j["resolvedUrl"] = flake.resolvedRef.to_string();
- j["resolved"] = fetchers::attrsToJSON(flake.resolvedRef.toAttrs());
- j["url"] = flake.lockedRef.to_string(); // FIXME: rename to lockedUrl
- j["locked"] = fetchers::attrsToJSON(flake.lockedRef.toAttrs());
- if (auto rev = flake.lockedRef.input.getRev())
- j["revision"] = rev->to_string(Base16, false);
- if (auto revCount = flake.lockedRef.input.getRevCount())
- j["revCount"] = *revCount;
- if (auto lastModified = flake.lockedRef.input.getLastModified())
- j["lastModified"] = *lastModified;
- j["path"] = store.printStorePath(flake.sourceInfo->storePath);
- return j;
-}
-
struct CmdFlakeUpdate : FlakeCommand
{
std::string description() override
@@ -110,6 +68,7 @@ struct CmdFlakeUpdate : FlakeCommand
removeFlag("recreate-lock-file");
removeFlag("update-input");
removeFlag("no-update-lock-file");
+ removeFlag("no-write-lock-file");
}
std::string doc() override
@@ -124,6 +83,8 @@ struct CmdFlakeUpdate : FlakeCommand
settings.tarballTtl = 0;
lockFlags.recreateLockFile = true;
+ lockFlags.writeLockFile = true;
+ lockFlags.applyNixConfig = true;
lockFlake();
}
@@ -136,6 +97,12 @@ struct CmdFlakeLock : FlakeCommand
return "create missing lock file entries";
}
+ CmdFlakeLock()
+ {
+ /* Remove flags that don't make sense. */
+ removeFlag("no-write-lock-file");
+ }
+
std::string doc() override
{
return
@@ -147,6 +114,9 @@ struct CmdFlakeLock : FlakeCommand
{
settings.tarballTtl = 0;
+ lockFlags.writeLockFile = true;
+ lockFlags.applyNixConfig = true;
+
lockFlake();
}
};
@@ -161,58 +131,86 @@ static void enumerateOutputs(EvalState & state, Value & vFlake,
state.forceAttrs(*aOutputs->value);
- for (auto & attr : *aOutputs->value->attrs)
- callback(attr.name, *attr.value, *attr.pos);
-}
+ auto sHydraJobs = state.symbols.create("hydraJobs");
-struct CmdFlakeInfo : FlakeCommand, MixJSON
-{
- std::string description() override
- {
- return "list info about a given flake";
- }
-
- std::string doc() override
- {
- return
- #include "flake-info.md"
- ;
- }
-
- void run(nix::ref<nix::Store> store) override
- {
- auto flake = getFlake();
+ /* Hack: ensure that hydraJobs is evaluated before anything
+ else. This way we can disable IFD for hydraJobs and then enable
+ it for other outputs. */
+ if (auto attr = aOutputs->value->attrs->get(sHydraJobs))
+ callback(attr->name, *attr->value, *attr->pos);
- if (json) {
- auto json = flakeToJSON(*store, flake);
- logger->cout("%s", json.dump());
- } else
- printFlakeInfo(*store, flake);
+ for (auto & attr : *aOutputs->value->attrs) {
+ if (attr.name != sHydraJobs)
+ callback(attr.name, *attr.value, *attr.pos);
}
-};
+}
-struct CmdFlakeListInputs : FlakeCommand, MixJSON
+struct CmdFlakeMetadata : FlakeCommand, MixJSON
{
std::string description() override
{
- return "list flake inputs";
+ return "show flake metadata";
}
std::string doc() override
{
return
- #include "flake-list-inputs.md"
+ #include "flake-metadata.md"
;
}
void run(nix::ref<nix::Store> store) override
{
- auto flake = lockFlake();
+ auto lockedFlake = lockFlake();
+ auto & flake = lockedFlake.flake;
- if (json)
- logger->cout("%s", flake.lockFile.toJSON());
- else {
- logger->cout("%s", flake.flake.lockedRef);
+ if (json) {
+ nlohmann::json j;
+ if (flake.description)
+ j["description"] = *flake.description;
+ j["originalUrl"] = flake.originalRef.to_string();
+ j["original"] = fetchers::attrsToJSON(flake.originalRef.toAttrs());
+ j["resolvedUrl"] = flake.resolvedRef.to_string();
+ j["resolved"] = fetchers::attrsToJSON(flake.resolvedRef.toAttrs());
+ j["url"] = flake.lockedRef.to_string(); // FIXME: rename to lockedUrl
+ j["locked"] = fetchers::attrsToJSON(flake.lockedRef.toAttrs());
+ if (auto rev = flake.lockedRef.input.getRev())
+ j["revision"] = rev->to_string(Base16, false);
+ if (auto revCount = flake.lockedRef.input.getRevCount())
+ j["revCount"] = *revCount;
+ if (auto lastModified = flake.lockedRef.input.getLastModified())
+ j["lastModified"] = *lastModified;
+ j["path"] = store->printStorePath(flake.sourceInfo->storePath);
+ j["locks"] = lockedFlake.lockFile.toJSON();
+ logger->cout("%s", j.dump());
+ } else {
+ logger->cout(
+ ANSI_BOLD "Resolved URL:" ANSI_NORMAL " %s",
+ flake.resolvedRef.to_string());
+ logger->cout(
+ ANSI_BOLD "Locked URL:" ANSI_NORMAL " %s",
+ flake.lockedRef.to_string());
+ if (flake.description)
+ logger->cout(
+ ANSI_BOLD "Description:" ANSI_NORMAL " %s",
+ *flake.description);
+ logger->cout(
+ ANSI_BOLD "Path:" ANSI_NORMAL " %s",
+ store->printStorePath(flake.sourceInfo->storePath));
+ if (auto rev = flake.lockedRef.input.getRev())
+ logger->cout(
+ ANSI_BOLD "Revision:" ANSI_NORMAL " %s",
+ rev->to_string(Base16, false));
+ if (auto revCount = flake.lockedRef.input.getRevCount())
+ logger->cout(
+ ANSI_BOLD "Revisions:" ANSI_NORMAL " %s",
+ *revCount);
+ if (auto lastModified = flake.lockedRef.input.getLastModified())
+ logger->cout(
+ ANSI_BOLD "Last modified:" ANSI_NORMAL " %s",
+ std::put_time(std::localtime(&*lastModified), "%F %T"));
+
+ logger->cout(ANSI_BOLD "Inputs:" ANSI_NORMAL);
std::unordered_set<std::shared_ptr<Node>> visited;
@@ -226,7 +224,7 @@ struct CmdFlakeListInputs : FlakeCommand, MixJSON
if (auto lockedNode = std::get_if<0>(&input.second)) {
logger->cout("%s" ANSI_BOLD "%s" ANSI_NORMAL ": %s",
prefix + (last ? treeLast : treeConn), input.first,
- *lockedNode ? (*lockedNode)->lockedRef : flake.flake.lockedRef);
+ *lockedNode ? (*lockedNode)->lockedRef : flake.lockedRef);
bool firstVisit = visited.insert(*lockedNode).second;
@@ -239,12 +237,21 @@ struct CmdFlakeListInputs : FlakeCommand, MixJSON
}
};
- visited.insert(flake.lockFile.root);
- recurse(*flake.lockFile.root, "");
+ visited.insert(lockedFlake.lockFile.root);
+ recurse(*lockedFlake.lockFile.root, "");
}
}
};
+struct CmdFlakeInfo : CmdFlakeMetadata
+{
+ void run(nix::ref<nix::Store> store) override
+ {
+ warn("'nix flake info' is a deprecated alias for 'nix flake metadata'");
+ CmdFlakeMetadata::run(store);
+ }
+};
+
struct CmdFlakeCheck : FlakeCommand
{
bool build = true;
@@ -272,33 +279,53 @@ struct CmdFlakeCheck : FlakeCommand
void run(nix::ref<nix::Store> store) override
{
- settings.readOnlyMode = !build;
+ if (!build) {
+ settings.readOnlyMode = true;
+ evalSettings.enableImportFromDerivation.setDefault(false);
+ }
auto state = getEvalState();
+
+ lockFlags.applyNixConfig = true;
auto flake = lockFlake();
+ bool hasErrors = false;
+ auto reportError = [&](const Error & e) {
+ try {
+ throw e;
+ } catch (Error & e) {
+ if (settings.keepGoing) {
+ ignoreException();
+ hasErrors = true;
+ }
+ else
+ throw;
+ }
+ };
+
// FIXME: rewrite to use EvalCache.
auto checkSystemName = [&](const std::string & system, const Pos & pos) {
// FIXME: what's the format of "system"?
if (system.find('-') == std::string::npos)
- throw Error("'%s' is not a valid system type, at %s", system, pos);
+ reportError(Error("'%s' is not a valid system type, at %s", system, pos));
};
- auto checkDerivation = [&](const std::string & attrPath, Value & v, const Pos & pos) {
+ auto checkDerivation = [&](const std::string & attrPath, Value & v, const Pos & pos) -> std::optional<StorePath> {
try {
auto drvInfo = getDerivation(*state, v, false);
if (!drvInfo)
throw Error("flake attribute '%s' is not a derivation", attrPath);
// FIXME: check meta attributes
- return store->parseStorePath(drvInfo->queryDrvPath());
+ return std::make_optional(store->parseStorePath(drvInfo->queryDrvPath()));
} catch (Error & e) {
e.addTrace(pos, hintfmt("while checking the derivation '%s'", attrPath));
- throw;
+ reportError(e);
}
+ return std::nullopt;
};
- std::vector<StorePathWithOutputs> drvPaths;
+ std::vector<DerivedPath> drvPaths;
auto checkApp = [&](const std::string & attrPath, Value & v, const Pos & pos) {
try {
@@ -312,23 +339,23 @@ struct CmdFlakeCheck : FlakeCommand
#endif
} catch (Error & e) {
e.addTrace(pos, hintfmt("while checking the app definition '%s'", attrPath));
- throw;
+ reportError(e);
}
};
auto checkOverlay = [&](const std::string & attrPath, Value & v, const Pos & pos) {
try {
state->forceValue(v, pos);
- if (!v.isLambda() || v.lambda.fun->matchAttrs || std::string(v.lambda.fun->arg) != "final")
+ if (!v.isLambda() || v.lambda.fun->hasFormals() || std::string(v.lambda.fun->arg) != "final")
throw Error("overlay does not take an argument named 'final'");
auto body = dynamic_cast<ExprLambda *>(v.lambda.fun->body);
- if (!body || body->matchAttrs || std::string(body->arg) != "prev")
+ if (!body || body->hasFormals() || std::string(body->arg) != "prev")
throw Error("overlay does not take an argument named 'prev'");
// FIXME: if we have a 'nixpkgs' input, use it to
// evaluate the overlay.
} catch (Error & e) {
e.addTrace(pos, hintfmt("while checking the overlay '%s'", attrPath));
- throw;
+ reportError(e);
}
};
@@ -336,7 +363,7 @@ struct CmdFlakeCheck : FlakeCommand
try {
state->forceValue(v, pos);
if (v.isLambda()) {
- if (!v.lambda.fun->matchAttrs || !v.lambda.fun->formals->ellipsis)
+ if (!v.lambda.fun->hasFormals() || !v.lambda.fun->formals->ellipsis)
throw Error("module must match an open attribute set ('{ config, ... }')");
} else if (v.type() == nAttrs) {
for (auto & attr : *v.attrs)
@@ -352,7 +379,7 @@ struct CmdFlakeCheck : FlakeCommand
// check the module.
} catch (Error & e) {
e.addTrace(pos, hintfmt("while checking the NixOS module '%s'", attrPath));
- throw;
+ reportError(e);
}
};
@@ -367,14 +394,18 @@ struct CmdFlakeCheck : FlakeCommand
for (auto & attr : *v.attrs) {
state->forceAttrs(*attr.value, *attr.pos);
- if (!state->isDerivation(*attr.value))
- checkHydraJobs(attrPath + "." + (std::string) attr.name,
- *attr.value, *attr.pos);
+ auto attrPath2 = attrPath + "." + (std::string) attr.name;
+ if (state->isDerivation(*attr.value)) {
+ Activity act(*logger, lvlChatty, actUnknown,
+ fmt("checking Hydra job '%s'", attrPath2));
+ checkDerivation(attrPath2, *attr.value, *attr.pos);
+ } else
+ checkHydraJobs(attrPath2, *attr.value, *attr.pos);
}
} catch (Error & e) {
e.addTrace(pos, hintfmt("while checking the Hydra jobset '%s'", attrPath));
- throw;
+ reportError(e);
}
};
@@ -389,7 +420,7 @@ struct CmdFlakeCheck : FlakeCommand
throw Error("attribute 'config.system.build.toplevel' is not a derivation");
} catch (Error & e) {
e.addTrace(pos, hintfmt("while checking the NixOS configuration '%s'", attrPath));
- throw;
+ reportError(e);
}
};
@@ -423,7 +454,7 @@ struct CmdFlakeCheck : FlakeCommand
}
} catch (Error & e) {
e.addTrace(pos, hintfmt("while checking the template '%s'", attrPath));
- throw;
+ reportError(e);
}
};
@@ -433,12 +464,12 @@ struct CmdFlakeCheck : FlakeCommand
if (!v.isLambda())
throw Error("bundler must be a function");
if (!v.lambda.fun->formals ||
- v.lambda.fun->formals->argNames.find(state->symbols.create("program")) == v.lambda.fun->formals->argNames.end() ||
- v.lambda.fun->formals->argNames.find(state->symbols.create("system")) == v.lambda.fun->formals->argNames.end())
+ !v.lambda.fun->formals->argNames.count(state->symbols.create("program")) ||
+ !v.lambda.fun->formals->argNames.count(state->symbols.create("system")))
throw Error("bundler must take formal arguments 'program' and 'system'");
} catch (Error & e) {
e.addTrace(pos, hintfmt("while checking the template '%s'", attrPath));
- throw;
+ reportError(e);
}
};
@@ -455,6 +486,8 @@ struct CmdFlakeCheck : FlakeCommand
fmt("checking flake output '%s'", name));
try {
+ evalSettings.enableImportFromDerivation.setDefault(name != "hydraJobs");
+
state->forceValue(vOutput, pos);
if (name == "checks") {
@@ -466,13 +499,13 @@ struct CmdFlakeCheck : FlakeCommand
auto drvPath = checkDerivation(
fmt("%s.%s.%s", name, attr.name, attr2.name),
*attr2.value, *attr2.pos);
- if ((std::string) attr.name == settings.thisSystem.get())
- drvPaths.push_back({drvPath});
+ if (drvPath && (std::string) attr.name == settings.thisSystem.get())
+ drvPaths.push_back(DerivedPath::Built{*drvPath});
}
}
}
- else if (name == "packages") {
+ else if (name == "packages" || name == "devShells") {
state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos);
@@ -579,7 +612,7 @@ struct CmdFlakeCheck : FlakeCommand
} catch (Error & e) {
e.addTrace(pos, hintfmt("while checking flake output '%s'", name));
- throw;
+ reportError(e);
}
});
}
@@ -588,6 +621,8 @@ struct CmdFlakeCheck : FlakeCommand
Activity act(*logger, lvlInfo, actUnknown, "running flake checks");
store->buildPaths(drvPaths);
}
+ if (hasErrors)
+ throw Error("some errors were encountered during the evaluation");
}
};
@@ -825,12 +860,12 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun
if (!dryRun && !dstUri.empty()) {
ref<Store> dstStore = dstUri.empty() ? openStore() : openStore(dstUri);
- copyPaths(store, dstStore, sources);
+ copyPaths(*store, *dstStore, sources);
}
}
};
-struct CmdFlakeShow : FlakeCommand
+struct CmdFlakeShow : FlakeCommand, MixJSON
{
bool showLegacy = false;
@@ -857,51 +892,69 @@ struct CmdFlakeShow : FlakeCommand
void run(nix::ref<nix::Store> store) override
{
+ evalSettings.enableImportFromDerivation.setDefault(false);
+
auto state = getEvalState();
auto flake = std::make_shared<LockedFlake>(lockFlake());
- std::function<void(eval_cache::AttrCursor & visitor, const std::vector<Symbol> & attrPath, const std::string & headerPrefix, const std::string & nextPrefix)> visit;
-
- visit = [&](eval_cache::AttrCursor & visitor, const std::vector<Symbol> & attrPath, const std::string & headerPrefix, const std::string & nextPrefix)
+ std::function<nlohmann::json(
+ eval_cache::AttrCursor & visitor,
+ const std::vector<Symbol> & attrPath,
+ const std::string & headerPrefix,
+ const std::string & nextPrefix)> visit;
+
+ visit = [&](
+ eval_cache::AttrCursor & visitor,
+ const std::vector<Symbol> & attrPath,
+ const std::string & headerPrefix,
+ const std::string & nextPrefix)
+ -> nlohmann::json
{
+ auto j = nlohmann::json::object();
+
Activity act(*logger, lvlInfo, actUnknown,
fmt("evaluating '%s'", concatStringsSep(".", attrPath)));
try {
auto recurse = [&]()
{
- logger->cout("%s", headerPrefix);
+ if (!json)
+ logger->cout("%s", headerPrefix);
auto attrs = visitor.getAttrs();
for (const auto & [i, attr] : enumerate(attrs)) {
bool last = i + 1 == attrs.size();
auto visitor2 = visitor.getAttr(attr);
auto attrPath2(attrPath);
attrPath2.push_back(attr);
- visit(*visitor2, attrPath2,
+ auto j2 = visit(*visitor2, attrPath2,
fmt(ANSI_GREEN "%s%s" ANSI_NORMAL ANSI_BOLD "%s" ANSI_NORMAL, nextPrefix, last ? treeLast : treeConn, attr),
nextPrefix + (last ? treeNull : treeLine));
+ if (json) j.emplace(attr, std::move(j2));
}
};
auto showDerivation = [&]()
{
auto name = visitor.getAttr(state->sName)->getString();
-
- /*
- std::string description;
-
- if (auto aMeta = visitor.maybeGetAttr("meta")) {
- if (auto aDescription = aMeta->maybeGetAttr("description"))
- description = aDescription->getString();
+ if (json) {
+ std::optional<std::string> description;
+ if (auto aMeta = visitor.maybeGetAttr("meta")) {
+ if (auto aDescription = aMeta->maybeGetAttr("description"))
+ description = aDescription->getString();
+ }
+ j.emplace("type", "derivation");
+ j.emplace("name", name);
+ if (description)
+ j.emplace("description", *description);
+ } else {
+ logger->cout("%s: %s '%s'",
+ headerPrefix,
+ attrPath.size() == 2 && attrPath[0] == "devShell" ? "development environment" :
+ attrPath.size() >= 2 && attrPath[0] == "devShells" ? "development environment" :
+ attrPath.size() == 3 && attrPath[0] == "checks" ? "derivation" :
+ attrPath.size() >= 1 && attrPath[0] == "hydraJobs" ? "derivation" :
+ "package",
+ name);
}
- */
-
- logger->cout("%s: %s '%s'",
- headerPrefix,
- attrPath.size() == 2 && attrPath[0] == "devShell" ? "development environment" :
- attrPath.size() == 3 && attrPath[0] == "checks" ? "derivation" :
- attrPath.size() >= 1 && attrPath[0] == "hydraJobs" ? "derivation" :
- "package",
- name);
};
if (attrPath.size() == 0
@@ -916,6 +969,7 @@ struct CmdFlakeShow : FlakeCommand
|| ((attrPath.size() == 1 || attrPath.size() == 2)
&& (attrPath[0] == "checks"
|| attrPath[0] == "packages"
+ || attrPath[0] == "devShells"
|| attrPath[0] == "apps"))
)
{
@@ -924,7 +978,7 @@ struct CmdFlakeShow : FlakeCommand
else if (
(attrPath.size() == 2 && (attrPath[0] == "defaultPackage" || attrPath[0] == "devShell"))
- || (attrPath.size() == 3 && (attrPath[0] == "checks" || attrPath[0] == "packages"))
+ || (attrPath.size() == 3 && (attrPath[0] == "checks" || attrPath[0] == "packages" || attrPath[0] == "devShells"))
)
{
if (visitor.isDerivation())
@@ -944,7 +998,7 @@ struct CmdFlakeShow : FlakeCommand
if (attrPath.size() == 1)
recurse();
else if (!showLegacy)
- logger->cout("%s: " ANSI_YELLOW "omitted" ANSI_NORMAL " (use '--legacy' to show)", headerPrefix);
+ logger->warn(fmt("%s: " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--legacy' to show)", headerPrefix));
else {
if (visitor.isDerivation())
showDerivation();
@@ -961,7 +1015,11 @@ struct CmdFlakeShow : FlakeCommand
auto aType = visitor.maybeGetAttr("type");
if (!aType || aType->getString() != "app")
throw EvalError("not an app definition");
- logger->cout("%s: app", headerPrefix);
+ if (json) {
+ j.emplace("type", "app");
+ } else {
+ logger->cout("%s: app", headerPrefix);
+ }
}
else if (
@@ -969,27 +1027,40 @@ struct CmdFlakeShow : FlakeCommand
(attrPath.size() == 2 && attrPath[0] == "templates"))
{
auto description = visitor.getAttr("description")->getString();
- logger->cout("%s: template: " ANSI_BOLD "%s" ANSI_NORMAL, headerPrefix, description);
+ if (json) {
+ j.emplace("type", "template");
+ j.emplace("description", description);
+ } else {
+ logger->cout("%s: template: " ANSI_BOLD "%s" ANSI_NORMAL, headerPrefix, description);
+ }
}
else {
- logger->cout("%s: %s",
- headerPrefix,
+ auto [type, description] =
(attrPath.size() == 1 && attrPath[0] == "overlay")
- || (attrPath.size() == 2 && attrPath[0] == "overlays") ? "Nixpkgs overlay" :
- attrPath.size() == 2 && attrPath[0] == "nixosConfigurations" ? "NixOS configuration" :
- attrPath.size() == 2 && attrPath[0] == "nixosModules" ? "NixOS module" :
- ANSI_YELLOW "unknown" ANSI_NORMAL);
+ || (attrPath.size() == 2 && attrPath[0] == "overlays") ? std::make_pair("nixpkgs-overlay", "Nixpkgs overlay") :
+ attrPath.size() == 2 && attrPath[0] == "nixosConfigurations" ? std::make_pair("nixos-configuration", "NixOS configuration") :
+ attrPath.size() == 2 && attrPath[0] == "nixosModules" ? std::make_pair("nixos-module", "NixOS module") :
+ std::make_pair("unknown", "unknown");
+ if (json) {
+ j.emplace("type", type);
+ } else {
+ logger->cout("%s: " ANSI_WARNING "%s" ANSI_NORMAL, headerPrefix, description);
+ }
}
} catch (EvalError & e) {
if (!(attrPath.size() > 0 && attrPath[0] == "legacyPackages"))
throw;
}
+
+ return j;
};
auto cache = openEvalCache(*state, flake);
- visit(*cache->getRoot(), {}, fmt(ANSI_BOLD "%s" ANSI_NORMAL, flake->flake.lockedRef), "");
+ auto j = visit(*cache->getRoot(), {}, fmt(ANSI_BOLD "%s" ANSI_NORMAL, flake->flake.lockedRef), "");
+ if (json)
+ logger->cout("%s", j.dump());
}
};
@@ -1038,8 +1109,8 @@ struct CmdFlake : NixMultiCommand
: MultiCommand({
{"update", []() { return make_ref<CmdFlakeUpdate>(); }},
{"lock", []() { return make_ref<CmdFlakeLock>(); }},
+ {"metadata", []() { return make_ref<CmdFlakeMetadata>(); }},
{"info", []() { return make_ref<CmdFlakeInfo>(); }},
- {"list-inputs", []() { return make_ref<CmdFlakeListInputs>(); }},
{"check", []() { return make_ref<CmdFlakeCheck>(); }},
{"init", []() { return make_ref<CmdFlakeInit>(); }},
{"new", []() { return make_ref<CmdFlakeNew>(); }},
diff --git a/src/nix/flake.md b/src/nix/flake.md
index 440c45dd1..3b5812a0a 100644
--- a/src/nix/flake.md
+++ b/src/nix/flake.md
@@ -70,7 +70,7 @@ Here are some examples of flake references in their URL-like representation:
* `/home/alice/src/patchelf`: A flake in some other directory.
* `nixpkgs`: The `nixpkgs` entry in the flake registry.
* `nixpkgs/a3a3dda3bacf61e8a39258a0ed9c924eeca8e293`: The `nixpkgs`
- entry in the flake registry, with its Git revision overriden to a
+ entry in the flake registry, with its Git revision overridden to a
specific value.
* `github:NixOS/nixpkgs`: The `master` branch of the `NixOS/nixpkgs`
repository on GitHub.
@@ -186,8 +186,8 @@ Currently the `type` attribute can be one of the following:
attribute `url`.
In URL form, the schema must be `http://`, `https://` or `file://`
- URLs and the extension must be `.zip`, `.tar`, `.tar.gz`, `.tar.xz`
- or `.tar.bz2`.
+ URLs and the extension must be `.zip`, `.tar`, `.tar.gz`, `.tar.xz`,
+ `.tar.bz2` or `.tar.zst`.
* `github`: A more efficient way to fetch repositories from
GitHub. The following attributes are required:
@@ -225,7 +225,7 @@ Currently the `type` attribute can be one of the following:
[flake:]<flake-id>(/<rev-or-ref>(/rev)?)?
```
- These perform a lookup of `<flake-id>` in the flake registry. or
+ These perform a lookup of `<flake-id>` in the flake registry. For
example, `nixpkgs` and `nixpkgs/release-20.09` are indirect flake
references. The specified `rev` and/or `ref` are merged with the
entry in the registry; see [nix registry](./nix3-registry.md) for
@@ -377,7 +377,7 @@ outputs = { self, nixpkgs, grcov }: {
};
```
-Transitive inputs can be overriden from a `flake.nix` file. For
+Transitive inputs can be overridden from a `flake.nix` file. For
example, the following overrides the `nixpkgs` input of the `nixops`
input:
@@ -395,7 +395,7 @@ the `nixpkgs` input of the top-level flake to be equal to the
`nixpkgs` input of the `dwarffs` input of the top-level flake:
```nix
-inputs.nixops.follows = "dwarffs/nixpkgs";
+inputs.nixpkgs.follows = "dwarffs/nixpkgs";
```
The value of the `follows` attribute is a `/`-separated sequence of
diff --git a/src/nix/get-env.sh b/src/nix/get-env.sh
index 091c0f573..42c806450 100644
--- a/src/nix/get-env.sh
+++ b/src/nix/get-env.sh
@@ -8,12 +8,123 @@ if [[ -n $stdenv ]]; then
source $stdenv/setup
fi
-for __output in $outputs; do
+# Better to use compgen, but stdenv bash doesn't have it.
+__vars="$(declare -p)"
+__functions="$(declare -F)"
+
+__dumpEnv() {
+ printf '{\n'
+
+ printf ' "bashFunctions": {\n'
+ local __first=1
+ while read __line; do
+ if ! [[ $__line =~ ^declare\ -f\ (.*) ]]; then continue; fi
+ __fun_name="${BASH_REMATCH[1]}"
+ __fun_body="$(type $__fun_name)"
+ if [[ $__fun_body =~ \{(.*)\} ]]; then
+ if [[ -z $__first ]]; then printf ',\n'; else __first=; fi
+ __fun_body="${BASH_REMATCH[1]}"
+ printf " "
+ __escapeString "$__fun_name"
+ printf ':'
+ __escapeString "$__fun_body"
+ else
+ printf "Cannot parse definition of function '%s'.\n" "$__fun_name" >&2
+ return 1
+ fi
+ done < <(printf "%s\n" "$__functions")
+ printf '\n },\n'
+
+ printf ' "variables": {\n'
+ local __first=1
+ while read __line; do
+ if ! [[ $__line =~ ^declare\ (-[^ ])\ ([^=]*) ]]; then continue; fi
+ local type="${BASH_REMATCH[1]}"
+ local __var_name="${BASH_REMATCH[2]}"
+
+ if [[ $__var_name =~ ^BASH_ || \
+ $__var_name = _ || \
+ $__var_name = DIRSTACK || \
+ $__var_name = EUID || \
+ $__var_name = FUNCNAME || \
+ $__var_name = HISTCMD || \
+ $__var_name = HOSTNAME || \
+ $__var_name = GROUPS || \
+ $__var_name = PIPESTATUS || \
+ $__var_name = PWD || \
+ $__var_name = RANDOM || \
+ $__var_name = SHLVL || \
+ $__var_name = SECONDS \
+ ]]; then continue; fi
+
+ if [[ -z $__first ]]; then printf ',\n'; else __first=; fi
+
+ printf " "
+ __escapeString "$__var_name"
+ printf ': {'
+
+ # FIXME: handle -i, -r, -n.
+ if [[ $type == -x ]]; then
+ printf '"type": "exported", "value": '
+ __escapeString "${!__var_name}"
+ elif [[ $type == -- ]]; then
+ printf '"type": "var", "value": '
+ __escapeString "${!__var_name}"
+ elif [[ $type == -a ]]; then
+ printf '"type": "array", "value": ['
+ local __first2=1
+ __var_name="$__var_name[@]"
+ for __i in "${!__var_name}"; do
+ if [[ -z $__first2 ]]; then printf ', '; else __first2=; fi
+ __escapeString "$__i"
+ printf ' '
+ done
+ printf ']'
+ elif [[ $type == -A ]]; then
+ printf '"type": "associative", "value": {\n'
+ local __first2=1
+ declare -n __var_name2="$__var_name"
+ for __i in "${!__var_name2[@]}"; do
+ if [[ -z $__first2 ]]; then printf ',\n'; else __first2=; fi
+ printf " "
+ __escapeString "$__i"
+ printf ": "
+ __escapeString "${__var_name2[$__i]}"
+ done
+ printf '\n }'
+ else
+ printf '"type": "unknown"'
+ fi
+
+ printf "}"
+ done < <(printf "%s\n" "$__vars")
+ printf '\n }\n}'
+}
+
+__escapeString() {
+ local __s="$1"
+ __s="${__s//\\/\\\\}"
+ __s="${__s//\"/\\\"}"
+ __s="${__s//$'\n'/\\n}"
+ __s="${__s//$'\r'/\\r}"
+ __s="${__s//$'\t'/\\t}"
+ printf '"%s"' "$__s"
+}
+
+# In case of `__structuredAttrs = true;` the list of outputs is an associative
+# array with a format like `outname => /nix/store/hash-drvname-outname`, so `__olist`
+# must contain the array's keys (hence `${!...[@]}`) in this case.
+if [ -e .attrs.sh ]; then
+ __olist="${!outputs[@]}"
+else
+ __olist=$outputs
+fi
+
+for __output in $__olist; do
if [[ -z $__done ]]; then
- export > ${!__output}
- set >> ${!__output}
+ __dumpEnv > ${!__output}
__done=1
else
- echo -n >> ${!__output}
+ echo -n >> "${!__output}"
fi
done
diff --git a/src/nix/local.mk b/src/nix/local.mk
index 83b6dd08b..e4ec7634d 100644
--- a/src/nix/local.mk
+++ b/src/nix/local.mk
@@ -14,7 +14,7 @@ nix_SOURCES := \
$(wildcard src/nix-instantiate/*.cc) \
$(wildcard src/nix-store/*.cc) \
-nix_CXXFLAGS += -I src/libutil -I src/libstore -I src/libfetchers -I src/libexpr -I src/libmain -I src/libcmd
+nix_CXXFLAGS += -I src/libutil -I src/libstore -I src/libfetchers -I src/libexpr -I src/libmain -I src/libcmd -I doc/manual
nix_LIBS = libexpr libmain libfetchers libstore libutil libcmd
@@ -30,3 +30,5 @@ src/nix-env/user-env.cc: src/nix-env/buildenv.nix.gen.hh
src/nix/develop.cc: src/nix/get-env.sh.gen.hh
src/nix-channel/nix-channel.cc: src/nix-channel/unpack-channel.nix.gen.hh
+
+src/nix/main.cc: doc/manual/generate-manpage.nix.gen.hh doc/manual/utils.nix.gen.hh
diff --git a/src/nix/log.cc b/src/nix/log.cc
index 67d3742d6..fd3c1d787 100644
--- a/src/nix/log.cc
+++ b/src/nix/log.cc
@@ -30,18 +30,18 @@ struct CmdLog : InstallableCommand
subs.push_front(store);
- auto b = installable->toBuildable();
+ auto b = installable->toDerivedPath();
RunPager pager;
for (auto & sub : subs) {
auto log = std::visit(overloaded {
- [&](BuildableOpaque bo) {
+ [&](const DerivedPath::Opaque & bo) {
return sub->getBuildLog(bo.path);
},
- [&](BuildableFromDrv bfd) {
+ [&](const DerivedPath::Built & bfd) {
return sub->getBuildLog(bfd.drvPath);
},
- }, b);
+ }, b.raw());
if (!log) continue;
stopProgressBar();
printInfo("got build log for '%s' from '%s'", installable->what(), sub->getUri());
diff --git a/src/nix/main.cc b/src/nix/main.cc
index 06e221682..2c3976689 100644
--- a/src/nix/main.cc
+++ b/src/nix/main.cc
@@ -10,6 +10,7 @@
#include "filetransfer.hh"
#include "finally.hh"
#include "loggers.hh"
+#include "markdown.hh"
#include <sys/types.h>
#include <sys/socket.h>
@@ -17,10 +18,6 @@
#include <netdb.h>
#include <netinet/in.h>
-#if __linux__
-#include <sys/resource.h>
-#endif
-
#include <nlohmann/json.hpp>
extern std::string chrootHelperName;
@@ -167,9 +164,46 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
}
};
-static void showHelp(std::vector<std::string> subcommand)
+/* Render the help for the specified subcommand to stdout using
+ lowdown. */
+static void showHelp(std::vector<std::string> subcommand, MultiCommand & toplevel)
{
- showManPage(subcommand.empty() ? "nix" : fmt("nix3-%s", concatStringsSep("-", subcommand)));
+ auto mdName = subcommand.empty() ? "nix" : fmt("nix3-%s", concatStringsSep("-", subcommand));
+
+ evalSettings.restrictEval = false;
+ evalSettings.pureEval = false;
+ EvalState state({}, openStore("dummy://"));
+
+ auto vGenerateManpage = state.allocValue();
+ state.eval(state.parseExprFromString(
+ #include "generate-manpage.nix.gen.hh"
+ , "/"), *vGenerateManpage);
+
+ auto vUtils = state.allocValue();
+ state.cacheFile(
+ "/utils.nix", "/utils.nix",
+ state.parseExprFromString(
+ #include "utils.nix.gen.hh"
+ , "/"),
+ *vUtils);
+
+ auto vArgs = state.allocValue();
+ state.mkAttrs(*vArgs, 16);
+ auto vJson = state.allocAttr(*vArgs, state.symbols.create("command"));
+ mkString(*vJson, toplevel.toJSON().dump());
+ vArgs->attrs->sort();
+
+ auto vRes = state.allocValue();
+ state.callFunction(*vGenerateManpage, *vArgs, *vRes, noPos);
+
+ auto attr = vRes->attrs->get(state.symbols.create(mdName + ".md"));
+ if (!attr)
+ throw UsageError("Nix has no subcommand '%s'", concatStringsSep("", subcommand));
+
+ auto markdown = state.forceString(*attr->value);
+
+ RunPager pager;
+ std::cout << renderMarkdownToTerminal(markdown) << "\n";
}
struct CmdHelp : Command
@@ -198,7 +232,10 @@ struct CmdHelp : Command
void run() override
{
- showHelp(subcommand);
+ assert(parent);
+ MultiCommand * toplevel = parent;
+ while (toplevel->parent) toplevel = toplevel->parent;
+ showHelp(subcommand, *toplevel);
}
};
@@ -281,7 +318,7 @@ void mainWrapped(int argc, char * * argv)
} else
break;
}
- showHelp(subcommand);
+ showHelp(subcommand, args);
return;
} catch (UsageError &) {
if (!completions) throw;
@@ -309,13 +346,13 @@ void mainWrapped(int argc, char * * argv)
if (!args.useNet) {
// FIXME: should check for command line overrides only.
- if (!settings.useSubstitutes.overriden)
+ if (!settings.useSubstitutes.overridden)
settings.useSubstitutes = false;
- if (!settings.tarballTtl.overriden)
+ if (!settings.tarballTtl.overridden)
settings.tarballTtl = std::numeric_limits<unsigned int>::max();
- if (!fileTransferSettings.tries.overriden)
+ if (!fileTransferSettings.tries.overridden)
fileTransferSettings.tries = 0;
- if (!fileTransferSettings.connectTimeout.overriden)
+ if (!fileTransferSettings.connectTimeout.overridden)
fileTransferSettings.connectTimeout = 1;
}
@@ -335,14 +372,7 @@ int main(int argc, char * * argv)
{
// Increase the default stack size for the evaluator and for
// libstdc++'s std::regex.
- #if __linux__
- rlim_t stackSize = 64 * 1024 * 1024;
- struct rlimit limit;
- if (getrlimit(RLIMIT_STACK, &limit) == 0 && limit.rlim_cur < stackSize) {
- limit.rlim_cur = stackSize;
- setrlimit(RLIMIT_STACK, &limit);
- }
- #endif
+ nix::setStackSize(64 * 1024 * 1024);
return nix::handleExceptions(argv[0], [&]() {
nix::mainWrapped(argc, argv);
diff --git a/src/nix/make-content-addressable.cc b/src/nix/make-content-addressable.cc
index f5bdc7e65..12f303a10 100644
--- a/src/nix/make-content-addressable.cc
+++ b/src/nix/make-content-addressable.cc
@@ -25,7 +25,7 @@ struct CmdMakeContentAddressable : StorePathsCommand, MixJSON
;
}
- void run(ref<Store> store, StorePaths storePaths) override
+ void run(ref<Store> store, StorePaths && storePaths) override
{
auto paths = store->topoSortPaths(StorePathSet(storePaths.begin(), storePaths.end()));
diff --git a/src/nix/path-info.cc b/src/nix/path-info.cc
index 518cd5568..3743d7504 100644
--- a/src/nix/path-info.cc
+++ b/src/nix/path-info.cc
@@ -79,7 +79,7 @@ struct CmdPathInfo : StorePathsCommand, MixJSON
std::cout << fmt("\t%6.1f%c", res, idents.at(power));
}
- void run(ref<Store> store, StorePaths storePaths) override
+ void run(ref<Store> store, StorePaths && storePaths) override
{
size_t pathLen = 0;
for (auto & storePath : storePaths)
diff --git a/src/nix/path-info.md b/src/nix/path-info.md
index 76a83e39d..7a1714ba4 100644
--- a/src/nix/path-info.md
+++ b/src/nix/path-info.md
@@ -82,7 +82,7 @@ This command shows information about the store paths produced by
By default, this command only prints the store paths. You can get
additional information by passing flags such as `--closure-size`,
---size`, `--sigs` or `--json`.
+`--size`, `--sigs` or `--json`.
> **Warning**
>
diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc
index b7da3ea5a..768d37595 100644
--- a/src/nix/prefetch.cc
+++ b/src/nix/prefetch.cc
@@ -199,26 +199,24 @@ static int main_nix_prefetch_url(int argc, char * * argv)
state->forceAttrs(v);
/* Extract the URL. */
- auto attr = v.attrs->find(state->symbols.create("urls"));
- if (attr == v.attrs->end())
- throw Error("attribute set does not contain a 'urls' attribute");
- state->forceList(*attr->value);
- if (attr->value->listSize() < 1)
+ auto & attr = v.attrs->need(state->symbols.create("urls"));
+ state->forceList(*attr.value);
+ if (attr.value->listSize() < 1)
throw Error("'urls' list is empty");
- url = state->forceString(*attr->value->listElems()[0]);
+ url = state->forceString(*attr.value->listElems()[0]);
/* Extract the hash mode. */
- attr = v.attrs->find(state->symbols.create("outputHashMode"));
- if (attr == v.attrs->end())
+ auto attr2 = v.attrs->get(state->symbols.create("outputHashMode"));
+ if (!attr2)
printInfo("warning: this does not look like a fetchurl call");
else
- unpack = state->forceString(*attr->value) == "recursive";
+ unpack = state->forceString(*attr2->value) == "recursive";
/* Extract the name. */
if (!name) {
- attr = v.attrs->find(state->symbols.create("name"));
- if (attr != v.attrs->end())
- name = state->forceString(*attr->value);
+ auto attr3 = v.attrs->get(state->symbols.create("name"));
+ if (!attr3)
+ name = state->forceString(*attr3->value);
}
}
@@ -283,8 +281,6 @@ struct CmdStorePrefetchFile : StoreCommand, MixJSON
expectArg("url", &url);
}
- Category category() override { return catUtility; }
-
std::string description() override
{
return "download a file into the Nix store";
diff --git a/src/nix/print-dev-env.md b/src/nix/print-dev-env.md
index b80252acf..2aad491de 100644
--- a/src/nix/print-dev-env.md
+++ b/src/nix/print-dev-env.md
@@ -8,12 +8,43 @@ R""(
# . <(nix print-dev-env nixpkgs#hello)
```
+* Get the build environment in JSON format:
+
+ ```console
+ # nix print-dev-env nixpkgs#hello --json
+ ```
+
+ The output will look like this:
+
+ ```json
+ {
+ "bashFunctions": {
+ "buildPhase": " \n runHook preBuild;\n...",
+ ...
+ },
+ "variables": {
+ "src": {
+ "type": "exported",
+ "value": "/nix/store/3x7dwzq014bblazs7kq20p9hyzz0qh8g-hello-2.10.tar.gz"
+ },
+ "postUnpackHooks": {
+ "type": "array",
+ "value": ["_updateSourceDateEpochFromSourceRoot"]
+ },
+ ...
+ }
+ }
+ ```
+
# Description
-This command prints a shell script that can be sourced by `b`ash and
-that sets the environment variables and shell functions defined by the
-build process of *installable*. This allows you to get a similar build
+This command prints a shell script that can be sourced by `bash` and
+that sets the variables and shell functions defined by the build
+process of *installable*. This allows you to get a similar build
environment in your current shell rather than in a subshell (as with
`nix develop`).
+With `--json`, the output is a JSON serialisation of the variables and
+functions defined by the build process.
+
)""
diff --git a/src/nix/profile-history.md b/src/nix/profile-history.md
index d0fe40c82..f0bfe5037 100644
--- a/src/nix/profile-history.md
+++ b/src/nix/profile-history.md
@@ -6,10 +6,10 @@ R""(
```console
# nix profile history
- Version 508 -> 509:
+ Version 508 (2020-04-10):
flake:nixpkgs#legacyPackages.x86_64-linux.awscli: ∅ -> 1.17.13
- Version 509 -> 510:
+ Version 509 (2020-05-16) <- 508:
flake:nixpkgs#legacyPackages.x86_64-linux.awscli: 1.17.13 -> 1.18.211
```
diff --git a/src/nix/profile-remove.md b/src/nix/profile-remove.md
index dcf825da9..ba85441d8 100644
--- a/src/nix/profile-remove.md
+++ b/src/nix/profile-remove.md
@@ -15,6 +15,7 @@ R""(
```
* Remove all packages:
+
```console
# nix profile remove '.*'
```
diff --git a/src/nix/profile-rollback.md b/src/nix/profile-rollback.md
new file mode 100644
index 000000000..6bb75aa5e
--- /dev/null
+++ b/src/nix/profile-rollback.md
@@ -0,0 +1,26 @@
+R""(
+
+# Examples
+
+* Roll back your default profile to the previous version:
+
+ ```console
+ # nix profile rollback
+ switching profile from version 519 to 518
+ ```
+
+* Switch your default profile to version 510:
+
+ ```console
+ # nix profile rollback --to 510
+ switching profile from version 518 to 510
+ ```
+
+# Description
+
+This command switches a profile to the most recent version older
+than the currently active version, or if `--to` *N* is given, to
+version *N* of the profile. To see the available versions of a
+profile, use `nix profile history`.
+
+)""
diff --git a/src/nix/profile-upgrade.md b/src/nix/profile-upgrade.md
index 2bd5d256d..e06e74abe 100644
--- a/src/nix/profile-upgrade.md
+++ b/src/nix/profile-upgrade.md
@@ -18,7 +18,7 @@ R""(
* Upgrade a specific profile element by number:
```console
- # nix profile info
+ # nix profile list
0 flake:nixpkgs#legacyPackages.x86_64-linux.spotify …
# nix profile upgrade 0
diff --git a/src/nix/profile-wipe-history.md b/src/nix/profile-wipe-history.md
new file mode 100644
index 000000000..b4b262864
--- /dev/null
+++ b/src/nix/profile-wipe-history.md
@@ -0,0 +1,20 @@
+R""(
+
+# Examples
+
+* Delete all versions of the default profile older than 100 days:
+
+ ```console
+ # nix profile wipe-history --profile /tmp/profile --older-than 100d
+ removing profile version 515
+ removing profile version 514
+ ```
+
+# Description
+
+This command deletes non-current versions of a profile, making it
+impossible to roll back to these versions. By default, all non-current
+versions are deleted. With `--older-than` *N*`d`, all non-current
+versions older than *N* days are deleted.
+
+)""
diff --git a/src/nix/profile.cc b/src/nix/profile.cc
index 4d275f577..96a20f673 100644
--- a/src/nix/profile.cc
+++ b/src/nix/profile.cc
@@ -12,6 +12,7 @@
#include <nlohmann/json.hpp>
#include <regex>
+#include <iomanip>
using namespace nix;
@@ -97,10 +98,8 @@ struct ProfileManifest
else if (pathExists(profile + "/manifest.nix")) {
// FIXME: needed because of pure mode; ugly.
- if (state.allowedPaths) {
- state.allowedPaths->insert(state.store->followLinksToStore(profile));
- state.allowedPaths->insert(state.store->followLinksToStore(profile + "/manifest.nix"));
- }
+ state.allowPath(state.store->followLinksToStore(profile));
+ state.allowPath(state.store->followLinksToStore(profile + "/manifest.nix"));
auto drvInfos = queryInstalled(state, state.store->followLinksToStore(profile));
@@ -233,7 +232,7 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
{
ProfileManifest manifest(*getEvalState(), *profile);
- std::vector<StorePathWithOutputs> pathsToBuild;
+ std::vector<DerivedPath> pathsToBuild;
for (auto & installable : installables) {
if (auto installable2 = std::dynamic_pointer_cast<InstallableFlake>(installable)) {
@@ -249,27 +248,30 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
attrPath,
};
- pathsToBuild.push_back({drv.drvPath, StringSet{drv.outputName}});
+ pathsToBuild.push_back(DerivedPath::Built{drv.drvPath, StringSet{drv.outputName}});
manifest.elements.emplace_back(std::move(element));
} else {
- auto buildables = build(store, Realise::Outputs, {installable}, bmNormal);
+ auto buildables = build(getEvalStore(), store, Realise::Outputs, {installable}, bmNormal);
for (auto & buildable : buildables) {
ProfileElement element;
std::visit(overloaded {
- [&](BuildableOpaque bo) {
- pathsToBuild.push_back({bo.path, {}});
+ [&](const BuiltPath::Opaque & bo) {
+ pathsToBuild.push_back(bo);
element.storePaths.insert(bo.path);
},
- [&](BuildableFromDrv bfd) {
+ [&](const BuiltPath::Built & bfd) {
+ // TODO: Why are we querying if we know the output
+ // names already? Is it just to figure out what the
+ // default one is?
for (auto & output : store->queryDerivationOutputMap(bfd.drvPath)) {
- pathsToBuild.push_back({bfd.drvPath, {output.first}});
+ pathsToBuild.push_back(DerivedPath::Built{bfd.drvPath, {output.first}});
element.storePaths.insert(output.second);
}
},
- }, buildable);
+ }, buildable.raw());
manifest.elements.emplace_back(std::move(element));
}
@@ -388,7 +390,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
auto matchers = getMatchers(store);
// FIXME: code duplication
- std::vector<StorePathWithOutputs> pathsToBuild;
+ std::vector<DerivedPath> pathsToBuild;
for (size_t i = 0; i < manifest.elements.size(); ++i) {
auto & element(manifest.elements[i]);
@@ -423,7 +425,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
attrPath,
};
- pathsToBuild.push_back({drv.drvPath, StringSet{"out"}}); // FIXME
+ pathsToBuild.push_back(DerivedPath::Built{drv.drvPath, {drv.outputName}});
}
}
@@ -525,10 +527,11 @@ struct CmdProfileHistory : virtual StoreCommand, EvalCommand, MixDefaultProfile
if (!first) std::cout << "\n";
first = false;
- if (prevGen)
- std::cout << fmt("Version %d -> %d:\n", prevGen->first.number, gen.number);
- else
- std::cout << fmt("Version %d:\n", gen.number);
+ std::cout << fmt("Version %s%d" ANSI_NORMAL " (%s)%s:\n",
+ gen.number == curGen ? ANSI_GREEN : ANSI_BOLD,
+ gen.number,
+ std::put_time(std::gmtime(&gen.creationTime), "%Y-%m-%d"),
+ prevGen ? fmt(" <- %d", prevGen->first.number) : "");
ProfileManifest::printDiff(
prevGen ? prevGen->second : ProfileManifest(),
@@ -540,6 +543,76 @@ struct CmdProfileHistory : virtual StoreCommand, EvalCommand, MixDefaultProfile
}
};
+struct CmdProfileRollback : virtual StoreCommand, MixDefaultProfile, MixDryRun
+{
+ std::optional<GenerationNumber> version;
+
+ CmdProfileRollback()
+ {
+ addFlag({
+ .longName = "to",
+ .description = "The profile version to roll back to.",
+ .labels = {"version"},
+ .handler = {&version},
+ });
+ }
+
+ std::string description() override
+ {
+ return "roll back to the previous version or a specified version of a profile";
+ }
+
+ std::string doc() override
+ {
+ return
+ #include "profile-rollback.md"
+ ;
+ }
+
+ void run(ref<Store> store) override
+ {
+ switchGeneration(*profile, version, dryRun);
+ }
+};
+
+struct CmdProfileWipeHistory : virtual StoreCommand, MixDefaultProfile, MixDryRun
+{
+ std::optional<std::string> minAge;
+
+ CmdProfileWipeHistory()
+ {
+ addFlag({
+ .longName = "older-than",
+ .description =
+ "Delete versions older than the specified age. *age* "
+ "must be in the format *N*`d`, where *N* denotes a number "
+ "of days.",
+ .labels = {"age"},
+ .handler = {&minAge},
+ });
+ }
+
+ std::string description() override
+ {
+ return "delete non-current versions of a profile";
+ }
+
+ std::string doc() override
+ {
+ return
+ #include "profile-wipe-history.md"
+ ;
+ }
+
+ void run(ref<Store> store) override
+ {
+ if (minAge)
+ deleteGenerationsOlderThan(*profile, *minAge, dryRun);
+ else
+ deleteOldGenerations(*profile, dryRun);
+ }
+};
+
struct CmdProfile : NixMultiCommand
{
CmdProfile()
@@ -550,6 +623,8 @@ struct CmdProfile : NixMultiCommand
{"list", []() { return make_ref<CmdProfileList>(); }},
{"diff-closures", []() { return make_ref<CmdProfileDiffClosures>(); }},
{"history", []() { return make_ref<CmdProfileHistory>(); }},
+ {"rollback", []() { return make_ref<CmdProfileRollback>(); }},
+ {"wipe-history", []() { return make_ref<CmdProfileWipeHistory>(); }},
})
{ }
diff --git a/src/nix/realisation.cc b/src/nix/realisation.cc
new file mode 100644
index 000000000..dfa8ff449
--- /dev/null
+++ b/src/nix/realisation.cc
@@ -0,0 +1,85 @@
+#include "command.hh"
+#include "common-args.hh"
+
+#include <nlohmann/json.hpp>
+
+using namespace nix;
+
+struct CmdRealisation : virtual NixMultiCommand
+{
+ CmdRealisation() : MultiCommand(RegisterCommand::getCommandsFor({"realisation"}))
+ { }
+
+ std::string description() override
+ {
+ return "manipulate a Nix realisation";
+ }
+
+ Category category() override { return catUtility; }
+
+ void run() override
+ {
+ if (!command)
+ throw UsageError("'nix realisation' requires a sub-command.");
+ command->second->prepare();
+ command->second->run();
+ }
+};
+
+static auto rCmdRealisation = registerCommand<CmdRealisation>("realisation");
+
+struct CmdRealisationInfo : BuiltPathsCommand, MixJSON
+{
+ std::string description() override
+ {
+ return "query information about one or several realisations";
+ }
+
+ std::string doc() override
+ {
+ return
+ #include "realisation/info.md"
+ ;
+ }
+
+ Category category() override { return catSecondary; }
+
+ void run(ref<Store> store, BuiltPaths && paths) override
+ {
+ settings.requireExperimentalFeature("ca-derivations");
+ RealisedPath::Set realisations;
+
+ for (auto & builtPath : paths) {
+ auto theseRealisations = builtPath.toRealisedPaths(*store);
+ realisations.insert(theseRealisations.begin(), theseRealisations.end());
+ }
+
+ if (json) {
+ nlohmann::json res = nlohmann::json::array();
+ for (auto & path : realisations) {
+ nlohmann::json currentPath;
+ if (auto realisation = std::get_if<Realisation>(&path.raw))
+ currentPath = realisation->toJSON();
+ else
+ currentPath["opaquePath"] = store->printStorePath(path.path());
+
+ res.push_back(currentPath);
+ }
+ std::cout << res.dump();
+ }
+ else {
+ for (auto & path : realisations) {
+ if (auto realisation = std::get_if<Realisation>(&path.raw)) {
+ std::cout <<
+ realisation->id.to_string() << " " <<
+ store->printStorePath(realisation->outPath);
+ } else
+ std::cout << store->printStorePath(path.path());
+
+ std::cout << std::endl;
+ }
+ }
+ }
+};
+
+static auto rCmdRealisationInfo = registerCommand2<CmdRealisationInfo>({"realisation", "info"});
diff --git a/src/nix/realisation/info.md b/src/nix/realisation/info.md
new file mode 100644
index 000000000..852240f44
--- /dev/null
+++ b/src/nix/realisation/info.md
@@ -0,0 +1,15 @@
+R"MdBoundary(
+# Description
+
+Display some informations about the given realisation
+
+# Examples
+
+Show some information about the realisation of the `hello` package:
+
+```console
+$ nix realisation info nixpkgs#hello --json
+[{"id":"sha256:3d382378a00588e064ee30be96dd0fa7e7df7cf3fbcace85a0e7b7dada1eef25!out","outPath":"fd3m7xawvrqcg98kgz5hc2vk3x9q0lh7-hello"}]
+```
+
+)MdBoundary"
diff --git a/src/nix/registry-add.md b/src/nix/registry-add.md
index 80a31996a..a947fa0b3 100644
--- a/src/nix/registry-add.md
+++ b/src/nix/registry-add.md
@@ -21,6 +21,13 @@ R""(
# nix registry add nixpkgs/nixos-20.03 ~/Dev/nixpkgs
```
+* Add `nixpkgs` pointing to `github:nixos/nixpkgs` to your custom flake
+ registry:
+
+ ```console
+ nix registry add --registry ./custom-flake-registry.json nixpkgs github:nixos/nixpkgs
+ ```
+
# Description
This command adds an entry to the user registry that maps flake
diff --git a/src/nix/registry-pin.md b/src/nix/registry-pin.md
index 6e97e003e..ebc0e3eff 100644
--- a/src/nix/registry-pin.md
+++ b/src/nix/registry-pin.md
@@ -24,6 +24,13 @@ R""(
```
+* Pin `nixpkgs` in a custom registry to its most recent Git revision:
+
+ ```console
+ # nix registry pin --registry ./custom-flake-registry.json nixpkgs
+ ```
+
+
# Description
This command adds an entry to the user registry that maps flake
diff --git a/src/nix/registry-remove.md b/src/nix/registry-remove.md
index 4c0eb4947..eecd4c6e7 100644
--- a/src/nix/registry-remove.md
+++ b/src/nix/registry-remove.md
@@ -8,6 +8,12 @@ R""(
# nix registry remove nixpkgs
```
+* Remove the entry `nixpkgs` from a custom registry:
+
+ ```console
+ # nix registry remove --registry ./custom-flake-registry.json nixpkgs
+ ```
+
# Description
This command removes from the user registry any entry for flake
diff --git a/src/nix/registry.cc b/src/nix/registry.cc
index f9719600f..6a92576c7 100644
--- a/src/nix/registry.cc
+++ b/src/nix/registry.cc
@@ -10,6 +10,46 @@
using namespace nix;
using namespace nix::flake;
+
+class RegistryCommand : virtual Args
+{
+ std::string registry_path;
+
+ std::shared_ptr<fetchers::Registry> registry;
+
+public:
+
+ RegistryCommand()
+ {
+ addFlag({
+ .longName = "registry",
+ .description = "The registry to operate on.",
+ .labels = {"registry"},
+ .handler = {&registry_path},
+ });
+ }
+
+ std::shared_ptr<fetchers::Registry> getRegistry()
+ {
+ if (registry) return registry;
+ if (registry_path.empty()) {
+ registry = fetchers::getUserRegistry();
+ } else {
+ registry = fetchers::getCustomRegistry(registry_path);
+ }
+ return registry;
+ }
+
+ Path getRegistryPath()
+ {
+ if (registry_path.empty()) {
+ return fetchers::getUserRegistryPath();
+ } else {
+ return registry_path;
+ }
+ }
+};
+
struct CmdRegistryList : StoreCommand
{
std::string description() override
@@ -45,7 +85,7 @@ struct CmdRegistryList : StoreCommand
}
};
-struct CmdRegistryAdd : MixEvalArgs, Command
+struct CmdRegistryAdd : MixEvalArgs, Command, RegistryCommand
{
std::string fromUrl, toUrl;
@@ -71,16 +111,16 @@ struct CmdRegistryAdd : MixEvalArgs, Command
{
auto fromRef = parseFlakeRef(fromUrl);
auto toRef = parseFlakeRef(toUrl);
+ auto registry = getRegistry();
fetchers::Attrs extraAttrs;
if (toRef.subdir != "") extraAttrs["dir"] = toRef.subdir;
- auto userRegistry = fetchers::getUserRegistry();
- userRegistry->remove(fromRef.input);
- userRegistry->add(fromRef.input, toRef.input, extraAttrs);
- userRegistry->write(fetchers::getUserRegistryPath());
+ registry->remove(fromRef.input);
+ registry->add(fromRef.input, toRef.input, extraAttrs);
+ registry->write(getRegistryPath());
}
};
-struct CmdRegistryRemove : virtual Args, MixEvalArgs, Command
+struct CmdRegistryRemove : RegistryCommand, Command
{
std::string url;
@@ -103,19 +143,21 @@ struct CmdRegistryRemove : virtual Args, MixEvalArgs, Command
void run() override
{
- auto userRegistry = fetchers::getUserRegistry();
- userRegistry->remove(parseFlakeRef(url).input);
- userRegistry->write(fetchers::getUserRegistryPath());
+ auto registry = getRegistry();
+ registry->remove(parseFlakeRef(url).input);
+ registry->write(getRegistryPath());
}
};
-struct CmdRegistryPin : virtual Args, EvalCommand
+struct CmdRegistryPin : RegistryCommand, EvalCommand
{
std::string url;
+ std::string locked;
+
std::string description() override
{
- return "pin a flake to its current version in user flake registry";
+ return "pin a flake to its current version or to the current version of a flake URL";
}
std::string doc() override
@@ -128,18 +170,31 @@ struct CmdRegistryPin : virtual Args, EvalCommand
CmdRegistryPin()
{
expectArg("url", &url);
+
+ expectArgs({
+ .label = "locked",
+ .optional = true,
+ .handler = {&locked},
+ .completer = {[&](size_t, std::string_view prefix) {
+ completeFlakeRef(getStore(), prefix);
+ }}
+ });
}
void run(nix::ref<nix::Store> store) override
{
+ if (locked.empty()) {
+ locked = url;
+ }
+ auto registry = getRegistry();
auto ref = parseFlakeRef(url);
- auto userRegistry = fetchers::getUserRegistry();
- userRegistry->remove(ref.input);
- auto [tree, resolved] = ref.resolve(store).input.fetch(store);
+ auto locked_ref = parseFlakeRef(locked);
+ registry->remove(ref.input);
+ auto [tree, resolved] = locked_ref.resolve(store).input.fetch(store);
fetchers::Attrs extraAttrs;
if (ref.subdir != "") extraAttrs["dir"] = ref.subdir;
- userRegistry->add(ref.input, resolved, extraAttrs);
- userRegistry->write(fetchers::getUserRegistryPath());
+ registry->add(ref.input, resolved, extraAttrs);
+ registry->write(getRegistryPath());
}
};
diff --git a/src/nix/registry.md b/src/nix/registry.md
index 557e5795b..a1674bd2e 100644
--- a/src/nix/registry.md
+++ b/src/nix/registry.md
@@ -41,7 +41,7 @@ A registry is a JSON file with the following format:
```json
{
"version": 2,
- [
+ "flakes": [
{
"from": {
"type": "indirect",
diff --git a/src/nix/repl.cc b/src/nix/repl.cc
index bce8d31dc..9c0d22438 100644
--- a/src/nix/repl.cc
+++ b/src/nix/repl.cc
@@ -68,6 +68,7 @@ struct NixRepl
StorePath getDerivationPath(Value & v);
bool processLine(string line);
void loadFile(const Path & path);
+ void loadFlake(const std::string & flakeRef);
void initEnv();
void reloadFiles();
void addAttrsToScope(Value & attrs);
@@ -104,6 +105,25 @@ NixRepl::~NixRepl()
write_history(historyFile.c_str());
}
+string runNix(Path program, const Strings & args,
+ const std::optional<std::string> & input = {})
+{
+ auto subprocessEnv = getEnv();
+ subprocessEnv["NIX_CONFIG"] = globalConfig.toKeyValue();
+
+ auto res = runProgram(RunOptions {
+ .program = settings.nixBinDir+ "/" + program,
+ .args = args,
+ .environment = subprocessEnv,
+ .input = input,
+ });
+
+ if (!statusOk(res.first))
+ throw ExecError(res.first, fmt("program '%1%' %2%", program, statusToString(res.first)));
+
+ return res.second;
+}
+
static NixRepl * curRepl; // ugly
static char * completionCallback(char * s, int *match) {
@@ -180,13 +200,13 @@ namespace {
void NixRepl::mainLoop(const std::vector<std::string> & files)
{
string error = ANSI_RED "error:" ANSI_NORMAL " ";
- std::cout << "Welcome to Nix version " << nixVersion << ". Type :? for help." << std::endl << std::endl;
+ notice("Welcome to Nix " + nixVersion + ". Type :? for help.\n");
for (auto & i : files)
loadedFiles.push_back(i);
reloadFiles();
- if (!loadedFiles.empty()) std::cout << std::endl;
+ if (!loadedFiles.empty()) notice("");
// Allow nix-repl specific settings in .inputrc
rl_readline_name = "nix-repl";
@@ -343,24 +363,6 @@ StringSet NixRepl::completePrefix(string prefix)
}
-static int runProgram(const string & program, const Strings & args)
-{
- Strings args2(args);
- args2.push_front(program);
-
- Pid pid;
- pid = fork();
- if (pid == -1) throw SysError("forking");
- if (pid == 0) {
- restoreAffinity();
- execvp(program.c_str(), stringsToCharPtrs(args2).data());
- _exit(1);
- }
-
- return pid.wait();
-}
-
-
bool isVarName(const string & s)
{
if (s.size() == 0) return false;
@@ -394,6 +396,8 @@ bool NixRepl::processLine(string line)
{
if (line == "") return true;
+ _isInterrupted = false;
+
string command, arg;
if (line[0] == ':') {
@@ -413,9 +417,10 @@ bool NixRepl::processLine(string line)
<< " <x> = <expr> Bind expression to variable\n"
<< " :a <expr> Add attributes from resulting set to scope\n"
<< " :b <expr> Build derivation\n"
- << " :e <expr> Open the derivation in $EDITOR\n"
+ << " :e <expr> Open package or function in $EDITOR\n"
<< " :i <expr> Build derivation, then install result into current profile\n"
<< " :l <path> Load Nix expression and add it to scope\n"
+ << " :lf <ref> Load Nix flake and add it to scope\n"
<< " :p <expr> Evaluate and print expression recursively\n"
<< " :q Exit nix-repl\n"
<< " :r Reload all files\n"
@@ -436,6 +441,10 @@ bool NixRepl::processLine(string line)
loadFile(arg);
}
+ else if (command == ":lf" || command == ":load-flake") {
+ loadFlake(arg);
+ }
+
else if (command == ":r" || command == ":reload") {
state->resetFileCache();
reloadFiles();
@@ -455,14 +464,14 @@ bool NixRepl::processLine(string line)
pos = v.lambda.fun->pos;
} else {
// assume it's a derivation
- pos = findDerivationFilename(*state, v, arg);
+ pos = findPackageFilename(*state, v, arg);
}
// Open in EDITOR
auto args = editorFor(pos);
auto editor = args.front();
args.pop_front();
- runProgram(editor, args);
+ runProgram(editor, true, args);
// Reload right after exiting the editor
state->resetFileCache();
@@ -472,16 +481,17 @@ bool NixRepl::processLine(string line)
else if (command == ":t") {
Value v;
evalString(arg, v);
- std::cout << showType(v) << std::endl;
+ logger->cout(showType(v));
+ }
- } else if (command == ":u") {
+ else if (command == ":u") {
Value v, f, result;
evalString(arg, v);
evalString("drv: (import <nixpkgs> {}).runCommand \"shell\" { buildInputs = [ drv ]; } \"\"", f);
state->callFunction(f, v, result, Pos());
StorePath drvPath = getDerivationPath(result);
- runProgram(settings.nixBinDir + "/nix-shell", Strings{state->store->printStorePath(drvPath)});
+ runNix("nix-shell", {state->store->printStorePath(drvPath)});
}
else if (command == ":b" || command == ":i" || command == ":s") {
@@ -491,19 +501,15 @@ bool NixRepl::processLine(string line)
Path drvPathRaw = state->store->printStorePath(drvPath);
if (command == ":b") {
- /* We could do the build in this process using buildPaths(),
- but doing it in a child makes it easier to recover from
- problems / SIGINT. */
- if (runProgram(settings.nixBinDir + "/nix", Strings{"build", "--no-link", drvPathRaw}) == 0) {
- auto drv = state->store->readDerivation(drvPath);
- std::cout << std::endl << "this derivation produced the following outputs:" << std::endl;
- for (auto & i : drv.outputsAndOptPaths(*state->store))
- std::cout << fmt(" %s -> %s\n", i.first, state->store->printStorePath(*i.second.second));
- }
+ state->store->buildPaths({DerivedPath::Built{drvPath}});
+ auto drv = state->store->readDerivation(drvPath);
+ logger->cout("\nThis derivation produced the following outputs:");
+ for (auto & i : drv.outputsAndOptPaths(*state->store))
+ logger->cout(" %s -> %s", i.first, state->store->printStorePath(*i.second.second));
} else if (command == ":i") {
- runProgram(settings.nixBinDir + "/nix-env", Strings{"-i", drvPathRaw});
+ runNix("nix-env", {"-i", drvPathRaw});
} else {
- runProgram(settings.nixBinDir + "/nix-shell", Strings{drvPathRaw});
+ runNix("nix-shell", {drvPathRaw});
}
}
@@ -532,9 +538,9 @@ bool NixRepl::processLine(string line)
+ concatStringsSep(" ", args) + "\n\n";
}
- markdown += trim(stripIndentation(doc->doc));
+ markdown += stripIndentation(doc->doc);
- std::cout << renderMarkdownToTerminal(markdown);
+ logger->cout(trim(renderMarkdownToTerminal(markdown)));
} else
throw Error("value does not have documentation");
}
@@ -575,6 +581,25 @@ void NixRepl::loadFile(const Path & path)
addAttrsToScope(v2);
}
+void NixRepl::loadFlake(const std::string & flakeRefS)
+{
+ auto flakeRef = parseFlakeRef(flakeRefS, absPath("."), true);
+ if (evalSettings.pureEval && !flakeRef.input.isImmutable())
+ throw Error("cannot use ':load-flake' on mutable flake reference '%s' (use --impure to override)", flakeRefS);
+
+ Value v;
+
+ flake::callFlake(*state,
+ flake::lockFlake(*state, flakeRef,
+ flake::LockFlags {
+ .updateLockFile = false,
+ .useRegistries = !evalSettings.pureEval,
+ .allowMutable = !evalSettings.pureEval,
+ }),
+ v);
+ addAttrsToScope(v);
+}
+
void NixRepl::initEnv()
{
@@ -598,9 +623,9 @@ void NixRepl::reloadFiles()
bool first = true;
for (auto & i : old) {
- if (!first) std::cout << std::endl;
+ if (!first) notice("");
first = false;
- std::cout << format("Loading '%1%'...") % i << std::endl;
+ notice("Loading '%1%'...", i);
loadFile(i);
}
}
@@ -611,7 +636,7 @@ void NixRepl::addAttrsToScope(Value & attrs)
state->forceAttrs(attrs);
for (auto & i : *attrs.attrs)
addVarToScope(i.name, *i.value);
- std::cout << format("Added %1% variables.") % attrs.attrs->size() << std::endl;
+ notice("Added %1% variables.", attrs.attrs->size());
}
@@ -679,7 +704,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
break;
case nString:
- str << ANSI_YELLOW;
+ str << ANSI_WARNING;
printStringValue(str, v.string.s);
str << ANSI_NORMAL;
break;
diff --git a/src/nix/run.cc b/src/nix/run.cc
index ec9388234..b01fdebaa 100644
--- a/src/nix/run.cc
+++ b/src/nix/run.cc
@@ -1,3 +1,4 @@
+#include "run.hh"
#include "command.hh"
#include "common-args.hh"
#include "shared.hh"
@@ -20,47 +21,43 @@ using namespace nix;
std::string chrootHelperName = "__run_in_chroot";
-struct RunCommon : virtual Command
-{
-
- using Command::run;
+namespace nix {
- void runProgram(ref<Store> store,
- const std::string & program,
- const Strings & args)
- {
- stopProgressBar();
+void runProgramInStore(ref<Store> store,
+ const std::string & program,
+ const Strings & args)
+{
+ stopProgressBar();
- restoreSignals();
+ restoreProcessContext();
- restoreAffinity();
+ /* If this is a diverted store (i.e. its "logical" location
+ (typically /nix/store) differs from its "physical" location
+ (e.g. /home/eelco/nix/store), then run the command in a
+ chroot. For non-root users, this requires running it in new
+ mount and user namespaces. Unfortunately,
+ unshare(CLONE_NEWUSER) doesn't work in a multithreaded program
+ (which "nix" is), so we exec() a single-threaded helper program
+ (chrootHelper() below) to do the work. */
+ auto store2 = store.dynamic_pointer_cast<LocalStore>();
- /* If this is a diverted store (i.e. its "logical" location
- (typically /nix/store) differs from its "physical" location
- (e.g. /home/eelco/nix/store), then run the command in a
- chroot. For non-root users, this requires running it in new
- mount and user namespaces. Unfortunately,
- unshare(CLONE_NEWUSER) doesn't work in a multithreaded
- program (which "nix" is), so we exec() a single-threaded
- helper program (chrootHelper() below) to do the work. */
- auto store2 = store.dynamic_pointer_cast<LocalStore>();
+ if (store2 && store->storeDir != store2->getRealStoreDir()) {
+ Strings helperArgs = { chrootHelperName, store->storeDir, store2->getRealStoreDir(), program };
+ for (auto & arg : args) helperArgs.push_back(arg);
- if (store2 && store->storeDir != store2->realStoreDir) {
- Strings helperArgs = { chrootHelperName, store->storeDir, store2->realStoreDir, program };
- for (auto & arg : args) helperArgs.push_back(arg);
+ execv(readLink("/proc/self/exe").c_str(), stringsToCharPtrs(helperArgs).data());
- execv(readLink("/proc/self/exe").c_str(), stringsToCharPtrs(helperArgs).data());
+ throw SysError("could not execute chroot helper");
+ }
- throw SysError("could not execute chroot helper");
- }
+ execvp(program.c_str(), stringsToCharPtrs(args).data());
- execvp(program.c_str(), stringsToCharPtrs(args).data());
+ throw SysError("unable to execute '%s'", program);
+}
- throw SysError("unable to execute '%s'", program);
- }
-};
+}
-struct CmdShell : InstallablesCommand, RunCommon, MixEnvironment
+struct CmdShell : InstallablesCommand, MixEnvironment
{
using InstallablesCommand::run;
@@ -95,7 +92,7 @@ struct CmdShell : InstallablesCommand, RunCommon, MixEnvironment
void run(ref<Store> store) override
{
- auto outPaths = toStorePaths(store, Realise::Outputs, OperateOn::Output, installables);
+ auto outPaths = toStorePaths(getEvalStore(), store, Realise::Outputs, OperateOn::Output, installables);
auto accessor = store->getFSAccessor();
@@ -127,13 +124,13 @@ struct CmdShell : InstallablesCommand, RunCommon, MixEnvironment
Strings args;
for (auto & arg : command) args.push_back(arg);
- runProgram(store, *command.begin(), args);
+ runProgramInStore(store, *command.begin(), args);
}
};
static auto rCmdShell = registerCommand<CmdShell>("shell");
-struct CmdRun : InstallableCommand, RunCommon
+struct CmdRun : InstallableCommand
{
using InstallableCommand::run;
@@ -170,7 +167,7 @@ struct CmdRun : InstallableCommand, RunCommon
Strings getDefaultFlakeAttrPathPrefixes() override
{
- Strings res{"apps." + settings.thisSystem.get() + ".", "packages"};
+ Strings res{"apps." + settings.thisSystem.get() + "."};
for (auto & s : SourceExprCommand::getDefaultFlakeAttrPathPrefixes())
res.push_back(s);
return res;
@@ -180,14 +177,12 @@ struct CmdRun : InstallableCommand, RunCommon
{
auto state = getEvalState();
- auto app = installable->toApp(*state);
-
- state->store->buildPaths(app.context);
+ auto app = installable->toApp(*state).resolve(getEvalStore(), store);
Strings allArgs{app.program};
for (auto & i : args) allArgs.push_back(i);
- runProgram(store, app.program, allArgs);
+ runProgramInStore(store, app.program, allArgs);
}
};
diff --git a/src/nix/run.hh b/src/nix/run.hh
new file mode 100644
index 000000000..6180a87dd
--- /dev/null
+++ b/src/nix/run.hh
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "store-api.hh"
+
+namespace nix {
+
+void runProgramInStore(ref<Store> store,
+ const std::string & program,
+ const Strings & args);
+
+}
diff --git a/src/nix/search.cc b/src/nix/search.cc
index c52a48d4e..0d8fdd5c2 100644
--- a/src/nix/search.cc
+++ b/src/nix/search.cc
@@ -62,6 +62,7 @@ struct CmdSearch : InstallableCommand, MixJSON
void run(ref<Store> store) override
{
settings.readOnlyMode = true;
+ evalSettings.enableImportFromDerivation.setDefault(false);
// Empty search string should match all packages
// Use "^" here instead of ".*" due to differences in resulting highlighting
diff --git a/src/nix/show-config.cc b/src/nix/show-config.cc
index 91721219b..29944e748 100644
--- a/src/nix/show-config.cc
+++ b/src/nix/show-config.cc
@@ -22,10 +22,7 @@ struct CmdShowConfig : Command, MixJSON
// FIXME: use appropriate JSON types (bool, ints, etc).
logger->cout("%s", globalConfig.toJSON().dump());
} else {
- std::map<std::string, Config::SettingInfo> settings;
- globalConfig.getSettings(settings);
- for (auto & s : settings)
- logger->cout("%s = %s", s.first, s.second.value);
+ logger->cout("%s", globalConfig.toKeyValue());
}
}
};
diff --git a/src/nix/show-derivation.cc b/src/nix/show-derivation.cc
index 2588a011d..c614be68d 100644
--- a/src/nix/show-derivation.cc
+++ b/src/nix/show-derivation.cc
@@ -65,18 +65,18 @@ struct CmdShowDerivation : InstallablesCommand
auto & outputName = _outputName; // work around clang bug
auto outputObj { outputsObj.object(outputName) };
std::visit(overloaded {
- [&](DerivationOutputInputAddressed doi) {
+ [&](const DerivationOutputInputAddressed & doi) {
outputObj.attr("path", store->printStorePath(doi.path));
},
- [&](DerivationOutputCAFixed dof) {
+ [&](const DerivationOutputCAFixed & dof) {
outputObj.attr("path", store->printStorePath(dof.path(*store, drv.name, outputName)));
outputObj.attr("hashAlgo", dof.hash.printMethodAlgo());
outputObj.attr("hash", dof.hash.hash.to_string(Base16, false));
},
- [&](DerivationOutputCAFloating dof) {
+ [&](const DerivationOutputCAFloating & dof) {
outputObj.attr("hashAlgo", makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType));
},
- [&](DerivationOutputDeferred) {},
+ [&](const DerivationOutputDeferred &) {},
}, output.output);
}
}
diff --git a/src/nix/sigs.cc b/src/nix/sigs.cc
index c64b472b6..43e0d9148 100644
--- a/src/nix/sigs.cc
+++ b/src/nix/sigs.cc
@@ -27,7 +27,7 @@ struct CmdCopySigs : StorePathsCommand
return "copy store path signatures from substituters";
}
- void run(ref<Store> store, StorePaths storePaths) override
+ void run(ref<Store> store, StorePaths && storePaths) override
{
if (substituterUris.empty())
throw UsageError("you must specify at least one substituter using '-s'");
@@ -113,7 +113,7 @@ struct CmdSign : StorePathsCommand
return "sign store paths";
}
- void run(ref<Store> store, StorePaths storePaths) override
+ void run(ref<Store> store, StorePaths && storePaths) override
{
if (secretKeyFile.empty())
throw UsageError("you must specify a secret key file using '-k'");
diff --git a/src/nix/store-delete.cc b/src/nix/store-delete.cc
index 10245978e..e4a3cb554 100644
--- a/src/nix/store-delete.cc
+++ b/src/nix/store-delete.cc
@@ -30,7 +30,7 @@ struct CmdStoreDelete : StorePathsCommand
;
}
- void run(ref<Store> store, std::vector<StorePath> storePaths) override
+ void run(ref<Store> store, std::vector<StorePath> && storePaths) override
{
for (auto & path : storePaths)
options.pathsToDelete.insert(path);
diff --git a/src/nix/store-prefetch-file.md b/src/nix/store-prefetch-file.md
index 1663b847b..f9fdcbc57 100644
--- a/src/nix/store-prefetch-file.md
+++ b/src/nix/store-prefetch-file.md
@@ -27,6 +27,6 @@ the resulting store path and the cryptographic hash of the contents of
the file.
The name component of the store path defaults to the last component of
-*url*, but this can be overriden using `--name`.
+*url*, but this can be overridden using `--name`.
)""
diff --git a/src/nix/store-repair.cc b/src/nix/store-repair.cc
index 1c7a4392e..8fcb3639a 100644
--- a/src/nix/store-repair.cc
+++ b/src/nix/store-repair.cc
@@ -17,7 +17,7 @@ struct CmdStoreRepair : StorePathsCommand
;
}
- void run(ref<Store> store, std::vector<StorePath> storePaths) override
+ void run(ref<Store> store, std::vector<StorePath> && storePaths) override
{
for (auto & path : storePaths)
store->repairPath(path);
diff --git a/src/nix/verify.cc b/src/nix/verify.cc
index 1721c7f16..e92df1303 100644
--- a/src/nix/verify.cc
+++ b/src/nix/verify.cc
@@ -59,7 +59,7 @@ struct CmdVerify : StorePathsCommand
;
}
- void run(ref<Store> store, StorePaths storePaths) override
+ void run(ref<Store> store, StorePaths && storePaths) override
{
std::vector<ref<Store>> substituters;
for (auto & s : substituterUris)
@@ -97,15 +97,11 @@ struct CmdVerify : StorePathsCommand
if (!noContents) {
- std::unique_ptr<AbstractHashSink> hashSink;
- if (!info->ca)
- hashSink = std::make_unique<HashSink>(info->narHash.type);
- else
- hashSink = std::make_unique<HashModuloSink>(info->narHash.type, std::string(info->path.hashPart()));
+ auto hashSink = HashSink(info->narHash.type);
- store->narFromPath(info->path, *hashSink);
+ store->narFromPath(info->path, hashSink);
- auto hash = hashSink->finish();
+ auto hash = hashSink.finish();
if (hash.first != info->narHash) {
corrupted++;
diff --git a/src/nix/why-depends.cc b/src/nix/why-depends.cc
index 7a4ca5172..2f6b361bb 100644
--- a/src/nix/why-depends.cc
+++ b/src/nix/why-depends.cc
@@ -62,9 +62,9 @@ struct CmdWhyDepends : SourceExprCommand
void run(ref<Store> store) override
{
auto package = parseInstallable(store, _package);
- auto packagePath = toStorePath(store, Realise::Outputs, operateOn, package);
+ auto packagePath = toStorePath(getEvalStore(), store, Realise::Outputs, operateOn, package);
auto dependency = parseInstallable(store, _dependency);
- auto dependencyPath = toStorePath(store, Realise::Derivation, operateOn, dependency);
+ auto dependencyPath = toStorePath(getEvalStore(), store, Realise::Derivation, operateOn, dependency);
auto dependencyPathHash = dependencyPath.hashPart();
StorePathSet closure;