diff options
Diffstat (limited to 'src/nix-store')
-rw-r--r-- | src/nix-store/Makefile.am | 27 | ||||
-rw-r--r-- | src/nix-store/dotgraph.cc | 135 | ||||
-rw-r--r-- | src/nix-store/dotgraph.hh | 8 | ||||
-rw-r--r-- | src/nix-store/nix-help.txt | 34 | ||||
-rw-r--r-- | src/nix-store/nix.cc | 304 |
5 files changed, 508 insertions, 0 deletions
diff --git a/src/nix-store/Makefile.am b/src/nix-store/Makefile.am new file mode 100644 index 000000000..a39d1e2ad --- /dev/null +++ b/src/nix-store/Makefile.am @@ -0,0 +1,27 @@ +bin_PROGRAMS = nix-store + +nix_store_SOURCES = nix.cc dotgraph.cc +nix_store_LDADD = ../libmain/libmain.a ../libstore/libstore.a ../libutil/libutil.a \ + ../boost/format/libformat.a -L../../externals/inst/lib -ldb_cxx -lATerm + +nix.o: nix-help.txt.hh + +%.hh: % + echo -n '"' > $@ + sed 's|\(.*\)|\1\\n\\|' < $< >> $@ + echo '"' >> $@ + +AM_CXXFLAGS = \ + -I.. -I../../externals/inst/include -I../libutil -I../libstore -I../libmain + +install-data-local: + $(INSTALL) -d $(localstatedir)/nix + $(INSTALL) -d $(localstatedir)/nix/db + $(INSTALL) -d $(localstatedir)/nix/links + rm -f $(prefix)/current + ln -sf $(localstatedir)/nix/links/current $(prefix)/current + $(INSTALL) -d $(localstatedir)/log/nix + $(INSTALL) -d $(prefix)/store + $(bindir)/nix-store --init + +EXTRA_DIST = *.hh diff --git a/src/nix-store/dotgraph.cc b/src/nix-store/dotgraph.cc new file mode 100644 index 000000000..c670bf19e --- /dev/null +++ b/src/nix-store/dotgraph.cc @@ -0,0 +1,135 @@ +#include "dotgraph.hh" +#include "normalise.hh" + + +static string dotQuote(const string & s) +{ + return "\"" + s + "\""; +} + + +static string nextColour() +{ + static int n = 0; + static string colours[] = + { "black", "red", "green", "blue" + , "magenta", "burlywood" }; + return colours[n++ % (sizeof(colours) / sizeof(string))]; +} + + +static string makeEdge(const string & src, const string & dst) +{ + format f = format("%1% -> %2% [color = %3%];\n") + % dotQuote(src) % dotQuote(dst) % dotQuote(nextColour()); + return f.str(); +} + + +static string makeNode(const string & id, const string & label, + const string & colour) +{ + format f = format("%1% [label = %2%, shape = box, " + "style = filled, fillcolor = %3%];\n") + % dotQuote(id) % dotQuote(label) % dotQuote(colour); + return f.str(); +} + + +static string symbolicName(const string & path) +{ + string p = baseNameOf(path); + if (isHash(string(p, 0, Hash::hashSize * 2)) && + p[Hash::hashSize * 2] == '-') + p = string(p, Hash::hashSize * 2 + 1); + return p; +} + + +string pathLabel(const Path & nePath, const string & elemPath) +{ + return (string) nePath + "-" + elemPath; +} + + +void printClosure(const Path & nePath, const StoreExpr & fs) +{ + PathSet workList(fs.closure.roots); + PathSet doneSet; + + for (PathSet::iterator i = workList.begin(); i != workList.end(); i++) { + cout << makeEdge(pathLabel(nePath, *i), nePath); + } + + while (!workList.empty()) { + Path path = *(workList.begin()); + workList.erase(path); + + if (doneSet.find(path) == doneSet.end()) { + doneSet.insert(path); + + ClosureElems::const_iterator elem = fs.closure.elems.find(path); + if (elem == fs.closure.elems.end()) + throw Error(format("bad closure, missing path `%1%'") % path); + + for (StringSet::const_iterator i = elem->second.refs.begin(); + i != elem->second.refs.end(); i++) + { + workList.insert(*i); + cout << makeEdge(pathLabel(nePath, *i), pathLabel(nePath, path)); + } + + cout << makeNode(pathLabel(nePath, path), + symbolicName(path), "#ff0000"); + } + } +} + + +void printDotGraph(const PathSet & roots) +{ + PathSet workList(roots); + PathSet doneSet; + + cout << "digraph G {\n"; + + while (!workList.empty()) { + Path nePath = *(workList.begin()); + workList.erase(nePath); + + if (doneSet.find(nePath) == doneSet.end()) { + doneSet.insert(nePath); + + StoreExpr ne = storeExprFromPath(nePath); + + string label, colour; + + if (ne.type == StoreExpr::neDerivation) { + for (PathSet::iterator i = ne.derivation.inputs.begin(); + i != ne.derivation.inputs.end(); i++) + { + workList.insert(*i); + cout << makeEdge(*i, nePath); + } + + label = "derivation"; + colour = "#00ff00"; + for (StringPairs::iterator i = ne.derivation.env.begin(); + i != ne.derivation.env.end(); i++) + if (i->first == "name") label = i->second; + } + + else if (ne.type == StoreExpr::neClosure) { + label = "<closure>"; + colour = "#00ffff"; + printClosure(nePath, ne); + } + + else abort(); + + cout << makeNode(nePath, label, colour); + } + } + + cout << "}\n"; +} diff --git a/src/nix-store/dotgraph.hh b/src/nix-store/dotgraph.hh new file mode 100644 index 000000000..ef389b30d --- /dev/null +++ b/src/nix-store/dotgraph.hh @@ -0,0 +1,8 @@ +#ifndef __DOTGRAPH_H +#define __DOTGRAPH_H + +#include "storeexpr.hh" + +void printDotGraph(const PathSet & roots); + +#endif /* !__DOTGRAPH_H */ diff --git a/src/nix-store/nix-help.txt b/src/nix-store/nix-help.txt new file mode 100644 index 000000000..d7f977025 --- /dev/null +++ b/src/nix-store/nix-help.txt @@ -0,0 +1,34 @@ +nix-store [OPTIONS...] [ARGUMENTS...] + +`nix-store' is a tool to manipulate the Nix store. + +Operations: + + --realise / -r: realise a Nix expression + --delete / -d: delete paths from the Nix store + --add / -A: copy a path to the Nix store + --query / -q: query information + + --successor: register a successor expression + --substitute: register a substitute expression + + --dump: dump a path as a Nix archive + --restore: restore a path from a Nix archive + + --init: initialise the Nix database + --verify: verify Nix structures + + --version: output version information + --help: display help + +Query flags: + + --list / -l: query the output paths (roots) of a Nix expression (default) + --requisites / -R: print all paths necessary to realise expression + --predecessors: print predecessors of a Nix expression + --graph: print a dot graph rooted at given ids + +Options: + + --verbose / -v: verbose operation (may be repeated) + --keep-failed / -K: keep temporary directories of failed builds diff --git a/src/nix-store/nix.cc b/src/nix-store/nix.cc new file mode 100644 index 000000000..d1766de39 --- /dev/null +++ b/src/nix-store/nix.cc @@ -0,0 +1,304 @@ +#include <iostream> +#include <sstream> + +#include "globals.hh" +#include "normalise.hh" +#include "archive.hh" +#include "shared.hh" +#include "dotgraph.hh" + + +typedef void (* Operation) (Strings opFlags, Strings opArgs); + + +static void printHelp() +{ + cout << +#include "nix-help.txt.hh" + ; + exit(0); +} + + + +static Path checkPath(const Path & arg) +{ + return arg; /* !!! check that arg is in the store */ +} + + +/* Realise paths from the given store expressions. */ +static void opRealise(Strings opFlags, Strings opArgs) +{ + if (!opFlags.empty()) throw UsageError("unknown flag"); + + for (Strings::iterator i = opArgs.begin(); + i != opArgs.end(); i++) + { + Path nfPath = normaliseStoreExpr(checkPath(*i)); + realiseClosure(nfPath); + cout << format("%1%\n") % (string) nfPath; + } +} + + +/* 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(checkPath(*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 i = opArgs.begin(); i != opArgs.end(); i++) + cout << format("%1%\n") % addToStore(*i); +} + + +Path maybeNormalise(const Path & ne, bool normalise) +{ + return normalise ? normaliseStoreExpr(ne) : ne; +} + + +/* Perform various sorts of queries. */ +static void opQuery(Strings opFlags, Strings opArgs) +{ + enum { qList, qRequisites, qPredecessors, 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 == "--predecessors") query = qPredecessors; + 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: { + for (Strings::iterator i = opArgs.begin(); + i != opArgs.end(); i++) + { + StringSet paths = storeExprRoots( + maybeNormalise(checkPath(*i), normalise)); + for (StringSet::iterator j = paths.begin(); + j != paths.end(); j++) + cout << format("%s\n") % *j; + } + break; + } + + case qRequisites: { + StringSet paths; + for (Strings::iterator i = opArgs.begin(); + i != opArgs.end(); i++) + { + StringSet paths2 = storeExprRequisites( + maybeNormalise(checkPath(*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 qPredecessors: { + for (Strings::iterator i = opArgs.begin(); + i != opArgs.end(); i++) + { + Paths preds = queryPredecessors(checkPath(*i)); + for (Paths::iterator j = preds.begin(); + j != preds.end(); j++) + cout << format("%s\n") % *j; + } + break; + } + + case qGraph: { + PathSet roots; + for (Strings::iterator i = opArgs.begin(); + i != opArgs.end(); i++) + roots.insert(maybeNormalise(checkPath(*i), normalise)); + printDotGraph(roots); + 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"); + + Transaction txn; + createStoreTransaction(txn); + for (Strings::iterator i = opArgs.begin(); + i != opArgs.end(); ) + { + Path path1 = checkPath(*i++); + Path path2 = checkPath(*i++); + registerSuccessor(txn, path1, path2); + } + txn.commit(); +} + + +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(); ) + { + Path src = checkPath(*i++); + Path sub = checkPath(*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 path = *opArgs.begin(); + 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 == "--realise" || arg == "-r") + op = opRealise; + 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 == "--verbose" || arg == "-v") + verbosity = (Verbosity) ((int) verbosity + 1); + else if (arg == "--keep-failed" || arg == "-K") + keepFailed = true; + 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"); + + openDB(); + + op(opFlags, opArgs); +} + + +string programId = "nix"; |