2016-02-09 22:28:29 +02:00
|
|
|
#include "command.hh"
|
|
|
|
#include "store-api.hh"
|
2020-10-09 23:18:08 +03:00
|
|
|
#include "local-fs-store.hh"
|
2017-04-25 14:20:26 +03:00
|
|
|
#include "derivations.hh"
|
2019-11-08 16:13:32 +02:00
|
|
|
#include "nixexpr.hh"
|
2020-03-30 20:14:17 +03:00
|
|
|
#include "profiles.hh"
|
|
|
|
|
2020-08-17 18:44:52 +03:00
|
|
|
#include <nlohmann/json.hpp>
|
|
|
|
|
2020-06-17 05:19:15 +03:00
|
|
|
extern char * * environ __attribute__((weak));
|
2016-02-09 22:28:29 +02:00
|
|
|
|
|
|
|
namespace nix {
|
|
|
|
|
2020-12-03 18:55:27 +02:00
|
|
|
RegisterCommand::Commands * RegisterCommand::commands = nullptr;
|
|
|
|
|
|
|
|
nix::Commands RegisterCommand::getCommandsFor(const std::vector<std::string> & prefix)
|
|
|
|
{
|
|
|
|
nix::Commands res;
|
|
|
|
for (auto & [name, command] : *RegisterCommand::commands)
|
|
|
|
if (name.size() == prefix.size() + 1) {
|
|
|
|
bool equal = true;
|
|
|
|
for (size_t i = 0; i < prefix.size(); ++i)
|
|
|
|
if (name[i] != prefix[i]) equal = false;
|
|
|
|
if (equal)
|
|
|
|
res.insert_or_assign(name[prefix.size()], command);
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
2016-02-09 22:28:29 +02:00
|
|
|
|
2020-08-17 18:44:52 +03:00
|
|
|
nlohmann::json NixMultiCommand::toJSON()
|
|
|
|
{
|
|
|
|
// FIXME: use Command::toJSON() as well.
|
|
|
|
return MultiCommand::toJSON();
|
|
|
|
}
|
2016-02-09 22:28:29 +02:00
|
|
|
|
2016-03-21 19:03:36 +02:00
|
|
|
StoreCommand::StoreCommand()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-04-25 12:20:37 +03:00
|
|
|
ref<Store> StoreCommand::getStore()
|
|
|
|
{
|
|
|
|
if (!_store)
|
|
|
|
_store = createStore();
|
|
|
|
return ref<Store>(_store);
|
|
|
|
}
|
|
|
|
|
2017-03-16 15:25:54 +02:00
|
|
|
ref<Store> StoreCommand::createStore()
|
|
|
|
{
|
2017-10-23 20:34:49 +03:00
|
|
|
return openStore();
|
2017-03-16 15:25:54 +02:00
|
|
|
}
|
|
|
|
|
2016-02-09 22:28:29 +02:00
|
|
|
void StoreCommand::run()
|
|
|
|
{
|
2017-09-08 15:46:55 +03:00
|
|
|
run(getStore());
|
2016-02-09 22:28:29 +02:00
|
|
|
}
|
|
|
|
|
2022-01-17 20:45:21 +02:00
|
|
|
CopyCommand::CopyCommand()
|
|
|
|
{
|
|
|
|
addFlag({
|
|
|
|
.longName = "from",
|
|
|
|
.description = "URL of the source Nix store.",
|
|
|
|
.labels = {"store-uri"},
|
|
|
|
.handler = {&srcUri},
|
|
|
|
});
|
|
|
|
|
|
|
|
addFlag({
|
|
|
|
.longName = "to",
|
|
|
|
.description = "URL of the destination Nix store.",
|
|
|
|
.labels = {"store-uri"},
|
|
|
|
.handler = {&dstUri},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
ref<Store> CopyCommand::createStore()
|
|
|
|
{
|
|
|
|
return srcUri.empty() ? StoreCommand::createStore() : openStore(srcUri);
|
|
|
|
}
|
|
|
|
|
|
|
|
ref<Store> CopyCommand::getDstStore()
|
|
|
|
{
|
|
|
|
if (srcUri.empty() && dstUri.empty())
|
|
|
|
throw UsageError("you must pass '--from' and/or '--to'");
|
|
|
|
|
|
|
|
return dstUri.empty() ? openStore() : openStore(dstUri);
|
|
|
|
}
|
|
|
|
|
2020-08-05 22:26:17 +03:00
|
|
|
EvalCommand::EvalCommand()
|
|
|
|
{
|
|
|
|
addFlag({
|
2021-02-01 16:50:58 +02:00
|
|
|
.longName = "debugger",
|
2020-08-05 22:26:17 +03:00
|
|
|
.description = "start an interactive environment if evaluation fails",
|
|
|
|
.handler = {&startReplOnEvalErrors, true},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-06-29 22:09:48 +03:00
|
|
|
EvalCommand::~EvalCommand()
|
|
|
|
{
|
|
|
|
if (evalState)
|
|
|
|
evalState->printStats();
|
|
|
|
}
|
|
|
|
|
|
|
|
ref<Store> EvalCommand::getEvalStore()
|
|
|
|
{
|
|
|
|
if (!evalStore)
|
|
|
|
evalStore = evalStoreUrl ? openStore(*evalStoreUrl) : getStore();
|
|
|
|
return ref<Store>(evalStore);
|
|
|
|
}
|
|
|
|
|
2021-04-29 00:50:11 +03:00
|
|
|
ref<EvalState> EvalCommand::getEvalState()
|
|
|
|
{
|
|
|
|
if (!evalState) {
|
2022-01-04 03:13:16 +02:00
|
|
|
evalState =
|
2022-01-17 14:58:26 +02:00
|
|
|
#if HAVE_BOEHMGC
|
2022-01-04 03:13:16 +02:00
|
|
|
std::allocate_shared<EvalState>(traceable_allocator<EvalState>(),
|
2022-01-17 14:58:26 +02:00
|
|
|
searchPath, getEvalStore(), getStore())
|
|
|
|
#else
|
2022-01-04 03:13:16 +02:00
|
|
|
std::make_shared<EvalState>(
|
2022-01-17 14:58:26 +02:00
|
|
|
searchPath, getEvalStore(), getStore())
|
|
|
|
#endif
|
|
|
|
;
|
2022-05-20 02:01:23 +03:00
|
|
|
|
2022-05-20 19:33:50 +03:00
|
|
|
if (startReplOnEvalErrors) {
|
2022-06-02 19:26:46 +03:00
|
|
|
evalState->debugRepl = &runRepl;
|
|
|
|
};
|
2021-04-29 00:50:11 +03:00
|
|
|
}
|
|
|
|
return ref<EvalState>(evalState);
|
|
|
|
}
|
|
|
|
|
2021-05-17 09:45:08 +03:00
|
|
|
BuiltPathsCommand::BuiltPathsCommand(bool recursive)
|
2017-09-27 19:28:54 +03:00
|
|
|
: recursive(recursive)
|
2016-03-29 15:29:50 +03:00
|
|
|
{
|
2017-09-27 19:28:54 +03:00
|
|
|
if (recursive)
|
2020-05-04 23:40:19 +03:00
|
|
|
addFlag({
|
|
|
|
.longName = "no-recursive",
|
2021-01-13 15:18:04 +02:00
|
|
|
.description = "Apply operation to specified paths only.",
|
2021-01-25 20:03:13 +02:00
|
|
|
.category = installablesCategory,
|
2020-05-04 23:40:19 +03:00
|
|
|
.handler = {&this->recursive, false},
|
|
|
|
});
|
2017-09-27 19:28:54 +03:00
|
|
|
else
|
2020-05-04 23:40:19 +03:00
|
|
|
addFlag({
|
|
|
|
.longName = "recursive",
|
|
|
|
.shortName = 'r',
|
2021-01-13 15:18:04 +02:00
|
|
|
.description = "Apply operation to closure of the specified paths.",
|
2021-01-25 20:03:13 +02:00
|
|
|
.category = installablesCategory,
|
2020-05-04 23:40:19 +03:00
|
|
|
.handler = {&this->recursive, true},
|
|
|
|
});
|
2017-09-27 19:28:54 +03:00
|
|
|
|
2021-01-25 20:03:13 +02:00
|
|
|
addFlag({
|
|
|
|
.longName = "all",
|
|
|
|
.description = "Apply the operation to every store path.",
|
|
|
|
.category = installablesCategory,
|
|
|
|
.handler = {&all, true},
|
|
|
|
});
|
2016-03-29 15:29:50 +03:00
|
|
|
}
|
|
|
|
|
2021-05-17 09:45:08 +03:00
|
|
|
void BuiltPathsCommand::run(ref<Store> store)
|
2016-03-29 15:29:50 +03:00
|
|
|
{
|
2021-05-17 09:45:08 +03:00
|
|
|
BuiltPaths paths;
|
2016-04-14 22:14:29 +03:00
|
|
|
if (all) {
|
2017-04-25 14:20:26 +03:00
|
|
|
if (installables.size())
|
2017-07-30 14:27:57 +03:00
|
|
|
throw UsageError("'--all' does not expect arguments");
|
2020-12-14 18:24:30 +02:00
|
|
|
// XXX: Only uses opaque paths, ignores all the realisations
|
2016-04-14 22:14:29 +03:00
|
|
|
for (auto & p : store->queryAllValidPaths())
|
2021-05-17 09:45:08 +03:00
|
|
|
paths.push_back(BuiltPath::Opaque{p});
|
2020-12-14 18:24:30 +02:00
|
|
|
} else {
|
2022-03-02 14:54:08 +02:00
|
|
|
paths = Installable::toBuiltPaths(getEvalStore(), store, realiseMode, operateOn, installables);
|
2016-04-14 22:14:29 +03:00
|
|
|
if (recursive) {
|
2021-05-17 09:45:08 +03:00
|
|
|
// XXX: This only computes the store path closure, ignoring
|
|
|
|
// intermediate realisations
|
|
|
|
StorePathSet pathsRoots, pathsClosure;
|
2021-09-27 11:57:11 +03:00
|
|
|
for (auto & root : paths) {
|
2021-05-17 09:45:08 +03:00
|
|
|
auto rootFromThis = root.outPaths();
|
|
|
|
pathsRoots.insert(rootFromThis.begin(), rootFromThis.end());
|
|
|
|
}
|
|
|
|
store->computeFSClosure(pathsRoots, pathsClosure);
|
|
|
|
for (auto & path : pathsClosure)
|
|
|
|
paths.push_back(BuiltPath::Opaque{path});
|
2016-04-14 22:14:29 +03:00
|
|
|
}
|
2016-03-29 15:29:50 +03:00
|
|
|
}
|
|
|
|
|
2020-12-14 18:24:30 +02:00
|
|
|
run(store, std::move(paths));
|
|
|
|
}
|
|
|
|
|
|
|
|
StorePathsCommand::StorePathsCommand(bool recursive)
|
2021-05-17 09:45:08 +03:00
|
|
|
: BuiltPathsCommand(recursive)
|
2020-12-14 18:24:30 +02:00
|
|
|
{
|
2016-03-29 15:29:50 +03:00
|
|
|
}
|
|
|
|
|
2021-09-27 11:53:09 +03:00
|
|
|
void StorePathsCommand::run(ref<Store> store, BuiltPaths && paths)
|
2017-05-04 15:16:26 +03:00
|
|
|
{
|
2021-09-27 11:57:11 +03:00
|
|
|
StorePathSet storePaths;
|
|
|
|
for (auto & builtPath : paths)
|
|
|
|
for (auto & p : builtPath.outPaths())
|
|
|
|
storePaths.insert(p);
|
2020-12-14 18:24:30 +02:00
|
|
|
|
2021-09-27 11:57:11 +03:00
|
|
|
auto sorted = store->topoSortPaths(storePaths);
|
|
|
|
std::reverse(sorted.begin(), sorted.end());
|
2017-05-04 15:16:26 +03:00
|
|
|
|
2021-09-27 11:57:11 +03:00
|
|
|
run(store, std::move(sorted));
|
2016-03-29 15:29:50 +03:00
|
|
|
}
|
|
|
|
|
2021-09-27 11:53:09 +03:00
|
|
|
void StorePathCommand::run(ref<Store> store, std::vector<StorePath> && storePaths)
|
2017-05-04 15:16:26 +03:00
|
|
|
{
|
|
|
|
if (storePaths.size() != 1)
|
|
|
|
throw UsageError("this command requires exactly one store path");
|
|
|
|
|
|
|
|
run(store, *storePaths.begin());
|
|
|
|
}
|
|
|
|
|
2022-03-04 21:54:50 +02:00
|
|
|
Strings editorFor(const Path & file, uint32_t line)
|
2019-11-08 16:13:32 +02:00
|
|
|
{
|
2019-11-22 17:06:44 +02:00
|
|
|
auto editor = getEnv("EDITOR").value_or("cat");
|
2019-11-08 16:13:32 +02:00
|
|
|
auto args = tokenizeString<Strings>(editor);
|
2022-03-04 21:54:50 +02:00
|
|
|
if (line > 0 && (
|
2019-11-08 16:13:32 +02:00
|
|
|
editor.find("emacs") != std::string::npos ||
|
|
|
|
editor.find("nano") != std::string::npos ||
|
2022-03-22 16:18:02 +02:00
|
|
|
editor.find("vim") != std::string::npos ||
|
|
|
|
editor.find("kak") != std::string::npos))
|
2022-03-04 21:54:50 +02:00
|
|
|
args.push_back(fmt("+%d", line));
|
|
|
|
args.push_back(file);
|
2019-11-08 16:13:32 +02:00
|
|
|
return args;
|
|
|
|
}
|
|
|
|
|
2020-03-30 20:14:17 +03:00
|
|
|
MixProfile::MixProfile()
|
|
|
|
{
|
2020-05-04 23:40:19 +03:00
|
|
|
addFlag({
|
|
|
|
.longName = "profile",
|
2021-01-13 15:18:04 +02:00
|
|
|
.description = "The profile to update.",
|
2020-05-04 23:40:19 +03:00
|
|
|
.labels = {"path"},
|
|
|
|
.handler = {&profile},
|
2020-05-10 22:35:07 +03:00
|
|
|
.completer = completePath
|
2020-05-04 23:40:19 +03:00
|
|
|
});
|
2020-03-30 20:14:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void MixProfile::updateProfile(const StorePath & storePath)
|
|
|
|
{
|
|
|
|
if (!profile) return;
|
|
|
|
auto store = getStore().dynamic_pointer_cast<LocalFSStore>();
|
|
|
|
if (!store) throw Error("'--profile' is not supported for this Nix store");
|
|
|
|
auto profile2 = absPath(*profile);
|
|
|
|
switchLink(profile2,
|
|
|
|
createGeneration(
|
|
|
|
ref<LocalFSStore>(store),
|
2020-09-03 12:06:56 +03:00
|
|
|
profile2, storePath));
|
2020-03-30 20:14:17 +03:00
|
|
|
}
|
|
|
|
|
2021-05-12 17:19:51 +03:00
|
|
|
void MixProfile::updateProfile(const BuiltPaths & buildables)
|
2020-03-30 20:14:17 +03:00
|
|
|
{
|
|
|
|
if (!profile) return;
|
|
|
|
|
2020-08-05 19:27:15 +03:00
|
|
|
std::vector<StorePath> result;
|
2020-03-30 20:14:17 +03:00
|
|
|
|
|
|
|
for (auto & buildable : buildables) {
|
2020-07-23 22:02:57 +03:00
|
|
|
std::visit(overloaded {
|
2021-10-01 00:31:21 +03:00
|
|
|
[&](const BuiltPath::Opaque & bo) {
|
2020-08-05 19:27:15 +03:00
|
|
|
result.push_back(bo.path);
|
2020-07-23 22:02:57 +03:00
|
|
|
},
|
2021-10-01 00:31:21 +03:00
|
|
|
[&](const BuiltPath::Built & bfd) {
|
2020-07-23 22:02:57 +03:00
|
|
|
for (auto & output : bfd.outputs) {
|
2021-05-17 09:45:08 +03:00
|
|
|
result.push_back(output.second);
|
2020-07-23 22:02:57 +03:00
|
|
|
}
|
|
|
|
},
|
2021-04-05 17:05:21 +03:00
|
|
|
}, buildable.raw());
|
2020-03-30 20:14:17 +03:00
|
|
|
}
|
|
|
|
|
2020-08-05 19:27:15 +03:00
|
|
|
if (result.size() != 1)
|
2021-06-30 00:28:43 +03:00
|
|
|
throw UsageError("'--profile' requires that the arguments produce a single store path, but there are %d", result.size());
|
2020-03-30 20:14:17 +03:00
|
|
|
|
2020-08-05 19:27:15 +03:00
|
|
|
updateProfile(result[0]);
|
2020-03-30 20:14:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
MixDefaultProfile::MixDefaultProfile()
|
|
|
|
{
|
|
|
|
profile = getDefaultProfile();
|
|
|
|
}
|
|
|
|
|
2020-05-04 23:40:19 +03:00
|
|
|
MixEnvironment::MixEnvironment() : ignoreEnvironment(false)
|
|
|
|
{
|
|
|
|
addFlag({
|
|
|
|
.longName = "ignore-environment",
|
|
|
|
.shortName = 'i',
|
2021-01-13 15:18:04 +02:00
|
|
|
.description = "Clear the entire environment (except those specified with `--keep`).",
|
2020-05-04 23:40:19 +03:00
|
|
|
.handler = {&ignoreEnvironment, true},
|
|
|
|
});
|
|
|
|
|
|
|
|
addFlag({
|
|
|
|
.longName = "keep",
|
|
|
|
.shortName = 'k',
|
2021-01-13 15:18:04 +02:00
|
|
|
.description = "Keep the environment variable *name*.",
|
2020-05-04 23:40:19 +03:00
|
|
|
.labels = {"name"},
|
|
|
|
.handler = {[&](std::string s) { keep.insert(s); }},
|
|
|
|
});
|
|
|
|
|
|
|
|
addFlag({
|
|
|
|
.longName = "unset",
|
|
|
|
.shortName = 'u',
|
2021-01-13 15:18:04 +02:00
|
|
|
.description = "Unset the environment variable *name*.",
|
2020-05-04 23:40:19 +03:00
|
|
|
.labels = {"name"},
|
|
|
|
.handler = {[&](std::string s) { unset.insert(s); }},
|
|
|
|
});
|
2020-03-30 20:14:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void MixEnvironment::setEnviron() {
|
|
|
|
if (ignoreEnvironment) {
|
|
|
|
if (!unset.empty())
|
|
|
|
throw UsageError("--unset does not make sense with --ignore-environment");
|
|
|
|
|
|
|
|
for (const auto & var : keep) {
|
|
|
|
auto val = getenv(var.c_str());
|
|
|
|
if (val) stringsEnv.emplace_back(fmt("%s=%s", var.c_str(), val));
|
|
|
|
}
|
|
|
|
|
|
|
|
vectorEnv = stringsToCharPtrs(stringsEnv);
|
|
|
|
environ = vectorEnv.data();
|
|
|
|
} else {
|
|
|
|
if (!keep.empty())
|
|
|
|
throw UsageError("--keep does not make sense without --ignore-environment");
|
|
|
|
|
|
|
|
for (const auto & var : unset)
|
|
|
|
unsetenv(var.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-09 22:28:29 +02:00
|
|
|
}
|