* Make dbRefs a mapping from Hash to [Path].
This commit is contained in:
parent
609a224848
commit
5895c160c4
11 changed files with 172 additions and 49 deletions
|
@ -14,7 +14,7 @@ fix_LDADD = libnix.a -ldb_cxx-4 -lATerm
|
||||||
|
|
||||||
TESTS = test
|
TESTS = test
|
||||||
|
|
||||||
test_SOURCES = test.cc
|
test_SOURCES = test.cc shared.cc
|
||||||
test_LDADD = libnix.a -ldb_cxx-4 -lATerm
|
test_LDADD = libnix.a -ldb_cxx-4 -lATerm
|
||||||
|
|
||||||
noinst_LIBRARIES = libnix.a
|
noinst_LIBRARIES = libnix.a
|
||||||
|
|
57
src/db.cc
57
src/db.cc
|
@ -73,6 +73,40 @@ bool queryDB(const string & filename, const string & dbname,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool queryListDB(const string & filename, const string & dbname,
|
||||||
|
const string & key, Strings & data)
|
||||||
|
{
|
||||||
|
string d;
|
||||||
|
|
||||||
|
if (!queryDB(filename, dbname, key, d))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
string::iterator it = d.begin();
|
||||||
|
|
||||||
|
while (it != d.end()) {
|
||||||
|
|
||||||
|
if (it + 4 > d.end())
|
||||||
|
throw Error(format("short db entry: `%1%'") % d);
|
||||||
|
|
||||||
|
unsigned int len;
|
||||||
|
len = (unsigned char) *it++;
|
||||||
|
len |= ((unsigned char) *it++) << 8;
|
||||||
|
len |= ((unsigned char) *it++) << 16;
|
||||||
|
len |= ((unsigned char) *it++) << 24;
|
||||||
|
|
||||||
|
if (it + len > d.end())
|
||||||
|
throw Error(format("short db entry: `%1%'") % d);
|
||||||
|
|
||||||
|
string s;
|
||||||
|
while (len--) s += *it++;
|
||||||
|
|
||||||
|
data.push_back(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void setDB(const string & filename, const string & dbname,
|
void setDB(const string & filename, const string & dbname,
|
||||||
const string & key, const string & data)
|
const string & key, const string & data)
|
||||||
{
|
{
|
||||||
|
@ -85,6 +119,29 @@ void setDB(const string & filename, const string & dbname,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void setListDB(const string & filename, const string & dbname,
|
||||||
|
const string & key, const Strings & data)
|
||||||
|
{
|
||||||
|
string d;
|
||||||
|
|
||||||
|
for (Strings::const_iterator it = data.begin();
|
||||||
|
it != data.end(); it++)
|
||||||
|
{
|
||||||
|
string s = *it;
|
||||||
|
unsigned int len = s.size();
|
||||||
|
|
||||||
|
d += len & 0xff;
|
||||||
|
d += (len >> 8) & 0xff;
|
||||||
|
d += (len >> 16) & 0xff;
|
||||||
|
d += (len >> 24) & 0xff;
|
||||||
|
|
||||||
|
d += s;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDB(filename, dbname, key, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void delDB(const string & filename, const string & dbname,
|
void delDB(const string & filename, const string & dbname,
|
||||||
const string & key)
|
const string & key)
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
|
#include "util.hh"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
typedef pair<string, string> DBPair;
|
typedef pair<string, string> DBPair;
|
||||||
|
@ -14,9 +16,15 @@ void createDB(const string & filename, const string & dbname);
|
||||||
bool queryDB(const string & filename, const string & dbname,
|
bool queryDB(const string & filename, const string & dbname,
|
||||||
const string & key, string & data);
|
const string & key, string & data);
|
||||||
|
|
||||||
|
bool queryListDB(const string & filename, const string & dbname,
|
||||||
|
const string & key, Strings & data);
|
||||||
|
|
||||||
void setDB(const string & filename, const string & dbname,
|
void setDB(const string & filename, const string & dbname,
|
||||||
const string & key, const string & data);
|
const string & key, const string & data);
|
||||||
|
|
||||||
|
void setListDB(const string & filename, const string & dbname,
|
||||||
|
const string & key, const Strings & data);
|
||||||
|
|
||||||
void delDB(const string & filename, const string & dbname,
|
void delDB(const string & filename, const string & dbname,
|
||||||
const string & key);
|
const string & key);
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ static bool isFState(Expr e, string & path)
|
||||||
}
|
}
|
||||||
else if (ATmatch(e, "Include(<str>)", &s1))
|
else if (ATmatch(e, "Include(<str>)", &s1))
|
||||||
{
|
{
|
||||||
string fn = queryFromStore(parseHash(s1));
|
string fn = queryPathByHash(parseHash(s1));
|
||||||
return isFState(evalFile(fn), path);
|
return isFState(evalFile(fn), path);
|
||||||
}
|
}
|
||||||
else return false;
|
else return false;
|
||||||
|
|
|
@ -167,7 +167,7 @@ struct RStatus
|
||||||
|
|
||||||
static ATerm termFromHash(const Hash & hash)
|
static ATerm termFromHash(const Hash & hash)
|
||||||
{
|
{
|
||||||
string path = queryFromStore(hash);
|
string path = queryPathByHash(hash);
|
||||||
ATerm t = ATreadFromNamedFile(path.c_str());
|
ATerm t = ATreadFromNamedFile(path.c_str());
|
||||||
if (!t) throw Error(format("cannot read aterm %1%") % path);
|
if (!t) throw Error(format("cannot read aterm %1%") % path);
|
||||||
return t;
|
return t;
|
||||||
|
@ -183,7 +183,7 @@ Hash writeTerm(ATerm t)
|
||||||
string path2 = nixStore + "/" + (string) hash + ".nix";
|
string path2 = nixStore + "/" + (string) hash + ".nix";
|
||||||
if (rename(path.c_str(), path2.c_str()) == -1)
|
if (rename(path.c_str(), path2.c_str()) == -1)
|
||||||
throw SysError(format("renaming %1% to %2%") % path % path2);
|
throw SysError(format("renaming %1% to %2%") % path % path2);
|
||||||
setDB(nixDB, dbRefs, hash, path2);
|
registerPath(path2, hash);
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,7 +259,7 @@ static FState realise(RStatus & status, FState fs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Do we know a path with that hash? If so, copy it. */
|
/* Do we know a path with that hash? If so, copy it. */
|
||||||
string path2 = queryFromStore(hash);
|
string path2 = queryPathByHash(hash);
|
||||||
copyPath(path2, path);
|
copyPath(path2, path);
|
||||||
|
|
||||||
return nf;
|
return nf;
|
||||||
|
@ -318,7 +318,7 @@ static FState realise(RStatus & status, FState fs)
|
||||||
|
|
||||||
/* Register targetHash -> targetPath. !!! this should be in
|
/* Register targetHash -> targetPath. !!! this should be in
|
||||||
values.cc. */
|
values.cc. */
|
||||||
setDB(nixDB, dbRefs, outHash, outPath);
|
registerPath(outPath, outHash);
|
||||||
|
|
||||||
/* Register the normal form of fs. */
|
/* Register the normal form of fs. */
|
||||||
FState nf = ATmake("Path(<str>, Hash(<str>), <term>)",
|
FState nf = ATmake("Path(<str>, Hash(<str>), <term>)",
|
||||||
|
|
|
@ -54,6 +54,9 @@ int main(int argc, char * * argv)
|
||||||
"Try `%2% --help' for more information.\n")
|
"Try `%2% --help' for more information.\n")
|
||||||
% e.what() % programId;
|
% e.what() % programId;
|
||||||
return 1;
|
return 1;
|
||||||
|
} catch (Error & e) {
|
||||||
|
cerr << format("error: %1%\n") % e.msg();
|
||||||
|
return 1;
|
||||||
} catch (exception & e) {
|
} catch (exception & e) {
|
||||||
cerr << format("error: %1%\n") % e.what();
|
cerr << format("error: %1%\n") % e.what();
|
||||||
return 1;
|
return 1;
|
||||||
|
|
93
src/store.cc
93
src/store.cc
|
@ -83,25 +83,89 @@ void copyPath(string src, string dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Hash registerPath(const string & _path, Hash hash)
|
||||||
|
{
|
||||||
|
string path(canonPath(_path));
|
||||||
|
|
||||||
|
if (hash == Hash()) hash = hashPath(path);
|
||||||
|
|
||||||
|
Strings paths;
|
||||||
|
queryListDB(nixDB, dbRefs, hash, paths); /* non-existence = ok */
|
||||||
|
|
||||||
|
for (Strings::iterator it = paths.begin();
|
||||||
|
it != paths.end(); it++)
|
||||||
|
if (*it == path) goto exists;
|
||||||
|
|
||||||
|
paths.push_back(path);
|
||||||
|
|
||||||
|
setListDB(nixDB, dbRefs, hash, paths);
|
||||||
|
|
||||||
|
exists:
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool isInPrefix(const string & path, const string & _prefix)
|
||||||
|
{
|
||||||
|
string prefix = canonPath(_prefix + "/");
|
||||||
|
return string(path, 0, prefix.size()) == prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static string queryPathByHashPrefix(Hash hash, const string & prefix)
|
||||||
|
{
|
||||||
|
Strings paths;
|
||||||
|
|
||||||
|
if (!queryListDB(nixDB, dbRefs, hash, paths))
|
||||||
|
throw Error(format("no paths known with hash `%1%'") % (string) hash);
|
||||||
|
|
||||||
|
/* Arbitrarily pick the first one that exists and still hash the
|
||||||
|
right hash. */
|
||||||
|
|
||||||
|
for (Strings::iterator it = paths.begin();
|
||||||
|
it != paths.end(); it++)
|
||||||
|
{
|
||||||
|
debug(*it);
|
||||||
|
string path = *it;
|
||||||
|
try {
|
||||||
|
debug(path);
|
||||||
|
debug(prefix);
|
||||||
|
if (isInPrefix(path, prefix) && hashPath(path) == hash)
|
||||||
|
return path;
|
||||||
|
} catch (Error & e) {
|
||||||
|
debug(format("checking hash: %1%") % e.msg());
|
||||||
|
/* try next one */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Error(format("all paths with hash `%1%' are stale") % (string) hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string queryPathByHash(Hash hash)
|
||||||
|
{
|
||||||
|
return queryPathByHashPrefix(hash, "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void addToStore(string srcPath, string & dstPath, Hash & hash)
|
void addToStore(string srcPath, string & dstPath, Hash & hash)
|
||||||
{
|
{
|
||||||
srcPath = absPath(srcPath);
|
srcPath = absPath(srcPath);
|
||||||
|
|
||||||
hash = hashPath(srcPath);
|
hash = hashPath(srcPath);
|
||||||
|
|
||||||
string path;
|
try {
|
||||||
if (queryDB(nixDB, dbRefs, hash, path)) {
|
dstPath = queryPathByHashPrefix(hash, nixStore);
|
||||||
debug((string) hash + " already known");
|
|
||||||
dstPath = path;
|
|
||||||
return;
|
return;
|
||||||
|
} catch (...) {
|
||||||
}
|
}
|
||||||
|
|
||||||
string baseName = baseNameOf(srcPath);
|
string baseName = baseNameOf(srcPath);
|
||||||
dstPath = nixStore + "/" + (string) hash + "-" + baseName;
|
dstPath = nixStore + "/" + (string) hash + "-" + baseName;
|
||||||
|
|
||||||
copyPath(srcPath, dstPath);
|
copyPath(srcPath, dstPath);
|
||||||
|
|
||||||
setDB(nixDB, dbRefs, hash, dstPath);
|
registerPath(dstPath, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -113,20 +177,3 @@ void deleteFromStore(const string & path)
|
||||||
deletePath(path);
|
deletePath(path);
|
||||||
// delDB(nixDB, dbRefs, hash);
|
// delDB(nixDB, dbRefs, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string queryFromStore(Hash hash)
|
|
||||||
{
|
|
||||||
string fn, url;
|
|
||||||
|
|
||||||
if (queryDB(nixDB, dbRefs, hash, fn)) {
|
|
||||||
|
|
||||||
/* Verify that the file hasn't changed. !!! race !!! slow */
|
|
||||||
if (hashPath(fn) != hash)
|
|
||||||
throw Error("file " + fn + " is stale");
|
|
||||||
|
|
||||||
return fn;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw Error(format("don't know a path with hash `%1%'") % (string) hash);
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,6 +10,12 @@ using namespace std;
|
||||||
|
|
||||||
void copyPath(string src, string dst);
|
void copyPath(string src, string dst);
|
||||||
|
|
||||||
|
/* Register a path keyed on its hash. */
|
||||||
|
Hash registerPath(const string & path, Hash hash = Hash());
|
||||||
|
|
||||||
|
/* Query a path (any path) through its hash. */
|
||||||
|
string queryPathByHash(Hash hash);
|
||||||
|
|
||||||
/* Copy a file to the nixStore directory and register it in dbRefs.
|
/* Copy a file to the nixStore directory and register it in dbRefs.
|
||||||
Return the hash code of the value. */
|
Return the hash code of the value. */
|
||||||
void addToStore(string srcPath, string & dstPath, Hash & hash);
|
void addToStore(string srcPath, string & dstPath, Hash & hash);
|
||||||
|
@ -17,8 +23,5 @@ void addToStore(string srcPath, string & dstPath, Hash & hash);
|
||||||
/* Delete a value from the nixStore directory. */
|
/* Delete a value from the nixStore directory. */
|
||||||
void deleteFromStore(const string & path);
|
void deleteFromStore(const string & path);
|
||||||
|
|
||||||
/* !!! */
|
|
||||||
string queryFromStore(Hash hash);
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* !__VALUES_H */
|
#endif /* !__VALUES_H */
|
||||||
|
|
15
src/test.cc
15
src/test.cc
|
@ -191,15 +191,10 @@ void runTests()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char * * argv)
|
void run(Strings args)
|
||||||
{
|
{
|
||||||
ATerm bottomOfStack;
|
runTests();
|
||||||
ATinit(argc, argv, &bottomOfStack);
|
|
||||||
|
|
||||||
try {
|
|
||||||
runTests();
|
|
||||||
} catch (exception & e) {
|
|
||||||
cerr << "error: " << e.what() << endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string programId = "test";
|
||||||
|
|
17
src/util.cc
17
src/util.cc
|
@ -33,13 +33,18 @@ string absPath(string path, string dir)
|
||||||
dir = buf;
|
dir = buf;
|
||||||
}
|
}
|
||||||
path = dir + "/" + path;
|
path = dir + "/" + path;
|
||||||
/* !!! canonicalise */
|
|
||||||
char resolved[PATH_MAX];
|
|
||||||
if (!realpath(path.c_str(), resolved))
|
|
||||||
throw SysError(format("cannot canonicalise path %1%") % path);
|
|
||||||
path = resolved;
|
|
||||||
}
|
}
|
||||||
return path;
|
return canonPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string canonPath(const string & path)
|
||||||
|
{
|
||||||
|
char resolved[PATH_MAX];
|
||||||
|
if (!realpath(path.c_str(), resolved))
|
||||||
|
throw SysError(format("cannot canonicalise path `%1%'") % path);
|
||||||
|
/* !!! check that this removes trailing slashes */
|
||||||
|
return resolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ public:
|
||||||
Error(const format & f);
|
Error(const format & f);
|
||||||
~Error() throw () { };
|
~Error() throw () { };
|
||||||
const char * what() const throw () { return err.c_str(); }
|
const char * what() const throw () { return err.c_str(); }
|
||||||
|
const string & msg() const throw () { return err; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class SysError : public Error
|
class SysError : public Error
|
||||||
|
@ -44,9 +45,13 @@ extern string thisSystem;
|
||||||
|
|
||||||
|
|
||||||
/* Return an absolutized path, resolving paths relative to the
|
/* Return an absolutized path, resolving paths relative to the
|
||||||
specified directory, or the current directory otherwise. */
|
specified directory, or the current directory otherwise. The path
|
||||||
|
is also canonicalised. */
|
||||||
string absPath(string path, string dir = "");
|
string absPath(string path, string dir = "");
|
||||||
|
|
||||||
|
/* Canonicalise a path (as in realpath(3)). */
|
||||||
|
string canonPath(const string & path);
|
||||||
|
|
||||||
/* Return the directory part of the given path, i.e., everything
|
/* Return the directory part of the given path, i.e., everything
|
||||||
before the final `/'. */
|
before the final `/'. */
|
||||||
string dirOf(string path);
|
string dirOf(string path);
|
||||||
|
|
Loading…
Reference in a new issue