aboutsummaryrefslogtreecommitdiff
path: root/src/nix
diff options
context:
space:
mode:
authorJohn Ericson <John.Ericson@Obsidian.Systems>2022-03-10 16:20:01 +0000
committerJohn Ericson <John.Ericson@Obsidian.Systems>2022-03-10 16:20:01 +0000
commit938650700fafe76e3755982d670855fed3db35c6 (patch)
tree7a6ac217f87cad07bb963ec658bd60625b868466 /src/nix
parent195daa82995b43b3cbd552735a678afb85f4ae28 (diff)
parent8ba089597fa19bfd49ba5f22a5e821740ca4eb5d (diff)
Merge branch 'path-info' into ca-drv-exotic
Diffstat (limited to 'src/nix')
-rw-r--r--src/nix/add-to-store.cc6
-rw-r--r--src/nix/app.cc7
-rw-r--r--src/nix/build.cc2
-rw-r--r--src/nix/bundle.cc45
-rw-r--r--src/nix/bundle.md46
-rw-r--r--src/nix/cat.cc2
-rw-r--r--src/nix/copy.cc37
-rw-r--r--src/nix/daemon.cc16
-rw-r--r--src/nix/develop.cc34
-rw-r--r--src/nix/develop.md7
-rw-r--r--src/nix/diff-closures.cc4
-rw-r--r--src/nix/eval.cc6
-rw-r--r--src/nix/flake-check.md18
-rw-r--r--src/nix/flake-init.md22
-rw-r--r--src/nix/flake-show.md11
-rw-r--r--src/nix/flake.cc98
-rw-r--r--src/nix/flake.md49
-rw-r--r--src/nix/hash.cc2
-rw-r--r--src/nix/ls.cc2
-rw-r--r--src/nix/main.cc39
-rw-r--r--src/nix/make-content-addressable.cc8
-rw-r--r--src/nix/nix.md46
-rw-r--r--src/nix/path-info.cc2
-rw-r--r--src/nix/ping-store.cc3
-rw-r--r--src/nix/prefetch.cc22
-rw-r--r--src/nix/profile-list.md2
-rw-r--r--src/nix/profile.cc153
-rw-r--r--src/nix/profile.md2
-rw-r--r--src/nix/realisation.cc2
-rw-r--r--src/nix/realisation/info.md2
-rw-r--r--src/nix/registry.cc1
-rw-r--r--src/nix/registry.md2
-rw-r--r--src/nix/repl.cc215
-rw-r--r--src/nix/repl.md7
-rw-r--r--src/nix/run.cc8
-rw-r--r--src/nix/run.md19
-rw-r--r--src/nix/search.cc51
-rw-r--r--src/nix/shell.md6
-rw-r--r--src/nix/show-derivation.cc2
-rw-r--r--src/nix/sigs.cc3
-rw-r--r--src/nix/store-copy-log.cc46
-rw-r--r--src/nix/store-copy-log.md33
-rw-r--r--src/nix/store-delete.cc5
-rw-r--r--src/nix/store-gc.cc5
-rw-r--r--src/nix/upgrade-nix.cc2
-rw-r--r--src/nix/why-depends.cc63
46 files changed, 775 insertions, 388 deletions
diff --git a/src/nix/add-to-store.cc b/src/nix/add-to-store.cc
index ab2b62da2..c2494dc9f 100644
--- a/src/nix/add-to-store.cc
+++ b/src/nix/add-to-store.cc
@@ -32,7 +32,7 @@ struct CmdAddToStore : MixDryRun, StoreCommand
StringSink sink;
dumpPath(path, sink);
- auto narHash = hashString(htSHA256, *sink.s);
+ auto narHash = hashString(htSHA256, sink.s);
Hash hash = narHash;
if (ingestionMethod == FileIngestionMethod::Flat) {
@@ -55,10 +55,10 @@ struct CmdAddToStore : MixDryRun, StoreCommand
},
narHash,
};
- info.narSize = sink.s->size();
+ info.narSize = sink.s.size();
if (!dryRun) {
- auto source = StringSource { *sink.s };
+ auto source = StringSource(sink.s);
store->addToStore(info, source);
}
diff --git a/src/nix/app.cc b/src/nix/app.cc
index 9719a65dd..2563180fb 100644
--- a/src/nix/app.cc
+++ b/src/nix/app.cc
@@ -19,7 +19,7 @@ struct InstallableDerivedPath : Installable
}
- std::string what() override { return derivedPath.to_string(*store); }
+ std::string what() const override { return derivedPath.to_string(*store); }
DerivedPaths toDerivedPaths() override
{
@@ -83,11 +83,14 @@ UnresolvedApp Installable::toApp(EvalState & state)
auto outPath = cursor->getAttr(state.sOutPath)->getString();
auto outputName = cursor->getAttr(state.sOutputName)->getString();
auto name = cursor->getAttr(state.sName)->getString();
+ auto aPname = cursor->maybeGetAttr("pname");
auto aMeta = cursor->maybeGetAttr("meta");
auto aMainProgram = aMeta ? aMeta->maybeGetAttr("mainProgram") : nullptr;
auto mainProgram =
aMainProgram
? aMainProgram->getString()
+ : aPname
+ ? aPname->getString()
: DrvName(name).name;
auto program = outPath + "/bin/" + mainProgram;
return UnresolvedApp { App {
@@ -111,7 +114,7 @@ App UnresolvedApp::resolve(ref<Store> evalStore, ref<Store> store)
installableContext.push_back(
std::make_shared<InstallableDerivedPath>(store, ctxElt.toDerivedPath()));
- auto builtContext = build(evalStore, store, Realise::Outputs, installableContext);
+ auto builtContext = Installable::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);
diff --git a/src/nix/build.cc b/src/nix/build.cc
index 6e31757a2..680db1c60 100644
--- a/src/nix/build.cc
+++ b/src/nix/build.cc
@@ -52,7 +52,7 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
void run(ref<Store> store) override
{
- auto buildables = build(
+ auto buildables = Installable::build(
getEvalStore(), store,
dryRun ? Realise::Derivation : Realise::Outputs,
installables, buildMode);
diff --git a/src/nix/bundle.cc b/src/nix/bundle.cc
index aca024bca..7ed558dee 100644
--- a/src/nix/bundle.cc
+++ b/src/nix/bundle.cc
@@ -49,9 +49,13 @@ struct CmdBundle : InstallableCommand
Category category() override { return catSecondary; }
+ // FIXME: cut&paste from CmdRun.
Strings getDefaultFlakeAttrPaths() override
{
- Strings res{"defaultApp." + settings.thisSystem.get()};
+ Strings res{
+ "apps." + settings.thisSystem.get() + ".default",
+ "defaultApp." + settings.thisSystem.get()
+ };
for (auto & s : SourceExprCommand::getDefaultFlakeAttrPaths())
res.push_back(s);
return res;
@@ -69,29 +73,21 @@ struct CmdBundle : InstallableCommand
{
auto evalState = getEvalState();
- auto app = installable->toApp(*evalState).resolve(getEvalStore(), store);
+ auto val = installable->toValue(*evalState).first;
auto [bundlerFlakeRef, bundlerName] = parseFlakeRefWithFragment(bundler, absPath("."));
const flake::LockFlags lockFlags{ .writeLockFile = false };
- auto bundler = InstallableFlake(this,
- evalState, std::move(bundlerFlakeRef),
- Strings{bundlerName == "" ? "defaultBundler" : bundlerName},
- Strings({"bundlers."}), lockFlags);
-
- Value * arg = evalState->allocValue();
- evalState->mkAttrs(*arg, 2);
-
- PathSet context;
- for (auto & i : app.context)
- context.insert("=" + store->printStorePath(i.path));
- mkString(*evalState->allocAttr(*arg, evalState->symbols.create("program")), app.program, context);
-
- mkString(*evalState->allocAttr(*arg, evalState->symbols.create("system")), settings.thisSystem.get());
-
- arg->attrs->sort();
+ InstallableFlake bundler{this,
+ evalState, std::move(bundlerFlakeRef), bundlerName,
+ {"bundlers." + settings.thisSystem.get() + ".default",
+ "defaultBundler." + settings.thisSystem.get()
+ },
+ {"bundlers." + settings.thisSystem.get() + "."},
+ lockFlags
+ };
auto vRes = evalState->allocValue();
- evalState->callFunction(*bundler.toValue(*evalState).first, *arg, *vRes, noPos);
+ evalState->callFunction(*bundler.toValue(*evalState).first, *val, *vRes, noPos);
if (!evalState->isDerivation(*vRes))
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
@@ -101,21 +97,24 @@ struct CmdBundle : InstallableCommand
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
PathSet context2;
- StorePath drvPath = store->parseStorePath(evalState->coerceToPath(*attr1->pos, *attr1->value, context2));
+ auto drvPath = evalState->coerceToStorePath(*attr1->pos, *attr1->value, context2);
auto attr2 = vRes->attrs->get(evalState->sOutPath);
if (!attr2)
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
- StorePath outPath = store->parseStorePath(evalState->coerceToPath(*attr2->pos, *attr2->value, context2));
+ auto outPath = evalState->coerceToStorePath(*attr2->pos, *attr2->value, context2);
store->buildPaths({ DerivedPath::Built { drvPath } });
auto outPathS = store->printStorePath(outPath);
- if (!outLink)
- outLink = baseNameOf(app.program);
+ if (!outLink) {
+ auto &attr = vRes->attrs->need(evalState->sName);
+ outLink = evalState->forceStringNoCtx(*attr.value,*attr.pos);
+ }
+ // TODO: will crash if not a localFSStore?
store.dynamic_pointer_cast<LocalFSStore>()->addPermRoot(outPath, absPath(*outLink));
}
};
diff --git a/src/nix/bundle.md b/src/nix/bundle.md
index 5e2298376..2bb70711f 100644
--- a/src/nix/bundle.md
+++ b/src/nix/bundle.md
@@ -18,19 +18,53 @@ R""(
nix (Nix) 2.4pre20201215_e3ddffb
```
+* Bundle a Hello using a specific bundler:
+
+ ```console
+ # nix bundle --bundler github:NixOS/bundlers#toDockerImage nixpkgs#hello
+ # docker load < hello-2.10.tar.gz
+ # docker run hello-2.10:latest hello
+ Hello, world!
+ ```
+
# Description
-`nix bundle` packs the closure of the [Nix app](./nix3-run.md)
-*installable* into a single self-extracting executable. See the
-[`nix-bundle` homepage](https://github.com/matthewbauer/nix-bundle)
-for more details.
+`nix bundle`, by default, packs the closure of the *installable* into a single
+self-extracting executable. See the [`bundlers`
+homepage](https://github.com/NixOS/bundlers) for more details.
> **Note**
>
> This command only works on Linux.
-# Bundler definitions
+# Flake output attributes
+
+If no flake output attribute is given, `nix bundle` tries the following
+flake output attributes:
+
+* `bundlers.<system>.default`
+
+If an attribute *name* is given, `nix run` tries the following flake
+output attributes:
+
+* `bundlers.<system>.<name>`
+
+# Bundlers
+
+A bundler is specified by a flake output attribute named
+`bundlers.<system>.<name>`. It looks like this:
+
+```nix
+bundlers.x86_64-linux = rec {
+ identity = drv: drv;
+
+ blender_2_79 = drv: self.packages.x86_64-linux.blender_2_79;
+
+ default = identity;
+};
+```
-TODO
+A bundler must be a function that accepts an arbitrary value (typically a
+derivation or app definition) and returns a derivation.
)""
diff --git a/src/nix/cat.cc b/src/nix/cat.cc
index e28ee3c50..6420a0f79 100644
--- a/src/nix/cat.cc
+++ b/src/nix/cat.cc
@@ -78,7 +78,7 @@ struct CmdCatNar : StoreCommand, MixCat
void run(ref<Store> store) override
{
- cat(makeNarAccessor(make_ref<std::string>(readFile(narPath))));
+ cat(makeNarAccessor(readFile(narPath)));
}
};
diff --git a/src/nix/copy.cc b/src/nix/copy.cc
index 197c85316..8730a9a5c 100644
--- a/src/nix/copy.cc
+++ b/src/nix/copy.cc
@@ -1,17 +1,11 @@
#include "command.hh"
#include "shared.hh"
#include "store-api.hh"
-#include "sync.hh"
-#include "thread-pool.hh"
-
-#include <atomic>
using namespace nix;
-struct CmdCopy : BuiltPathsCommand
+struct CmdCopy : virtual CopyCommand, virtual BuiltPathsCommand
{
- std::string srcUri, dstUri;
-
CheckSigsFlag checkSigs = CheckSigs;
SubstituteFlag substitute = NoSubstitute;
@@ -22,20 +16,6 @@ struct CmdCopy : BuiltPathsCommand
: BuiltPathsCommand(true)
{
addFlag({
- .longName = "from",
- .description = "URL of the source Nix store.",
- .labels = {"store-uri"},
- .handler = {&srcUri},
- });
-
- addFlag({
- .longName = "to",
- .description = "URL of the destination Nix store.",
- .labels = {"store-uri"},
- .handler = {&dstUri},
- });
-
- addFlag({
.longName = "no-check-sigs",
.description = "Do not require that paths are signed by trusted keys.",
.handler = {&checkSigs, NoCheckSigs},
@@ -65,22 +45,9 @@ struct CmdCopy : BuiltPathsCommand
Category category() override { return catSecondary; }
- ref<Store> createStore() override
- {
- return srcUri.empty() ? StoreCommand::createStore() : openStore(srcUri);
- }
-
- void run(ref<Store> store) override
- {
- if (srcUri.empty() && dstUri.empty())
- throw UsageError("you must pass '--from' and/or '--to'");
-
- BuiltPathsCommand::run(store);
- }
-
void run(ref<Store> srcStore, BuiltPaths && paths) override
{
- ref<Store> dstStore = dstUri.empty() ? openStore() : openStore(dstUri);
+ auto dstStore = getDstStore();
RealisedPath::Set stuffToCopy;
diff --git a/src/nix/daemon.cc b/src/nix/daemon.cc
index 6a40a0bd3..940923d3b 100644
--- a/src/nix/daemon.cc
+++ b/src/nix/daemon.cc
@@ -76,7 +76,7 @@ static void setSigChldAction(bool autoReap)
}
-bool matchUser(const string & user, const string & group, const Strings & users)
+bool matchUser(const std::string & user, const std::string & group, const Strings & users)
{
if (find(users.begin(), users.end(), "*") != users.end())
return true;
@@ -85,12 +85,12 @@ bool matchUser(const string & user, const string & group, const Strings & users)
return true;
for (auto & i : users)
- if (string(i, 0, 1) == "@") {
- if (group == string(i, 1)) return true;
+ if (i.substr(0, 1) == "@") {
+ if (group == i.substr(1)) return true;
struct group * gr = getgrnam(i.c_str() + 1);
if (!gr) continue;
for (char * * mem = gr->gr_mem; *mem; mem++)
- if (user == string(*mem)) return true;
+ if (user == std::string(*mem)) return true;
}
return false;
@@ -198,10 +198,10 @@ static void daemonLoop()
PeerInfo peer = getPeerInfo(remote.get());
struct passwd * pw = peer.uidKnown ? getpwuid(peer.uid) : 0;
- string user = pw ? pw->pw_name : std::to_string(peer.uid);
+ std::string user = pw ? pw->pw_name : std::to_string(peer.uid);
struct group * gr = peer.gidKnown ? getgrgid(peer.gid) : 0;
- string group = gr ? gr->gr_name : std::to_string(peer.gid);
+ std::string group = gr ? gr->gr_name : std::to_string(peer.gid);
Strings trustedUsers = settings.trustedUsers;
Strings allowedUsers = settings.allowedUsers;
@@ -212,7 +212,7 @@ static void daemonLoop()
if ((!trusted && !matchUser(user, group, allowedUsers)) || group == settings.buildUsersGroup)
throw Error("user '%1%' is not allowed to connect to the Nix daemon", user);
- printInfo(format((string) "accepted connection from pid %1%, user %2%" + (trusted ? " (trusted)" : ""))
+ printInfo(format((std::string) "accepted connection from pid %1%, user %2%" + (trusted ? " (trusted)" : ""))
% (peer.pidKnown ? std::to_string(peer.pid) : "<unknown>")
% (peer.uidKnown ? user : "<unknown>"));
@@ -234,7 +234,7 @@ static void daemonLoop()
// For debugging, stuff the pid into argv[1].
if (peer.pidKnown && savedArgv[1]) {
- string processName = std::to_string(peer.pid);
+ auto processName = std::to_string(peer.pid);
strncpy(savedArgv[1], processName.c_str(), strlen(savedArgv[1]));
}
diff --git a/src/nix/develop.cc b/src/nix/develop.cc
index c20b9f272..8af5da9d0 100644
--- a/src/nix/develop.cc
+++ b/src/nix/develop.cc
@@ -5,7 +5,6 @@
#include "store-api.hh"
#include "path-with-outputs.hh"
#include "derivations.hh"
-#include "affinity.hh"
#include "progress-bar.hh"
#include "run.hh"
@@ -195,7 +194,7 @@ static StorePath getDerivationEnvironment(ref<Store> store, ref<Store> evalStore
'buildDerivation', but that's privileged. */
drv.name += "-env";
drv.inputSrcs.insert(std::move(getEnvShPath));
- if (settings.isExperimentalFeatureEnabled("ca-derivations")) {
+ if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
for (auto & output : drv.outputs) {
output.second = {
.output = DerivationOutputDeferred{},
@@ -308,7 +307,7 @@ struct Common : InstallableCommand, MixProfile
for (auto & [installable_, dir_] : redirects) {
auto dir = absPath(dir_);
auto installable = parseInstallable(store, installable_);
- auto builtPaths = toStorePaths(
+ auto builtPaths = Installable::toStorePaths(
getEvalStore(), store, Realise::Nothing, OperateOn::Output, {installable});
for (auto & path: builtPaths) {
auto from = store->printStorePath(path);
@@ -326,8 +325,15 @@ struct Common : InstallableCommand, MixProfile
Strings getDefaultFlakeAttrPaths() override
{
- return {"devShell." + settings.thisSystem.get(), "defaultPackage." + settings.thisSystem.get()};
+ Strings paths{
+ "devShells." + settings.thisSystem.get() + ".default",
+ "devShell." + settings.thisSystem.get(),
+ };
+ for (auto & p : SourceExprCommand::getDefaultFlakeAttrPaths())
+ paths.push_back(p);
+ return paths;
}
+
Strings getDefaultFlakeAttrPathPrefixes() override
{
auto res = SourceExprCommand::getDefaultFlakeAttrPathPrefixes();
@@ -341,7 +347,7 @@ struct Common : InstallableCommand, MixProfile
if (path && hasSuffix(path->to_string(), "-env"))
return *path;
else {
- auto drvs = toDerivations(store, {installable});
+ auto drvs = Installable::toDerivations(store, {installable});
if (drvs.size() != 1)
throw Error("'%s' needs to evaluate to a single derivation, but it evaluated to %d derivations",
@@ -393,6 +399,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"}},
@@ -467,9 +479,11 @@ struct CmdDevelop : Common, MixEnvironment
else {
script = "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc;\n" + script;
if (developSettings.bashPrompt != "")
- script += fmt("[ -n \"$PS1\" ] && PS1=%s;\n", shellEscape(developSettings.bashPrompt));
+ script += fmt("[ -n \"$PS1\" ] && PS1=%s;\n",
+ shellEscape(developSettings.bashPrompt.get()));
if (developSettings.bashPromptSuffix != "")
- script += fmt("[ -n \"$PS1\" ] && PS1+=%s;\n", shellEscape(developSettings.bashPromptSuffix));
+ script += fmt("[ -n \"$PS1\" ] && PS1+=%s;\n",
+ shellEscape(developSettings.bashPromptSuffix.get()));
}
writeFull(rcFileFd.get(), script);
@@ -491,12 +505,14 @@ struct CmdDevelop : Common, MixEnvironment
this,
state,
installable->nixpkgsFlakeRef(),
- Strings{"bashInteractive"},
+ "bashInteractive",
+ Strings{},
Strings{"legacyPackages." + settings.thisSystem.get() + "."},
nixpkgsLockFlags);
shell = store->printStorePath(
- toStorePath(getEvalStore(), store, Realise::Outputs, OperateOn::Output, bashInstallable)) + "/bin/bash";
+ Installable::toStorePath(getEvalStore(), store, Realise::Outputs, OperateOn::Output, bashInstallable))
+ + "/bin/bash";
} catch (Error &) {
ignoreException();
}
diff --git a/src/nix/develop.md b/src/nix/develop.md
index c86c4872b..8bcff66c9 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
@@ -54,7 +55,7 @@ R""(
# nix develop /tmp/my-build-env
```
-* Replace all occurences of the store path corresponding to
+* Replace all occurrences of the store path corresponding to
`glibc.dev` with a writable directory:
```console
@@ -87,9 +88,9 @@ the flake's `nixConfig` attribute.
If no flake output attribute is given, `nix develop` tries the following
flake output attributes:
-* `devShell.<system>`
+* `devShells.<system>.default`
-* `defaultPackage.<system>`
+* `packages.<system>.default`
If a flake output *name* is given, `nix develop` tries the following flake
output attributes:
diff --git a/src/nix/diff-closures.cc b/src/nix/diff-closures.cc
index 734c41e0e..0621d662c 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(getEvalStore(), store, Realise::Outputs, operateOn, before);
+ auto beforePath = Installable::toStorePath(getEvalStore(), store, Realise::Outputs, operateOn, before);
auto after = parseInstallable(store, _after);
- auto afterPath = toStorePath(getEvalStore(), store, Realise::Outputs, operateOn, after);
+ auto afterPath = Installable::toStorePath(getEvalStore(), store, Realise::Outputs, operateOn, after);
printClosureDiff(store, beforePath, afterPath, "");
}
};
diff --git a/src/nix/eval.cc b/src/nix/eval.cc
index 65d61e005..8cd04d5fe 100644
--- a/src/nix/eval.cc
+++ b/src/nix/eval.cc
@@ -81,7 +81,7 @@ struct CmdEval : MixJSON, InstallableCommand
recurse = [&](Value & v, const Pos & pos, const Path & path)
{
- state->forceValue(v);
+ state->forceValue(v, pos);
if (v.type() == nString)
// FIXME: disallow strings with contexts?
writeFile(path, v.string.s);
@@ -107,12 +107,12 @@ struct CmdEval : MixJSON, InstallableCommand
else if (raw) {
stopProgressBar();
- std::cout << state->coerceToString(noPos, *v, context);
+ std::cout << *state->coerceToString(noPos, *v, context);
}
else if (json) {
JSONPlaceholder jsonOut(std::cout);
- printValueAsJSON(*state, true, *v, jsonOut, context);
+ printValueAsJSON(*state, true, *v, pos, jsonOut, context);
}
else {
diff --git a/src/nix/flake-check.md b/src/nix/flake-check.md
index d995d6274..07031c909 100644
--- a/src/nix/flake-check.md
+++ b/src/nix/flake-check.md
@@ -31,38 +31,38 @@ at the first error.
The following flake output attributes must be derivations:
* `checks.`*system*`.`*name*
-* `defaultPackage.`*system*`
-* `devShell.`*system*`
-* `devShells.`*system*`.`*name*`
-* `nixosConfigurations.`*name*`.config.system.build.toplevel
+* `defaultPackage.`*system*
+* `devShell.`*system*
+* `devShells.`*system*`.`*name*
+* `nixosConfigurations.`*name*`.config.system.build.toplevel`
* `packages.`*system*`.`*name*
The following flake output attributes must be [app
definitions](./nix3-run.md):
* `apps.`*system*`.`*name*
-* `defaultApp.`*system*`
+* `defaultApp.`*system*
The following flake output attributes must be [template
definitions](./nix3-flake-init.md):
* `defaultTemplate`
-* `templates`.`*name*
+* `templates.`*name*
The following flake output attributes must be *Nixpkgs overlays*:
* `overlay`
-* `overlays`.`*name*
+* `overlays.`*name*
The following flake output attributes must be *NixOS modules*:
* `nixosModule`
-* `nixosModules`.`*name*
+* `nixosModules.`*name*
The following flake output attributes must be
[bundlers](./nix3-bundle.md):
-* `bundlers`.`*name*
+* `bundlers.`*name*
* `defaultBundler`
In addition, the `hydraJobs` output is evaluated in the same way as
diff --git a/src/nix/flake-init.md b/src/nix/flake-init.md
index 890038016..fc1f4f805 100644
--- a/src/nix/flake-init.md
+++ b/src/nix/flake-init.md
@@ -24,19 +24,23 @@ 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 overridden
+template is `templates#templates.default`, but this can be overridden
using `-t`.
# Template definitions
-A flake can declare templates through its `templates` and
-`defaultTemplate` output attributes. A template has two attributes:
+A flake can declare templates through its `templates` output
+attribute. A template has two attributes:
* `description`: A one-line description of the template, in CommonMark
syntax.
* `path`: The path of the directory to be copied.
+* `welcomeText`: A block of markdown text to display when a user initializes a
+ new flake based on this template.
+
+
Here is an example:
```
@@ -45,9 +49,19 @@ outputs = { self }: {
templates.rust = {
path = ./rust;
description = "A simple Rust/Cargo project";
+ welcomeText = ''
+ # Simple Rust/Cargo Template
+ ## Intended usage
+ The intended usage of this flake is...
+
+ ## More info
+ - [Rust language](https://www.rust-lang.org/)
+ - [Rust on the NixOS Wiki](https://nixos.wiki/wiki/Rust)
+ - ...
+ '';
};
- templates.defaultTemplate = self.templates.rust;
+ templates.default = self.templates.rust;
}
```
diff --git a/src/nix/flake-show.md b/src/nix/flake-show.md
index e484cf47e..f3b74285d 100644
--- a/src/nix/flake-show.md
+++ b/src/nix/flake-show.md
@@ -13,10 +13,13 @@ R""(
│ │ └───build: derivation 'patchelf-0.12.20201207.f34751b'
│ └───x86_64-linux
│ └───build: derivation 'patchelf-0.12.20201207.f34751b'
- ├───defaultPackage
- │ ├───aarch64-linux: package 'patchelf-0.12.20201207.f34751b'
- │ ├───i686-linux: package 'patchelf-0.12.20201207.f34751b'
- │ └───x86_64-linux: package 'patchelf-0.12.20201207.f34751b'
+ ├───packages
+ │ ├───aarch64-linux
+ │ │ └───default: package 'patchelf-0.12.20201207.f34751b'
+ │ ├───i686-linux
+ │ │ └───default: package 'patchelf-0.12.20201207.f34751b'
+ │ └───x86_64-linux
+ │ └───default: package 'patchelf-0.12.20201207.f34751b'
├───hydraJobs
│ ├───build
│ │ ├───aarch64-linux: derivation 'patchelf-0.12.20201207.f34751b'
diff --git a/src/nix/flake.cc b/src/nix/flake.cc
index 7e4d23f6e..47a380238 100644
--- a/src/nix/flake.cc
+++ b/src/nix/flake.cc
@@ -13,6 +13,7 @@
#include "registry.hh"
#include "json.hh"
#include "eval-cache.hh"
+#include "markdown.hh"
#include <nlohmann/json.hpp>
#include <queue>
@@ -124,12 +125,13 @@ struct CmdFlakeLock : FlakeCommand
static void enumerateOutputs(EvalState & state, Value & vFlake,
std::function<void(const std::string & name, Value & vProvide, const Pos & pos)> callback)
{
- state.forceAttrs(vFlake);
+ auto pos = vFlake.determinePos(noPos);
+ state.forceAttrs(vFlake, pos);
auto aOutputs = vFlake.attrs->get(state.symbols.create("outputs"));
assert(aOutputs);
- state.forceAttrs(*aOutputs->value);
+ state.forceAttrs(*aOutputs->value, pos);
auto sHydraJobs = state.symbols.create("hydraJobs");
@@ -252,6 +254,14 @@ struct CmdFlakeInfo : CmdFlakeMetadata
}
};
+static bool argHasName(std::string_view arg, std::string_view expected)
+{
+ return
+ arg == expected
+ || arg == "_"
+ || (hasPrefix(arg, "_") && arg.substr(1) == expected);
+}
+
struct CmdFlakeCheck : FlakeCommand
{
bool build = true;
@@ -317,7 +327,7 @@ struct CmdFlakeCheck : FlakeCommand
if (!drvInfo)
throw Error("flake attribute '%s' is not a derivation", attrPath);
// FIXME: check meta attributes
- return std::make_optional(store->parseStorePath(drvInfo->queryDrvPath()));
+ return drvInfo->queryDrvPath();
} catch (Error & e) {
e.addTrace(pos, hintfmt("while checking the derivation '%s'", attrPath));
reportError(e);
@@ -346,10 +356,14 @@ struct CmdFlakeCheck : FlakeCommand
auto checkOverlay = [&](const std::string & attrPath, Value & v, const Pos & pos) {
try {
state->forceValue(v, pos);
- if (!v.isLambda() || v.lambda.fun->hasFormals() || std::string(v.lambda.fun->arg) != "final")
+ if (!v.isLambda()
+ || v.lambda.fun->hasFormals()
+ || !argHasName(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->hasFormals() || std::string(body->arg) != "prev")
+ if (!body
+ || body->hasFormals()
+ || !argHasName(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.
@@ -463,10 +477,7 @@ struct CmdFlakeCheck : FlakeCommand
state->forceValue(v, pos);
if (!v.isLambda())
throw Error("bundler must be a function");
- if (!v.lambda.fun->formals ||
- !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'");
+ // TODO: check types of inputs/outputs?
} catch (Error & e) {
e.addTrace(pos, hintfmt("while checking the template '%s'", attrPath));
reportError(e);
@@ -490,6 +501,17 @@ struct CmdFlakeCheck : FlakeCommand
state->forceValue(vOutput, pos);
+ std::string_view replacement =
+ name == "defaultPackage" ? "packages.<system>.default" :
+ name == "defaultApps" ? "apps.<system>.default" :
+ name == "defaultTemplate" ? "templates.default" :
+ name == "defaultBundler" ? "bundlers.<system>.default" :
+ name == "overlay" ? "overlays.default" :
+ name == "devShell" ? "devShells.<system>.default" :
+ "";
+ if (replacement != "")
+ warn("flake output attribute '%s' is deprecated; use '%s' instead", name, replacement);
+
if (name == "checks") {
state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) {
@@ -597,14 +619,27 @@ struct CmdFlakeCheck : FlakeCommand
*attr.value, *attr.pos);
}
- else if (name == "defaultBundler")
- checkBundler(name, vOutput, pos);
+ else if (name == "defaultBundler") {
+ state->forceAttrs(vOutput, pos);
+ for (auto & attr : *vOutput.attrs) {
+ checkSystemName(attr.name, *attr.pos);
+ checkBundler(
+ fmt("%s.%s", name, attr.name),
+ *attr.value, *attr.pos);
+ }
+ }
else if (name == "bundlers") {
state->forceAttrs(vOutput, pos);
- for (auto & attr : *vOutput.attrs)
- checkBundler(fmt("%s.%s", name, attr.name),
- *attr.value, *attr.pos);
+ for (auto & attr : *vOutput.attrs) {
+ checkSystemName(attr.name, *attr.pos);
+ state->forceAttrs(*attr.value, *attr.pos);
+ for (auto & attr2 : *attr.value->attrs) {
+ checkBundler(
+ fmt("%s.%s.%s", name, attr.name, attr2.name),
+ *attr2.value, *attr2.pos);
+ }
+ }
}
else
@@ -626,12 +661,14 @@ struct CmdFlakeCheck : FlakeCommand
}
};
+static Strings defaultTemplateAttrPathsPrefixes{"templates."};
+static Strings defaultTemplateAttrPaths = {"templates.default", "defaultTemplate"};
+
struct CmdFlakeInitCommon : virtual Args, EvalCommand
{
std::string templateUrl = "templates";
Path destDir;
- const Strings attrsPathPrefixes{"templates."};
const LockFlags lockFlags{ .writeLockFile = false };
CmdFlakeInitCommon()
@@ -646,8 +683,8 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
completeFlakeRefWithFragment(
getEvalState(),
lockFlags,
- attrsPathPrefixes,
- {"defaultTemplate"},
+ defaultTemplateAttrPathsPrefixes,
+ defaultTemplateAttrPaths,
prefix);
}}
});
@@ -662,15 +699,21 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
auto [templateFlakeRef, templateName] = parseFlakeRefWithFragment(templateUrl, absPath("."));
auto installable = InstallableFlake(nullptr,
- evalState, std::move(templateFlakeRef),
- Strings{templateName == "" ? "defaultTemplate" : templateName},
- Strings(attrsPathPrefixes), lockFlags);
+ evalState, std::move(templateFlakeRef), templateName,
+ defaultTemplateAttrPaths,
+ defaultTemplateAttrPathsPrefixes,
+ lockFlags);
auto [cursor, attrPath] = installable.getCursor(*evalState);
- auto templateDir = cursor->getAttr("path")->getString();
+ auto templateDirAttr = cursor->getAttr("path");
+ auto templateDir = templateDirAttr->getString();
- assert(store->isInStore(templateDir));
+ if (!store->isInStore(templateDir))
+ throw TypeError(
+ "'%s' was not found in the Nix store\n"
+ "If you've set '%s' to a string, try using a path instead.",
+ templateDir, templateDirAttr->getAttrPathStr());
std::vector<Path> files;
@@ -705,6 +748,7 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
else
throw Error("file '%s' has unsupported type", from2);
files.push_back(to2);
+ notice("wrote: %s", to2);
}
};
@@ -715,6 +759,11 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
for (auto & s : files) args.push_back(s);
runProgram("git", true, args);
}
+ auto welcomeText = cursor->maybeGetAttr("welcomeText");
+ if (welcomeText) {
+ notice("\n");
+ notice(renderMarkdownToTerminal(welcomeText->getString()));
+ }
}
};
@@ -1040,7 +1089,8 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
(attrPath.size() == 1 && attrPath[0] == "overlay")
|| (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") :
+ (attrPath.size() == 1 && attrPath[0] == "nixosModule")
+ || (attrPath.size() == 2 && attrPath[0] == "nixosModules") ? std::make_pair("nixos-module", "NixOS module") :
std::make_pair("unknown", "unknown");
if (json) {
j.emplace("type", type);
@@ -1138,7 +1188,7 @@ struct CmdFlake : NixMultiCommand
{
if (!command)
throw UsageError("'nix flake' requires a sub-command.");
- settings.requireExperimentalFeature("flakes");
+ settings.requireExperimentalFeature(Xp::Flakes);
command->second->prepare();
command->second->run();
}
diff --git a/src/nix/flake.md b/src/nix/flake.md
index 3b5812a0a..d59915eeb 100644
--- a/src/nix/flake.md
+++ b/src/nix/flake.md
@@ -137,15 +137,6 @@ Currently the `type` attribute can be one of the following:
*path* must be a directory in the file system containing a file
named `flake.nix`.
- If the directory or any of its parents is a Git repository, then
- this is essentially equivalent to `git+file://<path>` (see below),
- except that the `dir` parameter is derived automatically. For
- example, if `/foo/bar` is a Git repository, then the flake reference
- `/foo/bar/flake` is equivalent to `/foo/bar?dir=flake`.
-
- If the directory is not inside a Git repository, then the flake
- contents is the entire contents of *path*.
-
*path* generally must be an absolute path. However, on the command
line, it can be a relative path (e.g. `.` or `./foo`) which is
interpreted as relative to the current directory. In this case, it
@@ -218,6 +209,38 @@ Currently the `type` attribute can be one of the following:
* `github:edolstra/dwarffs/unstable`
* `github:edolstra/dwarffs/d3f2baba8f425779026c6ec04021b2e927f61e31`
+* `sourcehut`: Similar to `github`, is a more efficient way to fetch
+ SourceHut repositories. The following attributes are required:
+
+ * `owner`: The owner of the repository (including leading `~`).
+
+ * `repo`: The name of the repository.
+
+ Like `github`, these are downloaded as tarball archives.
+
+ The URL syntax for `sourcehut` flakes is:
+
+ `sourcehut:<owner>/<repo>(/<rev-or-ref>)?(\?<params>)?`
+
+ `<rev-or-ref>` works the same as `github`. Either a branch or tag name
+ (`ref`), or a commit hash (`rev`) can be specified.
+
+ Since SourceHut allows for self-hosting, you can specify `host` as
+ a parameter, to point to any instances other than `git.sr.ht`.
+
+ Currently, `ref` name resolution only works for Git repositories.
+ You can refer to Mercurial repositories by simply changing `host` to
+ `hg.sr.ht` (or any other Mercurial instance). With the caveat
+ that you must explicitly specify a commit hash (`rev`).
+
+ Some examples:
+
+ * `sourcehut:~misterio/nix-colors`
+ * `sourcehut:~misterio/nix-colors/main`
+ * `sourcehut:~misterio/nix-colors?host=git.example.org`
+ * `sourcehut:~misterio/nix-colors/182b4b8709b8ffe4e9774a4c5d6877bf6bb9a21c`
+ * `sourcehut:~misterio/nix-colors/21c1a380a6915d890d408e9f22203436a35bb2de?host=hg.sr.ht`
+
* `indirect`: Indirections through the flake registry. These have the
form
@@ -245,7 +268,7 @@ derivation):
outputs = { self, nixpkgs }: {
- defaultPackage.x86_64-linux =
+ packages.x86_64-linux.default =
# Notice the reference to nixpkgs here.
with import nixpkgs { system = "x86_64-linux"; };
stdenv.mkDerivation {
@@ -301,6 +324,12 @@ The following attributes are supported in `flake.nix`:
value (e.g. `packages.x86_64-linux` must be an attribute set of
derivations built for the `x86_64-linux` platform).
+* `nixConfig`: a set of `nix.conf` options to be set when evaluating any
+ part of a flake. In the interests of security, only a small set of
+ whitelisted options (currently `bash-prompt`, `bash-prompt-suffix`,
+ and `flake-registry`) are allowed to be set without confirmation so long as
+ `accept-flake-config` is not set in the global configuration.
+
## Flake inputs
The attribute `inputs` specifies the dependencies of a flake, as an
diff --git a/src/nix/hash.cc b/src/nix/hash.cc
index 4535e4ab0..60d9593a7 100644
--- a/src/nix/hash.cc
+++ b/src/nix/hash.cc
@@ -177,7 +177,7 @@ static int compatNixHash(int argc, char * * argv)
else if (*arg == "--base32") base32 = true;
else if (*arg == "--truncate") truncate = true;
else if (*arg == "--type") {
- string s = getArg(*arg, arg, end);
+ std::string s = getArg(*arg, arg, end);
ht = parseHashType(s);
}
else if (*arg == "--to-base16") op = opTo16;
diff --git a/src/nix/ls.cc b/src/nix/ls.cc
index c1dc9a95b..07554994b 100644
--- a/src/nix/ls.cc
+++ b/src/nix/ls.cc
@@ -157,7 +157,7 @@ struct CmdLsNar : Command, MixLs
void run() override
{
- list(makeNarAccessor(make_ref<std::string>(readFile(narPath))));
+ list(makeNarAccessor(readFile(narPath)));
}
};
diff --git a/src/nix/main.cc b/src/nix/main.cc
index 2c3976689..b923f2535 100644
--- a/src/nix/main.cc
+++ b/src/nix/main.cc
@@ -59,7 +59,6 @@ struct HelpRequested { };
struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
{
- bool printBuildLogs = false;
bool useNet = true;
bool refresh = false;
bool showVersion = false;
@@ -187,14 +186,11 @@ static void showHelp(std::vector<std::string> subcommand, MultiCommand & topleve
, "/"),
*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 attrs = state.buildBindings(16);
+ attrs.alloc("command").mkString(toplevel.toJSON().dump());
auto vRes = state.allocValue();
- state.callFunction(*vGenerateManpage, *vArgs, *vRes, noPos);
+ state.callFunction(*vGenerateManpage, state.allocValue()->mkAttrs(attrs), *vRes, noPos);
auto attr = vRes->attrs->get(state.symbols.create(mdName + ".md"));
if (!attr)
@@ -255,6 +251,16 @@ void mainWrapped(int argc, char * * argv)
initNix();
initGC();
+ #if __linux__
+ if (getuid() == 0) {
+ try {
+ saveMountNamespace();
+ if (unshare(CLONE_NEWNS) == -1)
+ throw SysError("setting up a private mount namespace");
+ } catch (Error & e) { }
+ }
+ #endif
+
programPath = argv[0];
auto programName = std::string(baseNameOf(programPath));
@@ -263,11 +269,15 @@ void mainWrapped(int argc, char * * argv)
if (legacy) return legacy(argc, argv);
}
- verbosity = lvlNotice;
- settings.verboseBuild = false;
evalSettings.pureEval = true;
setLogFormat("bar");
+ settings.verboseBuild = false;
+ if (isatty(STDERR_FILENO)) {
+ verbosity = lvlNotice;
+ } else {
+ verbosity = lvlInfo;
+ }
Finally f([] { logger->stop(); });
@@ -300,7 +310,14 @@ void mainWrapped(int argc, char * * argv)
Finally printCompletions([&]()
{
if (completions) {
- std::cout << (pathCompletions ? "filenames\n" : "no-filenames\n");
+ switch (completionType) {
+ case ctNormal:
+ std::cout << "normal\n"; break;
+ case ctFilenames:
+ std::cout << "filenames\n"; break;
+ case ctAttrs:
+ std::cout << "attrs\n"; break;
+ }
for (auto & s : *completions)
std::cout << s.completion << "\t" << s.description << "\n";
}
@@ -337,7 +354,7 @@ void mainWrapped(int argc, char * * argv)
if (args.command->first != "repl"
&& args.command->first != "doctor"
&& args.command->first != "upgrade-nix")
- settings.requireExperimentalFeature("nix-command");
+ settings.requireExperimentalFeature(Xp::NixCommand);
if (args.useNet && !haveInternet()) {
warn("you don't have Internet access; disabling some network-dependent features");
diff --git a/src/nix/make-content-addressable.cc b/src/nix/make-content-addressable.cc
index 9bbfe4747..cceaacac9 100644
--- a/src/nix/make-content-addressable.cc
+++ b/src/nix/make-content-addressable.cc
@@ -57,10 +57,10 @@ struct CmdMakeContentAddressable : StorePathsCommand, MixJSON
refs.references.insert(std::move(replacement));
}
- *sink.s = rewriteStrings(*sink.s, rewrites);
+ sink.s = rewriteStrings(sink.s, rewrites);
HashModuloSink hashModuloSink(htSHA256, oldHashPart);
- hashModuloSink(*sink.s);
+ hashModuloSink(sink.s);
auto narHash = hashModuloSink.finish().first;
@@ -78,14 +78,14 @@ struct CmdMakeContentAddressable : StorePathsCommand, MixJSON
},
narHash,
};
- info.narSize = sink.s->size();
+ info.narSize = sink.s.size();
if (!json)
notice("rewrote '%s' to '%s'", pathS, store->printStorePath(info.path));
auto source = sinkToSource([&](Sink & nextSink) {
RewritingSink rsink2(oldHashPart, std::string(info.path.hashPart()), nextSink);
- rsink2(*sink.s);
+ rsink2(sink.s);
rsink2.flush();
});
diff --git a/src/nix/nix.md b/src/nix/nix.md
index d10de7c01..0dacadee6 100644
--- a/src/nix/nix.md
+++ b/src/nix/nix.md
@@ -57,13 +57,49 @@ the Nix store. Here are the recognised types of installables:
These have the form *flakeref*[`#`*attrpath*], where *flakeref* is a
flake reference and *attrpath* is an optional attribute path. For
more information on flakes, see [the `nix flake` manual
- page](./nix3-flake.md). Flake references are most commonly a flake
- identifier in the flake registry (e.g. `nixpkgs`) or a path
- (e.g. `/path/to/my-flake` or `.`).
+ page](./nix3-flake.md). Flake references are most commonly a flake
+ identifier in the flake registry (e.g. `nixpkgs`), or a raw path
+ (e.g. `/path/to/my-flake` or `.` or `../foo`), or a full URL
+ (e.g. `github:nixos/nixpkgs` or `path:.`)
+
+ When the flake reference is a raw path (a path without any URL
+ scheme), it is interpreted as a `path:` or `git+file:` url in the following
+ way:
+
+ - If the path is within a Git repository, then the url will be of the form
+ `git+file://[GIT_REPO_ROOT]?dir=[RELATIVE_FLAKE_DIR_PATH]`
+ where `GIT_REPO_ROOT` is the path to the root of the git repository,
+ and `RELATIVE_FLAKE_DIR_PATH` is the path (relative to the directory
+ root) of the closest parent of the given path that contains a `flake.nix` within
+ the git repository.
+ If no such directory exists, then Nix will error-out.
+
+ Note that the search will only include files indexed by git. In particular, files
+ which are matched by `.gitignore` or have never been `git add`-ed will not be
+ available in the flake. If this is undesirable, specify `path:<directory>` explicitly;
+
+ For example, if `/foo/bar` is a git repository with the following structure:
+ ```
+ .
+ └── baz
+ ├── blah
+ │  └── file.txt
+ └── flake.nix
+ ```
+
+ Then `/foo/bar/baz/blah` will resolve to `git+file:///foo/bar?dir=baz`
+
+ - If the supplied path is not a git repository, then the url will have the form
+ `path:FLAKE_DIR_PATH` where `FLAKE_DIR_PATH` is the closest parent
+ of the supplied path that contains a `flake.nix` file (within the same file-system).
+ If no such directory exists, then Nix will error-out.
+
+ For example, if `/foo/bar/flake.nix` exists, then `/foo/bar/baz/` will resolve to
+ `path:/foo/bar`
If *attrpath* is omitted, Nix tries some default values; for most
- subcommands, the default is `defaultPackage.`*system*
- (e.g. `defaultPackage.x86_64-linux`), but some subcommands have
+ subcommands, the default is `packages.`*system*`.default`
+ (e.g. `packages.x86_64-linux.default`), but some subcommands have
other defaults. If *attrpath* *is* specified, *attrpath* is
interpreted as relative to one or more prefixes; for most
subcommands, these are `packages.`*system*,
diff --git a/src/nix/path-info.cc b/src/nix/path-info.cc
index 3743d7504..d690fe594 100644
--- a/src/nix/path-info.cc
+++ b/src/nix/path-info.cc
@@ -97,7 +97,7 @@ struct CmdPathInfo : StorePathsCommand, MixJSON
for (auto & storePath : storePaths) {
auto info = store->queryPathInfo(storePath);
- auto storePathS = store->printStorePath(storePath);
+ auto storePathS = store->printStorePath(info->path);
std::cout << storePathS;
diff --git a/src/nix/ping-store.cc b/src/nix/ping-store.cc
index 62b645b06..3c3b7bb45 100644
--- a/src/nix/ping-store.cc
+++ b/src/nix/ping-store.cc
@@ -20,7 +20,10 @@ struct CmdPingStore : StoreCommand
void run(ref<Store> store) override
{
+ notice("Store URL: %s", store->getUri());
store->connect();
+ if (auto version = store->getVersion())
+ notice("Version: %s", *version);
}
};
diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc
index c13603d42..35faa7a88 100644
--- a/src/nix/prefetch.cc
+++ b/src/nix/prefetch.cc
@@ -16,7 +16,7 @@ using namespace nix;
/* If ‘url’ starts with ‘mirror://’, then resolve it using the list of
mirrors defined in Nixpkgs. */
-string resolveMirrorUrl(EvalState & state, string url)
+std::string resolveMirrorUrl(EvalState & state, const std::string & url)
{
if (url.substr(0, 9) != "mirror://") return url;
@@ -28,18 +28,18 @@ string resolveMirrorUrl(EvalState & state, string url)
Value vMirrors;
// FIXME: use nixpkgs flake
state.eval(state.parseExprFromString("import <nixpkgs/pkgs/build-support/fetchurl/mirrors.nix>", "."), vMirrors);
- state.forceAttrs(vMirrors);
+ state.forceAttrs(vMirrors, noPos);
auto mirrorList = vMirrors.attrs->find(state.symbols.create(mirrorName));
if (mirrorList == vMirrors.attrs->end())
throw Error("unknown mirror name '%s'", mirrorName);
- state.forceList(*mirrorList->value);
+ state.forceList(*mirrorList->value, noPos);
if (mirrorList->value->listSize() < 1)
throw Error("mirror URL '%s' did not expand to anything", url);
- auto mirror = state.forceString(*mirrorList->value->listElems()[0]);
- return mirror + (hasSuffix(mirror, "/") ? "" : "/") + string(s, p + 1);
+ std::string mirror(state.forceString(*mirrorList->value->listElems()[0]));
+ return mirror + (hasSuffix(mirror, "/") ? "" : "/") + s.substr(p + 1);
}
std::tuple<StorePath, Hash> prefetchFile(
@@ -134,10 +134,10 @@ static int main_nix_prefetch_url(int argc, char * * argv)
{
{
HashType ht = htSHA256;
- std::vector<string> args;
+ std::vector<std::string> args;
bool printPath = getEnv("PRINT_PATH") == "1";
bool fromExpr = false;
- string attrPath;
+ std::string attrPath;
bool unpack = false;
bool executable = false;
std::optional<std::string> name;
@@ -153,7 +153,7 @@ static int main_nix_prefetch_url(int argc, char * * argv)
else if (*arg == "--version")
printVersion("nix-prefetch-url");
else if (*arg == "--type") {
- string s = getArg(*arg, arg, end);
+ auto s = getArg(*arg, arg, end);
ht = parseHashType(s);
}
else if (*arg == "--print-path")
@@ -192,7 +192,7 @@ static int main_nix_prefetch_url(int argc, char * * argv)
/* If -A is given, get the URL from the specified Nix
expression. */
- string url;
+ std::string url;
if (!fromExpr) {
if (args.empty())
throw UsageError("you must specify a URL");
@@ -202,11 +202,11 @@ static int main_nix_prefetch_url(int argc, char * * argv)
Value vRoot;
state->evalFile(path, vRoot);
Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot).first);
- state->forceAttrs(v);
+ state->forceAttrs(v, noPos);
/* Extract the URL. */
auto & attr = v.attrs->need(state->symbols.create("urls"));
- state->forceList(*attr.value);
+ state->forceList(*attr.value, noPos);
if (attr.value->listSize() < 1)
throw Error("'urls' list is empty");
url = state->forceString(*attr.value->listElems()[0]);
diff --git a/src/nix/profile-list.md b/src/nix/profile-list.md
index 5c29c0b02..bdab9a208 100644
--- a/src/nix/profile-list.md
+++ b/src/nix/profile-list.md
@@ -8,7 +8,7 @@ R""(
# nix profile list
0 flake:nixpkgs#legacyPackages.x86_64-linux.spotify github:NixOS/nixpkgs/c23db78bbd474c4d0c5c3c551877523b4a50db06#legacyPackages.x86_64-linux.spotify /nix/store/akpdsid105phbbvknjsdh7hl4v3fhjkr-spotify-1.1.46.916.g416cacf1
1 flake:nixpkgs#legacyPackages.x86_64-linux.zoom-us github:NixOS/nixpkgs/c23db78bbd474c4d0c5c3c551877523b4a50db06#legacyPackages.x86_64-linux.zoom-us /nix/store/89pmjmbih5qpi7accgacd17ybpgp4xfm-zoom-us-5.4.53350.1027
- 2 flake:blender-bin#defaultPackage.x86_64-linux github:edolstra/nix-warez/d09d7eea893dcb162e89bc67f6dc1ced14abfc27?dir=blender#defaultPackage.x86_64-linux /nix/store/zfgralhqjnam662kqsgq6isjw8lhrflz-blender-bin-2.91.0
+ 2 flake:blender-bin#packages.x86_64-linux.default github:edolstra/nix-warez/d09d7eea893dcb162e89bc67f6dc1ced14abfc27?dir=blender#packages.x86_64-linux.default /nix/store/zfgralhqjnam662kqsgq6isjw8lhrflz-blender-bin-2.91.0
```
# Description
diff --git a/src/nix/profile.cc b/src/nix/profile.cc
index b49be256e..6260b0c10 100644
--- a/src/nix/profile.cc
+++ b/src/nix/profile.cc
@@ -61,6 +61,27 @@ struct ProfileElement
{
return std::tuple(describe(), storePaths) < std::tuple(other.describe(), other.storePaths);
}
+
+ void updateStorePaths(ref<Store> evalStore, ref<Store> store, Installable & installable)
+ {
+ // FIXME: respect meta.outputsToInstall
+ storePaths.clear();
+ for (auto & buildable : getBuiltPaths(evalStore, store, installable.toDerivedPaths())) {
+ std::visit(overloaded {
+ [&](const BuiltPath::Opaque & bo) {
+ storePaths.insert(bo.path);
+ },
+ [&](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)) {
+ storePaths.insert(output.second);
+ }
+ },
+ }, buildable.raw());
+ }
+ }
};
struct ProfileManifest
@@ -105,7 +126,7 @@ struct ProfileManifest
for (auto & drvInfo : drvInfos) {
ProfileElement element;
- element.storePaths = {state.store->parseStorePath(drvInfo.queryOutPath())};
+ element.storePaths = {drvInfo.queryOutPath()};
elements.emplace_back(std::move(element));
}
}
@@ -157,7 +178,7 @@ struct ProfileManifest
StringSink sink;
dumpPath(tempDir, sink);
- auto narHash = hashString(htSHA256, *sink.s);
+ auto narHash = hashString(htSHA256, sink.s);
ValidPathInfo info {
*store,
@@ -174,9 +195,9 @@ struct ProfileManifest
narHash,
};
info.references = std::move(references);
- info.narSize = sink.s->size();
+ info.narSize = sink.s.size();
- auto source = StringSource { *sink.s };
+ StringSource source(sink.s);
store->addToStore(info, source);
return std::move(info.path);
@@ -241,54 +262,26 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
{
ProfileManifest manifest(*getEvalState(), *profile);
- std::vector<DerivedPath> pathsToBuild;
+ auto builtPaths = Installable::build(getEvalStore(), store, Realise::Outputs, installables, bmNormal);
for (auto & installable : installables) {
+ ProfileElement element;
+
if (auto installable2 = std::dynamic_pointer_cast<InstallableFlake>(installable)) {
+ // FIXME: make build() return this?
auto [attrPath, resolvedRef, drv] = installable2->toDerivation();
-
- ProfileElement element;
- if (!drv.outPath)
- throw UnimplementedError("CA derivations are not yet supported by 'nix profile'");
- element.storePaths = {*drv.outPath}; // FIXME
element.source = ProfileElementSource{
installable2->flakeRef,
resolvedRef,
attrPath,
};
+ }
- pathsToBuild.push_back(DerivedPath::Built{drv.drvPath, StringSet{drv.outputName}});
+ element.updateStorePaths(getEvalStore(), store, *installable);
- manifest.elements.emplace_back(std::move(element));
- } else {
- auto buildables = build(getEvalStore(), store, Realise::Outputs, {installable}, bmNormal);
-
- for (auto & buildable : buildables) {
- ProfileElement element;
-
- std::visit(overloaded {
- [&](const BuiltPath::Opaque & bo) {
- pathsToBuild.push_back(bo);
- element.storePaths.insert(bo.path);
- },
- [&](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(DerivedPath::Built{bfd.drvPath, {output.first}});
- element.storePaths.insert(output.second);
- }
- },
- }, buildable.raw());
-
- manifest.elements.emplace_back(std::move(element));
- }
- }
+ manifest.elements.push_back(std::move(element));
}
- store->buildPaths(pathsToBuild);
-
updateProfile(manifest.build(store));
}
};
@@ -304,7 +297,11 @@ public:
expectArgs("elements", &_matchers);
}
- typedef std::variant<size_t, Path, std::regex> Matcher;
+ struct RegexPattern {
+ std::string pattern;
+ std::regex reg;
+ };
+ typedef std::variant<size_t, Path, RegexPattern> Matcher;
std::vector<Matcher> getMatchers(ref<Store> store)
{
@@ -316,7 +313,7 @@ public:
else if (store->isStorePath(s))
res.push_back(s);
else
- res.push_back(std::regex(s, std::regex::extended | std::regex::icase));
+ res.push_back(RegexPattern{s,std::regex(s, std::regex::extended | std::regex::icase)});
}
return res;
@@ -329,9 +326,9 @@ public:
if (*n == pos) return true;
} else if (auto path = std::get_if<Path>(&matcher)) {
if (element.storePaths.count(store.parseStorePath(*path))) return true;
- } else if (auto regex = std::get_if<std::regex>(&matcher)) {
+ } else if (auto regex = std::get_if<RegexPattern>(&matcher)) {
if (element.source
- && std::regex_match(element.source->attrPath, *regex))
+ && std::regex_match(element.source->attrPath, regex->reg))
return true;
}
}
@@ -364,16 +361,30 @@ struct CmdProfileRemove : virtual EvalCommand, MixDefaultProfile, MixProfileElem
for (size_t i = 0; i < oldManifest.elements.size(); ++i) {
auto & element(oldManifest.elements[i]);
- if (!matches(*store, element, i, matchers))
+ if (!matches(*store, element, i, matchers)) {
newManifest.elements.push_back(std::move(element));
+ } else {
+ notice("removing '%s'", element.describe());
+ }
}
- // FIXME: warn about unused matchers?
-
+ auto removedCount = oldManifest.elements.size() - newManifest.elements.size();
printInfo("removed %d packages, kept %d packages",
- oldManifest.elements.size() - newManifest.elements.size(),
+ removedCount,
newManifest.elements.size());
+ if (removedCount == 0) {
+ for (auto matcher: matchers) {
+ if (const size_t * index = std::get_if<size_t>(&matcher)){
+ warn("'%d' is not a valid index", *index);
+ } else if (const Path * path = std::get_if<Path>(&matcher)){
+ warn("'%s' does not match any paths", *path);
+ } else if (const RegexPattern * regex = std::get_if<RegexPattern>(&matcher)){
+ warn("'%s' does not match any packages", regex->pattern);
+ }
+ }
+ warn ("Use 'nix profile list' to see the current profile.");
+ }
updateProfile(newManifest.build(store));
}
};
@@ -398,47 +409,69 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
auto matchers = getMatchers(store);
- // FIXME: code duplication
- std::vector<DerivedPath> pathsToBuild;
+ std::vector<std::shared_ptr<Installable>> installables;
+ std::vector<size_t> indices;
+
+ auto upgradedCount = 0;
for (size_t i = 0; i < manifest.elements.size(); ++i) {
auto & element(manifest.elements[i]);
if (element.source
- && !element.source->originalRef.input.isImmutable()
+ && !element.source->originalRef.input.isLocked()
&& matches(*store, element, i, matchers))
{
+ upgradedCount++;
+
Activity act(*logger, lvlChatty, actUnknown,
fmt("checking '%s' for updates", element.source->attrPath));
- InstallableFlake installable(
+ auto installable = std::make_shared<InstallableFlake>(
this,
getEvalState(),
FlakeRef(element.source->originalRef),
- {element.source->attrPath},
- {},
+ "",
+ Strings{element.source->attrPath},
+ Strings{},
lockFlags);
- auto [attrPath, resolvedRef, drv] = installable.toDerivation();
+ auto [attrPath, resolvedRef, drv] = installable->toDerivation();
if (element.source->resolvedRef == resolvedRef) continue;
printInfo("upgrading '%s' from flake '%s' to '%s'",
element.source->attrPath, element.source->resolvedRef, resolvedRef);
- if (!drv.outPath)
- throw UnimplementedError("CA derivations are not yet supported by 'nix profile'");
- element.storePaths = {*drv.outPath}; // FIXME
element.source = ProfileElementSource{
- installable.flakeRef,
+ installable->flakeRef,
resolvedRef,
attrPath,
};
- pathsToBuild.push_back(DerivedPath::Built{drv.drvPath, {drv.outputName}});
+ installables.push_back(installable);
+ indices.push_back(i);
}
}
- store->buildPaths(pathsToBuild);
+ if (upgradedCount == 0) {
+ for (auto & matcher : matchers) {
+ if (const size_t * index = std::get_if<size_t>(&matcher)){
+ warn("'%d' is not a valid index", *index);
+ } else if (const Path * path = std::get_if<Path>(&matcher)){
+ warn("'%s' does not match any paths", *path);
+ } else if (const RegexPattern * regex = std::get_if<RegexPattern>(&matcher)){
+ warn("'%s' does not match any packages", regex->pattern);
+ }
+ }
+ warn ("Use 'nix profile list' to see the current profile.");
+ }
+
+ auto builtPaths = Installable::build(getEvalStore(), store, Realise::Outputs, installables, bmNormal);
+
+ for (size_t i = 0; i < installables.size(); ++i) {
+ auto & installable = installables.at(i);
+ auto & element = manifest.elements[indices.at(i)];
+ element.updateStorePaths(getEvalStore(), store, *installable);
+ }
updateProfile(manifest.build(store));
}
diff --git a/src/nix/profile.md b/src/nix/profile.md
index d3ddcd3d1..0a4ff2fa9 100644
--- a/src/nix/profile.md
+++ b/src/nix/profile.md
@@ -96,7 +96,7 @@ has the following fields:
user specified, but the one resulting from applying the default
attribute paths and prefixes; for instance, `hello` might resolve to
`packages.x86_64-linux.hello` and the empty string to
- `defaultPackage.x86_64-linux`.
+ `packages.x86_64-linux.default`.
* `storePath`: The paths in the Nix store containing the package.
diff --git a/src/nix/realisation.cc b/src/nix/realisation.cc
index dfa8ff449..c9a7157cd 100644
--- a/src/nix/realisation.cc
+++ b/src/nix/realisation.cc
@@ -46,7 +46,7 @@ struct CmdRealisationInfo : BuiltPathsCommand, MixJSON
void run(ref<Store> store, BuiltPaths && paths) override
{
- settings.requireExperimentalFeature("ca-derivations");
+ settings.requireExperimentalFeature(Xp::CaDerivations);
RealisedPath::Set realisations;
for (auto & builtPath : paths) {
diff --git a/src/nix/realisation/info.md b/src/nix/realisation/info.md
index 852240f44..8aa986516 100644
--- a/src/nix/realisation/info.md
+++ b/src/nix/realisation/info.md
@@ -1,7 +1,7 @@
R"MdBoundary(
# Description
-Display some informations about the given realisation
+Display some information about the given realisation
# Examples
diff --git a/src/nix/registry.cc b/src/nix/registry.cc
index 6a92576c7..c496f94f8 100644
--- a/src/nix/registry.cc
+++ b/src/nix/registry.cc
@@ -226,6 +226,7 @@ struct CmdRegistry : virtual NixMultiCommand
void run() override
{
+ settings.requireExperimentalFeature(Xp::Flakes);
if (!command)
throw UsageError("'nix registry' requires a sub-command.");
command->second->prepare();
diff --git a/src/nix/registry.md b/src/nix/registry.md
index a1674bd2e..d5c9ef442 100644
--- a/src/nix/registry.md
+++ b/src/nix/registry.md
@@ -2,7 +2,7 @@ R""(
# Description
-`nix flake` provides subcommands for managing *flake
+`nix registry` provides subcommands for managing *flake
registries*. Flake registries are a convenience feature that allows
you to refer to flakes using symbolic identifiers such as `nixpkgs`,
rather than full URLs such as `git://github.com/NixOS/nixpkgs`. You
diff --git a/src/nix/repl.cc b/src/nix/repl.cc
index c1233ab46..3a51a13e6 100644
--- a/src/nix/repl.cc
+++ b/src/nix/repl.cc
@@ -28,7 +28,6 @@ extern "C" {
#include "common-eval-args.hh"
#include "get-drvs.hh"
#include "derivations.hh"
-#include "affinity.hh"
#include "globals.hh"
#include "command.hh"
#include "finally.hh"
@@ -46,7 +45,7 @@ struct NixRepl
: gc
#endif
{
- string curDir;
+ std::string curDir;
std::unique_ptr<EvalState> state;
Bindings * autoArgs;
@@ -63,30 +62,30 @@ struct NixRepl
NixRepl(const Strings & searchPath, nix::ref<Store> store);
~NixRepl();
void mainLoop(const std::vector<std::string> & files);
- StringSet completePrefix(string prefix);
- bool getLine(string & input, const std::string &prompt);
+ StringSet completePrefix(const std::string & prefix);
+ bool getLine(std::string & input, const std::string &prompt);
StorePath getDerivationPath(Value & v);
- bool processLine(string line);
+ bool processLine(std::string line);
void loadFile(const Path & path);
void loadFlake(const std::string & flakeRef);
void initEnv();
void reloadFiles();
void addAttrsToScope(Value & attrs);
void addVarToScope(const Symbol & name, Value & v);
- Expr * parseString(string s);
- void evalString(string s, Value & v);
+ Expr * parseString(std::string s);
+ void evalString(std::string s, Value & v);
- typedef set<Value *> ValuesSeen;
+ typedef std::set<Value *> ValuesSeen;
std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth);
std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen);
};
-string removeWhitespace(string s)
+std::string removeWhitespace(std::string s)
{
s = chomp(s);
size_t n = s.find_first_not_of(" \n\r\t");
- if (n != string::npos) s = string(s, n);
+ if (n != std::string::npos) s = std::string(s, n);
return s;
}
@@ -105,7 +104,7 @@ NixRepl::~NixRepl()
write_history(historyFile.c_str());
}
-string runNix(Path program, const Strings & args,
+std::string runNix(Path program, const Strings & args,
const std::optional<std::string> & input = {})
{
auto subprocessEnv = getEnv();
@@ -199,14 +198,14 @@ 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;
+ std::string error = ANSI_RED "error:" ANSI_NORMAL " ";
+ 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";
@@ -253,7 +252,7 @@ void NixRepl::mainLoop(const std::vector<std::string> & files)
}
-bool NixRepl::getLine(string & input, const std::string &prompt)
+bool NixRepl::getLine(std::string & input, const std::string & prompt)
{
struct sigaction act, old;
sigset_t savedSignalMask, set;
@@ -279,6 +278,7 @@ bool NixRepl::getLine(string & input, const std::string &prompt)
};
setupSignals();
+ Finally resetTerminal([&]() { rl_deprep_terminal(); });
char * s = readline(prompt.c_str());
Finally doFree([&]() { free(s); });
restoreSignals();
@@ -297,7 +297,7 @@ bool NixRepl::getLine(string & input, const std::string &prompt)
}
-StringSet NixRepl::completePrefix(string prefix)
+StringSet NixRepl::completePrefix(const std::string & prefix)
{
StringSet completions;
@@ -313,7 +313,7 @@ StringSet NixRepl::completePrefix(string prefix)
size_t slash, dot;
- if ((slash = cur.rfind('/')) != string::npos) {
+ if ((slash = cur.rfind('/')) != std::string::npos) {
try {
auto dir = std::string(cur, 0, slash);
auto prefix2 = std::string(cur, slash + 1);
@@ -323,11 +323,11 @@ StringSet NixRepl::completePrefix(string prefix)
}
} catch (Error &) {
}
- } else if ((dot = cur.rfind('.')) == string::npos) {
+ } else if ((dot = cur.rfind('.')) == std::string::npos) {
/* This is a variable name; look it up in the current scope. */
StringSet::iterator i = varNames.lower_bound(cur);
while (i != varNames.end()) {
- if (string(*i, 0, cur.size()) != cur) break;
+ if (i->substr(0, cur.size()) != cur) break;
completions.insert(prev + *i);
i++;
}
@@ -336,17 +336,17 @@ StringSet NixRepl::completePrefix(string prefix)
/* This is an expression that should evaluate to an
attribute set. Evaluate it to get the names of the
attributes. */
- string expr(cur, 0, dot);
- string cur2 = string(cur, dot + 1);
+ auto expr = cur.substr(0, dot);
+ auto cur2 = cur.substr(dot + 1);
Expr * e = parseString(expr);
Value v;
e->eval(*state, *env, v);
- state->forceAttrs(v);
+ state->forceAttrs(v, noPos);
for (auto & i : *v.attrs) {
- string name = i.name;
- if (string(name, 0, cur2.size()) != cur2) continue;
+ std::string name = i.name;
+ if (name.substr(0, cur2.size()) != cur2) continue;
completions.insert(prev + expr + "." + name);
}
@@ -356,6 +356,8 @@ StringSet NixRepl::completePrefix(string prefix)
// Quietly ignore evaluation errors.
} catch (UndefinedVarError & e) {
// Quietly ignore undefined variable errors.
+ } catch (BadURL & e) {
+ // Quietly ignore BadURL flake-related errors.
}
}
@@ -363,7 +365,7 @@ StringSet NixRepl::completePrefix(string prefix)
}
-bool isVarName(const string & s)
+static bool isVarName(std::string_view s)
{
if (s.size() == 0) return false;
char c = s[0];
@@ -382,26 +384,27 @@ StorePath NixRepl::getDerivationPath(Value & v) {
auto drvInfo = getDerivation(*state, v, false);
if (!drvInfo)
throw Error("expression does not evaluate to a derivation, so I can't build it");
- Path drvPathRaw = drvInfo->queryDrvPath();
- if (drvPathRaw == "")
- throw Error("expression did not evaluate to a valid derivation (no drv path)");
- StorePath drvPath = state->store->parseStorePath(drvPathRaw);
- if (!state->store->isValidPath(drvPath))
- throw Error("expression did not evaluate to a valid derivation (invalid drv path)");
- return drvPath;
+ auto drvPath = drvInfo->queryDrvPath();
+ if (!drvPath)
+ throw Error("expression did not evaluate to a valid derivation (no 'drvPath' attribute)");
+ if (!state->store->isValidPath(*drvPath))
+ throw Error("expression evaluated to invalid derivation '%s'", state->store->printStorePath(*drvPath));
+ return *drvPath;
}
-bool NixRepl::processLine(string line)
+bool NixRepl::processLine(std::string line)
{
if (line == "") return true;
- string command, arg;
+ _isInterrupted = false;
+
+ std::string command, arg;
if (line[0] == ':') {
size_t p = line.find_first_of(" \n\r\t");
- command = string(line, 0, p);
- if (p != string::npos) arg = removeWhitespace(string(line, p));
+ command = line.substr(0, p);
+ if (p != std::string::npos) arg = removeWhitespace(line.substr(p));
} else {
arg = line;
}
@@ -425,7 +428,9 @@ bool NixRepl::processLine(string line)
<< " :s <expr> Build dependencies of derivation, then start nix-shell\n"
<< " :t <expr> Describe result of evaluation\n"
<< " :u <expr> Build derivation, then start nix-shell\n"
- << " :doc <expr> Show documentation of a builtin function\n";
+ << " :doc <expr> Show documentation of a builtin function\n"
+ << " :log <expr> Show logs for a derivation\n"
+ << " :st [bool] Enable, disable or toggle showing traces for errors\n";
}
else if (command == ":a" || command == ":add") {
@@ -457,7 +462,7 @@ bool NixRepl::processLine(string line)
if (v.type() == nPath || v.type() == nString) {
PathSet context;
auto filename = state->coerceToString(noPos, v, context);
- pos.file = state->symbols.create(filename);
+ pos.file = state->symbols.create(*filename);
} else if (v.isLambda()) {
pos = v.lambda.fun->pos;
} else {
@@ -469,7 +474,10 @@ bool NixRepl::processLine(string line)
auto args = editorFor(pos);
auto editor = args.front();
args.pop_front();
- runProgram(editor, true, args);
+
+ // runProgram redirects stdout to a StringSink,
+ // using runProgram2 to allow editors to display their UI
+ runProgram2(RunOptions { .program = editor, .searchPath = true, .args = args });
// Reload right after exiting the editor
state->resetFileCache();
@@ -479,9 +487,10 @@ 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);
@@ -491,26 +500,41 @@ bool NixRepl::processLine(string line)
runNix("nix-shell", {state->store->printStorePath(drvPath)});
}
- else if (command == ":b" || command == ":i" || command == ":s") {
+ else if (command == ":b" || command == ":i" || command == ":s" || command == ":log") {
Value v;
evalString(arg, v);
StorePath drvPath = getDerivationPath(v);
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. */
- try {
- runNix("nix", {"build", "--no-link", drvPathRaw});
- 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));
- } catch (ExecError &) {
- }
+ state->store->buildPaths({DerivedPath::Built{drvPath}});
+ auto drv = state->store->readDerivation(drvPath);
+ logger->cout("\nThis derivation produced the following outputs:");
+ for (auto & [outputName, outputPath] : state->store->queryDerivationOutputMap(drvPath))
+ logger->cout(" %s -> %s", outputName, state->store->printStorePath(outputPath));
} else if (command == ":i") {
runNix("nix-env", {"-i", drvPathRaw});
+ } else if (command == ":log") {
+ settings.readOnlyMode = true;
+ Finally roModeReset([&]() {
+ settings.readOnlyMode = false;
+ });
+ auto subs = getDefaultSubstituters();
+
+ subs.push_front(state->store);
+
+ bool foundLog = false;
+ RunPager pager;
+ for (auto & sub : subs) {
+ auto log = sub->getBuildLog(drvPath);
+ if (log) {
+ printInfo("got build log for '%s' from '%s'", drvPathRaw, sub->getUri());
+ logger->writeToStdout(*log);
+ foundLog = true;
+ break;
+ }
+ }
+ if (!foundLog) throw Error("build log of '%s' is not available", drvPathRaw);
} else {
runNix("nix-shell", {drvPathRaw});
}
@@ -541,25 +565,37 @@ 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");
}
+ else if (command == ":st" || command == ":show-trace") {
+ if (arg == "false" || (arg == "" && loggerSettings.showTrace)) {
+ std::cout << "not showing error traces\n";
+ loggerSettings.showTrace = false;
+ } else if (arg == "true" || (arg == "" && !loggerSettings.showTrace)) {
+ std::cout << "showing error traces\n";
+ loggerSettings.showTrace = true;
+ } else {
+ throw Error("unexpected argument '%s' to %s", arg, command);
+ };
+ }
+
else if (command != "")
throw Error("unknown command '%1%'", command);
else {
size_t p = line.find('=');
- string name;
- if (p != string::npos &&
+ std::string name;
+ if (p != std::string::npos &&
p < line.size() &&
line[p + 1] != '=' &&
- isVarName(name = removeWhitespace(string(line, 0, p))))
+ isVarName(name = removeWhitespace(line.substr(0, p))))
{
- Expr * e = parseString(string(line, p + 1));
+ Expr * e = parseString(line.substr(p + 1));
Value & v(*state->allocValue());
v.mkThunk(env, e);
addVarToScope(state->symbols.create(name), v);
@@ -586,9 +622,12 @@ void NixRepl::loadFile(const Path & path)
void NixRepl::loadFlake(const std::string & flakeRefS)
{
+ if (flakeRefS.empty())
+ throw Error("cannot use ':load-flake' without a path specified. (Use '.' for the current working directory.)");
+
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);
+ if (evalSettings.pureEval && !flakeRef.input.isLocked())
+ throw Error("cannot use ':load-flake' on locked flake reference '%s' (use --impure to override)", flakeRefS);
Value v;
@@ -626,9 +665,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);
}
}
@@ -636,10 +675,18 @@ void NixRepl::reloadFiles()
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;
+ state->forceAttrs(attrs, [&]() { return attrs.determinePos(noPos); });
+ if (displ + attrs.attrs->size() >= envSize)
+ throw Error("environment full; cannot add more variables");
+
+ for (auto & i : *attrs.attrs) {
+ staticEnv.vars.emplace_back(i.name, displ);
+ env->values[displ++] = i.value;
+ varNames.insert((std::string) i.name);
+ }
+ staticEnv.sort();
+ staticEnv.deduplicate();
+ notice("Added %1% variables.", attrs.attrs->size());
}
@@ -647,24 +694,27 @@ void NixRepl::addVarToScope(const Symbol & name, Value & v)
{
if (displ >= envSize)
throw Error("environment full; cannot add more variables");
- staticEnv.vars[name] = displ;
+ if (auto oldVar = staticEnv.find(name); oldVar != staticEnv.vars.end())
+ staticEnv.vars.erase(oldVar);
+ staticEnv.vars.emplace_back(name, displ);
+ staticEnv.sort();
env->values[displ++] = &v;
- varNames.insert((string) name);
+ varNames.insert((std::string) name);
}
-Expr * NixRepl::parseString(string s)
+Expr * NixRepl::parseString(std::string s)
{
- Expr * e = state->parseExprFromString(s, curDir, staticEnv);
+ Expr * e = state->parseExprFromString(std::move(s), curDir, staticEnv);
return e;
}
-void NixRepl::evalString(string s, Value & v)
+void NixRepl::evalString(std::string s, Value & v)
{
Expr * e = parseString(s);
e->eval(*state, *env, v);
- state->forceValue(v);
+ state->forceValue(v, [&]() { return v.determinePos(noPos); });
}
@@ -694,7 +744,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
str.flush();
checkInterrupt();
- state->forceValue(v);
+ state->forceValue(v, [&]() { return v.determinePos(noPos); });
switch (v.type()) {
@@ -729,14 +779,17 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
str << "«derivation ";
Bindings::iterator i = v.attrs->find(state->sDrvPath);
PathSet context;
- Path drvPath = i != v.attrs->end() ? state->coerceToPath(*i->pos, *i->value, context) : "???";
- str << drvPath << "»";
+ if (i != v.attrs->end())
+ str << state->store->printStorePath(state->coerceToStorePath(*i->pos, *i->value, context));
+ else
+ str << "???";
+ str << "»";
}
else if (maxDepth > 0) {
str << "{ ";
- typedef std::map<string, Value *> Sorted;
+ typedef std::map<std::string, Value *> Sorted;
Sorted sorted;
for (auto & i : *v.attrs)
sorted[i.name] = i.value;
@@ -747,7 +800,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
else
printStringValue(str, i.first.c_str());
str << " = ";
- if (seen.find(i.second) != seen.end())
+ if (seen.count(i.second))
str << "«repeated»";
else
try {
@@ -770,12 +823,12 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
str << "[ ";
if (maxDepth > 0)
- for (unsigned int n = 0; n < v.listSize(); ++n) {
- if (seen.find(v.listElems()[n]) != seen.end())
+ for (auto elem : v.listItems()) {
+ if (seen.count(elem))
str << "«repeated»";
else
try {
- printValue(str, *v.listElems()[n], maxDepth - 1, seen);
+ printValue(str, *elem, maxDepth - 1, seen);
} catch (AssertionError & e) {
str << ANSI_RED "«error: " << e.msg() << "»" ANSI_NORMAL;
}
diff --git a/src/nix/repl.md b/src/nix/repl.md
index bba60f871..9b6f2bee3 100644
--- a/src/nix/repl.md
+++ b/src/nix/repl.md
@@ -35,14 +35,17 @@ R""(
nix-repl> emacs.drvPath
"/nix/store/lp0sjrhgg03y2n0l10n70rg0k7hhyz0l-emacs-27.1.drv"
- nix-repl> drv = runCommand "hello" { buildInputs = [ hello ]; } "hello > $out"
+ nix-repl> drv = runCommand "hello" { buildInputs = [ hello ]; } "hello; hello > $out"
- nix-repl> :b x
+ nix-repl> :b drv
this derivation produced the following outputs:
out -> /nix/store/0njwbgwmkwls0w5dv9mpc1pq5fj39q0l-hello
nix-repl> builtins.readFile drv
"Hello, world!\n"
+
+ nix-repl> :log drv
+ Hello, world!
```
# Description
diff --git a/src/nix/run.cc b/src/nix/run.cc
index b01fdebaa..033263c36 100644
--- a/src/nix/run.cc
+++ b/src/nix/run.cc
@@ -8,7 +8,6 @@
#include "finally.hh"
#include "fs-accessor.hh"
#include "progress-bar.hh"
-#include "affinity.hh"
#include "eval.hh"
#if __linux__
@@ -92,7 +91,7 @@ struct CmdShell : InstallablesCommand, MixEnvironment
void run(ref<Store> store) override
{
- auto outPaths = toStorePaths(getEvalStore(), store, Realise::Outputs, OperateOn::Output, installables);
+ auto outPaths = Installable::toStorePaths(getEvalStore(), store, Realise::Outputs, OperateOn::Output, installables);
auto accessor = store->getFSAccessor();
@@ -159,7 +158,10 @@ struct CmdRun : InstallableCommand
Strings getDefaultFlakeAttrPaths() override
{
- Strings res{"defaultApp." + settings.thisSystem.get()};
+ Strings res{
+ "apps." + settings.thisSystem.get() + ".default",
+ "defaultApp." + settings.thisSystem.get(),
+ };
for (auto & s : SourceExprCommand::getDefaultFlakeAttrPaths())
res.push_back(s);
return res;
diff --git a/src/nix/run.md b/src/nix/run.md
index a76750376..a0f362076 100644
--- a/src/nix/run.md
+++ b/src/nix/run.md
@@ -43,19 +43,24 @@ program specified by the app definition.
If *installable* evaluates to a derivation, it will try to execute the
program `<out>/bin/<name>`, where *out* is the primary output store
-path of the derivation and *name* is the `meta.mainProgram` attribute
-of the derivation if it exists, and otherwise the name part of the
-value of the `name` attribute of the derivation (e.g. if `name` is set
-to `hello-1.10`, it will run `$out/bin/hello`).
+path of the derivation, and *name* is the first of the following that
+exists:
+
+* The `meta.mainProgram` attribute of the derivation.
+* The `pname` attribute of the derivation.
+* The name part of the value of the `name` attribute of the derivation.
+
+For instance, if `name` is set to `hello-1.10`, `nix run` will run
+`$out/bin/hello`.
# Flake output attributes
If no flake output attribute is given, `nix run` tries the following
flake output attributes:
-* `defaultApp.<system>`
+* `apps.<system>.default`
-* `defaultPackage.<system>`
+* `packages.<system>.default`
If an attribute *name* is given, `nix run` tries the following flake
output attributes:
@@ -69,7 +74,7 @@ output attributes:
# Apps
An app is specified by a flake output attribute named
-`apps.<system>.<name>` or `defaultApp.<system>`. It looks like this:
+`apps.<system>.<name>`. It looks like this:
```nix
apps.x86_64-linux.blender_2_79 = {
diff --git a/src/nix/search.cc b/src/nix/search.cc
index 0d8fdd5c2..e9307342c 100644
--- a/src/nix/search.cc
+++ b/src/nix/search.cc
@@ -9,6 +9,7 @@
#include "shared.hh"
#include "eval-cache.hh"
#include "attr-path.hh"
+#include "fmt.hh"
#include <regex>
#include <fstream>
@@ -20,16 +21,6 @@ std::string wrap(std::string prefix, std::string s)
return prefix + s + ANSI_NORMAL;
}
-std::string hilite(const std::string & s, const std::smatch & m, std::string postfix)
-{
- return
- m.empty()
- ? s
- : std::string(m.prefix())
- + ANSI_GREEN + std::string(m.str()) + postfix
- + std::string(m.suffix());
-}
-
struct CmdSearch : InstallableCommand, MixJSON
{
std::vector<std::string> res;
@@ -100,8 +91,6 @@ struct CmdSearch : InstallableCommand, MixJSON
};
if (cursor.isDerivation()) {
- size_t found = 0;
-
DrvName name(cursor.getAttr("name")->getString());
auto aMeta = cursor.maybeGetAttr("meta");
@@ -110,21 +99,31 @@ struct CmdSearch : InstallableCommand, MixJSON
std::replace(description.begin(), description.end(), '\n', ' ');
auto attrPath2 = concatStringsSep(".", attrPath);
- std::smatch attrPathMatch;
- std::smatch descriptionMatch;
- std::smatch nameMatch;
+ std::vector<std::smatch> attrPathMatches;
+ std::vector<std::smatch> descriptionMatches;
+ std::vector<std::smatch> nameMatches;
+ bool found = false;
for (auto & regex : regexes) {
- std::regex_search(attrPath2, attrPathMatch, regex);
- std::regex_search(name.name, nameMatch, regex);
- std::regex_search(description, descriptionMatch, regex);
- if (!attrPathMatch.empty()
- || !nameMatch.empty()
- || !descriptionMatch.empty())
- found++;
+ found = false;
+ auto addAll = [&found](std::sregex_iterator it, std::vector<std::smatch> & vec) {
+ const auto end = std::sregex_iterator();
+ while (it != end) {
+ vec.push_back(*it++);
+ found = true;
+ }
+ };
+
+ addAll(std::sregex_iterator(attrPath2.begin(), attrPath2.end(), regex), attrPathMatches);
+ addAll(std::sregex_iterator(name.name.begin(), name.name.end(), regex), nameMatches);
+ addAll(std::sregex_iterator(description.begin(), description.end(), regex), descriptionMatches);
+
+ if (!found)
+ break;
}
- if (found == res.size()) {
+ if (found)
+ {
results++;
if (json) {
auto jsonElem = jsonOut->object(attrPath2);
@@ -132,15 +131,15 @@ struct CmdSearch : InstallableCommand, MixJSON
jsonElem.attr("version", name.version);
jsonElem.attr("description", description);
} else {
- auto name2 = hilite(name.name, nameMatch, "\e[0;2m");
+ auto name2 = hiliteMatches(name.name, std::move(nameMatches), ANSI_GREEN, "\e[0;2m");
if (results > 1) logger->cout("");
logger->cout(
"* %s%s",
- wrap("\e[0;1m", hilite(attrPath2, attrPathMatch, "\e[0;1m")),
+ wrap("\e[0;1m", hiliteMatches(attrPath2, std::move(attrPathMatches), ANSI_GREEN, "\e[0;1m")),
name.version != "" ? " (" + name.version + ")" : "");
if (description != "")
logger->cout(
- " %s", hilite(description, descriptionMatch, ANSI_NORMAL));
+ " %s", hiliteMatches(description, std::move(descriptionMatches), ANSI_GREEN, ANSI_NORMAL));
}
}
}
diff --git a/src/nix/shell.md b/src/nix/shell.md
index 2a379e03f..90b81fb2f 100644
--- a/src/nix/shell.md
+++ b/src/nix/shell.md
@@ -41,8 +41,8 @@ R""(
# Description
-`nix shell` runs a command in an environment in which the `$PATH`
-variable provides the specified *installables*. If not command is
-specified, it starts the default shell of your user account.
+`nix shell` runs a command in an environment in which the `$PATH` variable
+provides the specified *installables*. If no command is specified, it starts the
+default shell of your user account specified by `$SHELL`.
)""
diff --git a/src/nix/show-derivation.cc b/src/nix/show-derivation.cc
index f07fedaf0..66d172441 100644
--- a/src/nix/show-derivation.cc
+++ b/src/nix/show-derivation.cc
@@ -40,7 +40,7 @@ struct CmdShowDerivation : InstallablesCommand
void run(ref<Store> store) override
{
- auto drvPaths = toDerivations(store, installables, true);
+ auto drvPaths = Installable::toDerivations(store, installables, true);
if (recursive) {
StorePathSet closure;
diff --git a/src/nix/sigs.cc b/src/nix/sigs.cc
index e89ad2650..a08314a25 100644
--- a/src/nix/sigs.cc
+++ b/src/nix/sigs.cc
@@ -219,8 +219,7 @@ struct CmdKey : NixMultiCommand
void run() override
{
if (!command)
- throw UsageError("'nix flake' requires a sub-command.");
- settings.requireExperimentalFeature("flakes");
+ throw UsageError("'nix key' requires a sub-command.");
command->second->prepare();
command->second->run();
}
diff --git a/src/nix/store-copy-log.cc b/src/nix/store-copy-log.cc
new file mode 100644
index 000000000..079cd6b3e
--- /dev/null
+++ b/src/nix/store-copy-log.cc
@@ -0,0 +1,46 @@
+#include "command.hh"
+#include "shared.hh"
+#include "store-api.hh"
+#include "sync.hh"
+#include "thread-pool.hh"
+
+#include <atomic>
+
+using namespace nix;
+
+struct CmdCopyLog : virtual CopyCommand, virtual InstallablesCommand
+{
+ std::string description() override
+ {
+ return "copy build logs between Nix stores";
+ }
+
+ std::string doc() override
+ {
+ return
+ #include "store-copy-log.md"
+ ;
+ }
+
+ Category category() override { return catUtility; }
+
+ void run(ref<Store> srcStore) override
+ {
+ auto dstStore = getDstStore();
+
+ StorePathSet drvPaths;
+
+ for (auto & i : installables)
+ for (auto & drvPath : i->toDrvPaths(getEvalStore()))
+ drvPaths.insert(drvPath);
+
+ for (auto & drvPath : drvPaths) {
+ if (auto log = srcStore->getBuildLog(drvPath))
+ dstStore->addBuildLog(drvPath, *log);
+ else
+ throw Error("build log for '%s' is not available", srcStore->printStorePath(drvPath));
+ }
+ }
+};
+
+static auto rCmdCopyLog = registerCommand2<CmdCopyLog>({"store", "copy-log"});
diff --git a/src/nix/store-copy-log.md b/src/nix/store-copy-log.md
new file mode 100644
index 000000000..19ae57079
--- /dev/null
+++ b/src/nix/store-copy-log.md
@@ -0,0 +1,33 @@
+R""(
+
+# Examples
+
+* To copy the build log of the `hello` package from
+ https://cache.nixos.org to the local store:
+
+ ```console
+ # nix store copy-log --from https://cache.nixos.org --eval-store auto nixpkgs#hello
+ ```
+
+ You can verify that the log is available locally:
+
+ ```console
+ # nix log --substituters '' nixpkgs#hello
+ ```
+
+ (The flag `--substituters ''` avoids querying
+ `https://cache.nixos.org` for the log.)
+
+* To copy the log for a specific store derivation via SSH:
+
+ ```console
+ # nix store copy-log --to ssh-ng://machine /nix/store/ilgm50plpmcgjhcp33z6n4qbnpqfhxym-glibc-2.33-59.drv
+ ```
+
+# Description
+
+`nix store copy-log` copies build logs between two Nix stores. The
+source store is specified using `--from` and the destination using
+`--to`. If one of these is omitted, it defaults to the local store.
+
+)""
diff --git a/src/nix/store-delete.cc b/src/nix/store-delete.cc
index e4a3cb554..aa7a8b12f 100644
--- a/src/nix/store-delete.cc
+++ b/src/nix/store-delete.cc
@@ -2,6 +2,7 @@
#include "common-args.hh"
#include "shared.hh"
#include "store-api.hh"
+#include "gc-store.hh"
using namespace nix;
@@ -32,12 +33,14 @@ struct CmdStoreDelete : StorePathsCommand
void run(ref<Store> store, std::vector<StorePath> && storePaths) override
{
+ auto & gcStore = requireGcStore(*store);
+
for (auto & path : storePaths)
options.pathsToDelete.insert(path);
GCResults results;
PrintFreed freed(true, results);
- store->collectGarbage(options, results);
+ gcStore.collectGarbage(options, results);
}
};
diff --git a/src/nix/store-gc.cc b/src/nix/store-gc.cc
index a2d74066e..21718dc0c 100644
--- a/src/nix/store-gc.cc
+++ b/src/nix/store-gc.cc
@@ -2,6 +2,7 @@
#include "common-args.hh"
#include "shared.hh"
#include "store-api.hh"
+#include "gc-store.hh"
using namespace nix;
@@ -33,10 +34,12 @@ struct CmdStoreGC : StoreCommand, MixDryRun
void run(ref<Store> store) override
{
+ auto & gcStore = requireGcStore(*store);
+
options.action = dryRun ? GCOptions::gcReturnDead : GCOptions::gcDeleteDead;
GCResults results;
PrintFreed freed(options.action == GCOptions::gcDeleteDead, results);
- store->collectGarbage(options, results);
+ gcStore.collectGarbage(options, results);
}
};
diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc
index 9cd567896..17a5a77ee 100644
--- a/src/nix/upgrade-nix.cc
+++ b/src/nix/upgrade-nix.cc
@@ -140,7 +140,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand
auto state = std::make_unique<EvalState>(Strings(), store);
auto v = state->allocValue();
- state->eval(state->parseExprFromString(*res.data, "/no-such-path"), *v);
+ state->eval(state->parseExprFromString(res.data, "/no-such-path"), *v);
Bindings & bindings(*state->allocBindings(0));
auto v2 = findAlongAttrPath(*state, settings.thisSystem, bindings, *v).first;
diff --git a/src/nix/why-depends.cc b/src/nix/why-depends.cc
index 2f6b361bb..1d9ab28ba 100644
--- a/src/nix/why-depends.cc
+++ b/src/nix/why-depends.cc
@@ -31,11 +31,25 @@ struct CmdWhyDepends : SourceExprCommand
{
std::string _package, _dependency;
bool all = false;
+ bool precise = false;
CmdWhyDepends()
{
- expectArg("package", &_package);
- expectArg("dependency", &_dependency);
+ expectArgs({
+ .label = "package",
+ .handler = {&_package},
+ .completer = {[&](size_t, std::string_view prefix) {
+ completeInstallable(prefix);
+ }}
+ });
+
+ expectArgs({
+ .label = "dependency",
+ .handler = {&_dependency},
+ .completer = {[&](size_t, std::string_view prefix) {
+ completeInstallable(prefix);
+ }}
+ });
addFlag({
.longName = "all",
@@ -43,6 +57,12 @@ struct CmdWhyDepends : SourceExprCommand
.description = "Show all edges in the dependency graph leading from *package* to *dependency*, rather than just a shortest path.",
.handler = {&all, true},
});
+
+ addFlag({
+ .longName = "precise",
+ .description = "For each edge in the dependency graph, show the files in the parent that cause the dependency.",
+ .handler = {&precise, true},
+ });
}
std::string description() override
@@ -62,9 +82,9 @@ struct CmdWhyDepends : SourceExprCommand
void run(ref<Store> store) override
{
auto package = parseInstallable(store, _package);
- auto packagePath = toStorePath(getEvalStore(), store, Realise::Outputs, operateOn, package);
+ auto packagePath = Installable::toStorePath(getEvalStore(), store, Realise::Outputs, operateOn, package);
auto dependency = parseInstallable(store, _dependency);
- auto dependencyPath = toStorePath(getEvalStore(), store, Realise::Derivation, operateOn, dependency);
+ auto dependencyPath = Installable::toStorePath(getEvalStore(), store, Realise::Derivation, operateOn, dependency);
auto dependencyPathHash = dependencyPath.hashPart();
StorePathSet closure;
@@ -137,26 +157,28 @@ struct CmdWhyDepends : SourceExprCommand
closure (i.e., that have a non-infinite distance to
'dependency'). Print every edge on a path between `package`
and `dependency`. */
- std::function<void(Node &, const string &, const string &)> printNode;
+ std::function<void(Node &, const std::string &, const std::string &)> printNode;
struct BailOut { };
- printNode = [&](Node & node, const string & firstPad, const string & tailPad) {
+ printNode = [&](Node & node, const std::string & firstPad, const std::string & tailPad) {
auto pathS = store->printStorePath(node.path);
assert(node.dist != inf);
- logger->cout("%s%s%s%s" ANSI_NORMAL,
- firstPad,
- node.visited ? "\e[38;5;244m" : "",
- firstPad != "" ? "→ " : "",
- pathS);
+ if (precise) {
+ logger->cout("%s%s%s%s" ANSI_NORMAL,
+ firstPad,
+ node.visited ? "\e[38;5;244m" : "",
+ firstPad != "" ? "→ " : "",
+ pathS);
+ }
if (node.path == dependencyPath && !all
&& packagePath != dependencyPath)
throw BailOut();
if (node.visited) return;
- node.visited = true;
+ if (precise) node.visited = true;
/* Sort the references by distance to `dependency` to
ensure that the shortest path is printed first. */
@@ -224,9 +246,8 @@ struct CmdWhyDepends : SourceExprCommand
// FIXME: should use scanForReferences().
- visitPath(pathS);
+ if (precise) visitPath(pathS);
- RunPager pager;
for (auto & ref : refs) {
std::string hash(ref.second->path.hashPart());
@@ -240,13 +261,27 @@ struct CmdWhyDepends : SourceExprCommand
if (!all) break;
}
+ if (!precise) {
+ auto pathS = store->printStorePath(ref.second->path);
+ logger->cout("%s%s%s%s" ANSI_NORMAL,
+ firstPad,
+ ref.second->visited ? "\e[38;5;244m" : "",
+ last ? treeLast : treeConn,
+ pathS);
+ node.visited = true;
+ }
+
printNode(*ref.second,
tailPad + (last ? treeNull : treeLine),
tailPad + (last ? treeNull : treeLine));
}
};
+ RunPager pager;
try {
+ if (!precise) {
+ logger->cout("%s", store->printStorePath(graph.at(packagePath).path));
+ }
printNode(graph.at(packagePath), "", "");
} catch (BailOut & ) { }
}