* Get rid of identifiers since they are redundant now. This greatly

simplifies stuff.

* The format of Nix expressions and the database schema changed
  because of this, so it's best to delete old Nix installations.
This commit is contained in:
Eelco Dolstra 2003-10-08 15:06:59 +00:00
parent b9f4942bd2
commit 6baa2c4420
23 changed files with 485 additions and 631 deletions

View file

@ -49,7 +49,7 @@ static void writeString(const string & s, DumpSink & sink)
static void dump(const string & path, DumpSink & sink); static void dump(const string & path, DumpSink & sink);
static void dumpEntries(const string & path, DumpSink & sink) static void dumpEntries(const Path & path, DumpSink & sink)
{ {
DIR * dir = opendir(path.c_str()); DIR * dir = opendir(path.c_str());
if (!dir) throw SysError("opening directory " + path); if (!dir) throw SysError("opening directory " + path);
@ -82,7 +82,7 @@ static void dumpEntries(const string & path, DumpSink & sink)
} }
static void dumpContents(const string & path, unsigned int size, static void dumpContents(const Path & path, unsigned int size,
DumpSink & sink) DumpSink & sink)
{ {
writeString("contents", sink); writeString("contents", sink);
@ -110,7 +110,7 @@ static void dumpContents(const string & path, unsigned int size,
} }
static void dump(const string & path, DumpSink & sink) static void dump(const Path & path, DumpSink & sink)
{ {
struct stat st; struct stat st;
if (lstat(path.c_str(), &st)) if (lstat(path.c_str(), &st))
@ -150,7 +150,7 @@ static void dump(const string & path, DumpSink & sink)
} }
void dumpPath(const string & path, DumpSink & sink) void dumpPath(const Path & path, DumpSink & sink)
{ {
writeString(archiveVersion1, sink); writeString(archiveVersion1, sink);
dump(path, sink); dump(path, sink);
@ -207,10 +207,10 @@ static void skipGeneric(RestoreSource & source)
} }
static void restore(const string & path, RestoreSource & source); static void restore(const Path & path, RestoreSource & source);
static void restoreEntry(const string & path, RestoreSource & source) static void restoreEntry(const Path & path, RestoreSource & source)
{ {
string s, name; string s, name;
@ -235,7 +235,7 @@ static void restoreEntry(const string & path, RestoreSource & source)
} }
static void restoreContents(int fd, const string & path, RestoreSource & source) static void restoreContents(int fd, const Path & path, RestoreSource & source)
{ {
unsigned int size = readInt(source); unsigned int size = readInt(source);
unsigned int left = size; unsigned int left = size;
@ -254,7 +254,7 @@ static void restoreContents(int fd, const string & path, RestoreSource & source)
} }
static void restore(const string & path, RestoreSource & source) static void restore(const Path & path, RestoreSource & source)
{ {
string s; string s;
@ -331,7 +331,7 @@ static void restore(const string & path, RestoreSource & source)
} }
void restorePath(const string & path, RestoreSource & source) void restorePath(const Path & path, RestoreSource & source)
{ {
if (readString(source) != archiveVersion1) if (readString(source) != archiveVersion1)
throw badArchive("expected Nix archive"); throw badArchive("expected Nix archive");

View file

@ -1,6 +1,6 @@
#include <string> #include <string>
using namespace std; #include "util.hh"
/* dumpPath creates a Nix archive of the specified path. The format /* dumpPath creates a Nix archive of the specified path. The format
@ -45,7 +45,7 @@ struct DumpSink
virtual void operator () (const unsigned char * data, unsigned int len) = 0; virtual void operator () (const unsigned char * data, unsigned int len) = 0;
}; };
void dumpPath(const string & path, DumpSink & sink); void dumpPath(const Path & path, DumpSink & sink);
struct RestoreSource struct RestoreSource
@ -57,4 +57,4 @@ struct RestoreSource
virtual void operator () (unsigned char * data, unsigned int len) = 0; virtual void operator () (unsigned char * data, unsigned int len) = 0;
}; };
void restorePath(const string & path, RestoreSource & source); void restorePath(const Path & path, RestoreSource & source);

View file

@ -258,6 +258,8 @@ void Database::delPair(const Transaction & txn, TableId table,
Db * db = getDb(table); Db * db = getDb(table);
Dbt kt((void *) key.c_str(), key.length()); Dbt kt((void *) key.c_str(), key.length());
db->del(txn.txn, &kt, 0); db->del(txn.txn, &kt, 0);
/* Non-existence of a pair with the given key is not an
error. */
} catch (DbException e) { rethrow(e); } } catch (DbException e) { rethrow(e); }
} }

View file

