forked from lix-project/lix
8846465934
Renamed `fstateRefs' to `fstateRequisites'. The semantics of this function is that it returns a list of all paths necessary to realise a given expression. For a derive expression, this is the union of requisites of the inputs; for a slice expression, it is the path of each element in the slice. Also included are the paths of the expressions themselves. Optionally, one can also include the requisites of successor expressions (to recycle intermediate results). * `nix-switch' now distinguishes between an expression and its normal form. Usually, only the normal form is registered as a root of the garbage collector. With the `--source-root' flag, it will also register the original expression as a root. * `nix-collect-garbage' now has a flag `--keep-successors' which causes successors not to be included in the list of garbage paths. * `nix-collect-garbage' now has a flag `--invert' which will print all paths that should *not* be garbage collected.
389 lines
11 KiB
C++
389 lines
11 KiB
C++
#include <iostream>
|
|
#include <sstream>
|
|
|
|
#include "globals.hh"
|
|
#include "normalise.hh"
|
|
#include "archive.hh"
|
|
#include "shared.hh"
|
|
|
|
|
|
typedef void (* Operation) (Strings opFlags, Strings opArgs);
|
|
|
|
|
|
static bool pathArgs = false;
|
|
|
|
|
|
static void printHelp()
|
|
{
|
|
cout <<
|
|
#include "nix-help.txt.hh"
|
|
;
|
|
exit(0);
|
|
}
|
|
|
|
|
|
|
|
static FSId argToId(const string & arg)
|
|
{
|
|
if (!pathArgs)
|
|
return parseHash(arg);
|
|
else {
|
|
FSId id;
|
|
if (!queryPathId(arg, id))
|
|
throw Error(format("don't know id of `%1%'") % arg);
|
|
return id;
|
|
}
|
|
}
|
|
|
|
|
|
/* Realise (or install) paths from the given Nix fstate
|
|
expressions. */
|
|
static void opInstall(Strings opFlags, Strings opArgs)
|
|
{
|
|
if (!opFlags.empty()) throw UsageError("unknown flag");
|
|
|
|
for (Strings::iterator it = opArgs.begin();
|
|
it != opArgs.end(); it++)
|
|
{
|
|
FSId id = normaliseFState(argToId(*it));
|
|
realiseSlice(id);
|
|
cout << format("%1%\n") % (string) id;
|
|
}
|
|
}
|
|
|
|
|
|
/* Delete a path in the Nix store directory. */
|
|
static void opDelete(Strings opFlags, Strings opArgs)
|
|
{
|
|
if (!opFlags.empty()) throw UsageError("unknown flag");
|
|
|
|
for (Strings::iterator it = opArgs.begin();
|
|
it != opArgs.end(); it++)
|
|
deleteFromStore(absPath(*it));
|
|
}
|
|
|
|
|
|
/* Add paths to the Nix values directory and print the hashes of those
|
|
paths. */
|
|
static void opAdd(Strings opFlags, Strings opArgs)
|
|
{
|
|
if (!opFlags.empty()) throw UsageError("unknown flag");
|
|
|
|
for (Strings::iterator it = opArgs.begin();
|
|
it != opArgs.end(); it++)
|
|
{
|
|
string path;
|
|
FSId id;
|
|
addToStore(*it, path, id);
|
|
cout << format("%1% %2%\n") % (string) id % path;
|
|
}
|
|
}
|
|
|
|
|
|
static string dotQuote(const string & s)
|
|
{
|
|
return "\"" + s + "\"";
|
|
}
|
|
|
|
|
|
FSId maybeNormalise(const FSId & id, bool normalise)
|
|
{
|
|
return normalise ? normaliseFState(id) : id;
|
|
}
|
|
|
|
|
|
/* Perform various sorts of queries. */
|
|
static void opQuery(Strings opFlags, Strings opArgs)
|
|
{
|
|
enum { qList, qRequisites, qGenerators, qExpansion, qGraph
|
|
} query = qList;
|
|
bool normalise = false;
|
|
bool includeExprs = true;
|
|
bool includeSuccessors = false;
|
|
|
|
for (Strings::iterator i = opFlags.begin();
|
|
i != opFlags.end(); i++)
|
|
if (*i == "--list" || *i == "-l") query = qList;
|
|
else if (*i == "--requisites" || *i == "-r") query = qRequisites;
|
|
else if (*i == "--generators" || *i == "-g") query = qGenerators;
|
|
else if (*i == "--expansion" || *i == "-e") query = qExpansion;
|
|
else if (*i == "--graph") query = qGraph;
|
|
else if (*i == "--normalise" || *i == "-n") normalise = true;
|
|
else if (*i == "--exclude-exprs") includeExprs = false;
|
|
else if (*i == "--include-successors") includeSuccessors = true;
|
|
else throw UsageError(format("unknown flag `%1%'") % *i);
|
|
|
|
switch (query) {
|
|
|
|
case qList: {
|
|
StringSet paths;
|
|
for (Strings::iterator i = opArgs.begin();
|
|
i != opArgs.end(); i++)
|
|
{
|
|
Strings paths2 = fstatePaths(
|
|
maybeNormalise(argToId(*i), normalise));
|
|
paths.insert(paths2.begin(), paths2.end());
|
|
}
|
|
for (StringSet::iterator i = paths.begin();
|
|
i != paths.end(); i++)
|
|
cout << format("%s\n") % *i;
|
|
break;
|
|
}
|
|
|
|
case qRequisites: {
|
|
StringSet paths;
|
|
for (Strings::iterator i = opArgs.begin();
|
|
i != opArgs.end(); i++)
|
|
{
|
|
Strings paths2 = fstateRequisites(
|
|
maybeNormalise(argToId(*i), normalise),
|
|
includeExprs, includeSuccessors);
|
|
paths.insert(paths2.begin(), paths2.end());
|
|
}
|
|
for (StringSet::iterator i = paths.begin();
|
|
i != paths.end(); i++)
|
|
cout << format("%s\n") % *i;
|
|
break;
|
|
}
|
|
|
|
case qGenerators: {
|
|
FSIds outIds;
|
|
for (Strings::iterator i = opArgs.begin();
|
|
i != opArgs.end(); i++)
|
|
outIds.push_back(argToId(*i));
|
|
|
|
FSIds genIds = findGenerators(outIds);
|
|
|
|
for (FSIds::iterator i = genIds.begin();
|
|
i != genIds.end(); i++)
|
|
cout << format("%s\n") % expandId(*i);
|
|
break;
|
|
}
|
|
|
|
case qExpansion: {
|
|
for (Strings::iterator i = opArgs.begin();
|
|
i != opArgs.end(); i++)
|
|
/* !!! should not use substitutes; this is a query,
|
|
it should not have side-effects */
|
|
cout << format("%s\n") % expandId(parseHash(*i));
|
|
break;
|
|
}
|
|
|
|
case qGraph: {
|
|
|
|
FSIds workList;
|
|
|
|
for (Strings::iterator i = opArgs.begin();
|
|
i != opArgs.end(); i++)
|
|
workList.push_back(argToId(*i));
|
|
|
|
FSIdSet doneSet;
|
|
|
|
cout << "digraph G {\n";
|
|
|
|
while (!workList.empty()) {
|
|
FSId id = workList.front();
|
|
workList.pop_front();
|
|
|
|
if (doneSet.find(id) == doneSet.end()) {
|
|
doneSet.insert(id);
|
|
|
|
FState fs = parseFState(termFromId(id));
|
|
|
|
string label, shape;
|
|
|
|
if (fs.type == FState::fsDerive) {
|
|
for (FSIds::iterator i = fs.derive.inputs.begin();
|
|
i != fs.derive.inputs.end(); i++)
|
|
{
|
|
workList.push_back(*i);
|
|
cout << dotQuote(*i) << " -> "
|
|
<< dotQuote(id) << ";\n";
|
|
}
|
|
|
|
label = "derive";
|
|
shape = "box";
|
|
for (StringPairs::iterator i = fs.derive.env.begin();
|
|
i != fs.derive.env.end(); i++)
|
|
if (i->first == "name") label = i->second;
|
|
}
|
|
|
|
else if (fs.type == FState::fsSlice) {
|
|
label = baseNameOf((*fs.slice.elems.begin()).path);
|
|
shape = "ellipse";
|
|
if (isHash(string(label, 0, Hash::hashSize * 2)) &&
|
|
label[Hash::hashSize * 2] == '-')
|
|
label = string(label, Hash::hashSize * 2 + 1);
|
|
}
|
|
|
|
else abort();
|
|
|
|
cout << dotQuote(id) << "[label = "
|
|
<< dotQuote(label)
|
|
<< ", shape = " << shape
|
|
<< "];\n";
|
|
}
|
|
}
|
|
|
|
cout << "}\n";
|
|
break;
|
|
}
|
|
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
|
|
|
|
static void opSuccessor(Strings opFlags, Strings opArgs)
|
|
{
|
|
if (!opFlags.empty()) throw UsageError("unknown flag");
|
|
if (opArgs.size() % 2) throw UsageError("expecting even number of arguments");
|
|
|
|
for (Strings::iterator i = opArgs.begin();
|
|
i != opArgs.end(); )
|
|
{
|
|
FSId id1 = parseHash(*i++);
|
|
FSId id2 = parseHash(*i++);
|
|
registerSuccessor(id1, id2);
|
|
}
|
|
}
|
|
|
|
|
|
static void opSubstitute(Strings opFlags, Strings opArgs)
|
|
{
|
|
if (!opFlags.empty()) throw UsageError("unknown flag");
|
|
if (opArgs.size() % 2) throw UsageError("expecting even number of arguments");
|
|
|
|
for (Strings::iterator i = opArgs.begin();
|
|
i != opArgs.end(); )
|
|
{
|
|
FSId src = parseHash(*i++);
|
|
FSId sub = parseHash(*i++);
|
|
registerSubstitute(src, sub);
|
|
}
|
|
}
|
|
|
|
|
|
/* A sink that writes dump output to stdout. */
|
|
struct StdoutSink : DumpSink
|
|
{
|
|
virtual void operator ()
|
|
(const unsigned char * data, unsigned int len)
|
|
{
|
|
writeFull(STDOUT_FILENO, data, len);
|
|
}
|
|
};
|
|
|
|
|
|
/* Dump a path as a Nix archive. The archive is written to standard
|
|
output. */
|
|
static void opDump(Strings opFlags, Strings opArgs)
|
|
{
|
|
if (!opFlags.empty()) throw UsageError("unknown flag");
|
|
if (opArgs.size() != 1) throw UsageError("only one argument allowed");
|
|
|
|
StdoutSink sink;
|
|
string arg = *opArgs.begin();
|
|
string path = pathArgs ? arg : expandId(parseHash(arg));
|
|
|
|
dumpPath(path, sink);
|
|
}
|
|
|
|
|
|
/* A source that read restore intput to stdin. */
|
|
struct StdinSource : RestoreSource
|
|
{
|
|
virtual void operator () (unsigned char * data, unsigned int len)
|
|
{
|
|
readFull(STDIN_FILENO, data, len);
|
|
}
|
|
};
|
|
|
|
|
|
/* Restore a value from a Nix archive. The archive is written to
|
|
standard input. */
|
|
static void opRestore(Strings opFlags, Strings opArgs)
|
|
{
|
|
if (!opFlags.empty()) throw UsageError("unknown flag");
|
|
if (opArgs.size() != 1) throw UsageError("only one argument allowed");
|
|
|
|
StdinSource source;
|
|
restorePath(*opArgs.begin(), source);
|
|
}
|
|
|
|
|
|
/* Initialise the Nix databases. */
|
|
static void opInit(Strings opFlags, Strings opArgs)
|
|
{
|
|
if (!opFlags.empty()) throw UsageError("unknown flag");
|
|
if (!opArgs.empty())
|
|
throw UsageError("--init does not have arguments");
|
|
initDB();
|
|
}
|
|
|
|
|
|
/* Verify the consistency of the Nix environment. */
|
|
static void opVerify(Strings opFlags, Strings opArgs)
|
|
{
|
|
verifyStore();
|
|
}
|
|
|
|
|
|
/* Scan the arguments; find the operation, set global flags, put all
|
|
other flags in a list, and put all other arguments in another
|
|
list. */
|
|
void run(Strings args)
|
|
{
|
|
Strings opFlags, opArgs;
|
|
Operation op = 0;
|
|
|
|
for (Strings::iterator it = args.begin(); it != args.end(); )
|
|
{
|
|
string arg = *it++;
|
|
|
|
Operation oldOp = op;
|
|
|
|
if (arg == "--install" || arg == "-i")
|
|
op = opInstall;
|
|
else if (arg == "--delete" || arg == "-d")
|
|
op = opDelete;
|
|
else if (arg == "--add" || arg == "-A")
|
|
op = opAdd;
|
|
else if (arg == "--query" || arg == "-q")
|
|
op = opQuery;
|
|
else if (arg == "--successor")
|
|
op = opSuccessor;
|
|
else if (arg == "--substitute")
|
|
op = opSubstitute;
|
|
else if (arg == "--dump")
|
|
op = opDump;
|
|
else if (arg == "--restore")
|
|
op = opRestore;
|
|
else if (arg == "--init")
|
|
op = opInit;
|
|
else if (arg == "--verify")
|
|
op = opVerify;
|
|
else if (arg == "--path" || arg == "-p")
|
|
pathArgs = true;
|
|
else if (arg == "--verbose" || arg == "-v")
|
|
verbosity = (Verbosity) ((int) verbosity + 1);
|
|
else if (arg == "--help")
|
|
printHelp();
|
|
else if (arg[0] == '-')
|
|
opFlags.push_back(arg);
|
|
else
|
|
opArgs.push_back(arg);
|
|
|
|
if (oldOp && oldOp != op)
|
|
throw UsageError("only one operation may be specified");
|
|
}
|
|
|
|
if (!op) throw UsageError("no operation specified");
|
|
|
|
op(opFlags, opArgs);
|
|
}
|
|
|
|
|
|
string programId = "nix";
|