about summary refs log tree commit diff
path: root/src/nix
diff options
context:
space:
mode:
Diffstat (limited to 'src/nix')
-rw-r--r--src/nix/dotgraph.cc135
-rw-r--r--src/nix/dotgraph.hh8
-rw-r--r--src/nix/nix-help.txt32
-rw-r--r--src/nix/nix.cc304
4 files changed, 479 insertions, 0 deletions
diff --git a/src/nix/dotgraph.cc b/src/nix/dotgraph.cc
new file mode 100644
index 000000000000..36daf7f9966d
--- /dev/null
+++ b/src/nix/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 NixExpr & 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);
+
+	    NixExpr ne = exprFromPath(nePath);
+
+	    string label, colour;
+                    
+	    if (ne.type == NixExpr::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 == NixExpr::neClosure) {
+		label = "<closure>";
+		colour = "#00ffff";
+		printClosure(nePath, ne);
+	    }
+
+	    else abort();
+
+	    cout << makeNode(nePath, label, colour);
+	}
+    }
+
+    cout << "}\n";
+}
diff --git a/src/nix/dotgraph.hh b/src/nix/dotgraph.hh
new file mode 100644
index 000000000000..a5c5248f144e
--- /dev/null
+++ b/src/nix/dotgraph.hh
@@ -0,0 +1,8 @@
+#ifndef __DOTGRAPH_H
+#define __DOTGRAPH_H
+
+#include "expr.hh"
+
+void printDotGraph(const PathSet & roots);
+
+#endif /* !__DOTGRAPH_H */
diff --git a/src/nix/nix-help.txt b/src/nix/nix-help.txt
new file mode 100644
index 000000000000..bf2afd061205
--- /dev/null
+++ b/src/nix/nix-help.txt
@@ -0,0 +1,32 @@
+nix [OPTIONS...] [ARGUMENTS...]
+
+Operations:
+
+  --install / -i: 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/nix.cc b/src/nix/nix.cc
new file mode 100644
index 000000000000..1875689990a4
--- /dev/null
+++ b/src/nix/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 (or install) paths from the given Nix expressions. */
+static void opInstall(Strings opFlags, Strings opArgs)
+{
+    if (!opFlags.empty()) throw UsageError("unknown flag");
+
+    for (Strings::iterator i = opArgs.begin();
+         i != opArgs.end(); i++)
+    {
+        Path nfPath = normaliseNixExpr(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 ? normaliseNixExpr(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 = nixExprRoots(
+                    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 = nixExprRequisites(
+                    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 == "--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 == "--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";