@ -45,24 +45,24 @@ static string symbolicName(const string & path)
} }
string pathLabel(const FSId & id, const string & path) string pathLabel(const Path & nePath, const string & elemPath)
{ {
return (string) id + "-" + path; return (string) nePath + "-" + elemPath;
} }
void printClosure(const FSId & id, const NixExpr & fs) void printClosure(const Path & nePath, const NixExpr & fs)
{ {
Strings workList(fs.closure.roots.begin(), fs.closure.roots.end()); PathSet workList(fs.closure.roots);
StringSet doneSet; PathSet doneSet;
for (Strings::iterator i = workList.begin(); i != workList.end(); i++) { for (PathSet::iterator i = workList.begin(); i != workList.end(); i++) {
cout << makeEdge(pathLabel(id, *i), id); cout << makeEdge(pathLabel(nePath, *i), nePath);
} }
while (!workList.empty()) { while (!workList.empty()) {
string path = workList.front(); Path path = *(workList.begin());
workList.pop_front(); workList.erase(path);
if (doneSet.find(path) == doneSet.end()) { if (doneSet.find(path) == doneSet.end()) {
doneSet.insert(path); doneSet.insert(path);
@ -74,41 +74,41 @@ void printClosure(const FSId & id, const NixExpr & fs)
for (StringSet::const_iterator i = elem->second.refs.begin(); for (StringSet::const_iterator i = elem->second.refs.begin();
i != elem->second.refs.end(); i++) i != elem->second.refs.end(); i++)
{ {
workList.push_back(*i); workList.insert(*i);
cout << makeEdge(pathLabel(id, *i), pathLabel(id, path)); cout << makeEdge(pathLabel(nePath, *i), pathLabel(nePath, path));
} }
cout << makeNode(pathLabel(id, path), cout << makeNode(pathLabel(nePath, path),
symbolicName(path), "#ff0000"); symbolicName(path), "#ff0000");
} }
} }
} }
void printDotGraph(const FSIds & roots) void printDotGraph(const PathSet & roots)
{ {
FSIds workList(roots.begin(), roots.end()); PathSet workList(roots);
FSIdSet doneSet; PathSet doneSet;
cout << "digraph G {\n"; cout << "digraph G {\n";
while (!workList.empty()) { while (!workList.empty()) {
FSId id = workList.front(); Path nePath = *(workList.begin());
workList.pop_front(); workList.erase(nePath);
if (doneSet.find(id) == doneSet.end()) { if (doneSet.find(nePath) == doneSet.end()) {
doneSet.insert(id); doneSet.insert(nePath);
NixExpr ne = parseNixExpr(termFromId(id)); NixExpr ne = parseNixExpr(termFromPath(nePath));
string label, colour; string label, colour;
if (ne.type == NixExpr::neDerivation) { if (ne.type == NixExpr::neDerivation) {
for (FSIdSet::iterator i = ne.derivation.inputs.begin(); for (PathSet::iterator i = ne.derivation.inputs.begin();
i != ne.derivation.inputs.end(); i++) i != ne.derivation.inputs.end(); i++)
{ {
workList.push_back(*i); workList.insert(*i);
cout << makeEdge(*i, id); cout << makeEdge(*i, nePath);
} }
label = "derivation"; label = "derivation";
@ -121,12 +121,12 @@ void printDotGraph(const FSIds & roots)
else if (ne.type == NixExpr::neClosure) { else if (ne.type == NixExpr::neClosure) {
label = "<closure>"; label = "<closure>";
colour = "#00ffff"; colour = "#00ffff";
printClosure(id, ne); printClosure(nePath, ne);
} }
else abort(); else abort();
cout << makeNode(id, label, colour); cout << makeNode(nePath, label, colour);
} }
} }

View file

@ -3,6 +3,6 @@
#include "expr.hh" #include "expr.hh"
void printDotGraph(const FSIds & roots); void printDotGraph(const PathSet & roots);
#endif /* !__DOTGRAPH_H */ #endif /* !__DOTGRAPH_H */

View file

@ -22,37 +22,33 @@ Hash hashTerm(ATerm t)
} }
ATerm termFromId(const FSId & id) ATerm termFromPath(const Path & path)
{ {
string path = expandId(id);
ATerm t = ATreadFromNamedFile(path.c_str()); ATerm t = ATreadFromNamedFile(path.c_str());
if (!t) throw Error(format("cannot read aterm from `%1%'") % path); if (!t) throw Error(format("cannot read aterm from `%1%'") % path);
return t; return t;
} }
FSId writeTerm(ATerm t, const string & suffix, FSId id) Path writeTerm(ATerm t, const string & suffix)
{ {
/* By default, the id of a term is its hash. */ /* The id of a term is its hash. */
if (id == FSId()) id = hashTerm(t); Hash h = hashTerm(t);
string path = canonPath(nixStore + "/" + Path path = canonPath(nixStore + "/" +
(string) id + suffix + ".nix"); (string) h + suffix + ".nix");
if (!ATwriteToNamedTextFile(t, path.c_str())) if (!ATwriteToNamedTextFile(t, path.c_str()))
throw Error(format("cannot write aterm %1%") % path); throw Error(format("cannot write aterm %1%") % path);
// debug(format("written term %1% = %2%") % (string) id %
// printTerm(t));
Transaction txn(nixDB); Transaction txn(nixDB);
registerPath(txn, path, id); registerValidPath(txn, path);
txn.commit(); txn.commit();
return id; return path;
} }
static void parsePaths(ATermList paths, StringSet & out) static void parsePaths(ATermList paths, PathSet & out)
{ {
while (!ATisEmpty(paths)) { while (!ATisEmpty(paths)) {
char * s; char * s;
@ -70,19 +66,19 @@ static void checkClosure(const Closure & closure)
if (closure.elems.size() == 0) if (closure.elems.size() == 0)
throw Error("empty closure"); throw Error("empty closure");
StringSet decl; PathSet decl;
for (ClosureElems::const_iterator i = closure.elems.begin(); for (ClosureElems::const_iterator i = closure.elems.begin();
i != closure.elems.end(); i++) i != closure.elems.end(); i++)
decl.insert(i->first); decl.insert(i->first);
for (StringSet::const_iterator i = closure.roots.begin(); for (PathSet::const_iterator i = closure.roots.begin();
i != closure.roots.end(); i++) i != closure.roots.end(); i++)
if (decl.find(*i) == decl.end()) if (decl.find(*i) == decl.end())
throw Error(format("undefined root path `%1%'") % *i); throw Error(format("undefined root path `%1%'") % *i);
for (ClosureElems::const_iterator i = closure.elems.begin(); for (ClosureElems::const_iterator i = closure.elems.begin();
i != closure.elems.end(); i++) i != closure.elems.end(); i++)
for (StringSet::const_iterator j = i->second.refs.begin(); for (PathSet::const_iterator j = i->second.refs.begin();
j != i->second.refs.end(); j++) j != i->second.refs.end(); j++)
if (decl.find(*j) == decl.end()) if (decl.find(*j) == decl.end())
throw Error( throw Error(
@ -102,13 +98,12 @@ static bool parseClosure(ATerm t, Closure & closure)
parsePaths(roots, closure.roots); parsePaths(roots, closure.roots);
while (!ATisEmpty(elems)) { while (!ATisEmpty(elems)) {
char * s1, * s2; char * s1;
ATermList refs; ATermList refs;
ATerm t = ATgetFirst(elems); ATerm t = ATgetFirst(elems);
if (!ATmatch(t, "(<str>, <str>, [<list>])", &s1, &s2, &refs)) if (!ATmatch(t, "(<str>, [<list>])", &s1, &refs))
throw badTerm("not a closure element", t); throw badTerm("not a closure element", t);
ClosureElem elem; ClosureElem elem;
elem.id = parseHash(s2);
parsePaths(refs, elem.refs); parsePaths(refs, elem.refs);
closure.elems[s1] = elem; closure.elems[s1] = elem;
elems = ATgetNext(elems); elems = ATgetNext(elems);
@ -135,23 +130,8 @@ static bool parseDerivation(ATerm t, Derivation & derivation)
args = ATempty; args = ATempty;
} }
while (!ATisEmpty(outs)) { parsePaths(outs, derivation.outputs);
char * s1, * s2; parsePaths(ins, derivation.inputs);
ATerm t = ATgetFirst(outs);
if (!ATmatch(t, "(<str>, <str>)", &s1, &s2))
throw badTerm("not a derivation output", t);
derivation.outputs[s1] = parseHash(s2);
outs = ATgetNext(outs);
}
while (!ATisEmpty(ins)) {
char * s;
ATerm t = ATgetFirst(ins);
if (!ATmatch(t, "<str>", &s))
throw badTerm("not an id", t);
derivation.inputs.insert(parseHash(s));
ins = ATgetNext(ins);
}
derivation.builder = builder; derivation.builder = builder;
derivation.platform = platform; derivation.platform = platform;
@ -190,10 +170,10 @@ NixExpr parseNixExpr(ATerm t)
} }
static ATermList unparsePaths(const StringSet & paths) static ATermList unparsePaths(const PathSet & paths)
{ {
ATermList l = ATempty; ATermList l = ATempty;
for (StringSet::const_iterator i = paths.begin(); for (PathSet::const_iterator i = paths.begin();
i != paths.end(); i++) i != paths.end(); i++)
l = ATinsert(l, ATmake("<str>", i->c_str())); l = ATinsert(l, ATmake("<str>", i->c_str()));
return ATreverse(l); return ATreverse(l);
@ -208,9 +188,8 @@ static ATerm unparseClosure(const Closure & closure)
for (ClosureElems::const_iterator i = closure.elems.begin(); for (ClosureElems::const_iterator i = closure.elems.begin();
i != closure.elems.end(); i++) i != closure.elems.end(); i++)
elems = ATinsert(elems, elems = ATinsert(elems,
ATmake("(<str>, <str>, <term>)", ATmake("(<str>, <term>)",
i->first.c_str(), i->first.c_str(),
((string) i->second.id).c_str(),
unparsePaths(i->second.refs))); unparsePaths(i->second.refs)));
return ATmake("Closure(<term>, <term>)", roots, elems); return ATmake("Closure(<term>, <term>)", roots, elems);
@ -219,18 +198,6 @@ static ATerm unparseClosure(const Closure & closure)
static ATerm unparseDerivation(const Derivation & derivation) static ATerm unparseDerivation(const Derivation & derivation)
{ {
ATermList outs = ATempty;
for (DerivationOutputs::const_iterator i = derivation.outputs.begin();
i != derivation.outputs.end(); i++)
outs = ATinsert(outs,
ATmake("(<str>, <str>)",
i->first.c_str(), ((string) i->second).c_str()));
ATermList ins = ATempty;
for (FSIdSet::const_iterator i = derivation.inputs.begin();
i != derivation.inputs.end(); i++)
ins = ATinsert(ins, ATmake("<str>", ((string) *i).c_str()));
ATermList args = ATempty; ATermList args = ATempty;
for (Strings::const_iterator i = derivation.args.begin(); for (Strings::const_iterator i = derivation.args.begin();
i != derivation.args.end(); i++) i != derivation.args.end(); i++)
@ -244,8 +211,8 @@ static ATerm unparseDerivation(const Derivation & derivation)
i->first.c_str(), i->second.c_str())); i->first.c_str(), i->second.c_str()));
return ATmake("Derive(<term>, <term>, <str>, <str>, <term>, <term>)", return ATmake("Derive(<term>, <term>, <str>, <str>, <term>, <term>)",
ATreverse(outs), unparsePaths(derivation.outputs),
ATreverse(ins), unparsePaths(derivation.inputs),
derivation.platform.c_str(), derivation.platform.c_str(),
derivation.builder.c_str(), derivation.builder.c_str(),
ATreverse(args), ATreverse(args),

View file

@ -10,31 +10,27 @@ extern "C" {
/* Abstract syntax of Nix expressions. */ /* Abstract syntax of Nix expressions. */
typedef list<FSId> FSIds;
struct ClosureElem struct ClosureElem
{ {
FSId id; PathSet refs;
StringSet refs;
}; };
typedef map<string, ClosureElem> ClosureElems; typedef map<Path, ClosureElem> ClosureElems;
struct Closure struct Closure
{ {
StringSet roots; PathSet roots;
ClosureElems elems; ClosureElems elems;
}; };
typedef map<string, FSId> DerivationOutputs;
typedef map<string, string> StringPairs; typedef map<string, string> StringPairs;
struct Derivation struct Derivation
{ {
DerivationOutputs outputs; PathSet outputs;
FSIdSet inputs; PathSet inputs; /* Nix expressions, not actual inputs */
string platform; string platform;
string builder; Path builder;
Strings args; Strings args;
StringPairs env; StringPairs env;
}; };
@ -57,11 +53,11 @@ Error badTerm(const format & f, ATerm t);
/* Hash an aterm. */ /* Hash an aterm. */
Hash hashTerm(ATerm t); Hash hashTerm(ATerm t);
/* Read an aterm from disk, given its id. */ /* Read an aterm from disk. */
ATerm termFromId(const FSId & id); ATerm termFromPath(const Path & path);
/* Write an aterm to the Nix store directory, and return its hash. */ /* Write an aterm to the Nix store directory, and return its path. */
FSId writeTerm(ATerm t, const string & suffix, FSId id = FSId()); Path writeTerm(ATerm t, const string & suffix);
/* Parse a Nix expression. */ /* Parse a Nix expression. */
NixExpr parseNixExpr(ATerm t); NixExpr parseNixExpr(ATerm t);

View file

@ -9,12 +9,12 @@
typedef ATerm Expr; typedef ATerm Expr;
typedef map<ATerm, ATerm> NormalForms; typedef map<ATerm, ATerm> NormalForms;
typedef map<FSId, Strings> PkgPaths; typedef map<Path, PathSet> PkgPaths;
typedef map<FSId, Hash> PkgHashes; typedef map<Path, Hash> PkgHashes;
struct EvalState struct EvalState
{ {
Strings searchDirs; Paths searchDirs;
NormalForms normalForms; NormalForms normalForms;
PkgPaths pkgPaths; PkgPaths pkgPaths;
PkgHashes pkgHashes; /* normalised package hashes */ PkgHashes pkgHashes; /* normalised package hashes */
@ -28,18 +28,18 @@ struct EvalState
}; };
static Expr evalFile(EvalState & state, string fileName); static Expr evalFile(EvalState & state, const Path & path);
static Expr evalExpr(EvalState & state, Expr e); static Expr evalExpr(EvalState & state, Expr e);
static string searchPath(const Strings & searchDirs, string relPath) static Path searchPath(const Paths & searchDirs, const Path & relPath)
{ {
if (string(relPath, 0, 1) == "/") return relPath; if (string(relPath, 0, 1) == "/") return relPath;
for (Strings::const_iterator i = searchDirs.begin(); for (Paths::const_iterator i = searchDirs.begin();
i != searchDirs.end(); i++) i != searchDirs.end(); i++)
{ {
string path = *i + "/" + relPath; Path path = *i + "/" + relPath;
if (pathExists(path)) return path; if (pathExists(path)) return path;
} }
@ -121,14 +121,14 @@ static Expr substExprMany(ATermList formals, ATermList args, Expr body)
} }
static Strings nixExprPathsCached(EvalState & state, const FSId & id) static PathSet nixExprRootsCached(EvalState & state, const Path & nePath)
{ {
PkgPaths::iterator i = state.pkgPaths.find(id); PkgPaths::iterator i = state.pkgPaths.find(nePath);
if (i != state.pkgPaths.end()) if (i != state.pkgPaths.end())
return i->second; return i->second;
else { else {
Strings paths = nixExprPaths(id); PathSet paths = nixExprRoots(nePath);
state.pkgPaths[id] = paths; state.pkgPaths[nePath] = paths;
return paths; return paths;
} }
} }
@ -137,13 +137,13 @@ static Strings nixExprPathsCached(EvalState & state, const FSId & id)
static Hash hashPackage(EvalState & state, NixExpr ne) static Hash hashPackage(EvalState & state, NixExpr ne)
{ {
if (ne.type == NixExpr::neDerivation) { if (ne.type == NixExpr::neDerivation) {
FSIdSet inputs2; PathSet inputs2;
for (FSIdSet::iterator i = ne.derivation.inputs.begin(); for (PathSet::iterator i = ne.derivation.inputs.begin();
i != ne.derivation.inputs.end(); i++) i != ne.derivation.inputs.end(); i++)
{ {
PkgHashes::iterator j = state.pkgHashes.find(*i); PkgHashes::iterator j = state.pkgHashes.find(*i);
if (j == state.pkgHashes.end()) if (j == state.pkgHashes.end())
throw Error(format("unknown package id %1%") % (string) *i); throw Error(format("don't know expression `%1%'") % (string) *i);
inputs2.insert(j->second); inputs2.insert(j->second);
} }
ne.derivation.inputs = inputs2; ne.derivation.inputs = inputs2;
@ -156,12 +156,12 @@ static string processBinding(EvalState & state, Expr e, NixExpr & ne)
{ {
char * s1; char * s1;
if (ATmatch(e, "FSId(<str>)", &s1)) { if (ATmatch(e, "NixExpr(<str>)", &s1)) {
FSId id = parseHash(s1); Path nePath(s1);
Strings paths = nixExprPathsCached(state, id); PathSet paths = nixExprRootsCached(state, nePath);
if (paths.size() != 1) abort(); if (paths.size() != 1) abort();
string path = *(paths.begin()); Path path = *(paths.begin());
ne.derivation.inputs.insert(id); ne.derivation.inputs.insert(nePath);
return path; return path;
} }
@ -200,14 +200,14 @@ static Expr evalExpr2(EvalState & state, Expr e)
ATmatch(e, "True") || ATmatch(e, "True") ||
ATmatch(e, "False") || ATmatch(e, "False") ||
ATmatch(e, "Function([<list>], <term>)", &e1, &e2) || ATmatch(e, "Function([<list>], <term>)", &e1, &e2) ||
ATmatch(e, "FSId(<str>)", &s1)) ATmatch(e, "NixExpr(<str>)", &s1))
return e; return e;
try { try {
Hash pkgHash = hashPackage(state, parseNixExpr(e)); Hash pkgHash = hashPackage(state, parseNixExpr(e));
FSId pkgId = writeTerm(e, ""); Path pkgPath = writeTerm(e, "");
state.pkgHashes[pkgId] = pkgHash; state.pkgHashes[pkgPath] = pkgHash;
return ATmake("FSId(<str>)", ((string) pkgId).c_str()); return ATmake("NixExpr(<str>)", pkgPath.c_str());
} catch (...) { /* !!! catch parse errors only */ } catch (...) { /* !!! catch parse errors only */
} }
@ -254,32 +254,29 @@ static Expr evalExpr2(EvalState & state, Expr e)
/* Fix inclusion. */ /* Fix inclusion. */
if (ATmatch(e, "IncludeFix(<str>)", &s1)) { if (ATmatch(e, "IncludeFix(<str>)", &s1)) {
string fileName(s1); Path fileName(s1);
return evalFile(state, s1); return evalFile(state, s1);
} }
/* Relative files. */ /* Relative files. */
if (ATmatch(e, "Relative(<str>)", &s1)) { if (ATmatch(e, "Relative(<str>)", &s1)) {
string srcPath = searchPath(state.searchDirs, s1); Path srcPath = searchPath(state.searchDirs, s1);
string dstPath; Path dstPath = addToStore(srcPath);
FSId id;
addToStore(srcPath, dstPath, id, true);
ClosureElem elem; ClosureElem elem;
elem.id = id;
NixExpr ne; NixExpr ne;
ne.type = NixExpr::neClosure; ne.type = NixExpr::neClosure;
ne.closure.roots.insert(dstPath); ne.closure.roots.insert(dstPath);
ne.closure.elems[dstPath] = elem; ne.closure.elems[dstPath] = elem;
Hash pkgHash = hashPackage(state, ne); Hash pkgHash = hashPackage(state, ne);
FSId pkgId = writeTerm(unparseNixExpr(ne), ""); Path pkgPath = writeTerm(unparseNixExpr(ne), "");
state.pkgHashes[pkgId] = pkgHash; state.pkgHashes[pkgPath] = pkgHash;
msg(lvlChatty, format("copied `%1%' -> %2%") msg(lvlChatty, format("copied `%1%' -> closure `%2%'")
% srcPath % (string) pkgId); % srcPath % pkgPath);
return ATmake("FSId(<str>)", ((string) pkgId).c_str()); return ATmake("NixExpr(<str>)", pkgPath.c_str());
} }
/* Packages are transformed into Nix derivation expressions. */ /* Packages are transformed into Nix derivation expressions. */
@ -302,8 +299,8 @@ static Expr evalExpr2(EvalState & state, Expr e)
ne.type = NixExpr::neDerivation; ne.type = NixExpr::neDerivation;
ne.derivation.platform = SYSTEM; ne.derivation.platform = SYSTEM;
string name; string name;
FSId outId; Hash outHash;
bool outIdGiven = false; bool outHashGiven = false;
bnds = ATempty; bnds = ATempty;
for (map<string, ATerm>::iterator it = bndMap.begin(); for (map<string, ATerm>::iterator it = bndMap.begin();
@ -331,8 +328,8 @@ static Expr evalExpr2(EvalState & state, Expr e)
if (key == "build") ne.derivation.builder = s; if (key == "build") ne.derivation.builder = s;
if (key == "name") name = s; if (key == "name") name = s;
if (key == "id") { if (key == "id") {
outId = parseHash(s); outHash = parseHash(s);
outIdGiven = true; outHashGiven = true;
} }
} }
@ -348,23 +345,23 @@ static Expr evalExpr2(EvalState & state, Expr e)
/* Hash the Nix expression with no outputs to produce a /* Hash the Nix expression with no outputs to produce a
unique but deterministic path name for this package. */ unique but deterministic path name for this package. */
if (!outIdGiven) outId = hashPackage(state, ne); if (!outHashGiven) outHash = hashPackage(state, ne);
string outPath = Path outPath =
canonPath(nixStore + "/" + ((string) outId).c_str() + "-" + name); canonPath(nixStore + "/" + ((string) outHash).c_str() + "-" + name);
ne.derivation.env["out"] = outPath; ne.derivation.env["out"] = outPath;
ne.derivation.outputs[outPath] = outId; ne.derivation.outputs.insert(outPath);
/* Write the resulting term into the Nix store directory. */ /* Write the resulting term into the Nix store directory. */
Hash pkgHash = outIdGiven Hash pkgHash = outHashGiven
? hashString((string) outId + outPath) ? hashString((string) outHash + outPath)
: hashPackage(state, ne); : hashPackage(state, ne);
FSId pkgId = writeTerm(unparseNixExpr(ne), "-d-" + name); Path pkgPath = writeTerm(unparseNixExpr(ne), "-d-" + name);
state.pkgHashes[pkgId] = pkgHash; state.pkgHashes[pkgPath] = pkgHash;
msg(lvlChatty, format("instantiated `%1%' -> %2%") msg(lvlChatty, format("instantiated `%1%' -> `%2%'")
% name % (string) pkgId); % name % pkgPath);
return ATmake("FSId(<str>)", ((string) pkgId).c_str()); return ATmake("NixExpr(<str>)", pkgPath.c_str());
} }
/* BaseName primitive function. */ /* BaseName primitive function. */
@ -401,9 +398,9 @@ static Expr evalExpr(EvalState & state, Expr e)
} }
static Expr evalFile(EvalState & state, string relPath) static Expr evalFile(EvalState & state, const Path & relPath)
{ {
string path = searchPath(state.searchDirs, relPath); Path path = searchPath(state.searchDirs, relPath);
Nest nest(lvlTalkative, format("evaluating file `%1%'") % path); Nest nest(lvlTalkative, format("evaluating file `%1%'") % path);
Expr e = ATreadFromNamedFile(path.c_str()); Expr e = ATreadFromNamedFile(path.c_str());
if (!e) if (!e)
@ -422,16 +419,16 @@ static Expr evalStdin(EvalState & state)
} }
static void printFSId(EvalState & state, Expr e) static void printNixExpr(EvalState & state, Expr e)
{ {
ATermList es; ATermList es;
char * s; char * s;
if (ATmatch(e, "FSId(<str>)", &s)) { if (ATmatch(e, "NixExpr(<str>)", &s)) {
cout << format("%1%\n") % s; cout << format("%1%\n") % s;
} }
else if (ATmatch(e, "[<list>]", &es)) { else if (ATmatch(e, "[<list>]", &es)) {
while (!ATisEmpty(es)) { while (!ATisEmpty(es)) {
printFSId(state, evalExpr(state, ATgetFirst(es))); printNixExpr(state, evalExpr(state, ATgetFirst(es)));
es = ATgetNext(es); es = ATgetNext(es);
} }
} }
@ -472,14 +469,14 @@ void run(Strings args)
if (readStdin) { if (readStdin) {
Expr e = evalStdin(state); Expr e = evalStdin(state);
printFSId(state, e); printNixExpr(state, e);
} }
for (Strings::iterator it = files.begin(); for (Strings::iterator it = files.begin();
it != files.end(); it++) it != files.end(); it++)
{ {
Expr e = evalFile(state, *it); Expr e = evalFile(state, *it);
printFSId(state, e); printNixExpr(state, e);
} }
} }

View file

@ -5,8 +5,7 @@
Database nixDB; Database nixDB;
TableId dbPath2Id; TableId dbValidPaths;
TableId dbId2Paths;
TableId dbSuccessors; TableId dbSuccessors;
TableId dbSubstitutes; TableId dbSubstitutes;
@ -23,8 +22,7 @@ bool keepFailed = false;
void openDB() void openDB()
{ {
nixDB.open(nixDBPath); nixDB.open(nixDBPath);
dbPath2Id = nixDB.openTable("path2id"); dbValidPaths = nixDB.openTable("validpaths");
dbId2Paths = nixDB.openTable("id2paths");
dbSuccessors = nixDB.openTable("successors"); dbSuccessors = nixDB.openTable("successors");
dbSubstitutes = nixDB.openTable("substitutes"); dbSubstitutes = nixDB.openTable("substitutes");
} }

View file

@ -13,42 +13,36 @@ extern Database nixDB;
/* Database tables. */ /* Database tables. */
/* dbPath2Id :: Path -> FSId
Each pair (p, id) records that path $p$ contains an expansion of /* dbValidPaths :: Path -> ()
$id$. */
extern TableId dbPath2Id; The existence of a key $p$ indicates that path $p$ is valid (that
is, produced by a succesful build). */
extern TableId dbValidPaths;
/* dbId2Paths :: FSId -> [Path] /* dbSuccessors :: Path -> Path
A mapping from ids to lists of paths. */ Each pair $(p_1, p_2)$ in this mapping records the fact that the
extern TableId dbId2Paths; Nix expression stored at path $p_1$ has a successor expression
stored at path $p_2$.
Note that a term $y$ is a successor of $x$ iff there exists a
/* dbSuccessors :: FSId -> FSId
Each pair $(id_1, id_2)$ in this mapping records the fact that a
successor of a Nix expression stored in a file with identifier
$id_1$ is stored in a file with identifier $id_2$.
Note that a term $y$ is successor of $x$ iff there exists a
sequence of rewrite steps that rewrites $x$ into $y$. sequence of rewrite steps that rewrites $x$ into $y$.
*/ */
extern TableId dbSuccessors; extern TableId dbSuccessors;
/* dbSubstitutes :: FSId -> [FSId] /* dbSubstitutes :: Path -> [Path]
Each pair $(id, [ids])$ tells Nix that it can realise any of the Each pair $(p, [ps])$ tells Nix that it can realise any of the
Nix expressions referenced by the identifiers in $ids$ to Nix expressions stored at paths $ps$ to produce a path $p$.
generate a path with identifier $id$.
The main purpose of this is for distributed caching of derivates. The main purpose of this is for distributed caching of derivates.
One system can compute a derivate with hash $h$ and put it on a One system can compute a derivate and put it on a website (as a Nix
website (as a Nix archive), for instance, and then another system archive), for instance, and then another system can register a
can register a substitute for that derivate. The substitute in substitute for that derivate. The substitute in this case might be
this case might be a Nix expression that fetches the Nix archive. a Nix expression that fetches the Nix archive.
*/ */
extern TableId dbSubstitutes; extern TableId dbSubstitutes;

View file

@ -54,11 +54,11 @@ Hash parseHash(const string & s)
{ {
Hash hash; Hash hash;
if (s.length() != Hash::hashSize * 2) if (s.length() != Hash::hashSize * 2)
throw BadRefError("invalid hash: " + s); throw Error(format("invalid hash `%1%'") % s);
for (unsigned int i = 0; i < Hash::hashSize; i++) { for (unsigned int i = 0; i < Hash::hashSize; i++) {
string s2(s, i * 2, 2); string s2(s, i * 2, 2);
if (!isxdigit(s2[0]) || !isxdigit(s2[1])) if (!isxdigit(s2[0]) || !isxdigit(s2[1]))
throw BadRefError("invalid hash: " + s); throw Error(format("invalid hash `%1%'") % s);
istringstream str(s2); istringstream str(s2);
int n; int n;
str >> hex >> n; str >> hex >> n;
@ -89,15 +89,15 @@ Hash hashString(const string & s)
} }
Hash hashFile(const string & fileName) Hash hashFile(const Path & path)
{ {
Hash hash; Hash hash;
FILE * file = fopen(fileName.c_str(), "rb"); FILE * file = fopen(path.c_str(), "rb");
if (!file) if (!file)
throw SysError("file `" + fileName + "' does not exist"); throw SysError(format("file `%1%' does not exist") % path);
int err = md5_stream(file, hash.hash); int err = md5_stream(file, hash.hash);
fclose(file); fclose(file);
if (err) throw SysError("cannot hash file " + fileName); if (err) throw SysError(format("cannot hash file `%1%'") % path);
return hash; return hash;
} }
@ -113,7 +113,7 @@ struct HashSink : DumpSink
}; };
Hash hashPath(const string & path) Hash hashPath(const Path & path)
{ {
Hash hash; Hash hash;
HashSink sink; HashSink sink;

View file

@ -30,13 +30,6 @@ struct Hash
}; };
class BadRefError : public Error
{
public:
BadRefError(string _err) : Error(_err) { };
};
/* Parse a hexadecimal representation of a hash code. */ /* Parse a hexadecimal representation of a hash code. */
Hash parseHash(const string & s); Hash parseHash(const string & s);
@ -47,12 +40,12 @@ bool isHash(const string & s);
Hash hashString(const string & s); Hash hashString(const string & s);
/* Compute the hash of the given file. */ /* Compute the hash of the given file. */
Hash hashFile(const string & fileName); Hash hashFile(const Path & path);
/* Compute the hash of the given path. The hash is defined as /* Compute the hash of the given path. The hash is defined as
md5(dump(path)). md5(dump(path)).
*/ */
Hash hashPath(const string & path); Hash hashPath(const Path & path);
#endif /* !__HASH_H */ #endif /* !__HASH_H */

View file

@ -19,16 +19,11 @@ Operations:
--version: output version information --version: output version information
--help: display help --help: display help
Source selection for --install, --dump:
--path / -p: by file name !!! -> path
Query flags: Query flags:
--list / -l: query the output paths (roots) of a Nix expression (default) --list / -l: query the output paths (roots) of a Nix expression (default)
--requisites / -r: print all paths necessary to realise expression --requisites / -r: print all paths necessary to realise expression
--generators / -g: find expressions producing a subset of given ids --generators / -g: find expressions producing a subset of given ids
--expansion / -e: print a path containing id
--graph: print a dot graph rooted at given ids --graph: print a dot graph rooted at given ids
Options: Options:

View file

@ -11,9 +11,6 @@
typedef void (* Operation) (Strings opFlags, Strings opArgs); typedef void (* Operation) (Strings opFlags, Strings opArgs);
static bool pathArgs = false;
static void printHelp() static void printHelp()
{ {
cout << cout <<
@ -24,16 +21,9 @@ static void printHelp()
static FSId argToId(const string & arg) static Path checkPath(const Path & arg)
{ {
if (!pathArgs) return arg; /* !!! check that arg is in the store */
return parseHash(arg);
else {
FSId id;
if (!queryPathId(arg, id))
throw Error(format("don't know id of `%1%'") % arg);
return id;
}
} }
@ -42,12 +32,12 @@ static void opInstall(Strings opFlags, Strings opArgs)
{ {
if (!opFlags.empty()) throw UsageError("unknown flag"); if (!opFlags.empty()) throw UsageError("unknown flag");
for (Strings::iterator it = opArgs.begin(); for (Strings::iterator i = opArgs.begin();
it != opArgs.end(); it++) i != opArgs.end(); i++)
{ {
FSId id = normaliseNixExpr(argToId(*it)); Path nfPath = normaliseNixExpr(checkPath(*i));
realiseClosure(id); realiseClosure(nfPath);
cout << format("%1%\n") % (string) id; cout << format("%1%\n") % (string) nfPath;
} }
} }
@ -59,7 +49,7 @@ static void opDelete(Strings opFlags, Strings opArgs)
for (Strings::iterator it = opArgs.begin(); for (Strings::iterator it = opArgs.begin();
it != opArgs.end(); it++) it != opArgs.end(); it++)
deleteFromStore(absPath(*it)); deleteFromStore(checkPath(*it));
} }
@ -69,27 +59,21 @@ static void opAdd(Strings opFlags, Strings opArgs)
{ {
if (!opFlags.empty()) throw UsageError("unknown flag"); if (!opFlags.empty()) throw UsageError("unknown flag");
for (Strings::iterator it = opArgs.begin(); for (Strings::iterator i = opArgs.begin(); i != opArgs.end(); i++)
it != opArgs.end(); it++) cout << format("%1%\n") % addToStore(*i);
{
string path;
FSId id;
addToStore(*it, path, id);
cout << format("%1% %2%\n") % (string) id % path;
}
} }
FSId maybeNormalise(const FSId & id, bool normalise) Path maybeNormalise(const Path & ne, bool normalise)
{ {
return normalise ? normaliseNixExpr(id) : id; return normalise ? normaliseNixExpr(ne) : ne;
} }
/* Perform various sorts of queries. */ /* Perform various sorts of queries. */
static void opQuery(Strings opFlags, Strings opArgs) static void opQuery(Strings opFlags, Strings opArgs)
{ {
enum { qList, qRequisites, qGenerators, qExpansion, qGraph enum { qList, qRequisites, qGenerators, qGraph
} query = qList; } query = qList;
bool normalise = false; bool normalise = false;
bool includeExprs = true; bool includeExprs = true;
@ -100,7 +84,6 @@ static void opQuery(Strings opFlags, Strings opArgs)
if (*i == "--list" || *i == "-l") query = qList; if (*i == "--list" || *i == "-l") query = qList;
else if (*i == "--requisites" || *i == "-r") query = qRequisites; else if (*i == "--requisites" || *i == "-r") query = qRequisites;
else if (*i == "--generators" || *i == "-g") query = qGenerators; 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 == "--graph") query = qGraph;
else if (*i == "--normalise" || *i == "-n") normalise = true; else if (*i == "--normalise" || *i == "-n") normalise = true;
else if (*i == "--exclude-exprs") includeExprs = false; else if (*i == "--exclude-exprs") includeExprs = false;
@ -110,12 +93,12 @@ static void opQuery(Strings opFlags, Strings opArgs)
switch (query) { switch (query) {
case qList: { case qList: {
StringSet paths; PathSet paths;
for (Strings::iterator i = opArgs.begin(); for (Strings::iterator i = opArgs.begin();
i != opArgs.end(); i++) i != opArgs.end(); i++)
{ {
Strings paths2 = nixExprPaths( StringSet paths2 = nixExprRoots(
maybeNormalise(argToId(*i), normalise)); maybeNormalise(checkPath(*i), normalise));
paths.insert(paths2.begin(), paths2.end()); paths.insert(paths2.begin(), paths2.end());
} }
for (StringSet::iterator i = paths.begin(); for (StringSet::iterator i = paths.begin();
@ -129,8 +112,8 @@ static void opQuery(Strings opFlags, Strings opArgs)
for (Strings::iterator i = opArgs.begin(); for (Strings::iterator i = opArgs.begin();
i != opArgs.end(); i++) i != opArgs.end(); i++)
{ {
Strings paths2 = nixExprRequisites( StringSet paths2 = nixExprRequisites(
maybeNormalise(argToId(*i), normalise), maybeNormalise(checkPath(*i), normalise),
includeExprs, includeSuccessors); includeExprs, includeSuccessors);
paths.insert(paths2.begin(), paths2.end()); paths.insert(paths2.begin(), paths2.end());
} }
@ -140,11 +123,12 @@ static void opQuery(Strings opFlags, Strings opArgs)
break; break;
} }
#if 0
case qGenerators: { case qGenerators: {
FSIds outIds; FSIds outIds;
for (Strings::iterator i = opArgs.begin(); for (Strings::iterator i = opArgs.begin();
i != opArgs.end(); i++) i != opArgs.end(); i++)
outIds.push_back(argToId(*i)); outIds.push_back(checkPath(*i));
FSIds genIds = findGenerators(outIds); FSIds genIds = findGenerators(outIds);
@ -153,21 +137,13 @@ static void opQuery(Strings opFlags, Strings opArgs)
cout << format("%s\n") % expandId(*i); cout << format("%s\n") % expandId(*i);
break; break;
} }
#endif
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: { case qGraph: {
FSIds roots; PathSet roots;
for (Strings::iterator i = opArgs.begin(); for (Strings::iterator i = opArgs.begin();
i != opArgs.end(); i++) i != opArgs.end(); i++)
roots.push_back(maybeNormalise(argToId(*i), normalise)); roots.insert(maybeNormalise(checkPath(*i), normalise));
printDotGraph(roots); printDotGraph(roots);
break; break;
} }
@ -187,9 +163,9 @@ static void opSuccessor(Strings opFlags, Strings opArgs)
for (Strings::iterator i = opArgs.begin(); for (Strings::iterator i = opArgs.begin();
i != opArgs.end(); ) i != opArgs.end(); )
{ {
FSId id1 = parseHash(*i++); Path path1 = checkPath(*i++);
FSId id2 = parseHash(*i++); Path path2 = checkPath(*i++);
registerSuccessor(txn, id1, id2); registerSuccessor(txn, path1, path2);
} }
txn.commit(); txn.commit();
} }
@ -203,8 +179,8 @@ static void opSubstitute(Strings opFlags, Strings opArgs)
for (Strings::iterator i = opArgs.begin(); for (Strings::iterator i = opArgs.begin();
i != opArgs.end(); ) i != opArgs.end(); )
{ {
FSId src = parseHash(*i++); Path src = checkPath(*i++);
FSId sub = parseHash(*i++); Path sub = checkPath(*i++);
registerSubstitute(src, sub); registerSubstitute(src, sub);
} }
} }
@ -229,9 +205,7 @@ static void opDump(Strings opFlags, Strings opArgs)
if (opArgs.size() != 1) throw UsageError("only one argument allowed"); if (opArgs.size() != 1) throw UsageError("only one argument allowed");
StdoutSink sink; StdoutSink sink;
string arg = *opArgs.begin(); string path = *opArgs.begin();
string path = pathArgs ? arg : expandId(parseHash(arg));
dumpPath(path, sink); dumpPath(path, sink);
} }
@ -311,8 +285,6 @@ void run(Strings args)
op = opInit; op = opInit;
else if (arg == "--verify") else if (arg == "--verify")
op = opVerify; op = opVerify;
else if (arg == "--path" || arg == "-p")
pathArgs = true;
else if (arg == "--verbose" || arg == "-v") else if (arg == "--verbose" || arg == "-v")
verbosity = (Verbosity) ((int) verbosity + 1); verbosity = (Verbosity) ((int) verbosity + 1);
else if (arg == "--keep-failed" || arg == "-K") else if (arg == "--keep-failed" || arg == "-K")

View file

@ -9,47 +9,131 @@
void registerSuccessor(const Transaction & txn, void registerSuccessor(const Transaction & txn,
const FSId & id1, const FSId & id2) const Path & path1, const Path & path2)
{ {
nixDB.setString(txn, dbSuccessors, id1, id2); nixDB.setString(txn, dbSuccessors, path1, path2);
} }
static FSId useSuccessor(const FSId & id) static Path useSuccessor(const Path & path)
{ {
string idSucc; string pathSucc;
if (nixDB.queryString(noTxn, dbSuccessors, id, idSucc)) { if (nixDB.queryString(noTxn, dbSuccessors, path, pathSucc)) {
debug(format("successor %1% -> %2%") % (string) id % idSucc); debug(format("successor %1% -> %2%") % (string) path % pathSucc);
return parseHash(idSucc); return pathSucc;
} else } else
return id; return path;
} }
Strings pathsFromOutputs(const DerivationOutputs & ps) #if 0
/* Return a path whose contents have the given hash. If target is
not empty, ensure that such a path is realised in target (if
necessary by copying from another location). If prefix is not
empty, only return a path that is an descendent of prefix. */
string expandId(const FSId & id, const string & target = "",
const string & prefix = "/", FSIdSet pending = FSIdSet(),
bool ignoreSubstitutes = false)
{ {
Strings ss; xxx
for (DerivationOutputs::const_iterator i = ps.begin();
i != ps.end(); i++)
ss.push_back(i->first);
return ss;
} }
FSId normaliseNixExpr(FSId id, FSIdSet pending) string expandId(const FSId & id, const string & target,
const string & prefix, FSIdSet pending, bool ignoreSubstitutes)
{
Nest nest(lvlDebug, format("expanding %1%") % (string) id);
Strings paths;
if (!target.empty() && !isInPrefix(target, prefix))
abort();
nixDB.queryStrings(noTxn, dbId2Paths, id, paths);
/* Pick one equal to `target'. */
if (!target.empty()) {
for (Strings::iterator i = paths.begin();
i != paths.end(); i++)
{
string path = *i;
if (path == target && pathExists(path))
return path;
}
}
/* Arbitrarily pick the first one that exists and isn't stale. */
for (Strings::iterator it = paths.begin();
it != paths.end(); it++)
{
string path = *it;
if (isInPrefix(path, prefix) && pathExists(path)) {
if (target.empty())
return path;
else {
/* Acquire a lock on the target path. */
Strings lockPaths;
lockPaths.push_back(target);
PathLocks outputLock(lockPaths);
/* Copy. */
copyPath(path, target);
/* Register the target path. */
Transaction txn(nixDB);
registerPath(txn, target, id);
txn.commit();
return target;
}
}
}
if (!ignoreSubstitutes) {
if (pending.find(id) != pending.end())
throw Error(format("id %1% already being expanded") % (string) id);
pending.insert(id);
/* Try to realise the substitutes, but only if this id is not
already being realised by a substitute. */
Strings subs;
nixDB.queryStrings(noTxn, dbSubstitutes, id, subs); /* non-existence = ok */
for (Strings::iterator it = subs.begin(); it != subs.end(); it++) {
FSId subId = parseHash(*it);
debug(format("trying substitute %1%") % (string) subId);
realiseClosure(normaliseNixExpr(subId, pending), pending);
return expandId(id, target, prefix, pending);
}
}
throw Error(format("cannot expand id `%1%'") % (string) id);
}
#endif
Path normaliseNixExpr(const Path & _nePath, PathSet pending)
{ {
Nest nest(lvlTalkative, Nest nest(lvlTalkative,
format("normalising nix expression %1%") % (string) id); format("normalising expression in `%1%'") % (string) _nePath);
/* Try to substitute $id$ by any known successors in order to /* Try to substitute the expression by any known successors in
speed up the rewrite process. */ order to speed up the rewrite process. */
id = useSuccessor(id); Path nePath = useSuccessor(_nePath);
/* Get the Nix expression. */ /* Get the Nix expression. */
NixExpr ne = parseNixExpr(termFromId(id)); NixExpr ne = parseNixExpr(termFromPath(nePath));
/* If this is a normal form (i.e., a closure) we are done. */ /* If this is a normal form (i.e., a closure) we are done. */
if (ne.type == NixExpr::neClosure) return id; if (ne.type == NixExpr::neClosure) return nePath;
if (ne.type != NixExpr::neDerivation) abort(); if (ne.type != NixExpr::neDerivation) abort();
@ -62,8 +146,8 @@ FSId normaliseNixExpr(FSId id, FSIdSet pending)
/* Input paths, with their closure elements. */ /* Input paths, with their closure elements. */
ClosureElems inClosures; ClosureElems inClosures;
/* Referencable paths (i.e., input and output paths). */ /* Referenceable paths (i.e., input and output paths). */
StringSet allPaths; PathSet allPaths;
/* The environment to be passed to the builder. */ /* The environment to be passed to the builder. */
Environment env; Environment env;
@ -73,17 +157,17 @@ FSId normaliseNixExpr(FSId id, FSIdSet pending)
nf.type = NixExpr::neClosure; nf.type = NixExpr::neClosure;
/* Parse the outputs. */ /* The outputs are referenceable paths. */
for (DerivationOutputs::iterator i = ne.derivation.outputs.begin(); for (PathSet::iterator i = ne.derivation.outputs.begin();
i != ne.derivation.outputs.end(); i++) i != ne.derivation.outputs.end(); i++)
{ {
debug(format("building %1% in `%2%'") % (string) i->second % i->first); debug(format("building path `%1%'") % *i);
allPaths.insert(i->first); allPaths.insert(*i);
} }
/* Obtain locks on all output paths. The locks are automatically /* Obtain locks on all output paths. The locks are automatically
released when we exit this function or Nix crashes. */ released when we exit this function or Nix crashes. */
PathLocks outputLocks(pathsFromOutputs(ne.derivation.outputs)); PathLocks outputLocks(ne.derivation.outputs);
/* Now check again whether there is a successor. This is because /* Now check again whether there is a successor. This is because
another process may have started building in parallel. After another process may have started building in parallel. After
@ -94,13 +178,13 @@ FSId normaliseNixExpr(FSId id, FSIdSet pending)
other process can build this expression, so no further checks other process can build this expression, so no further checks
are necessary. */ are necessary. */
{ {
FSId id2 = useSuccessor(id); Path nePath2 = useSuccessor(nePath);
if (id2 != id) { if (nePath != nePath2) {
NixExpr ne = parseNixExpr(termFromId(id2)); NixExpr ne = parseNixExpr(termFromPath(nePath2));
debug(format("skipping build of %1%, someone beat us to it") debug(format("skipping build of expression `%1%', someone beat us to it")
% (string) id); % (string) nePath);
if (ne.type != NixExpr::neClosure) abort(); if (ne.type != NixExpr::neClosure) abort();
return id2; return nePath2;
} }
} }
@ -110,14 +194,14 @@ FSId normaliseNixExpr(FSId id, FSIdSet pending)
% ne.derivation.platform % thisSystem); % ne.derivation.platform % thisSystem);
/* Realise inputs (and remember all input paths). */ /* Realise inputs (and remember all input paths). */
for (FSIdSet::iterator i = ne.derivation.inputs.begin(); for (PathSet::iterator i = ne.derivation.inputs.begin();
i != ne.derivation.inputs.end(); i++) i != ne.derivation.inputs.end(); i++)
{ {
FSId nf = normaliseNixExpr(*i, pending); Path nfPath = normaliseNixExpr(*i, pending);
realiseClosure(nf, pending); realiseClosure(nfPath, pending);
/* !!! nf should be a root of the garbage collector while we /* !!! nfPath should be a root of the garbage collector while
are building */ we are building */
NixExpr ne = parseNixExpr(termFromId(nf)); NixExpr ne = parseNixExpr(termFromPath(nfPath));
if (ne.type != NixExpr::neClosure) abort(); if (ne.type != NixExpr::neClosure) abort();
for (ClosureElems::iterator j = ne.closure.elems.begin(); for (ClosureElems::iterator j = ne.closure.elems.begin();
j != ne.closure.elems.end(); j++) j != ne.closure.elems.end(); j++)
@ -147,8 +231,10 @@ FSId normaliseNixExpr(FSId id, FSIdSet pending)
/* We can skip running the builder if we can expand all output /* We can skip running the builder if we can expand all output
paths from their ids. */ paths from their ids. */
bool fastBuild = false;
#if 0
bool fastBuild = true; bool fastBuild = true;
for (DerivationOutputs::iterator i = ne.derivation.outputs.begin(); for (PathSet::iterator i = ne.derivation.outputs.begin();
i != ne.derivation.outputs.end(); i++) i != ne.derivation.outputs.end(); i++)
{ {
try { try {
@ -160,17 +246,17 @@ FSId normaliseNixExpr(FSId id, FSIdSet pending)
break; break;
} }
} }
#endif
if (!fastBuild) { if (!fastBuild) {
/* If any of the outputs already exist but are not registered, /* If any of the outputs already exist but are not registered,
delete them. */ delete them. */
for (DerivationOutputs::iterator i = ne.derivation.outputs.begin(); for (PathSet::iterator i = ne.derivation.outputs.begin();
i != ne.derivation.outputs.end(); i++) i != ne.derivation.outputs.end(); i++)
{ {
string path = i->first; Path path = *i;
FSId id; if (isValidPath(path))
if (queryPathId(path, id))
throw Error(format("obstructed build: path `%1%' exists") % path); throw Error(format("obstructed build: path `%1%' exists") % path);
if (pathExists(path)) { if (pathExists(path)) {
debug(format("removing unregistered path `%1%'") % path); debug(format("removing unregistered path `%1%'") % path);
@ -189,11 +275,11 @@ FSId normaliseNixExpr(FSId id, FSIdSet pending)
/* Check whether the output paths were created, and grep each /* Check whether the output paths were created, and grep each
output path to determine what other paths it references. Also make all output path to determine what other paths it references. Also make all
output paths read-only. */ output paths read-only. */
StringSet usedPaths; PathSet usedPaths;
for (DerivationOutputs::iterator i = ne.derivation.outputs.begin(); for (PathSet::iterator i = ne.derivation.outputs.begin();
i != ne.derivation.outputs.end(); i++) i != ne.derivation.outputs.end(); i++)
{ {
string path = i->first; Path path = *i;
if (!pathExists(path)) if (!pathExists(path))
throw Error(format("path `%1%' does not exist") % path); throw Error(format("path `%1%' does not exist") % path);
nf.closure.roots.insert(path); nf.closure.roots.insert(path);
@ -207,15 +293,14 @@ FSId normaliseNixExpr(FSId id, FSIdSet pending)
/* Construct a closure element for this output path. */ /* Construct a closure element for this output path. */
ClosureElem elem; ClosureElem elem;
elem.id = i->second;
/* For each path referenced by this output path, add its id to the /* For each path referenced by this output path, add its id to the
closure element and add the id to the `usedPaths' set (so that the closure element and add the id to the `usedPaths' set (so that the
elements referenced by *its* closure are added below). */ elements referenced by *its* closure are added below). */
for (Strings::iterator j = refPaths.begin(); for (Paths::iterator j = refPaths.begin();
j != refPaths.end(); j++) j != refPaths.end(); j++)
{ {
string path = *j; Path path = *j;
elem.refs.insert(path); elem.refs.insert(path);
if (inClosures.find(path) != inClosures.end()) if (inClosures.find(path) != inClosures.end())
usedPaths.insert(path); usedPaths.insert(path);
@ -228,11 +313,11 @@ FSId normaliseNixExpr(FSId id, FSIdSet pending)
/* Close the closure. That is, for any referenced path, add the paths /* Close the closure. That is, for any referenced path, add the paths
referenced by it. */ referenced by it. */
StringSet donePaths; PathSet donePaths;
while (!usedPaths.empty()) { while (!usedPaths.empty()) {
StringSet::iterator i = usedPaths.begin(); PathSet::iterator i = usedPaths.begin();
string path = *i; Path path = *i;
usedPaths.erase(i); usedPaths.erase(i);
if (donePaths.find(path) != donePaths.end()) continue; if (donePaths.find(path) != donePaths.end()) continue;
@ -243,7 +328,7 @@ FSId normaliseNixExpr(FSId id, FSIdSet pending)
nf.closure.elems[path] = j->second; nf.closure.elems[path] = j->second;
for (StringSet::iterator k = j->second.refs.begin(); for (PathSet::iterator k = j->second.refs.begin();
k != j->second.refs.end(); k++) k != j->second.refs.end(); k++)
usedPaths.insert(*k); usedPaths.insert(*k);
} }
@ -252,7 +337,7 @@ FSId normaliseNixExpr(FSId id, FSIdSet pending)
for (ClosureElems::iterator i = inClosures.begin(); for (ClosureElems::iterator i = inClosures.begin();
i != inClosures.end(); i++) i != inClosures.end(); i++)
{ {
StringSet::iterator j = donePaths.find(i->first); PathSet::iterator j = donePaths.find(i->first);
if (j == donePaths.end()) if (j == donePaths.end())
debug(format("NOT referenced: `%1%'") % i->first); debug(format("NOT referenced: `%1%'") % i->first);
else else
@ -263,7 +348,7 @@ FSId normaliseNixExpr(FSId id, FSIdSet pending)
transaction below because writing terms is idem-potent. */ transaction below because writing terms is idem-potent. */
ATerm nfTerm = unparseNixExpr(nf); ATerm nfTerm = unparseNixExpr(nf);
msg(lvlVomit, format("normal form: %1%") % printTerm(nfTerm)); msg(lvlVomit, format("normal form: %1%") % printTerm(nfTerm));
FSId idNF = writeTerm(nfTerm, "-s-" + (string) id); Path nfPath = writeTerm(nfTerm, "-s");
/* Register each outpat path, and register the normal form. This /* Register each outpat path, and register the normal form. This
is wrapped in one database transaction to ensure that if we is wrapped in one database transaction to ensure that if we
@ -272,63 +357,58 @@ FSId normaliseNixExpr(FSId id, FSIdSet pending)
deleted arbitrarily, while registered paths can only be deleted deleted arbitrarily, while registered paths can only be deleted
by running the garbage collector. */ by running the garbage collector. */
Transaction txn(nixDB); Transaction txn(nixDB);
for (DerivationOutputs::iterator i = ne.derivation.outputs.begin(); for (PathSet::iterator i = ne.derivation.outputs.begin();
i != ne.derivation.outputs.end(); i++) i != ne.derivation.outputs.end(); i++)
registerPath(txn, i->first, i->second); registerValidPath(txn, *i);
registerSuccessor(txn, id, idNF); registerSuccessor(txn, nePath, nfPath);
txn.commit(); txn.commit();
return idNF; return nfPath;
} }
void realiseClosure(const FSId & id, FSIdSet pending) void realiseClosure(const Path & nePath, PathSet pending)
{ {
Nest nest(lvlDebug, Nest nest(lvlDebug, format("realising closure `%1%'") % nePath);
format("realising closure %1%") % (string) id);
NixExpr ne = parseNixExpr(termFromId(id)); NixExpr ne = parseNixExpr(termFromPath(nePath));
if (ne.type != NixExpr::neClosure) if (ne.type != NixExpr::neClosure)
throw Error(format("expected closure in %1%") % (string) id); throw Error(format("expected closure in `%1%'") % nePath);
for (ClosureElems::const_iterator i = ne.closure.elems.begin(); for (ClosureElems::const_iterator i = ne.closure.elems.begin();
i != ne.closure.elems.end(); i++) i != ne.closure.elems.end(); i++)
assert(isValidPath(i->first));
#if 0
expandId(i->second.id, i->first, "/", pending); expandId(i->second.id, i->first, "/", pending);
#endif
} }
Strings nixExprPaths(const FSId & id) PathSet nixExprRoots(const Path & nePath)
{ {
Strings paths; PathSet paths;
NixExpr ne = parseNixExpr(termFromId(id)); NixExpr ne = parseNixExpr(termFromPath(nePath));
if (ne.type == NixExpr::neClosure) { if (ne.type == NixExpr::neClosure)
for (StringSet::const_iterator i = ne.closure.roots.begin(); paths.insert(ne.closure.roots.begin(), ne.closure.roots.end());
i != ne.closure.roots.end(); i++) else if (ne.type == NixExpr::neDerivation)
paths.push_back(*i); paths.insert(ne.derivation.outputs.begin(),
} ne.derivation.outputs.end());
else if (ne.type == NixExpr::neDerivation) {
for (DerivationOutputs::iterator i = ne.derivation.outputs.begin();
i != ne.derivation.outputs.end(); i++)
paths.push_back(i->first);
}
else abort(); else abort();
return paths; return paths;
} }
static void nixExprRequisitesSet(const FSId & id, static void requisitesWorker(const Path & nePath,
bool includeExprs, bool includeSuccessors, StringSet & paths, bool includeExprs, bool includeSuccessors,
FSIdSet & doneSet) PathSet & paths, PathSet & doneSet)
{ {
if (doneSet.find(id) != doneSet.end()) return; if (doneSet.find(nePath) != doneSet.end()) return;
doneSet.insert(id); doneSet.insert(nePath);
NixExpr ne = parseNixExpr(termFromId(id)); NixExpr ne = parseNixExpr(termFromPath(nePath));
if (ne.type == NixExpr::neClosure) if (ne.type == NixExpr::neClosure)
for (ClosureElems::iterator i = ne.closure.elems.begin(); for (ClosureElems::iterator i = ne.closure.elems.begin();
@ -336,35 +416,35 @@ static void nixExprRequisitesSet(const FSId & id,
paths.insert(i->first); paths.insert(i->first);
else if (ne.type == NixExpr::neDerivation) else if (ne.type == NixExpr::neDerivation)
for (FSIdSet::iterator i = ne.derivation.inputs.begin(); for (PathSet::iterator i = ne.derivation.inputs.begin();
i != ne.derivation.inputs.end(); i++) i != ne.derivation.inputs.end(); i++)
nixExprRequisitesSet(*i, requisitesWorker(*i,
includeExprs, includeSuccessors, paths, doneSet); includeExprs, includeSuccessors, paths, doneSet);
else abort(); else abort();
if (includeExprs) if (includeExprs) paths.insert(nePath);
paths.insert(expandId(id));
string idSucc; string nfPath;
if (includeSuccessors && if (includeSuccessors && (nfPath = useSuccessor(nePath)) != nePath)
nixDB.queryString(noTxn, dbSuccessors, id, idSucc)) requisitesWorker(nfPath, includeExprs, includeSuccessors,
nixExprRequisitesSet(parseHash(idSucc), paths, doneSet);
includeExprs, includeSuccessors, paths, doneSet);
} }
Strings nixExprRequisites(const FSId & id, PathSet nixExprRequisites(const Path & nePath,
bool includeExprs, bool includeSuccessors) bool includeExprs, bool includeSuccessors)
{ {
StringSet paths; PathSet paths;
FSIdSet doneSet; PathSet doneSet;
nixExprRequisitesSet(id, includeExprs, includeSuccessors, paths, doneSet); requisitesWorker(nePath, includeExprs, includeSuccessors,
return Strings(paths.begin(), paths.end()); paths, doneSet);
return paths;
} }
FSIds findGenerators(const FSIds & _ids) #if 0
PathSet findGenerators(const PathSet & outputs)
{ {
FSIdSet ids(_ids.begin(), _ids.end()); FSIdSet ids(_ids.begin(), _ids.end());
FSIds generators; FSIds generators;
@ -407,3 +487,4 @@ FSIds findGenerators(const FSIds & _ids)
return generators; return generators;
} }
#endif

View file

@ -4,15 +4,22 @@
#include "expr.hh" #include "expr.hh"
/* Normalise a Nix expression, that is, return an equivalent /* Normalise a Nix expression. That is, if the expression is a
closure. (For the meaning of `pending', see expandId()). */ derivation, a path containing an equivalent closure expression is
FSId normaliseNixExpr(FSId id, FSIdSet pending = FSIdSet()); returned. This requires that the derivation is performed, unless a
successor is known. */
Path normaliseNixExpr(const Path & nePath, PathSet pending = PathSet());
/* Realise a Closure in the file system. */ /* Realise a closure expression in the file system.
void realiseClosure(const FSId & id, FSIdSet pending = FSIdSet());
The pending paths are those that are already being realised. This
prevents infinite recursion for paths realised through a substitute
(since when we build the substitute, we would first try to realise
its output paths through substitutes... kaboom!). */
void realiseClosure(const Path & nePath, PathSet pending = PathSet());
/* Get the list of root (output) paths of the given Nix expression. */ /* Get the list of root (output) paths of the given Nix expression. */
Strings nixExprPaths(const FSId & id); PathSet nixExprRoots(const Path & nePath);
/* Get the list of paths that are required to realise the given /* Get the list of paths that are required to realise the given
expression. For a derive expression, this is the union of expression. For a derive expression, this is the union of
@ -20,16 +27,16 @@ Strings nixExprPaths(const FSId & id);
each element in the closure. If `includeExprs' is true, include the each element in the closure. If `includeExprs' is true, include the
paths of the Nix expressions themselves. If `includeSuccessors' is paths of the Nix expressions themselves. If `includeSuccessors' is
true, include the requisites of successors. */ true, include the requisites of successors. */
Strings nixExprRequisites(const FSId & id, PathSet nixExprRequisites(const Path & nePath,
bool includeExprs, bool includeSuccessors); bool includeExprs, bool includeSuccessors);
/* Return the list of the ids of all known Nix expressions whose /* Return the list of the paths of all known Nix expressions whose
output ids are completely contained in `ids'. */ output paths are completely contained in the set `outputs'. */
FSIds findGenerators(const FSIds & ids); PathSet findGenerators(const PathSet & outputs);
/* Register a successor. */ /* Register a successor. */
void registerSuccessor(const Transaction & txn, void registerSuccessor(const Transaction & txn,
const FSId & id1, const FSId & id2); const Path & path1, const Path & path2);
#endif /* !__NORMALISE_H */ #endif /* !__NORMALISE_H */

View file

@ -13,20 +13,20 @@
static StringSet lockedPaths; /* !!! not thread-safe */ static StringSet lockedPaths; /* !!! not thread-safe */
PathLocks::PathLocks(const Strings & _paths) PathLocks::PathLocks(const PathSet & _paths)
{ {
/* Note that `fds' is built incrementally so that the destructor /* Note that `fds' is built incrementally so that the destructor
will only release those locks that we have already acquired. */ will only release those locks that we have already acquired. */
/* Sort the paths. This assures that locks are always acquired in /* Sort the paths. This assures that locks are always acquired in
the same order, thus preventing deadlocks. */ the same order, thus preventing deadlocks. */
Strings paths(_paths); Paths paths(_paths.begin(), _paths.end());
paths.sort(); paths.sort();
/* Acquire the lock for each path. */ /* Acquire the lock for each path. */
for (Strings::iterator i = paths.begin(); i != paths.end(); i++) { for (Paths::iterator i = paths.begin(); i != paths.end(); i++) {
string path = *i; Path path = *i;
string lockPath = path + ".lock"; Path lockPath = path + ".lock";
debug(format("locking path `%1%'") % path); debug(format("locking path `%1%'") % path);
@ -64,6 +64,6 @@ PathLocks::~PathLocks()
for (list<int>::iterator i = fds.begin(); i != fds.end(); i++) for (list<int>::iterator i = fds.begin(); i != fds.end(); i++)
close(*i); close(*i);
for (Strings::iterator i = paths.begin(); i != paths.end(); i++) for (Paths::iterator i = paths.begin(); i != paths.end(); i++)
lockedPaths.erase(*i); lockedPaths.erase(*i);
} }

View file

@ -8,10 +8,10 @@ class PathLocks
{ {
private: private:
list<int> fds; list<int> fds;
Strings paths; Paths paths;
public: public:
PathLocks(const Strings & _paths); PathLocks(const PathSet & _paths);
~PathLocks(); ~PathLocks();
}; };

View file

@ -4,7 +4,7 @@
#include "util.hh" #include "util.hh"
Strings filterReferences(const string & path, const Strings & refs); Strings filterReferences(const Path & path, const Strings & refs);
#endif /* !__VALUES_H */ #endif /* !__VALUES_H */

View file

@ -72,5 +72,3 @@ int main(int argc, char * * argv)
return 0; return 0;
} }

View file

@ -8,7 +8,6 @@
#include "db.hh" #include "db.hh"
#include "archive.hh" #include "archive.hh"
#include "pathlocks.hh" #include "pathlocks.hh"
#include "normalise.hh"
struct CopySink : DumpSink struct CopySink : DumpSink
@ -31,7 +30,7 @@ struct CopySource : RestoreSource
}; };
void copyPath(string src, string dst) void copyPath(const Path & src, const Path & dst)
{ {
debug(format("copying `%1%' to `%2%'") % src % dst); debug(format("copying `%1%' to `%2%'") % src % dst);
@ -82,7 +81,7 @@ void copyPath(string src, string dst)
} }
void registerSubstitute(const FSId & srcId, const FSId & subId) void registerSubstitute(const Path & srcPath, const Path & subPath)
{ {
#if 0 #if 0
Strings subs; Strings subs;
@ -98,202 +97,89 @@ void registerSubstitute(const FSId & srcId, const FSId & subId)
/* For now, accept only one substitute per id. */ /* For now, accept only one substitute per id. */
Strings subs; Strings subs;
subs.push_back(subId); subs.push_back(subPath);
Transaction txn(nixDB); Transaction txn(nixDB);
nixDB.setStrings(txn, dbSubstitutes, srcId, subs); nixDB.setStrings(txn, dbSubstitutes, srcPath, subs);
txn.commit(); txn.commit();
} }
void registerPath(const Transaction & txn, void registerValidPath(const Transaction & txn, const Path & _path)
const string & _path, const FSId & id)
{ {
string path(canonPath(_path)); Path path(canonPath(_path));
debug(format("registering path `%1%'") % path);
debug(format("registering path `%1%' with id %2%") nixDB.setString(txn, dbValidPaths, path, "");
% path % (string) id);
string oldId;
if (nixDB.queryString(txn, dbPath2Id, path, oldId)) {
if (id != parseHash(oldId))
throw Error(format("path `%1%' already contains id %2%")
% path % oldId);
return;
}
nixDB.setString(txn, dbPath2Id, path, id);
Strings paths;
nixDB.queryStrings(txn, dbId2Paths, id, paths); /* non-existence = ok */
paths.push_back(path);
nixDB.setStrings(txn, dbId2Paths, id, paths);
} }
void unregisterPath(const string & _path) bool isValidPath(const Path & path)
{ {
string path(canonPath(_path)); string s;
return nixDB.queryString(noTxn, dbValidPaths, path, s);
}
void unregisterValidPath(const Path & _path)
{
Path path(canonPath(_path));
Transaction txn(nixDB); Transaction txn(nixDB);
debug(format("unregistering path `%1%'") % path); debug(format("unregistering path `%1%'") % path);
string _id; nixDB.delPair(txn, dbValidPaths, path);
if (!nixDB.queryString(txn, dbPath2Id, path, _id)) {
txn.abort();
return;
}
FSId id(parseHash(_id));
nixDB.delPair(txn, dbPath2Id, path);
Strings paths, paths2;
nixDB.queryStrings(txn, dbId2Paths, id, paths); /* non-existence = ok */
for (Strings::iterator it = paths.begin();
it != paths.end(); it++)
if (*it != path) paths2.push_back(*it);
nixDB.setStrings(txn, dbId2Paths, id, paths2);
txn.commit(); txn.commit();
} }
bool queryPathId(const string & path, FSId & id) static bool isInPrefix(const string & path, const string & _prefix)
{
string s;
if (!nixDB.queryString(noTxn, dbPath2Id, absPath(path), s)) return false;
id = parseHash(s);
return true;
}
bool isInPrefix(const string & path, const string & _prefix)
{ {
string prefix = canonPath(_prefix + "/"); string prefix = canonPath(_prefix + "/");
return string(path, 0, prefix.size()) == prefix; return string(path, 0, prefix.size()) == prefix;
} }
string expandId(const FSId & id, const string & target, Path addToStore(const Path & _srcPath)
const string & prefix, FSIdSet pending, bool ignoreSubstitutes)
{
Nest nest(lvlDebug, format("expanding %1%") % (string) id);
Strings paths;
if (!target.empty() && !isInPrefix(target, prefix))
abort();
nixDB.queryStrings(noTxn, dbId2Paths, id, paths);
/* Pick one equal to `target'. */
if (!target.empty()) {
for (Strings::iterator i = paths.begin();
i != paths.end(); i++)
{
string path = *i;
if (path == target && pathExists(path))
return path;
}
}
/* Arbitrarily pick the first one that exists and isn't stale. */
for (Strings::iterator it = paths.begin();
it != paths.end(); it++)
{
string path = *it;
if (isInPrefix(path, prefix) && pathExists(path)) {
if (target.empty())
return path;
else {
/* Acquire a lock on the target path. */
Strings lockPaths;
lockPaths.push_back(target);
PathLocks outputLock(lockPaths);
/* Copy. */
copyPath(path, target);
/* Register the target path. */
Transaction txn(nixDB);
registerPath(txn, target, id);
txn.commit();
return target;
}
}
}
if (!ignoreSubstitutes) {
if (pending.find(id) != pending.end())
throw Error(format("id %1% already being expanded") % (string) id);
pending.insert(id);
/* Try to realise the substitutes, but only if this id is not
already being realised by a substitute. */
Strings subs;
nixDB.queryStrings(noTxn, dbSubstitutes, id, subs); /* non-existence = ok */
for (Strings::iterator it = subs.begin(); it != subs.end(); it++) {
FSId subId = parseHash(*it);
debug(format("trying substitute %1%") % (string) subId);
realiseClosure(normaliseNixExpr(subId, pending), pending);
return expandId(id, target, prefix, pending);
}
}
throw Error(format("cannot expand id `%1%'") % (string) id);
}
void addToStore(string srcPath, string & dstPath, FSId & id,
bool deterministicName)
{ {
Path srcPath(absPath(_srcPath));
debug(format("adding `%1%' to the store") % srcPath); debug(format("adding `%1%' to the store") % srcPath);
srcPath = absPath(srcPath); Hash h = hashPath(srcPath);
id = hashPath(srcPath);
string baseName = baseNameOf(srcPath); string baseName = baseNameOf(srcPath);
dstPath = canonPath(nixStore + "/" + (string) id + "-" + baseName); Path dstPath = canonPath(nixStore + "/" + (string) h + "-" + baseName);
try { if (!isValidPath(dstPath)) {
dstPath = expandId(id, deterministicName ? dstPath : "",
nixStore, FSIdSet(), true); /* The first check above is an optimisation to prevent
return; unnecessary lock acquisition. */
} catch (...) {
PathSet lockPaths;
lockPaths.insert(dstPath);
PathLocks outputLock(lockPaths);
if (!isValidPath(dstPath)) {
copyPath(srcPath, dstPath);
Transaction txn(nixDB);
registerValidPath(txn, dstPath);
txn.commit();
}
} }
Strings lockPaths;
lockPaths.push_back(dstPath);
PathLocks outputLock(lockPaths);
copyPath(srcPath, dstPath); return dstPath;
Transaction txn(nixDB);
registerPath(txn, dstPath, id);
txn.commit();
} }
void deleteFromStore(const string & path) void deleteFromStore(const Path & _path)
{ {
string prefix = + "/"; Path path(canonPath(_path));
if (!isInPrefix(path, nixStore))
throw Error(format("path %1% is not in the store") % path);
unregisterPath(path); if (!isInPrefix(path, nixStore))
throw Error(format("path `%1%' is not in the store") % path);
unregisterValidPath(path);
deletePath(path); deletePath(path);
} }
@ -305,6 +191,7 @@ void verifyStore()
/* !!! verify that the result is consistent */ /* !!! verify that the result is consistent */
#if 0
Strings paths; Strings paths;
nixDB.enumTable(txn, dbPath2Id, paths); nixDB.enumTable(txn, dbPath2Id, paths);
@ -421,6 +308,7 @@ void verifyStore()
} }
} }
} }
#endif
txn.commit(); txn.commit();
} }

View file

@ -9,44 +9,27 @@
using namespace std; using namespace std;
typedef Hash FSId;
typedef set<FSId> FSIdSet;
/* Copy a path recursively. */ /* Copy a path recursively. */
void copyPath(string src, string dst); void copyPath(const Path & src, const Path & dst);
/* Register a substitute. */ /* Register a substitute. */
void registerSubstitute(const FSId & srcId, const FSId & subId); void registerSubstitute(const Path & srcPath, const Path & subPath);
/* Register a path keyed on its id. */ /* Register the validity of a path. */
void registerPath(const Transaction & txn, void registerValidPath(const Transaction & txn, const Path & path);
const string & path, const FSId & id);
/* Query the id of a path. */ /* Unregister the validity of a path. */
bool queryPathId(const string & path, FSId & id); void unregisterValidPath(const Path & path);
/* Return a path whose contents have the given hash. If target is /* Checks whether a path is valid. */
not empty, ensure that such a path is realised in target (if bool isValidPath(const Path & path);
necessary by copying from another location). If prefix is not
empty, only return a path that is an descendent of prefix.
The list of pending ids are those that already being expanded. /* Copy the contents of a path to the store and register the validity
This prevents infinite recursion for ids realised through a the resulting path. The resulting path is returned. */
substitute (since when we build the substitute, we would first try Path addToStore(const Path & srcPath);
to expand the id... kaboom!). */
string expandId(const FSId & id, const string & target = "",
const string & prefix = "/", FSIdSet pending = FSIdSet(),
bool ignoreSubstitutes = false);
/* Copy a file to the nixStore directory and register it in dbRefs.
Return the hash code of the value. */
void addToStore(string srcPath, string & dstPath, FSId & id,
bool deterministicName = false);
/* Delete a value from the nixStore directory. */ /* Delete a value from the nixStore directory. */
void deleteFromStore(const string & path); void deleteFromStore(const Path & path);
void verifyStore(); void verifyStore();

View file

@ -10,10 +10,10 @@
#include "globals.hh" #include "globals.hh"
void realise(FSId id) void realise(Path nePath)
{ {
Nest nest(lvlDebug, format("TEST: realising %1%") % (string) id); Nest nest(lvlDebug, format("TEST: realising `%1%'") % nePath);
realiseClosure(normaliseNixExpr(id)); realiseClosure(normaliseNixExpr(nePath));
} }
@ -48,12 +48,12 @@ void runTests()
try { try {
h = parseHash("blah blah"); h = parseHash("blah blah");
abort(); abort();
} catch (BadRefError err) { }; } catch (Error err) { };
try { try {
h = parseHash("0b0ffd0538622bfe20b92c4aa57254d99"); h = parseHash("0b0ffd0538622bfe20b92c4aa57254d99");
abort(); abort();
} catch (BadRefError err) { }; } catch (Error err) { };
/* Path canonicalisation. */ /* Path canonicalisation. */
cout << canonPath("/./../././//") << endl; cout << canonPath("/./../././//") << endl;
@ -97,76 +97,59 @@ void runTests()
/* Expression evaluation. */ /* Expression evaluation. */
FSId builder1id; Path builder1fn;
string builder1fn; builder1fn = addToStore("./test-builder-1.sh");
addToStore("./test-builder-1.sh", builder1fn, builder1id);
ATerm fs1 = ATmake( ATerm fs1 = ATmake(
"Closure([<str>], [(<str>, <str>, [])])", "Closure([<str>], [(<str>, [])])",
builder1fn.c_str(), builder1fn.c_str(),
builder1fn.c_str(), builder1fn.c_str());
((string) builder1id).c_str()); Path fs1ne = writeTerm(fs1, "-c");
FSId fs1id = writeTerm(fs1, "");
realise(fs1id); realise(fs1ne);
realise(fs1id); realise(fs1ne);
ATerm fs2 = ATmake( string out1h = hashString("foo"); /* !!! bad */
"Closure([<str>], [(<str>, <str>, [])])", Path out1fn = nixStore + "/" + (string) out1h + "-hello.txt";
(builder1fn + "_bla").c_str(),
(builder1fn + "_bla").c_str(),
((string) builder1id).c_str());
FSId fs2id = writeTerm(fs2, "");
realise(fs2id);
realise(fs2id);
string out1id = hashString("foo"); /* !!! bad */
string out1fn = nixStore + "/" + (string) out1id + "-hello.txt";
ATerm fs3 = ATmake( ATerm fs3 = ATmake(
"Derive([(<str>, <str>)], [<str>], <str>, <str>, [], [(\"out\", <str>)])", "Derive([<str>], [<str>], <str>, <str>, [], [(\"out\", <str>)])",
out1fn.c_str(), out1fn.c_str(),
((string) out1id).c_str(), fs1ne.c_str(),
((string) fs1id).c_str(),
thisSystem.c_str(), thisSystem.c_str(),
((string) builder1fn).c_str(), builder1fn.c_str(),
out1fn.c_str()); out1fn.c_str());
debug(printTerm(fs3)); debug(printTerm(fs3));
FSId fs3id = writeTerm(fs3, ""); Path fs3ne = writeTerm(fs3, "-d");
realise(fs3id); realise(fs3ne);
realise(fs3id); realise(fs3ne);
FSId builder4id; Path builder4fn = addToStore("./test-builder-2.sh");
string builder4fn;
addToStore("./test-builder-2.sh", builder4fn, builder4id);
ATerm fs4 = ATmake( ATerm fs4 = ATmake(
"Closure([<str>], [(<str>, <str>, [])])", "Closure([<str>], [(<str>, [])])",
builder4fn.c_str(), builder4fn.c_str(),
builder4fn.c_str(), builder4fn.c_str());
((string) builder4id).c_str()); Path fs4ne = writeTerm(fs4, "-c");
FSId fs4id = writeTerm(fs4, "");
realise(fs4id); realise(fs4ne);
string out5id = hashString("bar"); /* !!! bad */ string out5h = hashString("bar"); /* !!! bad */
string out5fn = nixStore + "/" + (string) out5id + "-hello2"; Path out5fn = nixStore + "/" + (string) out5h + "-hello2";
ATerm fs5 = ATmake( ATerm fs5 = ATmake(
"Derive([(<str>, <str>)], [<str>], <str>, <str>, [], [(\"out\", <str>), (\"builder\", <str>)])", "Derive([<str>], [<str>], <str>, <str>, [], [(\"out\", <str>), (\"builder\", <str>)])",
out5fn.c_str(), out5fn.c_str(),
((string) out5id).c_str(), fs4ne.c_str(),
((string) fs4id).c_str(),
thisSystem.c_str(), thisSystem.c_str(),
((string) builder4fn).c_str(), builder4fn.c_str(),
out5fn.c_str(), out5fn.c_str(),
((string) builder4fn).c_str()); builder4fn.c_str());
debug(printTerm(fs5)); debug(printTerm(fs5));
FSId fs5id = writeTerm(fs5, ""); Path fs5ne = writeTerm(fs5, "-d");
realise(fs5id); realise(fs5ne);
realise(fs5id); realise(fs5ne);
} }