aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJohn Ericson <John.Ericson@Obsidian.Systems>2021-01-15 02:01:24 +0000
committerJohn Ericson <John.Ericson@Obsidian.Systems>2021-01-15 02:01:24 +0000
commit0027b05a15e5845c5ce70c86b5b1a34e7caff039 (patch)
tree9264ad326d070ec2dcf5d19a373721672eb1df2e /src
parentfed123724679de89d3f56a4c01b5c4c96f93e584 (diff)
parent7a472a76d4dcbbd0eb7832c0bdcb120d32881e8b (diff)
Merge remote-tracking branch 'upstream/master' into non-local-store-build
Diffstat (limited to 'src')
-rw-r--r--src/libexpr/attr-path.cc14
-rw-r--r--src/libexpr/common-eval-args.cc10
-rw-r--r--src/libexpr/eval-cache.cc2
-rw-r--r--src/libexpr/flake/flake.cc19
-rw-r--r--src/libexpr/get-drvs.cc8
-rw-r--r--src/libexpr/primops/fetchTree.cc2
-rw-r--r--src/libfetchers/attrs.cc4
-rw-r--r--src/libfetchers/github.cc4
-rw-r--r--src/libfetchers/mercurial.cc2
-rw-r--r--src/libfetchers/path.cc6
-rw-r--r--src/libfetchers/tarball.cc2
-rw-r--r--src/libmain/common-args.cc13
-rw-r--r--src/libmain/common-args.hh4
-rw-r--r--src/libmain/shared.cc41
-rw-r--r--src/libmain/shared.hh18
-rw-r--r--src/libstore/build/derivation-goal.cc41
-rw-r--r--src/libstore/crypto.cc54
-rw-r--r--src/libstore/crypto.hh24
-rw-r--r--src/libstore/filetransfer.hh2
-rw-r--r--src/libstore/globals.cc30
-rw-r--r--src/libstore/globals.hh4
-rw-r--r--src/libstore/local-store.cc73
-rw-r--r--src/libstore/local-store.hh6
-rw-r--r--src/libstore/names.cc12
-rw-r--r--src/libstore/nar-info.cc8
-rw-r--r--src/libstore/parsed-derivations.cc4
-rw-r--r--src/libstore/profiles.cc11
-rw-r--r--src/libstore/references.cc3
-rw-r--r--src/libstore/store-api.cc13
-rw-r--r--src/libstore/store-api.hh5
-rw-r--r--src/libutil/args.hh56
-rw-r--r--src/libutil/config.cc4
-rw-r--r--src/libutil/tests/tests.cc26
-rw-r--r--src/libutil/util.hh40
-rw-r--r--src/nix-env/nix-env.cc19
-rw-r--r--src/nix-env/user-env.cc8
-rw-r--r--src/nix-prefetch-url/nix-prefetch-url.cc232
-rw-r--r--src/nix-store/nix-store.cc22
-rw-r--r--src/nix/add-to-store.cc2
-rw-r--r--src/nix/build.cc6
-rw-r--r--src/nix/bundle.cc6
-rw-r--r--src/nix/command.cc14
-rw-r--r--src/nix/command.hh4
-rw-r--r--src/nix/copy.cc8
-rw-r--r--src/nix/daemon.cc (renamed from src/nix-daemon/nix-daemon.cc)114
-rw-r--r--src/nix/daemon.md21
-rw-r--r--src/nix/develop.cc16
-rw-r--r--src/nix/eval.cc6
-rw-r--r--src/nix/flake-prefetch.md28
-rw-r--r--src/nix/flake.cc48
-rw-r--r--src/nix/hash.cc24
-rw-r--r--src/nix/installables.cc26
-rw-r--r--src/nix/key-convert-secret-to-public.md19
-rw-r--r--src/nix/key-generate-secret.md48
-rw-r--r--src/nix/local.mk1
-rw-r--r--src/nix/ls.cc6
-rw-r--r--src/nix/main.cc19
-rw-r--r--src/nix/path-info.cc8
-rw-r--r--src/nix/prefetch.cc319
-rw-r--r--src/nix/profile-history.md26
-rw-r--r--src/nix/profile-list.md (renamed from src/nix/profile-info.md)2
-rw-r--r--src/nix/profile.cc125
-rw-r--r--src/nix/run.cc2
-rw-r--r--src/nix/show-derivation.cc2
-rw-r--r--src/nix/sigs.cc95
-rw-r--r--src/nix/store-delete.cc44
-rw-r--r--src/nix/store-delete.md24
-rw-r--r--src/nix/store-gc.cc43
-rw-r--r--src/nix/store-gc.md21
-rw-r--r--src/nix/store-prefetch-file.md32
-rw-r--r--src/nix/store-repair.cc27
-rw-r--r--src/nix/store-repair.md32
-rw-r--r--src/nix/upgrade-nix.cc4
-rw-r--r--src/nix/verify.cc16
-rw-r--r--src/nix/why-depends.cc2
75 files changed, 1428 insertions, 658 deletions
diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc
index 2d37dcb7e..9dd557205 100644
--- a/src/libexpr/attr-path.cc
+++ b/src/libexpr/attr-path.cc
@@ -52,9 +52,7 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
for (auto & attr : tokens) {
/* Is i an index (integer) or a normal attribute name? */
- enum { apAttr, apIndex } apType = apAttr;
- unsigned int attrIndex;
- if (string2Int(attr, attrIndex)) apType = apIndex;
+ auto attrIndex = string2Int<unsigned int>(attr);
/* Evaluate the expression. */
Value * vNew = state.allocValue();
@@ -65,7 +63,7 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
/* It should evaluate to either a set or an expression,
according to what is specified in the attrPath. */
- if (apType == apAttr) {
+ if (!attrIndex) {
if (v->type() != nAttrs)
throw TypeError(
@@ -82,17 +80,17 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
pos = *a->pos;
}
- else if (apType == apIndex) {
+ else {
if (!v->isList())
throw TypeError(
"the expression selected by the selection path '%1%' should be a list but is %2%",
attrPath,
showType(*v));
- if (attrIndex >= v->listSize())
- throw AttrPathNotFound("list index %1% in selection path '%2%' is out of range", attrIndex, attrPath);
+ if (*attrIndex >= v->listSize())
+ throw AttrPathNotFound("list index %1% in selection path '%2%' is out of range", *attrIndex, attrPath);
- v = v->listElems()[attrIndex];
+ v = v->listElems()[*attrIndex];
pos = noPos;
}
diff --git a/src/libexpr/common-eval-args.cc b/src/libexpr/common-eval-args.cc
index 10c1a6975..ffe782454 100644
--- a/src/libexpr/common-eval-args.cc
+++ b/src/libexpr/common-eval-args.cc
@@ -14,14 +14,14 @@ MixEvalArgs::MixEvalArgs()
{
addFlag({
.longName = "arg",
- .description = "argument to be passed to Nix functions",
+ .description = "Pass the value *expr* as the argument *name* to Nix functions.",
.labels = {"name", "expr"},
.handler = {[&](std::string name, std::string expr) { autoArgs[name] = 'E' + expr; }}
});
addFlag({
.longName = "argstr",
- .description = "string-valued argument to be passed to Nix functions",
+ .description = "Pass the string *string* as the argument *name* to Nix functions.",
.labels = {"name", "string"},
.handler = {[&](std::string name, std::string s) { autoArgs[name] = 'S' + s; }},
});
@@ -29,14 +29,14 @@ MixEvalArgs::MixEvalArgs()
addFlag({
.longName = "include",
.shortName = 'I',
- .description = "add a path to the list of locations used to look up `<...>` file names",
+ .description = "Add *path* to the list of locations used to look up `<...>` file names.",
.labels = {"path"},
.handler = {[&](std::string s) { searchPath.push_back(s); }}
});
addFlag({
.longName = "impure",
- .description = "allow access to mutable paths and repositories",
+ .description = "Allow access to mutable paths and repositories.",
.handler = {[&]() {
evalSettings.pureEval = false;
}},
@@ -44,7 +44,7 @@ MixEvalArgs::MixEvalArgs()
addFlag({
.longName = "override-flake",
- .description = "override a flake registry value",
+ .description = "Override the flake registries, redirecting *original-ref* to *resolved-ref*.",
.labels = {"original-ref", "resolved-ref"},
.handler = {[&](std::string _from, std::string _to) {
auto from = parseFlakeRef(_from, absPath("."));
diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc
index 75e9af787..98d91c905 100644
--- a/src/libexpr/eval-cache.cc
+++ b/src/libexpr/eval-cache.cc
@@ -394,7 +394,7 @@ Value & AttrCursor::forceValue()
cachedValue = {root->db->setString(getKey(), v.string.s, v.string.context),
string_t{v.string.s, {}}};
else if (v.type() == nPath)
- cachedValue = {root->db->setString(getKey(), v.path), v.path};
+ cachedValue = {root->db->setString(getKey(), v.path), string_t{v.path, {}}};
else if (v.type() == nBool)
cachedValue = {root->db->setBool(getKey(), v.boolean), v.boolean};
else if (v.type() == nAttrs)
diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc
index 4f021570c..61aeae543 100644
--- a/src/libexpr/flake/flake.cc
+++ b/src/libexpr/flake/flake.cc
@@ -120,11 +120,20 @@ static FlakeInput parseFlakeInput(EvalState & state,
expectType(state, nString, *attr.value, *attr.pos);
input.follows = parseInputPath(attr.value->string.s);
} else {
- if (attr.value->type() == nString)
- attrs.emplace(attr.name, attr.value->string.s);
- else
- throw TypeError("flake input attribute '%s' is %s while a string is expected",
- attr.name, showType(*attr.value));
+ switch (attr.value->type()) {
+ case nString:
+ attrs.emplace(attr.name, attr.value->string.s);
+ break;
+ case nBool:
+ attrs.emplace(attr.name, Explicit<bool> { attr.value->boolean });
+ break;
+ case nInt:
+ attrs.emplace(attr.name, attr.value->integer);
+ break;
+ default:
+ throw TypeError("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
+ attr.name, showType(*attr.value));
+ }
}
} catch (Error & e) {
e.addTrace(*attr.pos, hintfmt("in flake attribute '%s'", attr.name));
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index 32c115c12..1a3990ea1 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -214,8 +214,8 @@ NixInt DrvInfo::queryMetaInt(const string & name, NixInt def)
if (v->type() == nString) {
/* Backwards compatibility with before we had support for
integer meta fields. */
- NixInt n;
- if (string2Int(v->string.s, n)) return n;
+ if (auto n = string2Int<NixInt>(v->string.s))
+ return *n;
}
return def;
}
@@ -228,8 +228,8 @@ NixFloat DrvInfo::queryMetaFloat(const string & name, NixFloat def)
if (v->type() == nString) {
/* Backwards compatibility with before we had support for
float meta fields. */
- NixFloat n;
- if (string2Float(v->string.s, n)) return n;
+ if (auto n = string2Float<NixFloat>(v->string.s))
+ return *n;
}
return def;
}
diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc
index 133299030..ab80be2d3 100644
--- a/src/libexpr/primops/fetchTree.cc
+++ b/src/libexpr/primops/fetchTree.cc
@@ -104,7 +104,7 @@ static void fetchTree(
else if (attr.value->type() == nBool)
attrs.emplace(attr.name, Explicit<bool>{attr.value->boolean});
else if (attr.value->type() == nInt)
- attrs.emplace(attr.name, attr.value->integer);
+ attrs.emplace(attr.name, uint64_t(attr.value->integer));
else
throw TypeError("fetchTree argument '%s' is %s while a string, Boolean or integer is expected",
attr.name, showType(*attr.value));
diff --git a/src/libfetchers/attrs.cc b/src/libfetchers/attrs.cc
index 720b19fcd..a565d19d4 100644
--- a/src/libfetchers/attrs.cc
+++ b/src/libfetchers/attrs.cc
@@ -11,11 +11,11 @@ Attrs jsonToAttrs(const nlohmann::json & json)
for (auto & i : json.items()) {
if (i.value().is_number())
- attrs.emplace(i.key(), i.value().get<int64_t>());
+ attrs.emplace(i.key(), i.value().get<uint64_t>());
else if (i.value().is_string())
attrs.emplace(i.key(), i.value().get<std::string>());
else if (i.value().is_boolean())
- attrs.emplace(i.key(), i.value().get<bool>());
+ attrs.emplace(i.key(), Explicit<bool> { i.value().get<bool>() });
else
throw Error("unsupported input attribute type in lock file");
}
diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc
index 92ff224f7..db1ced5d6 100644
--- a/src/libfetchers/github.cc
+++ b/src/libfetchers/github.cc
@@ -195,14 +195,14 @@ struct GitArchiveInputScheme : InputScheme
auto [tree, lastModified] = downloadTarball(store, url.url, "source", true, url.headers);
- input.attrs.insert_or_assign("lastModified", lastModified);
+ input.attrs.insert_or_assign("lastModified", uint64_t(lastModified));
getCache()->add(
store,
immutableAttrs,
{
{"rev", rev->gitRev()},
- {"lastModified", lastModified}
+ {"lastModified", uint64_t(lastModified)}
},
tree.storePath,
true);
diff --git a/src/libfetchers/mercurial.cc b/src/libfetchers/mercurial.cc
index 07a51059d..0eb401e10 100644
--- a/src/libfetchers/mercurial.cc
+++ b/src/libfetchers/mercurial.cc
@@ -301,7 +301,7 @@ struct MercurialInputScheme : InputScheme
Attrs infoAttrs({
{"rev", input.getRev()->gitRev()},
- {"revCount", (int64_t) revCount},
+ {"revCount", (uint64_t) revCount},
});
if (!_input.getRev())
diff --git a/src/libfetchers/path.cc b/src/libfetchers/path.cc
index bcb904c0d..d1003de57 100644
--- a/src/libfetchers/path.cc
+++ b/src/libfetchers/path.cc
@@ -20,10 +20,10 @@ struct PathInputScheme : InputScheme
if (name == "rev" || name == "narHash")
input.attrs.insert_or_assign(name, value);
else if (name == "revCount" || name == "lastModified") {
- uint64_t n;
- if (!string2Int(value, n))
+ if (auto n = string2Int<uint64_t>(value))
+ input.attrs.insert_or_assign(name, *n);
+ else
throw Error("path URL '%s' has invalid parameter '%s'", url.to_string(), name);
- input.attrs.insert_or_assign(name, n);
}
else
throw Error("path URL '%s' has unsupported parameter '%s'", url.to_string(), name);
diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc
index 8c0f20475..56c014a8c 100644
--- a/src/libfetchers/tarball.cc
+++ b/src/libfetchers/tarball.cc
@@ -152,7 +152,7 @@ std::pair<Tree, time_t> downloadTarball(
}
Attrs infoAttrs({
- {"lastModified", lastModified},
+ {"lastModified", uint64_t(lastModified)},
{"etag", res.etag},
});
diff --git a/src/libmain/common-args.cc b/src/libmain/common-args.cc
index 3e4e475e5..bd5573e5d 100644
--- a/src/libmain/common-args.cc
+++ b/src/libmain/common-args.cc
@@ -10,25 +10,25 @@ MixCommonArgs::MixCommonArgs(const string & programName)
addFlag({
.longName = "verbose",
.shortName = 'v',
- .description = "increase verbosity level",
+ .description = "Increase the logging verbosity level.",
.handler = {[]() { verbosity = (Verbosity) (verbosity + 1); }},
});
addFlag({
.longName = "quiet",
- .description = "decrease verbosity level",
+ .description = "Decrease the logging verbosity level.",
.handler = {[]() { verbosity = verbosity > lvlError ? (Verbosity) (verbosity - 1) : lvlError; }},
});
addFlag({
.longName = "debug",
- .description = "enable debug output",
+ .description = "Set the logging verbosity level to 'debug'.",
.handler = {[]() { verbosity = lvlDebug; }},
});
addFlag({
.longName = "option",
- .description = "set a Nix configuration option (overriding `nix.conf`)",
+ .description = "Set the Nix configuration setting *name* to *value* (overriding `nix.conf`).",
.labels = {"name", "value"},
.handler = {[](std::string name, std::string value) {
try {
@@ -51,8 +51,7 @@ MixCommonArgs::MixCommonArgs(const string & programName)
addFlag({
.longName = "log-format",
- .description = "format of log output; `raw`, `internal-json`, `bar` "
- "or `bar-with-logs`",
+ .description = "Set the format of log output; one of `raw`, `internal-json`, `bar` or `bar-with-logs`.",
.labels = {"format"},
.handler = {[](std::string format) { setLogFormat(format); }},
});
@@ -60,7 +59,7 @@ MixCommonArgs::MixCommonArgs(const string & programName)
addFlag({
.longName = "max-jobs",
.shortName = 'j',
- .description = "maximum number of parallel builds",
+ .description = "The maximum number of parallel builds.",
.labels = Strings{"jobs"},
.handler = {[=](std::string s) {
settings.set("max-jobs", s);
diff --git a/src/libmain/common-args.hh b/src/libmain/common-args.hh
index a4de3dccf..47f341619 100644
--- a/src/libmain/common-args.hh
+++ b/src/libmain/common-args.hh
@@ -16,7 +16,7 @@ struct MixDryRun : virtual Args
MixDryRun()
{
- mkFlag(0, "dry-run", "show what this command would do without doing it", &dryRun);
+ mkFlag(0, "dry-run", "Show what this command would do without doing it.", &dryRun);
}
};
@@ -26,7 +26,7 @@ struct MixJSON : virtual Args
MixJSON()
{
- mkFlag(0, "json", "produce JSON output", &json);
+ mkFlag(0, "json", "Produce output in JSON format, suitable for consumption by another program.", &json);
}
};
diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc
index 2247aeca4..7e27e95c2 100644
--- a/src/libmain/shared.cc
+++ b/src/libmain/shared.cc
@@ -18,6 +18,8 @@
#include <openssl/crypto.h>
+#include <sodium.h>
+
namespace nix {
@@ -126,6 +128,9 @@ void initNix()
CRYPTO_set_locking_callback(opensslLockCallback);
#endif
+ if (sodium_init() == -1)
+ throw Error("could not initialise libsodium");
+
loadConfFile();
startSignalHandlerThread();
@@ -181,50 +186,58 @@ LegacyArgs::LegacyArgs(const std::string & programName,
addFlag({
.longName = "no-build-output",
.shortName = 'Q',
- .description = "do not show build output",
+ .description = "Do not show build output.",
.handler = {[&]() {setLogFormat(LogFormat::raw); }},
});
addFlag({
.longName = "keep-failed",
.shortName ='K',
- .description = "keep temporary directories of failed builds",
+ .description = "Keep temporary directories of failed builds.",
.handler = {&(bool&) settings.keepFailed, true},
});
addFlag({
.longName = "keep-going",
.shortName ='k',
- .description = "keep going after a build fails",
+ .description = "Keep going after a build fails.",
.handler = {&(bool&) settings.keepGoing, true},
});
addFlag({
.longName = "fallback",
- .description = "build from source if substitution fails",
+ .description = "Build from source if substitution fails.",
.handler = {&(bool&) settings.tryFallback, true},
});
auto intSettingAlias = [&](char shortName, const std::string & longName,
- const std::string & description, const std::string & dest) {
- mkFlag<unsigned int>(shortName, longName, description, [=](unsigned int n) {
- settings.set(dest, std::to_string(n));
+ const std::string & description, const std::string & dest)
+ {
+ addFlag({
+ .longName = longName,
+ .shortName = shortName,
+ .description = description,
+ .labels = {"n"},
+ .handler = {[=](std::string s) {
+ auto n = string2IntWithUnitPrefix<uint64_t>(s);
+ settings.set(dest, std::to_string(n));
+ }}
});
};
- intSettingAlias(0, "cores", "maximum number of CPU cores to use inside a build", "cores");
- intSettingAlias(0, "max-silent-time", "number of seconds of silence before a build is killed", "max-silent-time");
- intSettingAlias(0, "timeout", "number of seconds before a build is killed", "timeout");
+ intSettingAlias(0, "cores", "Maximum number of CPU cores to use inside a build.", "cores");
+ intSettingAlias(0, "max-silent-time", "Number of seconds of silence before a build is killed.", "max-silent-time");
+ intSettingAlias(0, "timeout", "Number of seconds before a build is killed.", "timeout");
- mkFlag(0, "readonly-mode", "do not write to the Nix store",
+ mkFlag(0, "readonly-mode", "Do not write to the Nix store.",
&settings.readOnlyMode);
- mkFlag(0, "no-gc-warning", "disable warning about not using '--add-root'",
+ mkFlag(0, "no-gc-warning", "Disable warnings about not using `--add-root`.",
&gcWarning, false);
addFlag({
.longName = "store",
- .description = "URI of the Nix store to use",
+ .description = "The URL of the Nix store to use.",
.labels = {"store-uri"},
.handler = {&(std::string&) settings.storeUri},
});
@@ -274,9 +287,7 @@ void printVersion(const string & programName)
#if HAVE_BOEHMGC
cfg.push_back("gc");
#endif
-#if HAVE_SODIUM
cfg.push_back("signed-caches");
-#endif
std::cout << "System type: " << settings.thisSystem << "\n";
std::cout << "Additional system types: " << concatStringsSep(", ", settings.extraPlatforms.get()) << "\n";
std::cout << "Features: " << concatStringsSep(", ", cfg) << "\n";
diff --git a/src/libmain/shared.hh b/src/libmain/shared.hh
index ffae5d796..edc7b5efa 100644
--- a/src/libmain/shared.hh
+++ b/src/libmain/shared.hh
@@ -57,23 +57,7 @@ template<class N> N getIntArg(const string & opt,
{
++i;
if (i == end) throw UsageError("'%1%' requires an argument", opt);
- string s = *i;
- N multiplier = 1;
- if (allowUnit && !s.empty()) {
- char u = std::toupper(*s.rbegin());
- if (std::isalpha(u)) {
- if (u == 'K') multiplier = 1ULL << 10;
- else if (u == 'M') multiplier = 1ULL << 20;
- else if (u == 'G') multiplier = 1ULL << 30;
- else if (u == 'T') multiplier = 1ULL << 40;
- else throw UsageError("invalid unit specifier '%1%'", u);
- s.resize(s.size() - 1);
- }
- }
- N n;
- if (!string2Int(s, n))
- throw UsageError("'%1%' requires an integer argument", opt);
- return n * multiplier;
+ return string2IntWithUnitPrefix<N>(*i);
}
diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc
index 17f39a86e..953e241d8 100644
--- a/src/libstore/build/derivation-goal.cc
+++ b/src/libstore/build/derivation-goal.cc
@@ -50,6 +50,11 @@
#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old))
#endif
+#if __APPLE__
+#include <spawn.h>
+#include <sys/sysctl.h>
+#endif
+
#include <pwd.h>
#include <grp.h>
@@ -683,11 +688,7 @@ void DerivationGoal::tryToBuild()
}
void DerivationGoal::tryLocalBuild() {
- bool buildLocally = buildMode != bmNormal || parsedDrv->willBuildLocally(worker.store);
-
- /* Make sure that we are allowed to start a build. If this
- derivation prefers to be done locally, do it even if
- maxBuildJobs is 0. */
+ /* Make sure that we are allowed to start a build. */
if (!dynamic_cast<LocalStore *>(&worker.store)) {
throw Error(
"unable to build with a primary store that isn't a local store; "
@@ -695,7 +696,7 @@ void DerivationGoal::tryLocalBuild() {
"\nhttps://nixos.org/nix/manual/#chap-distributed-builds");
}
unsigned int curBuilds = worker.getNrLocalBuilds();
- if (curBuilds >= settings.maxBuildJobs && !(buildLocally && curBuilds == 0)) {
+ if (curBuilds >= settings.maxBuildJobs) {
worker.waitForBuildSlot(shared_from_this());
outputLocks.unlock();
return;
@@ -1714,12 +1715,10 @@ void DerivationGoal::startBuilder()
userNamespaceSync.writeSide = -1;
});
- pid_t tmp;
auto ss = tokenizeString<std::vector<std::string>>(readLine(builderOut.readSide.get()));
assert(ss.size() == 2);
usingUserNamespace = ss[0] == "1";
- if (!string2Int<pid_t>(ss[1], tmp)) abort();
- pid = tmp;
+ pid = string2Int<pid_t>(ss[1]).value();
if (usingUserNamespace) {
/* Set the UID/GID mapping of the builder's user namespace
@@ -2877,7 +2876,31 @@ void DerivationGoal::runChild()
}
}
+#if __APPLE__
+ posix_spawnattr_t attrp;
+
+ if (posix_spawnattr_init(&attrp))
+ throw SysError("failed to initialize builder");
+
+ if (posix_spawnattr_setflags(&attrp, POSIX_SPAWN_SETEXEC))
+ throw SysError("failed to initialize builder");
+
+ if (drv->platform == "aarch64-darwin") {
+ // Unset kern.curproc_arch_affinity so we can escape Rosetta
+ int affinity = 0;
+ sysctlbyname("kern.curproc_arch_affinity", NULL, NULL, &affinity, sizeof(affinity));
+
+ cpu_type_t cpu = CPU_TYPE_ARM64;
+ posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL);
+ } else if (drv->platform == "x86_64-darwin") {
+ cpu_type_t cpu = CPU_TYPE_X86_64;
+ posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL);
+ }
+
+ posix_spawn(NULL, builder, NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
+#else
execve(builder, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
+#endif
throw SysError("executing '%1%'", drv->builder);
diff --git a/src/libstore/crypto.cc b/src/libstore/crypto.cc
index 9ec8abd22..1027469c9 100644
--- a/src/libstore/crypto.cc
+++ b/src/libstore/crypto.cc
@@ -2,21 +2,19 @@
#include "util.hh"
#include "globals.hh"
-#if HAVE_SODIUM
#include <sodium.h>
-#endif
namespace nix {
-static std::pair<std::string, std::string> split(const string & s)
+static std::pair<std::string_view, std::string_view> split(std::string_view s)
{
size_t colon = s.find(':');
if (colon == std::string::npos || colon == 0)
return {"", ""};
- return {std::string(s, 0, colon), std::string(s, colon + 1)};
+ return {s.substr(0, colon), s.substr(colon + 1)};
}
-Key::Key(const string & s)
+Key::Key(std::string_view s)
{
auto ss = split(s);
@@ -29,62 +27,57 @@ Key::Key(const string & s)
key = base64Decode(key);
}
-SecretKey::SecretKey(const string & s)
- : Key(s)
+std::string Key::to_string() const
{
-#if HAVE_SODIUM
- if (key.size() != crypto_sign_SECRETKEYBYTES)
- throw Error("secret key is not valid");
-#endif
+ return name + ":" + base64Encode(key);
}
-#if !HAVE_SODIUM
-[[noreturn]] static void noSodium()
+SecretKey::SecretKey(std::string_view s)
+ : Key(s)
{
- throw Error("Nix was not compiled with libsodium, required for signed binary cache support");
+ if (key.size() != crypto_sign_SECRETKEYBYTES)
+ throw Error("secret key is not valid");
}
-#endif
-std::string SecretKey::signDetached(const std::string & data) const
+std::string SecretKey::signDetached(std::string_view data) const
{
-#if HAVE_SODIUM
unsigned char sig[crypto_sign_BYTES];
unsigned long long sigLen;
crypto_sign_detached(sig, &sigLen, (unsigned char *) data.data(), data.size(),
(unsigned char *) key.data());
return name + ":" + base64Encode(std::string((char *) sig, sigLen));
-#else
- noSodium();
-#endif
}
PublicKey SecretKey::toPublicKey() const
{
-#if HAVE_SODIUM
unsigned char pk[crypto_sign_PUBLICKEYBYTES];
crypto_sign_ed25519_sk_to_pk(pk, (unsigned char *) key.data());
return PublicKey(name, std::string((char *) pk, crypto_sign_PUBLICKEYBYTES));
-#else
- noSodium();
-#endif
}
-PublicKey::PublicKey(const string & s)
+SecretKey SecretKey::generate(std::string_view name)
+{
+ unsigned char pk[crypto_sign_PUBLICKEYBYTES];
+ unsigned char sk[crypto_sign_SECRETKEYBYTES];
+ if (crypto_sign_keypair(pk, sk) != 0)
+ throw Error("key generation failed");
+
+ return SecretKey(name, std::string((char *) sk, crypto_sign_SECRETKEYBYTES));
+}
+
+PublicKey::PublicKey(std::string_view s)
: Key(s)
{
-#if HAVE_SODIUM
if (key.size() != crypto_sign_PUBLICKEYBYTES)
throw Error("public key is not valid");
-#endif
}
bool verifyDetached(const std::string & data, const std::string & sig,
const PublicKeys & publicKeys)
{
-#if HAVE_SODIUM
auto ss = split(sig);
- auto key = publicKeys.find(ss.first);
+ auto key = publicKeys.find(std::string(ss.first));
if (key == publicKeys.end()) return false;
auto sig2 = base64Decode(ss.second);
@@ -94,9 +87,6 @@ bool verifyDetached(const std::string & data, const std::string & sig,
return crypto_sign_verify_detached((unsigned char *) sig2.data(),
(unsigned char *) data.data(), data.size(),
(unsigned char *) key->second.key.data()) == 0;
-#else
- noSodium();
-#endif
}
PublicKeys getDefaultPublicKeys()
diff --git a/src/libstore/crypto.hh b/src/libstore/crypto.hh
index 9110af3aa..03f85c103 100644
--- a/src/libstore/crypto.hh
+++ b/src/libstore/crypto.hh
@@ -13,32 +13,40 @@ struct Key
/* Construct Key from a string in the format
‘<name>:<key-in-base64>’. */
- Key(const std::string & s);
+ Key(std::string_view s);
+
+ std::string to_string() const;
protected:
- Key(const std::string & name, const std::string & key)
- : name(name), key(key) { }
+ Key(std::string_view name, std::string && key)
+ : name(name), key(std::move(key)) { }
};
struct PublicKey;
struct SecretKey : Key
{
- SecretKey(const std::string & s);
+ SecretKey(std::string_view s);
/* Return a detached signature of the given string. */
- std::string signDetached(const std::string & s) const;
+ std::string signDetached(std::string_view s) const;
PublicKey toPublicKey() const;
+
+ static SecretKey generate(std::string_view name);
+
+private:
+ SecretKey(std::string_view name, std::string && key)
+ : Key(name, std::move(key)) { }
};
struct PublicKey : Key
{
- PublicKey(const std::string & data);
+ PublicKey(std::string_view data);
private:
- PublicKey(const std::string & name, const std::string & key)
- : Key(name, key) { }
+ PublicKey(std::string_view name, std::string && key)
+ : Key(name, std::move(key)) { }
friend struct SecretKey;
};
diff --git a/src/libstore/filetransfer.hh b/src/libstore/filetransfer.hh
index afc7e7aa6..45d9ccf89 100644
--- a/src/libstore/filetransfer.hh
+++ b/src/libstore/filetransfer.hh
@@ -63,7 +63,7 @@ struct FileTransferRequest
std::string mimeType;
std::function<void(std::string_view data)> dataCallback;
- FileTransferRequest(const std::string & uri)
+ FileTransferRequest(std::string_view uri)
: uri(uri), parentAct(getCurActivity()) { }
std::string verb()
diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc
index f38601d6d..0531aad9f 100644
--- a/src/libstore/globals.cc
+++ b/src/libstore/globals.cc
@@ -131,6 +131,28 @@ StringSet Settings::getDefaultSystemFeatures()
return features;
}
+StringSet Settings::getDefaultExtraPlatforms()
+{
+ if (std::string{SYSTEM} == "x86_64-linux" && !isWSL1())
+ return StringSet{"i686-linux"};
+#if __APPLE__
+ // Rosetta 2 emulation layer can run x86_64 binaries on aarch64
+ // machines. Note that we can’t force processes from executing
+ // x86_64 in aarch64 environments or vice versa since they can
+ // always exec with their own binary preferences.
+ else if (pathExists("/Library/Apple/System/Library/LaunchDaemons/com.apple.oahd.plist")) {
+ if (std::string{SYSTEM} == "x86_64-darwin")
+ return StringSet{"aarch64-darwin"};
+ else if (std::string{SYSTEM} == "aarch64-darwin")
+ return StringSet{"x86_64-darwin"};
+ else
+ return StringSet{};
+ }
+#endif
+ else
+ return StringSet{};
+}
+
bool Settings::isExperimentalFeatureEnabled(const std::string & name)
{
auto & f = experimentalFeatures.get();
@@ -206,8 +228,12 @@ template<> void BaseSetting<SandboxMode>::convertToArg(Args & args, const std::s
void MaxBuildJobsSetting::set(const std::string & str, bool append)
{
if (str == "auto") value = std::max(1U, std::thread::hardware_concurrency());
- else if (!string2Int(str, value))
- throw UsageError("configuration setting '%s' should be 'auto' or an integer", name);
+ else {
+ if (auto n = string2Int<decltype(value)>(str))
+ value = *n;
+ else
+ throw UsageError("configuration setting '%s' should be 'auto' or an integer", name);
+ }
}
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index 6b4775683..1d968ef3e 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -34,6 +34,8 @@ class Settings : public Config {
StringSet getDefaultSystemFeatures();
+ StringSet getDefaultExtraPlatforms();
+
bool isWSL1();
public:
@@ -545,7 +547,7 @@ public:
Setting<StringSet> extraPlatforms{
this,
- std::string{SYSTEM} == "x86_64-linux" && !isWSL1() ? StringSet{"i686-linux"} : StringSet{},
+ getDefaultExtraPlatforms(),
"extra-platforms",
R"(
Platforms other than the native one which this machine is capable of
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index 1eb2dec75..4f48522c6 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -66,8 +66,10 @@ int getSchema(Path schemaPath)
int curSchema = 0;
if (pathExists(schemaPath)) {
string s = readFile(schemaPath);
- if (!string2Int(s, curSchema))
+ auto n = string2Int<int>(s);
+ if (!n)
throw Error("'%1%' is corrupt", schemaPath);
+ curSchema = *n;
}
return curSchema;
}
@@ -736,54 +738,59 @@ void LocalStore::queryPathInfoUncached(const StorePath & path,
Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept
{
try {
- callback(retrySQLite<std::shared_ptr<ValidPathInfo>>([&]() {
+ callback(retrySQLite<std::shared_ptr<const ValidPathInfo>>([&]() {
auto state(_state.lock());
+ return queryPathInfoInternal(*state, path);
+ }));
- /* Get the path info. */
- auto useQueryPathInfo(state->stmts->QueryPathInfo.use()(printStorePath(path)));
+ } catch (...) { callback.rethrow(); }
+}
- if (!useQueryPathInfo.next())
- return std::shared_ptr<ValidPathInfo>();
- auto id = useQueryPathInfo.getInt(0);
+std::shared_ptr<const ValidPathInfo> LocalStore::queryPathInfoInternal(State & state, const StorePath & path)
+{
+ /* Get the path info. */
+ auto useQueryPathInfo(state.stmts->QueryPathInfo.use()(printStorePath(path)));
- auto narHash = Hash::dummy;
- try {
- narHash = Hash::parseAnyPrefixed(useQueryPathInfo.getStr(1));
- } catch (BadHash & e) {
- throw Error("invalid-path entry for '%s': %s", printStorePath(path), e.what());
- }
+ if (!useQueryPathInfo.next())
+ return std::shared_ptr<ValidPathInfo>();
- auto info = std::make_shared<ValidPathInfo>(path, narHash);
+ auto id = useQueryPathInfo.getInt(0);
- info->id = id;
+ auto narHash = Hash::dummy;
+ try {
+ narHash = Hash::parseAnyPrefixed(useQueryPathInfo.getStr(1));
+ } catch (BadHash & e) {
+ throw Error("invalid-path entry for '%s': %s", printStorePath(path), e.what());
+ }
- info->registrationTime = useQueryPathInfo.getInt(2);
+ auto info = std::make_shared<ValidPathInfo>(path, narHash);
- auto s = (const char *) sqlite3_column_text(state->stmts->QueryPathInfo, 3);
- if (s) info->deriver = parseStorePath(s);
+ info->id = id;
- /* Note that narSize = NULL yields 0. */
- info->narSize = useQueryPathInfo.getInt(4);
+ info->registrationTime = useQueryPathInfo.getInt(2);
- info->ultimate = useQueryPathInfo.getInt(5) == 1;
+ auto s = (const char *) sqlite3_column_text(state.stmts->QueryPathInfo, 3);
+ if (s) info->deriver = parseStorePath(s);
- s = (const char *) sqlite3_column_text(state->stmts->QueryPathInfo, 6);
- if (s) info->sigs = tokenizeString<StringSet>(s, " ");
+ /* Note that narSize = NULL yields 0. */
+ info->narSize = useQueryPathInfo.getInt(4);
- s = (const char *) sqlite3_column_text(state->stmts->QueryPathInfo, 7);
- if (s) info->ca = parseContentAddressOpt(s);
+ info->ultimate = useQueryPathInfo.getInt(5) == 1;
- /* Get the references. */
- auto useQueryReferences(state->stmts->QueryReferences.use()(info->id));
+ s = (const char *) sqlite3_column_text(state.stmts->QueryPathInfo, 6);
+ if (s) info->sigs = tokenizeString<StringSet>(s, " ");
- while (useQueryReferences.next())
- info->references.insert(parseStorePath(useQueryReferences.getStr(0)));
+ s = (const char *) sqlite3_column_text(state.stmts->QueryPathInfo, 7);
+ if (s) info->ca = parseContentAddressOpt(s);
- return info;
- }));
+ /* Get the references. */
+ auto useQueryReferences(state.stmts->QueryReferences.use()(info->id));
- } catch (...) { callback.rethrow(); }
+ while (useQueryReferences.next())
+ info->references.insert(parseStorePath(useQueryReferences.getStr(0)));
+
+ return info;
}
@@ -1599,7 +1606,7 @@ void LocalStore::addSignatures(const StorePath & storePath, const StringSet & si
SQLiteTxn txn(state->db);
- auto info = std::const_pointer_cast<ValidPathInfo>(std::shared_ptr<const ValidPathInfo>(queryPathInfo(storePath)));
+ auto info = std::const_pointer_cast<ValidPathInfo>(queryPathInfoInternal(*state, storePath));
info->sigs.insert(sigs.begin(), sigs.end());
diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh
index aa5de31f0..69704d266 100644
--- a/src/libstore/local-store.hh
+++ b/src/libstore/local-store.hh
@@ -177,9 +177,7 @@ public:
void vacuumDB();
- /* Repair the contents of the given path by redownloading it using
- a substituter (if available). */
- void repairPath(const StorePath & path);
+ void repairPath(const StorePath & path) override;
void addSignatures(const StorePath & storePath, const StringSet & sigs) override;
@@ -214,6 +212,8 @@ private:
void verifyPath(const Path & path, const StringSet & store,
PathSet & done, StorePathSet & validPaths, RepairFlag repair, bool & errors);
+ std::shared_ptr<const ValidPathInfo> queryPathInfoInternal(State & state, const StorePath & path);
+
void updatePathInfo(State & state, const ValidPathInfo & info);
void upgradeStore6();
diff --git a/src/libstore/names.cc b/src/libstore/names.cc
index 41e28dc99..ce808accc 100644
--- a/src/libstore/names.cc
+++ b/src/libstore/names.cc
@@ -80,16 +80,16 @@ string nextComponent(string::const_iterator & p,
static bool componentsLT(const string & c1, const string & c2)
{
- int n1, n2;
- bool c1Num = string2Int(c1, n1), c2Num = string2Int(c2, n2);
+ auto n1 = string2Int<int>(c1);
+ auto n2 = string2Int<int>(c2);
- if (c1Num && c2Num) return n1 < n2;
- else if (c1 == "" && c2Num) return true;
+ if (n1 && n2) return *n1 < *n2;
+ else if (c1 == "" && n2) return true;
else if (c1 == "pre" && c2 != "pre") return true;
else if (c2 == "pre") return false;
/* Assume that `2.3a' < `2.3.1'. */
- else if (c2Num) return true;
- else if (c1Num) return false;
+ else if (n2) return true;
+ else if (n1) return false;
else return c1 < c2;
}
diff --git a/src/libstore/nar-info.cc b/src/libstore/nar-info.cc
index 3454f34bb..49079388a 100644
--- a/src/libstore/nar-info.cc
+++ b/src/libstore/nar-info.cc
@@ -46,14 +46,18 @@ NarInfo::NarInfo(const Store & store, const std::string & s, const std::string &
else if (name == "FileHash")
fileHash = parseHashField(value);
else if (name == "FileSize") {
- if (!string2Int(value, fileSize)) throw corrupt();
+ auto n = string2Int<decltype(fileSize)>(value);
+ if (!n) throw corrupt();
+ fileSize = *n;
}
else if (name == "NarHash") {
narHash = parseHashField(value);
haveNarHash = true;
}
else if (name == "NarSize") {
- if (!string2Int(value, narSize)) throw corrupt();
+ auto n = string2Int<decltype(narSize)>(value);
+ if (!n) throw corrupt();
+ narSize = *n;
}
else if (name == "References") {
auto refs = tokenizeString<Strings>(value, " ");
diff --git a/src/libstore/parsed-derivations.cc b/src/libstore/parsed-derivations.cc
index e7b7202d4..c5c3ae3dc 100644
--- a/src/libstore/parsed-derivations.cc
+++ b/src/libstore/parsed-derivations.cc
@@ -101,6 +101,10 @@ bool ParsedDerivation::canBuildLocally(Store & localStore) const
&& !drv.isBuiltin())
return false;
+ if (settings.maxBuildJobs.get() == 0
+ && !drv.isBuiltin())
+ return false;
+
for (auto & feature : getRequiredSystemFeatures())
if (!localStore.systemFeatures.get().count(feature)) return false;
diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc
index ed10dd519..5d1723886 100644
--- a/src/libstore/profiles.cc
+++ b/src/libstore/profiles.cc
@@ -21,9 +21,8 @@ static std::optional<GenerationNumber> parseName(const string & profileName, con
string s = string(name, profileName.size() + 1);
string::size_type p = s.find("-link");
if (p == string::npos) return {};
- unsigned int n;
- if (string2Int(string(s, 0, p), n) && n >= 0)
- return n;
+ if (auto n = string2Int<unsigned int>(s.substr(0, p)))
+ return *n;
else
return {};
}
@@ -214,12 +213,12 @@ void deleteGenerationsOlderThan(const Path & profile, const string & timeSpec, b
{
time_t curTime = time(0);
string strDays = string(timeSpec, 0, timeSpec.size() - 1);
- int days;
+ auto days = string2Int<int>(strDays);
- if (!string2Int(strDays, days) || days < 1)
+ if (!days || *days < 1)
throw Error("invalid number of days specifier '%1%'", timeSpec);
- time_t oldTime = curTime - days * 24 * 3600;
+ time_t oldTime = curTime - *days * 24 * 3600;
deleteGenerationsOlderThan(profile, oldTime, dryRun);
}
diff --git a/src/libstore/references.cc b/src/libstore/references.cc
index eb117b5ba..39c4970c6 100644
--- a/src/libstore/references.cc
+++ b/src/libstore/references.cc
@@ -88,9 +88,6 @@ PathSet scanForReferences(Sink & toTee,
TeeSink sink { refsSink, toTee };
std::map<string, Path> backMap;
- /* For efficiency (and a higher hit rate), just search for the
- hash part of the file name. (This assumes that all references
- have the form `HASH-bla'). */
for (auto & i : refs) {
auto baseName = std::string(baseNameOf(i));
string::size_type pos = baseName.find('-');
diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc
index f12a564a1..d4ec1497f 100644
--- a/src/libstore/store-api.cc
+++ b/src/libstore/store-api.cc
@@ -909,19 +909,20 @@ std::optional<ValidPathInfo> decodeValidPathInfo(const Store & store, std::istre
getline(str, s);
auto narHash = Hash::parseAny(s, htSHA256);
getline(str, s);
- uint64_t narSize;
- if (!string2Int(s, narSize)) throw Error("number expected");
- hashGiven = { narHash, narSize };
+ auto narSize = string2Int<uint64_t>(s);
+ if (!narSize) throw Error("number expected");
+ hashGiven = { narHash, *narSize };
}
ValidPathInfo info(store.parseStorePath(path), hashGiven->first);
info.narSize = hashGiven->second;
std::string deriver;
getline(str, deriver);
if (deriver != "") info.deriver = store.parseStorePath(deriver);
- string s; int n;
+ string s;
getline(str, s);
- if (!string2Int(s, n)) throw Error("number expected");
- while (n--) {
+ auto n = string2Int<int>(s);
+ if (!n) throw Error("number expected");
+ while ((*n)--) {
getline(str, s);
info.references.insert(store.parseStorePath(s));
}
diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh
index 4db980fe9..e6a14afc3 100644
--- a/src/libstore/store-api.hh
+++ b/src/libstore/store-api.hh
@@ -608,6 +608,11 @@ public:
virtual ref<FSAccessor> getFSAccessor()
{ unsupported("getFSAccessor"); }
+ /* Repair the contents of the given path by redownloading it using
+ a substituter (if available). */
+ virtual void repairPath(const StorePath & path)
+ { unsupported("repairPath"); }
+
/* Add signatures to the specified store path. The signatures are
not verified. */
virtual void addSignatures(const StorePath & storePath, const StringSet & sigs)
diff --git a/src/libutil/args.hh b/src/libutil/args.hh
index 6ed541a32..3783bc84f 100644
--- a/src/libutil/args.hh
+++ b/src/libutil/args.hh
@@ -68,8 +68,12 @@ protected:
, arity(ArityAny)
{ }
- template<class T>
- Handler(T * dest)
+ Handler(std::string * dest)
+ : fun([=](std::vector<std::string> ss) { *dest = ss[0]; })
+ , arity(1)
+ { }
+
+ Handler(std::optional<std::string> * dest)
: fun([=](std::vector<std::string> ss) { *dest = ss[0]; })
, arity(1)
{ }
@@ -79,6 +83,14 @@ protected:
: fun([=](std::vector<std::string> ss) { *dest = val; })
, arity(0)
{ }
+
+ template<class I>
+ Handler(I * dest)
+ : fun([=](std::vector<std::string> ss) {
+ *dest = string2IntWithUnitPrefix<I>(ss[0]);
+ })
+ , arity(1)
+ { }
};
/* Flags. */
@@ -130,19 +142,6 @@ public:
/* Helper functions for constructing flags / positional
arguments. */
- void mkFlag1(char shortName, const std::string & longName,
- const std::string & label, const std::string & description,
- std::function<void(std::string)> fun)
- {
- addFlag({
- .longName = longName,
- .shortName = shortName,
- .description = description,
- .labels = {label},
- .handler = {[=](std::string s) { fun(s); }}
- });
- }
-
void mkFlag(char shortName, const std::string & name,
const std::string & description, bool * dest)
{
@@ -161,33 +160,6 @@ public:
});
}
- template<class I>
- void mkIntFlag(char shortName, const std::string & longName,
- const std::string & description, I * dest)
- {
- mkFlag<I>(shortName, longName, description, [=](I n) {
- *dest = n;
- });
- }
-
- template<class I>
- void mkFlag(char shortName, const std::string & longName,
- const std::string & description, std::function<void(I)> fun)
- {
- addFlag({
- .longName = longName,
- .shortName = shortName,
- .description = description,
- .labels = {"N"},
- .handler = {[=](std::string s) {
- I n;
- if (!string2Int(s, n))
- throw UsageError("flag '--%s' requires a integer argument", longName);
- fun(n);
- }}
- });
- }
-
void expectArgs(ExpectedArg && arg)
{
expectedArgs.emplace_back(std::move(arg));
diff --git a/src/libutil/config.cc b/src/libutil/config.cc
index 7af3e7883..7467e5ac0 100644
--- a/src/libutil/config.cc
+++ b/src/libutil/config.cc
@@ -230,7 +230,9 @@ template<typename T>
void BaseSetting<T>::set(const std::string & str, bool append)
{
static_assert(std::is_integral<T>::value, "Integer required.");
- if (!string2Int(str, value))
+ if (auto n = string2Int<T>(str))
+ value = *n;
+ else
throw UsageError("setting '%s' has invalid value '%s'", name, str);
}
diff --git a/src/libutil/tests/tests.cc b/src/libutil/tests/tests.cc
index 35a5d27bb..58df9c5ac 100644
--- a/src/libutil/tests/tests.cc
+++ b/src/libutil/tests/tests.cc
@@ -320,20 +320,15 @@ namespace nix {
* --------------------------------------------------------------------------*/
TEST(string2Float, emptyString) {
- double n;
- ASSERT_EQ(string2Float("", n), false);
+ ASSERT_EQ(string2Float<double>(""), std::nullopt);
}
TEST(string2Float, trivialConversions) {
- double n;
- ASSERT_EQ(string2Float("1.0", n), true);
- ASSERT_EQ(n, 1.0);
+ ASSERT_EQ(string2Float<double>("1.0"), 1.0);
- ASSERT_EQ(string2Float("0.0", n), true);
- ASSERT_EQ(n, 0.0);
+ ASSERT_EQ(string2Float<double>("0.0"), 0.0);
- ASSERT_EQ(string2Float("-100.25", n), true);
- ASSERT_EQ(n, (-100.25));
+ ASSERT_EQ(string2Float<double>("-100.25"), -100.25);
}
/* ----------------------------------------------------------------------------
@@ -341,20 +336,15 @@ namespace nix {
* --------------------------------------------------------------------------*/
TEST(string2Int, emptyString) {
- double n;
- ASSERT_EQ(string2Int("", n), false);
+ ASSERT_EQ(string2Int<int>(""), std::nullopt);
}
TEST(string2Int, trivialConversions) {
- double n;
- ASSERT_EQ(string2Int("1", n), true);
- ASSERT_EQ(n, 1);
+ ASSERT_EQ(string2Int<int>("1"), 1);
- ASSERT_EQ(string2Int("0", n), true);
- ASSERT_EQ(n, 0);
+ ASSERT_EQ(string2Int<int>("0"), 0);
- ASSERT_EQ(string2Int("-100", n), true);
- ASSERT_EQ(n, (-100));
+ ASSERT_EQ(string2Int<int>("-100"), -100);
}
/* ----------------------------------------------------------------------------
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index 0f82bed78..ab0bd865a 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -397,21 +397,49 @@ bool statusOk(int status);
/* Parse a string into an integer. */
-template<class N> bool string2Int(const string & s, N & n)
+template<class N>
+std::optional<N> string2Int(const std::string & s)
{
- if (string(s, 0, 1) == "-" && !std::numeric_limits<N>::is_signed)
- return false;
+ if (s.substr(0, 1) == "-" && !std::numeric_limits<N>::is_signed)
+ return std::nullopt;
std::istringstream str(s);
+ N n;
str >> n;
- return str && str.get() == EOF;
+ if (str && str.get() == EOF) return n;
+ return std::nullopt;
+}
+
+/* Like string2Int(), but support an optional suffix 'K', 'M', 'G' or
+ 'T' denoting a binary unit prefix. */
+template<class N>
+N string2IntWithUnitPrefix(std::string s)
+{
+ N multiplier = 1;
+ if (!s.empty()) {
+ char u = std::toupper(*s.rbegin());
+ if (std::isalpha(u)) {
+ if (u == 'K') multiplier = 1ULL << 10;
+ else if (u == 'M') multiplier = 1ULL << 20;
+ else if (u == 'G') multiplier = 1ULL << 30;
+ else if (u == 'T') multiplier = 1ULL << 40;
+ else throw UsageError("invalid unit specifier '%1%'", u);
+ s.resize(s.size() - 1);
+ }
+ }
+ if (auto n = string2Int<N>(s))
+ return *n * multiplier;
+ throw UsageError("'%s' is not an integer", s);
}
/* Parse a string into a float. */
-template<class N> bool string2Float(const string & s, N & n)
+template<class N>
+std::optional<N> string2Float(const string & s)
{
std::istringstream str(s);
+ N n;
str >> n;
- return str && str.get() == EOF;
+ if (str && str.get() == EOF) return n;
+ return std::nullopt;
}
diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc
index 6c2e075ed..9963f05d9 100644
--- a/src/nix-env/nix-env.cc
+++ b/src/nix-env/nix-env.cc
@@ -1250,11 +1250,10 @@ static void opSwitchGeneration(Globals & globals, Strings opFlags, Strings opArg
if (opArgs.size() != 1)
throw UsageError("exactly one argument expected");
- GenerationNumber dstGen;
- if (!string2Int(opArgs.front(), dstGen))
+ if (auto dstGen = string2Int<GenerationNumber>(opArgs.front()))
+ switchGeneration(globals, *dstGen);
+ else
throw UsageError("expected a generation number");
-
- switchGeneration(globals, dstGen);
}
@@ -1308,17 +1307,17 @@ static void opDeleteGenerations(Globals & globals, Strings opFlags, Strings opAr
if(opArgs.front().size() < 2)
throw Error("invalid number of generations ‘%1%’", opArgs.front());
string str_max = string(opArgs.front(), 1, opArgs.front().size());
- GenerationNumber max;
- if (!string2Int(str_max, max) || max == 0)
+ auto max = string2Int<GenerationNumber>(str_max);
+ if (!max || *max == 0)
throw Error("invalid number of generations to keep ‘%1%’", opArgs.front());
- deleteGenerationsGreaterThan(globals.profile, max, globals.dryRun);
+ deleteGenerationsGreaterThan(globals.profile, *max, globals.dryRun);
} else {
std::set<GenerationNumber> gens;
for (auto & i : opArgs) {
- GenerationNumber n;
- if (!string2Int(i, n))
+ if (auto n = string2Int<GenerationNumber>(i))
+ gens.insert(*n);
+ else
throw UsageError("invalid generation number '%1%'", i);
- gens.insert(n);
}
deleteGenerations(globals.profile, gens, globals.dryRun);
}
diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc
index 87387e794..168ac492b 100644
--- a/src/nix-env/user-env.cc
+++ b/src/nix-env/user-env.cc
@@ -53,10 +53,12 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
output paths, and optionally the derivation path, as well
as the meta attributes. */
Path drvPath = keepDerivations ? i.queryDrvPath() : "";
+ DrvInfo::Outputs outputs = i.queryOutputs(true);
+ StringSet metaNames = i.queryMetaNames();
Value & v(*state.allocValue());
manifest.listElems()[n++] = &v;
- state.mkAttrs(v, 16);
+ state.mkAttrs(v, 7 + outputs.size());
mkString(*state.allocAttr(v, state.sType), "derivation");
mkString(*state.allocAttr(v, state.sName), i.queryName());
@@ -68,7 +70,6 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
mkString(*state.allocAttr(v, state.sDrvPath), i.queryDrvPath());
// Copy each output meant for installation.
- DrvInfo::Outputs outputs = i.queryOutputs(true);
Value & vOutputs = *state.allocAttr(v, state.sOutputs);
state.mkList(vOutputs, outputs.size());
unsigned int m = 0;
@@ -88,8 +89,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
// Copy the meta attributes.
Value & vMeta = *state.allocAttr(v, state.sMeta);
- state.mkAttrs(vMeta, 16);
- StringSet metaNames = i.queryMetaNames();
+ state.mkAttrs(vMeta, metaNames.size());
for (auto & j : metaNames) {
Value * v = i.queryMeta(j);
if (!v) continue;
diff --git a/src/nix-prefetch-url/nix-prefetch-url.cc b/src/nix-prefetch-url/nix-prefetch-url.cc
deleted file mode 100644
index 3bdee55a7..000000000
--- a/src/nix-prefetch-url/nix-prefetch-url.cc
+++ /dev/null
@@ -1,232 +0,0 @@
-#include "hash.hh"
-#include "shared.hh"
-#include "filetransfer.hh"
-#include "store-api.hh"
-#include "eval.hh"
-#include "eval-inline.hh"
-#include "common-eval-args.hh"
-#include "attr-path.hh"
-#include "finally.hh"
-#include "../nix/legacy.hh"
-#include "progress-bar.hh"
-#include "tarfile.hh"
-
-#include <iostream>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-using namespace nix;
-
-
-/* If ‘uri’ starts with ‘mirror://’, then resolve it using the list of
- mirrors defined in Nixpkgs. */
-string resolveMirrorUri(EvalState & state, string uri)
-{
- if (string(uri, 0, 9) != "mirror://") return uri;
-
- string s(uri, 9);
- auto p = s.find('/');
- if (p == string::npos) throw Error("invalid mirror URI");
- string mirrorName(s, 0, p);
-
- Value vMirrors;
- state.eval(state.parseExprFromString("import <nixpkgs/pkgs/build-support/fetchurl/mirrors.nix>", "."), vMirrors);
- state.forceAttrs(vMirrors);
-
- auto mirrorList = vMirrors.attrs->find(state.symbols.create(mirrorName));
- if (mirrorList == vMirrors.attrs->end())
- throw Error("unknown mirror name '%1%'", mirrorName);
- state.forceList(*mirrorList->value);
-
- if (mirrorList->value->listSize() < 1)
- throw Error("mirror URI '%1%' did not expand to anything", uri);
-
- string mirror = state.forceString(*mirrorList->value->listElems()[0]);
- return mirror + (hasSuffix(mirror, "/") ? "" : "/") + string(s, p + 1);
-}
-
-
-static int main_nix_prefetch_url(int argc, char * * argv)
-{
- {
- HashType ht = htSHA256;
- std::vector<string> args;
- bool printPath = getEnv("PRINT_PATH") == "1";
- bool fromExpr = false;
- string attrPath;
- bool unpack = false;
- bool executable = false;
- string name;
-
- struct MyArgs : LegacyArgs, MixEvalArgs
- {
- using LegacyArgs::LegacyArgs;
- };
-
- MyArgs myArgs(std::string(baseNameOf(argv[0])), [&](Strings::iterator & arg, const Strings::iterator & end) {
- if (*arg == "--help")
- showManPage("nix-prefetch-url");
- else if (*arg == "--version")
- printVersion("nix-prefetch-url");
- else if (*arg == "--type") {
- string s = getArg(*arg, arg, end);
- ht = parseHashType(s);
- }
- else if (*arg == "--print-path")
- printPath = true;
- else if (*arg == "--attr" || *arg == "-A") {
- fromExpr = true;
- attrPath = getArg(*arg, arg, end);
- }
- else if (*arg == "--unpack")
- unpack = true;
- else if (*arg == "--executable")
- executable = true;
- else if (*arg == "--name")
- name = getArg(*arg, arg, end);
- else if (*arg != "" && arg->at(0) == '-')
- return false;
- else
- args.push_back(*arg);
- return true;
- });
-
- myArgs.parseCmdline(argvToStrings(argc, argv));
-
- initPlugins();
-
- if (args.size() > 2)
- throw UsageError("too many arguments");
-
- Finally f([]() { stopProgressBar(); });
-
- if (isatty(STDERR_FILENO))
- startProgressBar();
-
- auto store = openStore();
- auto state = std::make_unique<EvalState>(myArgs.searchPath, store);
-
- Bindings & autoArgs = *myArgs.getAutoArgs(*state);
-
- /* If -A is given, get the URI from the specified Nix
- expression. */
- string uri;
- if (!fromExpr) {
- if (args.empty())
- throw UsageError("you must specify a URI");
- uri = args[0];
- } else {
- Path path = resolveExprPath(lookupFileArg(*state, args.empty() ? "." : args[0]));
- Value vRoot;
- state->evalFile(path, vRoot);
- Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot).first);
- state->forceAttrs(v);
-
- /* Extract the URI. */
- 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)
- throw Error("'urls' list is empty");
- uri = state->forceString(*attr->value->listElems()[0]);
-
- /* Extract the hash mode. */
- attr = v.attrs->find(state->symbols.create("outputHashMode"));
- if (attr == v.attrs->end())
- printInfo("warning: this does not look like a fetchurl call");
- else
- unpack = state->forceString(*attr->value) == "recursive";
-
- /* Extract the name. */
- if (name.empty()) {
- attr = v.attrs->find(state->symbols.create("name"));
- if (attr != v.attrs->end())
- name = state->forceString(*attr->value);
- }
- }
-
- /* Figure out a name in the Nix store. */
- if (name.empty())
- name = baseNameOf(uri);
- if (name.empty())
- throw Error("cannot figure out file name for '%1%'", uri);
-
- /* If an expected hash is given, the file may already exist in
- the store. */
- std::optional<Hash> expectedHash;
- Hash hash(ht);
- std::optional<StorePath> storePath;
- if (args.size() == 2) {
- expectedHash = Hash::parseAny(args[1], ht);
- const auto recursive = unpack ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat;
- storePath = store->makeFixedOutputPath(recursive, *expectedHash, name);
- if (store->isValidPath(*storePath))
- hash = *expectedHash;
- else
- storePath.reset();
- }
-
- if (!storePath) {
-
- auto actualUri = resolveMirrorUri(*state, uri);
-
- AutoDelete tmpDir(createTempDir(), true);
- Path tmpFile = (Path) tmpDir + "/tmp";
-
- /* Download the file. */
- {
- auto mode = 0600;
- if (executable)
- mode = 0700;
-
- AutoCloseFD fd = open(tmpFile.c_str(), O_WRONLY | O_CREAT | O_EXCL, mode);
- if (!fd) throw SysError("creating temporary file '%s'", tmpFile);
-
- FdSink sink(fd.get());
-
- FileTransferRequest req(actualUri);
- req.decompress = false;
- getFileTransfer()->download(std::move(req), sink);
- }
-
- /* Optionally unpack the file. */
- if (unpack) {
- printInfo("unpacking...");
- Path unpacked = (Path) tmpDir + "/unpacked";
- createDirs(unpacked);
- unpackTarfile(tmpFile, unpacked);
-
- /* If the archive unpacks to a single file/directory, then use
- that as the top-level. */
- auto entries = readDirectory(unpacked);
- if (entries.size() == 1)
- tmpFile = unpacked + "/" + entries[0].name;
- else
- tmpFile = unpacked;
- }
-
- const auto method = unpack || executable ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat;
-
- auto info = store->addToStoreSlow(name, tmpFile, method, ht, expectedHash);
- storePath = info.path;
- assert(info.ca);
- hash = getContentAddressHash(*info.ca);
- }
-
- stopProgressBar();
-
- if (!printPath)
- printInfo("path is '%s'", store->printStorePath(*storePath));
-
- std::cout << printHash16or32(hash) << std::endl;
- if (printPath)
- std::cout << store->printStorePath(*storePath) << std::endl;
-
- return 0;
- }
-}
-
-static RegisterLegacyCommand r_nix_prefetch_url("nix-prefetch-url", main_nix_prefetch_url);
diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc
index 54394e921..b97f684a4 100644
--- a/src/nix-store/nix-store.cc
+++ b/src/nix-store/nix-store.cc
@@ -19,10 +19,6 @@
#include <sys/stat.h>
#include <fcntl.h>
-#if HAVE_SODIUM
-#include <sodium.h>
-#endif
-
namespace nix_store {
@@ -761,7 +757,7 @@ static void opRepairPath(Strings opFlags, Strings opArgs)
throw UsageError("no flags expected");
for (auto & i : opArgs)
- ensureLocalStore()->repairPath(store->followLinksToStorePath(i));
+ store->repairPath(store->followLinksToStorePath(i));
}
/* Optimise the disk space usage of the Nix store by hard-linking
@@ -980,21 +976,11 @@ static void opGenerateBinaryCacheKey(Strings opFlags, Strings opArgs)
string secretKeyFile = *i++;
string publicKeyFile = *i++;
-#if HAVE_SODIUM
- if (sodium_init() == -1)
- throw Error("could not initialise libsodium");
-
- unsigned char pk[crypto_sign_PUBLICKEYBYTES];
- unsigned char sk[crypto_sign_SECRETKEYBYTES];
- if (crypto_sign_keypair(pk, sk) != 0)
- throw Error("key generation failed");
+ auto secretKey = SecretKey::generate(keyName);
- writeFile(publicKeyFile, keyName + ":" + base64Encode(string((char *) pk, crypto_sign_PUBLICKEYBYTES)));
+ writeFile(publicKeyFile, secretKey.toPublicKey().to_string());
umask(0077);
- writeFile(secretKeyFile, keyName + ":" + base64Encode(string((char *) sk, crypto_sign_SECRETKEYBYTES)));
-#else
- throw Error("Nix was not compiled with libsodium, required for signed binary cache support");
-#endif
+ writeFile(secretKeyFile, secretKey.to_string());
}
diff --git a/src/nix/add-to-store.cc b/src/nix/add-to-store.cc
index ea4bbbab9..2ae042789 100644
--- a/src/nix/add-to-store.cc
+++ b/src/nix/add-to-store.cc
@@ -19,7 +19,7 @@ struct CmdAddToStore : MixDryRun, StoreCommand
addFlag({
.longName = "name",
.shortName = 'n',
- .description = "name component of the store path",
+ .description = "Override the name component of the store path. It defaults to the base name of *path*.",
.labels = {"name"},
.handler = {&namePart},
});
diff --git a/src/nix/build.cc b/src/nix/build.cc
index c2974d983..4cb8ade08 100644
--- a/src/nix/build.cc
+++ b/src/nix/build.cc
@@ -19,7 +19,7 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
addFlag({
.longName = "out-link",
.shortName = 'o',
- .description = "path of the symlink to the build result",
+ .description = "Use *path* as prefix for the symlinks to the build results. It defaults to `result`.",
.labels = {"path"},
.handler = {&outLink},
.completer = completePath
@@ -27,13 +27,13 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
addFlag({
.longName = "no-link",
- .description = "do not create a symlink to the build result",
+ .description = "Do not create symlinks to the build results.",
.handler = {&outLink, Path("")},
});
addFlag({
.longName = "rebuild",
- .description = "rebuild an already built package and compare the result to the existing store paths",
+ .description = "Rebuild an already built package and compare the result to the existing store paths.",
.handler = {&buildMode, bmCheck},
});
}
diff --git a/src/nix/bundle.cc b/src/nix/bundle.cc
index 5f558b01e..1789e4598 100644
--- a/src/nix/bundle.cc
+++ b/src/nix/bundle.cc
@@ -16,7 +16,7 @@ struct CmdBundle : InstallableCommand
{
addFlag({
.longName = "bundler",
- .description = "use custom bundler",
+ .description = fmt("Use a custom bundler instead of the default (`%s`).", bundler),
.labels = {"flake-url"},
.handler = {&bundler},
.completer = {[&](size_t, std::string_view prefix) {
@@ -27,7 +27,7 @@ struct CmdBundle : InstallableCommand
addFlag({
.longName = "out-link",
.shortName = 'o',
- .description = "path of the symlink to the build result",
+ .description = "Override the name of the symlink to the build result. It defaults to the base name of the app.",
.labels = {"path"},
.handler = {&outLink},
.completer = completePath
@@ -90,7 +90,7 @@ struct CmdBundle : InstallableCommand
mkString(*evalState->allocAttr(*arg, evalState->symbols.create("system")), settings.thisSystem.get());
arg->attrs->sort();
-
+
auto vRes = evalState->allocValue();
evalState->callFunction(*bundler.toValue(*evalState).first, *arg, *vRes, noPos);
diff --git a/src/nix/command.cc b/src/nix/command.cc
index 596217775..ba58c7d6b 100644
--- a/src/nix/command.cc
+++ b/src/nix/command.cc
@@ -65,18 +65,18 @@ StorePathsCommand::StorePathsCommand(bool recursive)
if (recursive)
addFlag({
.longName = "no-recursive",
- .description = "apply operation to specified paths only",
+ .description = "Apply operation to specified paths only.",
.handler = {&this->recursive, false},
});
else
addFlag({
.longName = "recursive",
.shortName = 'r',
- .description = "apply operation to closure of the specified paths",
+ .description = "Apply operation to closure of the specified paths.",
.handler = {&this->recursive, true},
});
- mkFlag(0, "all", "apply operation to the entire store", &all);
+ mkFlag(0, "all", "Apply the operation to every store path.", &all);
}
void StorePathsCommand::run(ref<Store> store)
@@ -133,7 +133,7 @@ MixProfile::MixProfile()
{
addFlag({
.longName = "profile",
- .description = "profile to update",
+ .description = "The profile to update.",
.labels = {"path"},
.handler = {&profile},
.completer = completePath
@@ -190,14 +190,14 @@ MixEnvironment::MixEnvironment() : ignoreEnvironment(false)
addFlag({
.longName = "ignore-environment",
.shortName = 'i',
- .description = "clear the entire environment (except those specified with --keep)",
+ .description = "Clear the entire environment (except those specified with `--keep`).",
.handler = {&ignoreEnvironment, true},
});
addFlag({
.longName = "keep",
.shortName = 'k',
- .description = "keep specified environment variable",
+ .description = "Keep the environment variable *name*.",
.labels = {"name"},
.handler = {[&](std::string s) { keep.insert(s); }},
});
@@ -205,7 +205,7 @@ MixEnvironment::MixEnvironment() : ignoreEnvironment(false)
addFlag({
.longName = "unset",
.shortName = 'u',
- .description = "unset specified environment variable",
+ .description = "Unset the environment variable *name*.",
.labels = {"name"},
.handler = {[&](std::string s) { unset.insert(s); }},
});
diff --git a/src/nix/command.hh b/src/nix/command.hh
index 6882db195..f325cd906 100644
--- a/src/nix/command.hh
+++ b/src/nix/command.hh
@@ -13,6 +13,8 @@ namespace nix {
extern std::string programPath;
+extern char * * savedArgv;
+
class EvalState;
struct Pos;
class Store;
@@ -261,6 +263,8 @@ void completeFlakeRefWithFragment(
const Strings & defaultFlakeAttrPaths,
std::string_view prefix);
+std::string showVersions(const std::set<std::string> & versions);
+
void printClosureDiff(
ref<Store> store,
const StorePath & beforePath,
diff --git a/src/nix/copy.cc b/src/nix/copy.cc
index 2394eb46d..f15031a45 100644
--- a/src/nix/copy.cc
+++ b/src/nix/copy.cc
@@ -21,28 +21,28 @@ struct CmdCopy : StorePathsCommand
{
addFlag({
.longName = "from",
- .description = "URI of the source Nix store",
+ .description = "URL of the source Nix store.",
.labels = {"store-uri"},
.handler = {&srcUri},
});
addFlag({
.longName = "to",
- .description = "URI of the destination Nix store",
+ .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",
+ .description = "Do not require that paths are signed by trusted keys.",
.handler = {&checkSigs, NoCheckSigs},
});
addFlag({
.longName = "substitute-on-destination",
.shortName = 's',
- .description = "whether to try substitutes on the destination store (only supported by SSH)",
+ .description = "Whether to try substitutes on the destination store (only supported by SSH stores).",
.handler = {&substitute, Substitute},
});
diff --git a/src/nix-daemon/nix-daemon.cc b/src/nix/daemon.cc
index fc6195cf0..204d4ce6b 100644
--- a/src/nix-daemon/nix-daemon.cc
+++ b/src/nix/daemon.cc
@@ -1,3 +1,4 @@
+#include "command.hh"
#include "shared.hh"
#include "local-store.hh"
#include "remote-store.hh"
@@ -150,7 +151,7 @@ static ref<Store> openUncachedStore()
}
-static void daemonLoop(char * * argv)
+static void daemonLoop()
{
if (chdir("/") == -1)
throw SysError("cannot change current directory");
@@ -232,9 +233,9 @@ static void daemonLoop(char * * argv)
setSigChldAction(false);
// For debugging, stuff the pid into argv[1].
- if (peer.pidKnown && argv[1]) {
+ if (peer.pidKnown && savedArgv[1]) {
string processName = std::to_string(peer.pid);
- strncpy(argv[1], processName.c_str(), strlen(argv[1]));
+ strncpy(savedArgv[1], processName.c_str(), strlen(savedArgv[1]));
}
// Handle the connection.
@@ -264,6 +265,48 @@ static void daemonLoop(char * * argv)
}
}
+static void runDaemon(bool stdio)
+{
+ if (stdio) {
+ if (auto store = openUncachedStore().dynamic_pointer_cast<RemoteStore>()) {
+ auto conn = store->openConnectionWrapper();
+ int from = conn->from.fd;
+ int to = conn->to.fd;
+
+ auto nfds = std::max(from, STDIN_FILENO) + 1;
+ while (true) {
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(from, &fds);
+ FD_SET(STDIN_FILENO, &fds);
+ if (select(nfds, &fds, nullptr, nullptr, nullptr) == -1)
+ throw SysError("waiting for data from client or server");
+ if (FD_ISSET(from, &fds)) {
+ auto res = splice(from, nullptr, STDOUT_FILENO, nullptr, SSIZE_MAX, SPLICE_F_MOVE);
+ if (res == -1)
+ throw SysError("splicing data from daemon socket to stdout");
+ else if (res == 0)
+ throw EndOfFile("unexpected EOF from daemon socket");
+ }
+ if (FD_ISSET(STDIN_FILENO, &fds)) {
+ auto res = splice(STDIN_FILENO, nullptr, to, nullptr, SSIZE_MAX, SPLICE_F_MOVE);
+ if (res == -1)
+ throw SysError("splicing data from stdin to daemon socket");
+ else if (res == 0)
+ return;
+ }
+ }
+ } else {
+ FdSource from(STDIN_FILENO);
+ FdSink to(STDOUT_FILENO);
+ /* Auth hook is empty because in this mode we blindly trust the
+ standard streams. Limiting access to those is explicitly
+ not `nix-daemon`'s responsibility. */
+ processConnection(openUncachedStore(), from, to, Trusted, NotRecursive, [&](Store & _){});
+ }
+ } else
+ daemonLoop();
+}
static int main_nix_daemon(int argc, char * * argv)
{
@@ -285,49 +328,34 @@ static int main_nix_daemon(int argc, char * * argv)
initPlugins();
- if (stdio) {
- if (auto store = openUncachedStore().dynamic_pointer_cast<RemoteStore>()) {
- auto conn = store->openConnectionWrapper();
- int from = conn->from.fd;
- int to = conn->to.fd;
-
- auto nfds = std::max(from, STDIN_FILENO) + 1;
- while (true) {
- fd_set fds;
- FD_ZERO(&fds);
- FD_SET(from, &fds);
- FD_SET(STDIN_FILENO, &fds);
- if (select(nfds, &fds, nullptr, nullptr, nullptr) == -1)
- throw SysError("waiting for data from client or server");
- if (FD_ISSET(from, &fds)) {
- auto res = splice(from, nullptr, STDOUT_FILENO, nullptr, SSIZE_MAX, SPLICE_F_MOVE);
- if (res == -1)
- throw SysError("splicing data from daemon socket to stdout");
- else if (res == 0)
- throw EndOfFile("unexpected EOF from daemon socket");
- }
- if (FD_ISSET(STDIN_FILENO, &fds)) {
- auto res = splice(STDIN_FILENO, nullptr, to, nullptr, SSIZE_MAX, SPLICE_F_MOVE);
- if (res == -1)
- throw SysError("splicing data from stdin to daemon socket");
- else if (res == 0)
- return 0;
- }
- }
- } else {
- FdSource from(STDIN_FILENO);
- FdSink to(STDOUT_FILENO);
- /* Auth hook is empty because in this mode we blindly trust the
- standard streams. Limitting access to thoses is explicitly
- not `nix-daemon`'s responsibility. */
- processConnection(openUncachedStore(), from, to, Trusted, NotRecursive, [&](Store & _){});
- }
- } else {
- daemonLoop(argv);
- }
+ runDaemon(stdio);
return 0;
}
}
static RegisterLegacyCommand r_nix_daemon("nix-daemon", main_nix_daemon);
+
+struct CmdDaemon : StoreCommand
+{
+ std::string description() override
+ {
+ return "daemon to perform store operations on behalf of non-root clients";
+ }
+
+ Category category() override { return catUtility; }
+
+ std::string doc() override
+ {
+ return
+ #include "daemon.md"
+ ;
+ }
+
+ void run(ref<Store> store) override
+ {
+ runDaemon(false);
+ }
+};
+
+static auto rCmdDaemon = registerCommand2<CmdDaemon>({"daemon"});
diff --git a/src/nix/daemon.md b/src/nix/daemon.md
new file mode 100644
index 000000000..e97016a94
--- /dev/null
+++ b/src/nix/daemon.md
@@ -0,0 +1,21 @@
+R""(
+
+# Example
+
+* Run the daemon in the foreground:
+
+ ```console
+ # nix daemon
+ ```
+
+# Description
+
+This command runs the Nix daemon, which is a required component in
+multi-user Nix installations. It performs build actions and other
+operations on the Nix store on behalf of non-root users. Usually you
+don't run the daemon directly; instead it's managed by a service
+management framework such as `systemd`.
+
+Note that this daemon does not fork into the background.
+
+)""
diff --git a/src/nix/develop.cc b/src/nix/develop.cc
index edd87f246..578258394 100644
--- a/src/nix/develop.cc
+++ b/src/nix/develop.cc
@@ -204,7 +204,7 @@ struct Common : InstallableCommand, MixProfile
{
addFlag({
.longName = "redirect",
- .description = "redirect a store path to a mutable location",
+ .description = "Redirect a store path to a mutable location.",
.labels = {"installable", "outputs-dir"},
.handler = {[&](std::string installable, std::string outputsDir) {
redirects.push_back({installable, outputsDir});
@@ -334,7 +334,7 @@ struct CmdDevelop : Common, MixEnvironment
addFlag({
.longName = "command",
.shortName = 'c',
- .description = "command and arguments to be executed instead of an interactive shell",
+ .description = "Instead of starting an interactive shell, start the specified command and arguments.",
.labels = {"command", "args"},
.handler = {[&](std::vector<std::string> ss) {
if (ss.empty()) throw UsageError("--command requires at least one argument");
@@ -344,38 +344,38 @@ struct CmdDevelop : Common, MixEnvironment
addFlag({
.longName = "phase",
- .description = "phase to run (e.g. `build` or `configure`)",
+ .description = "The stdenv phase to run (e.g. `build` or `configure`).",
.labels = {"phase-name"},
.handler = {&phase},
});
addFlag({
.longName = "configure",
- .description = "run the configure phase",
+ .description = "Run the `configure` phase.",
.handler = {&phase, {"configure"}},
});
addFlag({
.longName = "build",
- .description = "run the build phase",
+ .description = "Run the `build` phase.",
.handler = {&phase, {"build"}},
});
addFlag({
.longName = "check",
- .description = "run the check phase",
+ .description = "Run the `check` phase.",
.handler = {&phase, {"check"}},
});
addFlag({
.longName = "install",
- .description = "run the install phase",
+ .description = "Run the `install` phase.",
.handler = {&phase, {"install"}},
});
addFlag({
.longName = "installcheck",
- .description = "run the installcheck phase",
+ .description = "Run the `installcheck` phase.",
.handler = {&phase, {"installCheck"}},
});
}
diff --git a/src/nix/eval.cc b/src/nix/eval.cc
index 321df7495..b5049ac65 100644
--- a/src/nix/eval.cc
+++ b/src/nix/eval.cc
@@ -18,18 +18,18 @@ struct CmdEval : MixJSON, InstallableCommand
CmdEval()
{
- mkFlag(0, "raw", "print strings unquoted", &raw);
+ mkFlag(0, "raw", "Print strings without quotes or escaping.", &raw);
addFlag({
.longName = "apply",
- .description = "apply a function to each argument",
+ .description = "Apply the function *expr* to each argument.",
.labels = {"expr"},
.handler = {&apply},
});
addFlag({
.longName = "write-to",
- .description = "write a string or attrset of strings to 'path'",
+ .description = "Write a string or attrset of strings to *path*.",
.labels = {"path"},
.handler = {&writeTo},
});
diff --git a/src/nix/flake-prefetch.md b/src/nix/flake-prefetch.md
new file mode 100644
index 000000000..a1cf0289a
--- /dev/null
+++ b/src/nix/flake-prefetch.md
@@ -0,0 +1,28 @@
+R""(
+
+# Examples
+
+* Download a tarball and unpack it:
+
+ ```console
+ # nix flake prefetch https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.5.tar.xz
+ Downloaded 'https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.5.tar.xz?narHash=sha256-3XYHZANT6AFBV0BqegkAZHbba6oeDkIUCDwbATLMhAY='
+ to '/nix/store/sl5vvk8mb4ma1sjyy03kwpvkz50hd22d-source' (hash
+ 'sha256-3XYHZANT6AFBV0BqegkAZHbba6oeDkIUCDwbATLMhAY=').
+ ```
+
+* Download the `dwarffs` flake (looked up in the flake registry):
+
+ ```console
+ # nix flake prefetch dwarffs --json
+ {"hash":"sha256-VHg3MYVgQ12LeRSU2PSoDeKlSPD8PYYEFxxwkVVDRd0="
+ ,"storePath":"/nix/store/hang3792qwdmm2n0d9nsrs5n6bsws6kv-source"}
+ ```
+
+# Description
+
+This command downloads the source tree denoted by flake reference
+*flake-url*. Note that this does not need to be a flake (i.e. it does
+not have to contain a `flake.nix` file).
+
+)""
diff --git a/src/nix/flake.cc b/src/nix/flake.cc
index 2b91faa64..4cd7d77a0 100644
--- a/src/nix/flake.cc
+++ b/src/nix/flake.cc
@@ -222,7 +222,7 @@ struct CmdFlakeCheck : FlakeCommand
{
addFlag({
.longName = "no-build",
- .description = "do not build checks",
+ .description = "Do not build checks.",
.handler = {&build, false}
});
}
@@ -573,7 +573,7 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
addFlag({
.longName = "template",
.shortName = 't',
- .description = "the template to use",
+ .description = "The template to use.",
.labels = {"template"},
.handler = {&templateUrl},
.completer = {[&](size_t, std::string_view prefix) {
@@ -717,7 +717,7 @@ struct CmdFlakeClone : FlakeCommand
addFlag({
.longName = "dest",
.shortName = 'f',
- .description = "destination path",
+ .description = "Clone the flake to path *dest*.",
.labels = {"path"},
.handler = {&destDir}
});
@@ -807,7 +807,7 @@ struct CmdFlakeShow : FlakeCommand
{
addFlag({
.longName = "legacy",
- .description = "show the contents of the 'legacyPackages' output",
+ .description = "Show the contents of the `legacyPackages` output.",
.handler = {&showLegacy, true}
});
}
@@ -960,6 +960,45 @@ struct CmdFlakeShow : FlakeCommand
}
};
+struct CmdFlakePrefetch : FlakeCommand, MixJSON
+{
+ CmdFlakePrefetch()
+ {
+ }
+
+ std::string description() override
+ {
+ return "download the source tree denoted by a flake reference into the Nix store";
+ }
+
+ std::string doc() override
+ {
+ return
+ #include "flake-prefetch.md"
+ ;
+ }
+
+ void run(ref<Store> store) override
+ {
+ auto originalRef = getFlakeRef();
+ auto resolvedRef = originalRef.resolve(store);
+ auto [tree, lockedRef] = resolvedRef.fetchTree(store);
+ auto hash = store->queryPathInfo(tree.storePath)->narHash;
+
+ if (json) {
+ auto res = nlohmann::json::object();
+ res["storePath"] = store->printStorePath(tree.storePath);
+ res["hash"] = hash.to_string(SRI, true);
+ logger->cout(res.dump());
+ } else {
+ notice("Downloaded '%s' to '%s' (hash '%s').",
+ lockedRef.to_string(),
+ store->printStorePath(tree.storePath),
+ hash.to_string(SRI, true));
+ }
+ }
+};
+
struct CmdFlake : NixMultiCommand
{
CmdFlake()
@@ -973,6 +1012,7 @@ struct CmdFlake : NixMultiCommand
{"clone", []() { return make_ref<CmdFlakeClone>(); }},
{"archive", []() { return make_ref<CmdFlakeArchive>(); }},
{"show", []() { return make_ref<CmdFlakeShow>(); }},
+ {"prefetch", []() { return make_ref<CmdFlakePrefetch>(); }},
})
{
}
diff --git a/src/nix/hash.cc b/src/nix/hash.cc
index 101b67e6a..79d506ace 100644
--- a/src/nix/hash.cc
+++ b/src/nix/hash.cc
@@ -19,15 +19,15 @@ struct CmdHashBase : Command
CmdHashBase(FileIngestionMethod mode) : mode(mode)
{
- mkFlag(0, "sri", "print hash in SRI format", &base, SRI);
- mkFlag(0, "base64", "print hash in base-64", &base, Base64);
- mkFlag(0, "base32", "print hash in base-32 (Nix-specific)", &base, Base32);
- mkFlag(0, "base16", "print hash in base-16", &base, Base16);
+ mkFlag(0, "sri", "Print the hash in SRI format.", &base, SRI);
+ mkFlag(0, "base64", "Print the hash in base-64 format.", &base, Base64);
+ mkFlag(0, "base32", "Print the hash in base-32 (Nix-specific) format.", &base, Base32);
+ mkFlag(0, "base16", "Print the hash in base-16 format.", &base, Base16);
addFlag(Flag::mkHashTypeFlag("type", &ht));
#if 0
mkFlag()
.longName("modulo")
- .description("compute hash modulo specified string")
+ .description("Compute the hash modulo specified the string.")
.labels({"modulus"})
.dest(&modulus);
#endif
@@ -40,15 +40,14 @@ struct CmdHashBase : Command
std::string description() override
{
- const char* d;
switch (mode) {
case FileIngestionMethod::Flat:
- d = "print cryptographic hash of a regular file";
- break;
+ return "print cryptographic hash of a regular file";
case FileIngestionMethod::Recursive:
- d = "print cryptographic hash of the NAR serialisation of a path";
+ return "print cryptographic hash of the NAR serialisation of a path";
+ default:
+ assert(false);
};
- return d;
}
void run() override
@@ -132,11 +131,6 @@ struct CmdHash : NixMultiCommand
command->second->prepare();
command->second->run();
}
-
- void printHelp(const string & programName, std::ostream & out) override
- {
- MultiCommand::printHelp(programName, out);
- }
};
static auto rCmdHash = registerCommand<CmdHash>("hash");
diff --git a/src/nix/installables.cc b/src/nix/installables.cc
index 3506c3fcc..50e3b29c4 100644
--- a/src/nix/installables.cc
+++ b/src/nix/installables.cc
@@ -60,37 +60,37 @@ MixFlakeOptions::MixFlakeOptions()
{
addFlag({
.longName = "recreate-lock-file",
- .description = "recreate lock file from scratch",
+ .description = "Recreate the flake's lock file from scratch.",
.handler = {&lockFlags.recreateLockFile, true}
});
addFlag({
.longName = "no-update-lock-file",
- .description = "do not allow any updates to the lock file",
+ .description = "Do not allow any updates to the flake's lock file.",
.handler = {&lockFlags.updateLockFile, false}
});
addFlag({
.longName = "no-write-lock-file",
- .description = "do not write the newly generated lock file",
+ .description = "Do not write the flake's newly generated lock file.",
.handler = {&lockFlags.writeLockFile, false}
});
addFlag({
.longName = "no-registries",
- .description = "don't use flake registries",
+ .description = "Don't allow lookups in the flake registries.",
.handler = {&lockFlags.useRegistries, false}
});
addFlag({
.longName = "commit-lock-file",
- .description = "commit changes to the lock file",
+ .description = "Commit changes to the flake's lock file.",
.handler = {&lockFlags.commitLockFile, true}
});
addFlag({
.longName = "update-input",
- .description = "update a specific flake input",
+ .description = "Update a specific flake input (ignoring its previous entry in the lock file).",
.labels = {"input-path"},
.handler = {[&](std::string s) {
lockFlags.inputUpdates.insert(flake::parseInputPath(s));
@@ -103,7 +103,7 @@ MixFlakeOptions::MixFlakeOptions()
addFlag({
.longName = "override-input",
- .description = "override a specific flake input (e.g. `dwarffs/nixpkgs`)",
+ .description = "Override a specific flake input (e.g. `dwarffs/nixpkgs`).",
.labels = {"input-path", "flake-url"},
.handler = {[&](std::string inputPath, std::string flakeRef) {
lockFlags.inputOverrides.insert_or_assign(
@@ -114,7 +114,7 @@ MixFlakeOptions::MixFlakeOptions()
addFlag({
.longName = "inputs-from",
- .description = "use the inputs of the specified flake as registry entries",
+ .description = "Use the inputs of the specified flake as registry entries.",
.labels = {"flake-url"},
.handler = {[&](std::string flakeRef) {
auto evalState = getEvalState();
@@ -143,22 +143,22 @@ SourceExprCommand::SourceExprCommand()
addFlag({
.longName = "file",
.shortName = 'f',
- .description = "evaluate *file* rather than the default",
+ .description = "Interpret installables as attribute paths relative to the Nix expression stored in *file*.",
.labels = {"file"},
.handler = {&file},
.completer = completePath
});
addFlag({
- .longName ="expr",
- .description = "evaluate attributes from *expr*",
+ .longName = "expr",
+ .description = "Interpret installables as attribute paths relative to the Nix expression *expr*.",
.labels = {"expr"},
.handler = {&expr}
});
addFlag({
- .longName ="derivation",
- .description = "operate on the store derivation rather than its outputs",
+ .longName = "derivation",
+ .description = "Operate on the store derivation rather than its outputs.",
.handler = {&operateOn, OperateOn::Derivation},
});
}
diff --git a/src/nix/key-convert-secret-to-public.md b/src/nix/key-convert-secret-to-public.md
new file mode 100644
index 000000000..3adc18502
--- /dev/null
+++ b/src/nix/key-convert-secret-to-public.md
@@ -0,0 +1,19 @@
+R""(
+
+# Examples
+
+* Convert a secret key to a public key:
+
+ ```console
+ # echo cache.example.org-0:E7lAO+MsPwTFfPXsdPtW8GKui/5ho4KQHVcAGnX+Tti1V4dUxoVoqLyWJ4YESuZJwQ67GVIksDt47og+tPVUZw== \
+ | nix key convert-secret-to-public
+ cache.example.org-0:tVeHVMaFaKi8lieGBErmScEOuxlSJLA7eO6IPrT1VGc=
+ ```
+
+# Description
+
+This command reads a Ed25519 secret key from standard input, and
+writes the corresponding public key to standard output. For more
+details, see [nix key generate-secret](./nix3-key-generate-secret.md).
+
+)""
diff --git a/src/nix/key-generate-secret.md b/src/nix/key-generate-secret.md
new file mode 100644
index 000000000..4938f637c
--- /dev/null
+++ b/src/nix/key-generate-secret.md
@@ -0,0 +1,48 @@
+R""(
+
+# Examples
+
+* Generate a new secret key:
+
+ ```console
+ # nix key generate-secret --key-name cache.example.org-1 > ./secret-key
+ ```
+
+ We can then use this key to sign the closure of the Hello package:
+
+ ```console
+ # nix build nixpkgs#hello
+ # nix store sign --key-file ./secret-key --recursive ./result
+ ```
+
+ Finally, we can verify the store paths using the corresponding
+ public key:
+
+ ```
+ # nix store verify --trusted-public-keys $(nix key convert-secret-to-public < ./secret-key) ./result
+ ```
+
+# Description
+
+This command generates a new Ed25519 secret key for signing store
+paths and prints it on standard output. Use `nix key
+convert-secret-to-public` to get the corresponding public key for
+verifying signed store paths.
+
+The mandatory argument `--key-name` specifies a key name (such as
+`cache.example.org-1). It is used to look up keys on the client when
+it verifies signatures. It can be anything, but it’s suggested to use
+the host name of your cache (e.g. `cache.example.org`) with a suffix
+denoting the number of the key (to be incremented every time you need
+to revoke a key).
+
+# Format
+
+Both secret and public keys are represented as the key name followed
+by a base-64 encoding of the Ed25519 key data, e.g.
+
+```
+cache.example.org-0:E7lAO+MsPwTFfPXsdPtW8GKui/5ho4KQHVcAGnX+Tti1V4dUxoVoqLyWJ4YESuZJwQ67GVIksDt47og+tPVUZw==
+```
+
+)""
diff --git a/src/nix/local.mk b/src/nix/local.mk
index f37b73384..23c08fc86 100644
--- a/src/nix/local.mk
+++ b/src/nix/local.mk
@@ -12,7 +12,6 @@ nix_SOURCES := \
$(wildcard src/nix-daemon/*.cc) \
$(wildcard src/nix-env/*.cc) \
$(wildcard src/nix-instantiate/*.cc) \
- $(wildcard src/nix-prefetch-url/*.cc) \
$(wildcard src/nix-store/*.cc) \
nix_CXXFLAGS += -I src/libutil -I src/libstore -I src/libfetchers -I src/libexpr -I src/libmain
diff --git a/src/nix/ls.cc b/src/nix/ls.cc
index d48287f27..c0b1ecb32 100644
--- a/src/nix/ls.cc
+++ b/src/nix/ls.cc
@@ -17,9 +17,9 @@ struct MixLs : virtual Args, MixJSON
MixLs()
{
- mkFlag('R', "recursive", "list subdirectories recursively", &recursive);
- mkFlag('l', "long", "show more file information", &verbose);
- mkFlag('d', "directory", "show directories rather than their contents", &showDirectory);
+ mkFlag('R', "recursive", "List subdirectories recursively.", &recursive);
+ mkFlag('l', "long", "Show detailed file information.", &verbose);
+ mkFlag('d', "directory", "Show directories rather than their contents.", &showDirectory);
}
void listText(ref<FSAccessor> accessor)
diff --git a/src/nix/main.cc b/src/nix/main.cc
index b2406fafe..418396280 100644
--- a/src/nix/main.cc
+++ b/src/nix/main.cc
@@ -52,6 +52,7 @@ static bool haveInternet()
}
std::string programPath;
+char * * savedArgv;
struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
{
@@ -69,15 +70,15 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
addFlag({
.longName = "help",
- .description = "show usage information",
+ .description = "Show usage information.",
.handler = {[&]() { if (!completions) showHelpAndExit(); }},
});
addFlag({
.longName = "help-config",
- .description = "show configuration options",
+ .description = "Show configuration settings.",
.handler = {[&]() {
- std::cout << "The following configuration options are available:\n\n";
+ std::cout << "The following configuration settings are available:\n\n";
Table2 tbl;
std::map<std::string, Config::SettingInfo> settings;
globalConfig.getSettings(settings);
@@ -91,25 +92,25 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
addFlag({
.longName = "print-build-logs",
.shortName = 'L',
- .description = "print full build logs on stderr",
+ .description = "Print full build logs on standard error.",
.handler = {[&]() {setLogFormat(LogFormat::barWithLogs); }},
});
addFlag({
.longName = "version",
- .description = "show version information",
+ .description = "Show version information.",
.handler = {[&]() { if (!completions) printVersion(programName); }},
});
addFlag({
.longName = "no-net",
- .description = "disable substituters and consider all previously downloaded files up-to-date",
+ .description = "Disable substituters and consider all previously downloaded files up-to-date.",
.handler = {[&]() { useNet = false; }},
});
addFlag({
.longName = "refresh",
- .description = "consider all previously downloaded files out-of-date",
+ .description = "Consider all previously downloaded files out-of-date.",
.handler = {[&]() { refresh = true; }},
});
}
@@ -129,7 +130,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
{"make-content-addressable", {"store", "make-content-addressable"}},
{"optimise-store", {"store", "optimise"}},
{"ping-store", {"store", "ping"}},
- {"sign-paths", {"store", "sign-paths"}},
+ {"sign-paths", {"store", "sign"}},
{"to-base16", {"hash", "to-base16"}},
{"to-base32", {"hash", "to-base32"}},
{"to-base64", {"hash", "to-base64"}},
@@ -232,6 +233,8 @@ static auto rCmdHelp = registerCommand<CmdHelp>("help");
void mainWrapped(int argc, char * * argv)
{
+ savedArgv = argv;
+
/* The chroot helper needs to be run before any threads have been
started. */
if (argc > 0 && argv[0] == chrootHelperName) {
diff --git a/src/nix/path-info.cc b/src/nix/path-info.cc
index 30b6a50f8..0fa88f1bf 100644
--- a/src/nix/path-info.cc
+++ b/src/nix/path-info.cc
@@ -18,10 +18,10 @@ struct CmdPathInfo : StorePathsCommand, MixJSON
CmdPathInfo()
{
- mkFlag('s', "size", "print size of the NAR dump of each path", &showSize);
- mkFlag('S', "closure-size", "print sum size of the NAR dumps of the closure of each path", &showClosureSize);
- mkFlag('h', "human-readable", "with -s and -S, print sizes like 1K 234M 5.67G etc.", &humanReadable);
- mkFlag(0, "sigs", "show signatures", &showSigs);
+ mkFlag('s', "size", "Print the size of the NAR serialisation of each path.", &showSize);
+ mkFlag('S', "closure-size", "Print the sum of the sizes of the NAR serialisations of the closure of each path.", &showClosureSize);
+ mkFlag('h', "human-readable", "With `-s` and `-S`, print sizes in a human-friendly format such as `5.67G`.", &humanReadable);
+ mkFlag(0, "sigs", "Show signatures.", &showSigs);
}
std::string description() override
diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc
new file mode 100644
index 000000000..a831dcd15
--- /dev/null
+++ b/src/nix/prefetch.cc
@@ -0,0 +1,319 @@
+#include "command.hh"
+#include "common-args.hh"
+#include "shared.hh"
+#include "store-api.hh"
+#include "filetransfer.hh"
+#include "finally.hh"
+#include "progress-bar.hh"
+#include "tarfile.hh"
+#include "attr-path.hh"
+#include "eval-inline.hh"
+#include "legacy.hh"
+
+#include <nlohmann/json.hpp>
+
+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)
+{
+ if (url.substr(0, 9) != "mirror://") return url;
+
+ std::string s(url, 9);
+ auto p = s.find('/');
+ if (p == std::string::npos) throw Error("invalid mirror URL '%s'", url);
+ std::string mirrorName(s, 0, p);
+
+ Value vMirrors;
+ // FIXME: use nixpkgs flake
+ state.eval(state.parseExprFromString("import <nixpkgs/pkgs/build-support/fetchurl/mirrors.nix>", "."), vMirrors);
+ state.forceAttrs(vMirrors);
+
+ 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);
+
+ 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::tuple<StorePath, Hash> prefetchFile(
+ ref<Store> store,
+ std::string_view url,
+ std::optional<std::string> name,
+ HashType hashType,
+ std::optional<Hash> expectedHash,
+ bool unpack,
+ bool executable)
+{
+ auto ingestionMethod = unpack || executable ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat;
+
+ /* Figure out a name in the Nix store. */
+ if (!name) {
+ name = baseNameOf(url);
+ if (name->empty())
+ throw Error("cannot figure out file name for '%s'", url);
+ }
+
+ std::optional<StorePath> storePath;
+ std::optional<Hash> hash;
+
+ /* If an expected hash is given, the file may already exist in
+ the store. */
+ if (expectedHash) {
+ hashType = expectedHash->type;
+ storePath = store->makeFixedOutputPath(ingestionMethod, *expectedHash, *name);
+ if (store->isValidPath(*storePath))
+ hash = expectedHash;
+ else
+ storePath.reset();
+ }
+
+ if (!storePath) {
+
+ AutoDelete tmpDir(createTempDir(), true);
+ Path tmpFile = (Path) tmpDir + "/tmp";
+
+ /* Download the file. */
+ {
+ auto mode = 0600;
+ if (executable)
+ mode = 0700;
+
+ AutoCloseFD fd = open(tmpFile.c_str(), O_WRONLY | O_CREAT | O_EXCL, mode);
+ if (!fd) throw SysError("creating temporary file '%s'", tmpFile);
+
+ FdSink sink(fd.get());
+
+ FileTransferRequest req(url);
+ req.decompress = false;
+ getFileTransfer()->download(std::move(req), sink);
+ }
+
+ /* Optionally unpack the file. */
+ if (unpack) {
+ Activity act(*logger, lvlChatty, actUnknown,
+ fmt("unpacking '%s'", url));
+ Path unpacked = (Path) tmpDir + "/unpacked";
+ createDirs(unpacked);
+ unpackTarfile(tmpFile, unpacked);
+
+ /* If the archive unpacks to a single file/directory, then use
+ that as the top-level. */
+ auto entries = readDirectory(unpacked);
+ if (entries.size() == 1)
+ tmpFile = unpacked + "/" + entries[0].name;
+ else
+ tmpFile = unpacked;
+ }
+
+ Activity act(*logger, lvlChatty, actUnknown,
+ fmt("adding '%s' to the store", url));
+
+ auto info = store->addToStoreSlow(*name, tmpFile, ingestionMethod, hashType, expectedHash);
+ storePath = info.path;
+ assert(info.ca);
+ hash = getContentAddressHash(*info.ca);
+ }
+
+ return {storePath.value(), hash.value()};
+}
+
+static int main_nix_prefetch_url(int argc, char * * argv)
+{
+ {
+ HashType ht = htSHA256;
+ std::vector<string> args;
+ bool printPath = getEnv("PRINT_PATH") == "1";
+ bool fromExpr = false;
+ string attrPath;
+ bool unpack = false;
+ bool executable = false;
+ std::optional<std::string> name;
+
+ struct MyArgs : LegacyArgs, MixEvalArgs
+ {
+ using LegacyArgs::LegacyArgs;
+ };
+
+ MyArgs myArgs(std::string(baseNameOf(argv[0])), [&](Strings::iterator & arg, const Strings::iterator & end) {
+ if (*arg == "--help")
+ showManPage("nix-prefetch-url");
+ else if (*arg == "--version")
+ printVersion("nix-prefetch-url");
+ else if (*arg == "--type") {
+ string s = getArg(*arg, arg, end);
+ ht = parseHashType(s);
+ }
+ else if (*arg == "--print-path")
+ printPath = true;
+ else if (*arg == "--attr" || *arg == "-A") {
+ fromExpr = true;
+ attrPath = getArg(*arg, arg, end);
+ }
+ else if (*arg == "--unpack")
+ unpack = true;
+ else if (*arg == "--executable")
+ executable = true;
+ else if (*arg == "--name")
+ name = getArg(*arg, arg, end);
+ else if (*arg != "" && arg->at(0) == '-')
+ return false;
+ else
+ args.push_back(*arg);
+ return true;
+ });
+
+ myArgs.parseCmdline(argvToStrings(argc, argv));
+
+ initPlugins();
+
+ if (args.size() > 2)
+ throw UsageError("too many arguments");
+
+ Finally f([]() { stopProgressBar(); });
+
+ if (isatty(STDERR_FILENO))
+ startProgressBar();
+
+ auto store = openStore();
+ auto state = std::make_unique<EvalState>(myArgs.searchPath, store);
+
+ Bindings & autoArgs = *myArgs.getAutoArgs(*state);
+
+ /* If -A is given, get the URL from the specified Nix
+ expression. */
+ string url;
+ if (!fromExpr) {
+ if (args.empty())
+ throw UsageError("you must specify a URL");
+ url = args[0];
+ } else {
+ Path path = resolveExprPath(lookupFileArg(*state, args.empty() ? "." : args[0]));
+ Value vRoot;
+ state->evalFile(path, vRoot);
+ Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot).first);
+ 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)
+ throw Error("'urls' list is empty");
+ url = state->forceString(*attr->value->listElems()[0]);
+
+ /* Extract the hash mode. */
+ attr = v.attrs->find(state->symbols.create("outputHashMode"));
+ if (attr == v.attrs->end())
+ printInfo("warning: this does not look like a fetchurl call");
+ else
+ unpack = state->forceString(*attr->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);
+ }
+ }
+
+ std::optional<Hash> expectedHash;
+ if (args.size() == 2)
+ expectedHash = Hash::parseAny(args[1], ht);
+
+ auto [storePath, hash] = prefetchFile(
+ store, resolveMirrorUrl(*state, url), name, ht, expectedHash, unpack, executable);
+
+ stopProgressBar();
+
+ if (!printPath)
+ printInfo("path is '%s'", store->printStorePath(storePath));
+
+ std::cout << printHash16or32(hash) << std::endl;
+ if (printPath)
+ std::cout << store->printStorePath(storePath) << std::endl;
+
+ return 0;
+ }
+}
+
+static RegisterLegacyCommand r_nix_prefetch_url("nix-prefetch-url", main_nix_prefetch_url);
+
+struct CmdStorePrefetchFile : StoreCommand, MixJSON
+{
+ std::string url;
+ bool executable = false;
+ std::optional<std::string> name;
+ HashType hashType = htSHA256;
+ std::optional<Hash> expectedHash;
+
+ CmdStorePrefetchFile()
+ {
+ addFlag({
+ .longName = "name",
+ .description = "Override the name component of the resulting store path. It defaults to the base name of *url*.",
+ .labels = {"name"},
+ .handler = {&name}
+ });
+
+ addFlag({
+ .longName = "expected-hash",
+ .description = "The expected hash of the file.",
+ .labels = {"hash"},
+ .handler = {[&](std::string s) {
+ expectedHash = Hash::parseAny(s, hashType);
+ }}
+ });
+
+ addFlag(Flag::mkHashTypeFlag("hash-type", &hashType));
+
+ addFlag({
+ .longName = "executable",
+ .description =
+ "Make the resulting file executable. Note that this causes the "
+ "resulting hash to be a NAR hash rather than a flat file hash.",
+ .handler = {&executable, true},
+ });
+
+ expectArg("url", &url);
+ }
+
+ Category category() override { return catUtility; }
+
+ std::string description() override
+ {
+ return "download a file into the Nix store";
+ }
+
+ std::string doc() override
+ {
+ return
+ #include "store-prefetch-file.md"
+ ;
+ }
+ void run(ref<Store> store) override
+ {
+ auto [storePath, hash] = prefetchFile(store, url, name, hashType, expectedHash, false, executable);
+
+ if (json) {
+ auto res = nlohmann::json::object();
+ res["storePath"] = store->printStorePath(storePath);
+ res["hash"] = hash.to_string(SRI, true);
+ logger->cout(res.dump());
+ } else {
+ notice("Downloaded '%s' to '%s' (hash '%s').",
+ url,
+ store->printStorePath(storePath),
+ hash.to_string(SRI, true));
+ }
+ }
+};
+
+static auto rCmdStorePrefetchFile = registerCommand2<CmdStorePrefetchFile>({"store", "prefetch-file"});
diff --git a/src/nix/profile-history.md b/src/nix/profile-history.md
new file mode 100644
index 000000000..d0fe40c82
--- /dev/null
+++ b/src/nix/profile-history.md
@@ -0,0 +1,26 @@
+R""(
+
+# Examples
+
+* Show the changes between each version of your default profile:
+
+ ```console
+ # nix profile history
+ Version 508 -> 509:
+ flake:nixpkgs#legacyPackages.x86_64-linux.awscli: ∅ -> 1.17.13
+
+ Version 509 -> 510:
+ flake:nixpkgs#legacyPackages.x86_64-linux.awscli: 1.17.13 -> 1.18.211
+ ```
+
+# Description
+
+This command shows what packages were added, removed or upgraded
+between subsequent versions of a profile. It only shows top-level
+packages, not dependencies; for that, use [`nix profile
+diff-closures`](./nix3-profile-diff-closures.md).
+
+The addition of a package to a profile is denoted by the string `∅ ->`
+*version*, whereas the removal is denoted by *version* `-> ∅`.
+
+)""
diff --git a/src/nix/profile-info.md b/src/nix/profile-list.md
index a0c04fc8c..5c29c0b02 100644
--- a/src/nix/profile-info.md
+++ b/src/nix/profile-list.md
@@ -5,7 +5,7 @@ R""(
* Show what packages are installed in the default profile:
```console
- # nix profile info
+ # 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
diff --git a/src/nix/profile.cc b/src/nix/profile.cc
index d8d2b3a70..ca95817d0 100644
--- a/src/nix/profile.cc
+++ b/src/nix/profile.cc
@@ -8,6 +8,7 @@
#include "flake/flakeref.hh"
#include "../nix-env/user-env.hh"
#include "profiles.hh"
+#include "names.hh"
#include <nlohmann/json.hpp>
#include <regex>
@@ -21,6 +22,13 @@ struct ProfileElementSource
FlakeRef resolvedRef;
std::string attrPath;
// FIXME: output names
+
+ bool operator < (const ProfileElementSource & other) const
+ {
+ return
+ std::pair(originalRef.to_string(), attrPath) <
+ std::pair(other.originalRef.to_string(), other.attrPath);
+ }
};
struct ProfileElement
@@ -29,6 +37,29 @@ struct ProfileElement
std::optional<ProfileElementSource> source;
bool active = true;
// FIXME: priority
+
+ std::string describe() const
+ {
+ if (source)
+ return fmt("%s#%s", source->originalRef, source->attrPath);
+ StringSet names;
+ for (auto & path : storePaths)
+ names.insert(DrvName(path.name()).name);
+ return concatStringsSep(", ", names);
+ }
+
+ std::string versions() const
+ {
+ StringSet versions;
+ for (auto & path : storePaths)
+ versions.insert(DrvName(path.name()).version);
+ return showVersions(versions);
+ }
+
+ bool operator < (const ProfileElement & other) const
+ {
+ return std::tuple(describe(), storePaths) < std::tuple(other.describe(), other.storePaths);
+ }
};
struct ProfileManifest
@@ -142,6 +173,46 @@ struct ProfileManifest
return std::move(info.path);
}
+
+ static void printDiff(const ProfileManifest & prev, const ProfileManifest & cur, std::string_view indent)
+ {
+ auto prevElems = prev.elements;
+ std::sort(prevElems.begin(), prevElems.end());
+
+ auto curElems = cur.elements;
+ std::sort(curElems.begin(), curElems.end());
+
+ auto i = prevElems.begin();
+ auto j = curElems.begin();
+
+ bool changes = false;
+
+ while (i != prevElems.end() || j != curElems.end()) {
+ if (j != curElems.end() && (i == prevElems.end() || i->describe() > j->describe())) {
+ std::cout << fmt("%s%s: ∅ -> %s\n", indent, j->describe(), j->versions());
+ changes = true;
+ ++j;
+ }
+ else if (i != prevElems.end() && (j == curElems.end() || i->describe() < j->describe())) {
+ std::cout << fmt("%s%s: %s -> ∅\n", indent, i->describe(), i->versions());
+ changes = true;
+ ++i;
+ }
+ else {
+ auto v1 = i->versions();
+ auto v2 = j->versions();
+ if (v1 != v2) {
+ std::cout << fmt("%s%s: %s -> %s\n", indent, i->describe(), v1, v2);
+ changes = true;
+ }
+ ++i;
+ ++j;
+ }
+ }
+
+ if (!changes)
+ std::cout << fmt("%sNo changes.\n", indent);
+ }
};
struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
@@ -209,9 +280,8 @@ public:
std::vector<Matcher> res;
for (auto & s : _matchers) {
- size_t n;
- if (string2Int(s, n))
- res.push_back(n);
+ if (auto n = string2Int<size_t>(s))
+ res.push_back(*n);
else if (store->isStorePath(s))
res.push_back(s);
else
@@ -337,7 +407,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
}
};
-struct CmdProfileInfo : virtual EvalCommand, virtual StoreCommand, MixDefaultProfile
+struct CmdProfileList : virtual EvalCommand, virtual StoreCommand, MixDefaultProfile
{
std::string description() override
{
@@ -347,7 +417,7 @@ struct CmdProfileInfo : virtual EvalCommand, virtual StoreCommand, MixDefaultPro
std::string doc() override
{
return
- #include "profile-info.md"
+ #include "profile-list.md"
;
}
@@ -402,6 +472,48 @@ struct CmdProfileDiffClosures : virtual StoreCommand, MixDefaultProfile
}
};
+struct CmdProfileHistory : virtual StoreCommand, EvalCommand, MixDefaultProfile
+{
+ std::string description() override
+ {
+ return "show all versions of a profile";
+ }
+
+ std::string doc() override
+ {
+ return
+ #include "profile-history.md"
+ ;
+ }
+
+ void run(ref<Store> store) override
+ {
+ auto [gens, curGen] = findGenerations(*profile);
+
+ std::optional<std::pair<Generation, ProfileManifest>> prevGen;
+ bool first = true;
+
+ for (auto & gen : gens) {
+ ProfileManifest manifest(*getEvalState(), gen.path);
+
+ 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);
+
+ ProfileManifest::printDiff(
+ prevGen ? prevGen->second : ProfileManifest(),
+ manifest,
+ " ");
+
+ prevGen = {gen, std::move(manifest)};
+ }
+ }
+};
+
struct CmdProfile : NixMultiCommand
{
CmdProfile()
@@ -409,8 +521,9 @@ struct CmdProfile : NixMultiCommand
{"install", []() { return make_ref<CmdProfileInstall>(); }},
{"remove", []() { return make_ref<CmdProfileRemove>(); }},
{"upgrade", []() { return make_ref<CmdProfileUpgrade>(); }},
- {"info", []() { return make_ref<CmdProfileInfo>(); }},
+ {"list", []() { return make_ref<CmdProfileList>(); }},
{"diff-closures", []() { return make_ref<CmdProfileDiffClosures>(); }},
+ {"history", []() { return make_ref<CmdProfileHistory>(); }},
})
{ }
diff --git a/src/nix/run.cc b/src/nix/run.cc
index 1340dd46f..ec9388234 100644
--- a/src/nix/run.cc
+++ b/src/nix/run.cc
@@ -72,7 +72,7 @@ struct CmdShell : InstallablesCommand, RunCommon, MixEnvironment
addFlag({
.longName = "command",
.shortName = 'c',
- .description = "command and arguments to be executed; defaults to '$SHELL'",
+ .description = "Command and arguments to be executed, defaulting to `$SHELL`",
.labels = {"command", "args"},
.handler = {[&](std::vector<std::string> ss) {
if (ss.empty()) throw UsageError("--command requires at least one argument");
diff --git a/src/nix/show-derivation.cc b/src/nix/show-derivation.cc
index 13f2c8e69..2588a011d 100644
--- a/src/nix/show-derivation.cc
+++ b/src/nix/show-derivation.cc
@@ -19,7 +19,7 @@ struct CmdShowDerivation : InstallablesCommand
addFlag({
.longName = "recursive",
.shortName = 'r',
- .description = "include the dependencies of the specified derivations",
+ .description = "Include the dependencies of the specified derivations.",
.handler = {&recursive, true}
});
}
diff --git a/src/nix/sigs.cc b/src/nix/sigs.cc
index 37b8a6712..3445182f2 100644
--- a/src/nix/sigs.cc
+++ b/src/nix/sigs.cc
@@ -16,7 +16,7 @@ struct CmdCopySigs : StorePathsCommand
addFlag({
.longName = "substituter",
.shortName = 's',
- .description = "use signatures from specified store",
+ .description = "Use signatures from specified store.",
.labels = {"store-uri"},
.handler = {[&](std::string s) { substituterUris.push_back(s); }},
});
@@ -92,16 +92,16 @@ struct CmdCopySigs : StorePathsCommand
static auto rCmdCopySigs = registerCommand2<CmdCopySigs>({"store", "copy-sigs"});
-struct CmdSignPaths : StorePathsCommand
+struct CmdSign : StorePathsCommand
{
Path secretKeyFile;
- CmdSignPaths()
+ CmdSign()
{
addFlag({
.longName = "key-file",
.shortName = 'k',
- .description = "file containing the secret signing key",
+ .description = "File containing the secret signing key.",
.labels = {"file"},
.handler = {&secretKeyFile},
.completer = completePath
@@ -140,4 +140,89 @@ struct CmdSignPaths : StorePathsCommand
}
};
-static auto rCmdSignPaths = registerCommand2<CmdSignPaths>({"store", "sign-paths"});
+static auto rCmdSign = registerCommand2<CmdSign>({"store", "sign"});
+
+struct CmdKeyGenerateSecret : Command
+{
+ std::optional<std::string> keyName;
+
+ CmdKeyGenerateSecret()
+ {
+ addFlag({
+ .longName = "key-name",
+ .description = "Identifier of the key (e.g. `cache.example.org-1`).",
+ .labels = {"name"},
+ .handler = {&keyName},
+ });
+ }
+
+ std::string description() override
+ {
+ return "generate a secret key for signing store paths";
+ }
+
+ std::string doc() override
+ {
+ return
+ #include "key-generate-secret.md"
+ ;
+ }
+
+ void run() override
+ {
+ if (!keyName)
+ throw UsageError("required argument '--key-name' is missing");
+
+ std::cout << SecretKey::generate(*keyName).to_string();
+ }
+};
+
+struct CmdKeyConvertSecretToPublic : Command
+{
+ std::string description() override
+ {
+ return "generate a public key for verifying store paths from a secret key read from standard input";
+ }
+
+ std::string doc() override
+ {
+ return
+ #include "key-convert-secret-to-public.md"
+ ;
+ }
+
+ void run() override
+ {
+ SecretKey secretKey(drainFD(STDIN_FILENO));
+ std::cout << secretKey.toPublicKey().to_string();
+ }
+};
+
+struct CmdKey : NixMultiCommand
+{
+ CmdKey()
+ : MultiCommand({
+ {"generate-secret", []() { return make_ref<CmdKeyGenerateSecret>(); }},
+ {"convert-secret-to-public", []() { return make_ref<CmdKeyConvertSecretToPublic>(); }},
+ })
+ {
+ }
+
+ std::string description() override
+ {
+ return "generate and convert Nix signing keys";
+ }
+
+ Category category() override { return catUtility; }
+
+ void run() override
+ {
+ if (!command)
+ throw UsageError("'nix flake' requires a sub-command.");
+ settings.requireExperimentalFeature("flakes");
+ command->second->prepare();
+ command->second->run();
+ }
+};
+
+static auto rCmdKey = registerCommand<CmdKey>("key");
diff --git a/src/nix/store-delete.cc b/src/nix/store-delete.cc
new file mode 100644
index 000000000..10245978e
--- /dev/null
+++ b/src/nix/store-delete.cc
@@ -0,0 +1,44 @@
+#include "command.hh"
+#include "common-args.hh"
+#include "shared.hh"
+#include "store-api.hh"
+
+using namespace nix;
+
+struct CmdStoreDelete : StorePathsCommand
+{
+ GCOptions options { .action = GCOptions::gcDeleteSpecific };
+
+ CmdStoreDelete()
+ {
+ addFlag({
+ .longName = "ignore-liveness",
+ .description = "Do not check whether the paths are reachable from a root.",
+ .handler = {&options.ignoreLiveness, true}
+ });
+ }
+
+ std::string description() override
+ {
+ return "delete paths from the Nix store";
+ }
+
+ std::string doc() override
+ {
+ return
+ #include "store-delete.md"
+ ;
+ }
+
+ void run(ref<Store> store, std::vector<StorePath> storePaths) override
+ {
+ for (auto & path : storePaths)
+ options.pathsToDelete.insert(path);
+
+ GCResults results;
+ PrintFreed freed(true, results);
+ store->collectGarbage(options, results);
+ }
+};
+
+static auto rCmdStoreDelete = registerCommand2<CmdStoreDelete>({"store", "delete"});
diff --git a/src/nix/store-delete.md b/src/nix/store-delete.md
new file mode 100644
index 000000000..db535f87c
--- /dev/null
+++ b/src/nix/store-delete.md
@@ -0,0 +1,24 @@
+R""(
+
+# Examples
+
+* Delete a specific store path:
+
+ ```console
+ # nix store delete /nix/store/yb5q57zxv6hgqql42d5r8b5k5mcq6kay-hello-2.10
+ ```
+
+# Description
+
+This command deletes the store paths specified by *installables*. ,
+but only if it is safe to do so; that is, when the path is not
+reachable from a root of the garbage collector. This means that you
+can only delete paths that would also be deleted by `nix store
+gc`. Thus, `nix store delete` is a more targeted version of `nix store
+gc`.
+
+With the option `--ignore-liveness`, reachability from the roots is
+ignored. However, the path still won't be deleted if there are other
+paths in the store that refer to it (i.e., depend on it).
+
+)""
diff --git a/src/nix/store-gc.cc b/src/nix/store-gc.cc
new file mode 100644
index 000000000..a2d74066e
--- /dev/null
+++ b/src/nix/store-gc.cc
@@ -0,0 +1,43 @@
+#include "command.hh"
+#include "common-args.hh"
+#include "shared.hh"
+#include "store-api.hh"
+
+using namespace nix;
+
+struct CmdStoreGC : StoreCommand, MixDryRun
+{
+ GCOptions options;
+
+ CmdStoreGC()
+ {
+ addFlag({
+ .longName = "max",
+ .description = "Stop after freeing *n* bytes of disk space.",
+ .labels = {"n"},
+ .handler = {&options.maxFreed}
+ });
+ }
+
+ std::string description() override
+ {
+ return "perform garbage collection on a Nix store";
+ }
+
+ std::string doc() override
+ {
+ return
+ #include "store-gc.md"
+ ;
+ }
+
+ void run(ref<Store> store) override
+ {
+ options.action = dryRun ? GCOptions::gcReturnDead : GCOptions::gcDeleteDead;
+ GCResults results;
+ PrintFreed freed(options.action == GCOptions::gcDeleteDead, results);
+ store->collectGarbage(options, results);
+ }
+};
+
+static auto rCmdStoreGC = registerCommand2<CmdStoreGC>({"store", "gc"});
diff --git a/src/nix/store-gc.md b/src/nix/store-gc.md
new file mode 100644
index 000000000..956b3c872
--- /dev/null
+++ b/src/nix/store-gc.md
@@ -0,0 +1,21 @@
+R""(
+
+# Examples
+
+* Delete unreachable paths in the Nix store:
+
+ ```console
+ # nix store gc
+ ```
+
+* Delete up to 1 gigabyte of garbage:
+
+ ```console
+ # nix store gc --max 1G
+ ```
+
+# Description
+
+This command deletes unreachable paths in the Nix store.
+
+)""
diff --git a/src/nix/store-prefetch-file.md b/src/nix/store-prefetch-file.md
new file mode 100644
index 000000000..1663b847b
--- /dev/null
+++ b/src/nix/store-prefetch-file.md
@@ -0,0 +1,32 @@
+R""(
+
+# Examples
+
+* Download a file to the Nix store:
+
+ ```console
+ # nix store prefetch-file https://releases.nixos.org/nix/nix-2.3.10/nix-2.3.10.tar.xz
+ Downloaded 'https://releases.nixos.org/nix/nix-2.3.10/nix-2.3.10.tar.xz' to
+ '/nix/store/vbdbi42hgnc4h7pyqzp6h2yf77kw93aw-source' (hash
+ 'sha256-qKheVd5D0BervxMDbt+1hnTKE2aRWC8XCAwc0SeHt6s=').
+ ```
+
+* Download a file and get the SHA-512 hash:
+
+ ```console
+ # nix store prefetch-file --json --hash-type sha512 \
+ https://releases.nixos.org/nix/nix-2.3.10/nix-2.3.10.tar.xz \
+ | jq -r .hash
+ sha512-6XJxfym0TNH9knxeH4ZOvns6wElFy3uahunl2hJgovACCMEMXSy42s69zWVyGJALXTI+86tpDJGlIcAySEKBbA==
+ ```
+
+# Description
+
+This command downloads the file *url* to the Nix store. It prints out
+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`.
+
+)""
diff --git a/src/nix/store-repair.cc b/src/nix/store-repair.cc
new file mode 100644
index 000000000..1c7a4392e
--- /dev/null
+++ b/src/nix/store-repair.cc
@@ -0,0 +1,27 @@
+#include "command.hh"
+#include "store-api.hh"
+
+using namespace nix;
+
+struct CmdStoreRepair : StorePathsCommand
+{
+ std::string description() override
+ {
+ return "repair store paths";
+ }
+
+ std::string doc() override
+ {
+ return
+ #include "store-repair.md"
+ ;
+ }
+
+ void run(ref<Store> store, std::vector<StorePath> storePaths) override
+ {
+ for (auto & path : storePaths)
+ store->repairPath(path);
+ }
+};
+
+static auto rStoreRepair = registerCommand2<CmdStoreRepair>({"store", "repair"});
diff --git a/src/nix/store-repair.md b/src/nix/store-repair.md
new file mode 100644
index 000000000..92d2205a9
--- /dev/null
+++ b/src/nix/store-repair.md
@@ -0,0 +1,32 @@
+R""(
+
+# Examples
+
+* Repair a store path, after determining that it is corrupt:
+
+ ```console
+ # nix store verify /nix/store/yb5q57zxv6hgqql42d5r8b5k5mcq6kay-hello-2.10
+ path '/nix/store/yb5q57zxv6hgqql42d5r8b5k5mcq6kay-hello-2.10' was
+ modified! expected hash
+ 'sha256:1hd5vnh6xjk388gdk841vflicy8qv7qzj2hb7xlyh8lpb43j921l', got
+ 'sha256:1a25lf78x5wi6pfkrxalf0n13kdaca0bqmjqnp7wfjza2qz5ssgl'
+
+ # nix store repair /nix/store/yb5q57zxv6hgqql42d5r8b5k5mcq6kay-hello-2.10
+ ```
+
+# Description
+
+This command attempts to "repair" the store paths specified by
+*installables* by redownloading them using the available
+substituters. If no substitutes are available, then repair is not
+possible.
+
+> **Warning**
+>
+> During repair, there is a very small time window during which the old
+> path (if it exists) is moved out of the way and replaced with the new
+> path. If repair is interrupted in between, then the system may be left
+> in a broken state (e.g., if the path contains a critical system
+> component like the GNU C Library).
+
+)""
diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc
index 79be31e73..299ea40aa 100644
--- a/src/nix/upgrade-nix.cc
+++ b/src/nix/upgrade-nix.cc
@@ -19,14 +19,14 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand
addFlag({
.longName = "profile",
.shortName = 'p',
- .description = "the Nix profile to upgrade",
+ .description = "The path to the Nix profile to upgrade.",
.labels = {"profile-dir"},
.handler = {&profileDir}
});
addFlag({
.longName = "nix-store-paths-url",
- .description = "URL of the file that contains the store paths of the latest Nix release",
+ .description = "The URL of the file that contains the store paths of the latest Nix release.",
.labels = {"url"},
.handler = {&storePathsUrl}
});
diff --git a/src/nix/verify.cc b/src/nix/verify.cc
index 16d42349f..b2963cf74 100644
--- a/src/nix/verify.cc
+++ b/src/nix/verify.cc
@@ -18,16 +18,24 @@ struct CmdVerify : StorePathsCommand
CmdVerify()
{
- mkFlag(0, "no-contents", "do not verify the contents of each store path", &noContents);
- mkFlag(0, "no-trust", "do not verify whether each store path is trusted", &noTrust);
+ mkFlag(0, "no-contents", "Do not verify the contents of each store path.", &noContents);
+ mkFlag(0, "no-trust", "Do not verify whether each store path is trusted.", &noTrust);
+
addFlag({
.longName = "substituter",
.shortName = 's',
- .description = "use signatures from specified store",
+ .description = "Use signatures from the specified store.",
.labels = {"store-uri"},
.handler = {[&](std::string s) { substituterUris.push_back(s); }}
});
- mkIntFlag('n', "sigs-needed", "require that each path has at least N valid signatures", &sigsNeeded);
+
+ addFlag({
+ .longName = "sigs-needed",
+ .shortName = 'n',
+ .description = "Require that each path has at least *n* valid signatures.",
+ .labels = {"n"},
+ .handler = {&sigsNeeded}
+ });
}
std::string description() override
diff --git a/src/nix/why-depends.cc b/src/nix/why-depends.cc
index 297b638cc..7a4ca5172 100644
--- a/src/nix/why-depends.cc
+++ b/src/nix/why-depends.cc
@@ -40,7 +40,7 @@ struct CmdWhyDepends : SourceExprCommand
addFlag({
.longName = "all",
.shortName = 'a',
- .description = "show all edges in the dependency graph leading from 'package' to 'dependency', rather than just a shortest path",
+ .description = "Show all edges in the dependency graph leading from *package* to *dependency*, rather than just a shortest path.",
.handler = {&all, true},
});
}