forked from lix-project/lix
* Basic work on allowing derive expressions to build multiple paths.
This is not entirely trivial since this introduces the possibility of mutual recursion. * Made normal forms self-contained. * Use unique ids, not content hashes, for content referencing.
This commit is contained in:
parent
8898e86b4f
commit
f5b6fa5256
240
src/fstate.cc
240
src/fstate.cc
|
@ -154,55 +154,47 @@ FState hash2fstate(Hash hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ATerm termFromHash(const Hash & hash, string * p)
|
ATerm termFromId(const FSId & id, string * p)
|
||||||
{
|
{
|
||||||
string path = expandHash(hash);
|
string path = expandId(id);
|
||||||
if (p) *p = path;
|
if (p) *p = path;
|
||||||
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 from `%1%'") % path);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Hash writeTerm(ATerm t, const string & suffix, string * p)
|
FSId writeTerm(ATerm t, const string & suffix, string * p)
|
||||||
{
|
{
|
||||||
string path = nixStore + "/tmp.nix"; /* !!! */
|
FSId id = hashTerm(t);
|
||||||
|
|
||||||
|
string path = canonPath(nixStore + "/" +
|
||||||
|
(string) id + 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);
|
||||||
Hash hash = hashPath(path);
|
|
||||||
string path2 = canonPath(nixStore + "/" +
|
registerPath(path, id);
|
||||||
(string) hash + suffix + ".nix");
|
if (p) *p = path;
|
||||||
if (rename(path.c_str(), path2.c_str()) == -1)
|
|
||||||
throw SysError(format("renaming %1% to %2%") % path % path2);
|
return id;
|
||||||
registerPath(path2, hash);
|
|
||||||
if (p) *p = path2;
|
|
||||||
return hash;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void registerSuccessor(const Hash & fsHash, const Hash & scHash)
|
void registerSuccessor(const FSId & id1, const FSId & id2)
|
||||||
{
|
{
|
||||||
setDB(nixDB, dbSuccessors, fsHash, scHash);
|
setDB(nixDB, dbSuccessors, id1, id2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
FState storeSuccessor(FState fs, FState sc, StringSet & paths)
|
static FSId storeSuccessor(const FSId & id1, FState sc)
|
||||||
{
|
{
|
||||||
if (fs == sc) return sc;
|
FSId id2 = writeTerm(sc, "-s-" + (string) id1, 0);
|
||||||
|
registerSuccessor(id1, id2);
|
||||||
string path;
|
return id2;
|
||||||
Hash fsHash = hashTerm(fs);
|
}
|
||||||
Hash scHash = writeTerm(sc, "-s-" + (string) fsHash, &path);
|
|
||||||
registerSuccessor(fsHash, scHash);
|
|
||||||
paths.insert(path);
|
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
return ATmake("Include(<str>)", ((string) scHash).c_str());
|
|
||||||
#endif
|
|
||||||
return sc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static FState realise(FState fs, StringSet & paths)
|
static FState realise(FState fs, StringSet & paths)
|
||||||
{
|
{
|
||||||
char * s1, * s2, * s3;
|
char * s1, * s2, * s3;
|
||||||
|
@ -421,3 +413,193 @@ void fstateRefs(FState fs, StringSet & paths)
|
||||||
{
|
{
|
||||||
fstateRefs2(fs, paths);
|
fstateRefs2(fs, paths);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static void parseIds(ATermList ids, FSIds & out)
|
||||||
|
{
|
||||||
|
while (!ATisEmpty(ids)) {
|
||||||
|
char * s;
|
||||||
|
ATerm id = ATgetFirst(ids);
|
||||||
|
if (!ATmatch(id, "<str>", &s))
|
||||||
|
throw badTerm("not an id", id);
|
||||||
|
out.push_back(parseHash(s));
|
||||||
|
debug(s);
|
||||||
|
ids = ATgetNext(ids);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Parse a slice. */
|
||||||
|
static Slice parseSlice(FState fs)
|
||||||
|
{
|
||||||
|
Slice slice;
|
||||||
|
ATermList roots, elems;
|
||||||
|
|
||||||
|
if (!ATmatch(fs, "Slice([<list>], [<list>])", &roots, &elems))
|
||||||
|
throw badTerm("not a slice", fs);
|
||||||
|
|
||||||
|
parseIds(roots, slice.roots);
|
||||||
|
|
||||||
|
while (!ATisEmpty(elems)) {
|
||||||
|
char * s1, * s2;
|
||||||
|
ATermList refs;
|
||||||
|
ATerm t = ATgetFirst(elems);
|
||||||
|
if (!ATmatch(t, "(<str>, <str>, [<list>])", &s1, &s2, &refs))
|
||||||
|
throw badTerm("not a slice element", t);
|
||||||
|
SliceElem elem;
|
||||||
|
elem.path = s1;
|
||||||
|
elem.id = parseHash(s2);
|
||||||
|
parseIds(refs, elem.refs);
|
||||||
|
slice.elems.push_back(elem);
|
||||||
|
elems = ATgetNext(elems);
|
||||||
|
}
|
||||||
|
|
||||||
|
return slice;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Slice normaliseFState(FSId id)
|
||||||
|
{
|
||||||
|
debug(format("normalising fstate"));
|
||||||
|
Nest nest(true);
|
||||||
|
|
||||||
|
/* Try to substitute $id$ by any known successors in order to
|
||||||
|
speed up the rewrite process. */
|
||||||
|
string idSucc;
|
||||||
|
while (queryDB(nixDB, dbSuccessors, id, idSucc)) {
|
||||||
|
debug(format("successor %1% -> %2%") % (string) id % idSucc);
|
||||||
|
id = parseHash(idSucc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the fstate expression. */
|
||||||
|
FState fs = termFromId(id);
|
||||||
|
|
||||||
|
/* Already in normal form (i.e., a slice)? */
|
||||||
|
if (ATgetType(fs) == AT_APPL &&
|
||||||
|
(string) ATgetName(ATgetAFun(fs)) == "Slice")
|
||||||
|
return parseSlice(fs);
|
||||||
|
|
||||||
|
/* Then we it's a Derive node. */
|
||||||
|
ATermList outs, ins, bnds;
|
||||||
|
char * builder;
|
||||||
|
char * platform;
|
||||||
|
if (!ATmatch(fs, "Derive([<list>], [<list>], <str>, <str>, [<list>])",
|
||||||
|
&outs, &ins, &builder, &platform, &bnds))
|
||||||
|
throw badTerm("not a derive", fs);
|
||||||
|
|
||||||
|
/* Right platform? */
|
||||||
|
checkPlatform(platform);
|
||||||
|
|
||||||
|
/* Realise inputs (and remember all input paths). */
|
||||||
|
FSIds inIds;
|
||||||
|
parseIds(ins, inIds);
|
||||||
|
|
||||||
|
SliceElems inElems; /* !!! duplicates */
|
||||||
|
StringSet inPathsSet;
|
||||||
|
for (FSIds::iterator i = inIds.begin(); i != inIds.end(); i++) {
|
||||||
|
Slice slice = normaliseFState(*i);
|
||||||
|
realiseSlice(slice);
|
||||||
|
|
||||||
|
for (SliceElems::iterator j = slice.elems.begin();
|
||||||
|
j != slice.elems.end(); j++)
|
||||||
|
{
|
||||||
|
inElems.push_back(*j);
|
||||||
|
inPathsSet.insert(j->path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Strings inPaths;
|
||||||
|
copy(inPathsSet.begin(), inPathsSet.end(),
|
||||||
|
inserter(inPaths, inPaths.begin()));
|
||||||
|
|
||||||
|
/* Build the environment. */
|
||||||
|
Environment env;
|
||||||
|
while (!ATisEmpty(bnds)) {
|
||||||
|
char * s1, * s2;
|
||||||
|
ATerm bnd = ATgetFirst(bnds);
|
||||||
|
if (!ATmatch(bnd, "(<str>, <str>)", &s1, &s2))
|
||||||
|
throw badTerm("tuple of strings expected", bnd);
|
||||||
|
env[s1] = s2;
|
||||||
|
bnds = ATgetNext(bnds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that none of the output paths exist. */
|
||||||
|
typedef pair<string, FSId> OutPath;
|
||||||
|
list<OutPath> outPaths;
|
||||||
|
while (!ATisEmpty(outs)) {
|
||||||
|
ATerm t = ATgetFirst(outs);
|
||||||
|
char * s1, * s2;
|
||||||
|
if (!ATmatch(t, "(<str>, <str>)", &s1, &s2))
|
||||||
|
throw badTerm("string expected", t);
|
||||||
|
outPaths.push_back(OutPath(s1, parseHash(s2)));
|
||||||
|
outs = ATgetNext(outs);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (list<OutPath>::iterator i = outPaths.begin();
|
||||||
|
i != outPaths.end(); i++)
|
||||||
|
if (pathExists(i->first))
|
||||||
|
throw Error(format("path `%1%' exists") % i->first);
|
||||||
|
|
||||||
|
/* Run the builder. */
|
||||||
|
runProgram(builder, env);
|
||||||
|
|
||||||
|
Slice slice;
|
||||||
|
|
||||||
|
/* Check whether the output paths were created, and register each
|
||||||
|
one. */
|
||||||
|
for (list<OutPath>::iterator i = outPaths.begin();
|
||||||
|
i != outPaths.end(); i++)
|
||||||
|
{
|
||||||
|
string path = i->first;
|
||||||
|
if (!pathExists(path))
|
||||||
|
throw Error(format("path `%1%' does not exist") % path);
|
||||||
|
registerPath(path, i->second);
|
||||||
|
slice.roots.push_back(i->second);
|
||||||
|
|
||||||
|
Strings outPaths = filterReferences(path, inPaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
return slice;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void realiseSlice(Slice slice)
|
||||||
|
{
|
||||||
|
debug(format("realising slice"));
|
||||||
|
Nest nest(true);
|
||||||
|
|
||||||
|
if (slice.elems.size() == 0)
|
||||||
|
throw Error("empty slice");
|
||||||
|
|
||||||
|
/* Perhaps all paths already contain the right id? */
|
||||||
|
|
||||||
|
bool missing = false;
|
||||||
|
for (SliceElems::iterator i = slice.elems.begin();
|
||||||
|
i != slice.elems.end(); i++)
|
||||||
|
{
|
||||||
|
SliceElem elem = *i;
|
||||||
|
string id;
|
||||||
|
if (!queryDB(nixDB, dbPath2Id, elem.path, id)) {
|
||||||
|
if (pathExists(elem.path))
|
||||||
|
throw Error(format("path `%1%' obstructed") % elem.path);
|
||||||
|
missing = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (parseHash(id) != elem.id)
|
||||||
|
throw Error(format("path `%1%' obstructed") % elem.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!missing) {
|
||||||
|
debug(format("already installed"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For each element, expand its id at its path. */
|
||||||
|
for (SliceElems::iterator i = slice.elems.begin();
|
||||||
|
i != slice.elems.end(); i++)
|
||||||
|
{
|
||||||
|
SliceElem elem = *i;
|
||||||
|
expandId(elem.id, elem.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ extern "C" {
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "hash.hh"
|
#include "hash.hh"
|
||||||
|
#include "store.hh"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
@ -17,33 +18,24 @@ using namespace std;
|
||||||
A Nix file system state expression, or FState, describes a
|
A Nix file system state expression, or FState, describes a
|
||||||
(partial) state of the file system.
|
(partial) state of the file system.
|
||||||
|
|
||||||
Path : Path * Content * [FState] -> FState
|
Slice : [Id] * [(Path, Id, [Id])] -> FState
|
||||||
|
|
||||||
|
(update)
|
||||||
Path(path, content, refs) specifies a file object (its full path
|
Path(path, content, refs) specifies a file object (its full path
|
||||||
and contents), along with all file objects referenced by it (that
|
and contents), along with all file objects referenced by it (that
|
||||||
is, that it has pointers to). We assume that all files are
|
is, that it has pointers to). We assume that all files are
|
||||||
self-referential. This prevents us from having to deal with
|
self-referential. This prevents us from having to deal with
|
||||||
cycles.
|
cycles.
|
||||||
|
|
||||||
Derive : String * Path * [FState] * Path * [(String, String)] -> FState
|
Derive : [(Path, Id)] * [FStateId] * Path * [(String, String)] -> FState
|
||||||
|
|
||||||
|
(update)
|
||||||
Derive(platform, builder, ins, outs, env) specifies the creation of
|
Derive(platform, builder, ins, outs, env) specifies the creation of
|
||||||
new file objects (in paths declared by `outs') by the execution of
|
new file objects (in paths declared by `outs') by the execution of
|
||||||
a program `builder' on a platform `platform'. This execution takes
|
a program `builder' on a platform `platform'. This execution takes
|
||||||
place in a file system state given by `ins'. `env' specifies a
|
place in a file system state given by `ins'. `env' specifies a
|
||||||
mapping of strings to strings.
|
mapping of strings to strings.
|
||||||
|
|
||||||
[ !!! NOT IMPLEMENTED
|
|
||||||
Regular : String -> Content
|
|
||||||
Directory : [(String, Content)] -> Content
|
|
||||||
(this complicates unambiguous normalisation)
|
|
||||||
]
|
|
||||||
CHash : Hash -> Content
|
|
||||||
|
|
||||||
File content, given either in situ, or through an external reference
|
|
||||||
to the file system or url-space decorated with a hash to preserve
|
|
||||||
purity.
|
|
||||||
|
|
||||||
A FState expression is in {\em $f$-normal form} if all Derive nodes
|
A FState expression is in {\em $f$-normal form} if all Derive nodes
|
||||||
have been reduced to File nodes.
|
have been reduced to File nodes.
|
||||||
|
|
||||||
|
@ -63,7 +55,26 @@ typedef ATerm Content;
|
||||||
|
|
||||||
typedef set<string> StringSet;
|
typedef set<string> StringSet;
|
||||||
|
|
||||||
|
typedef list<FSId> FSIds;
|
||||||
|
|
||||||
|
|
||||||
|
struct SliceElem
|
||||||
|
{
|
||||||
|
string path;
|
||||||
|
FSId id;
|
||||||
|
FSIds refs;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef list<SliceElem> SliceElems;
|
||||||
|
|
||||||
|
struct Slice
|
||||||
|
{
|
||||||
|
FSIds roots;
|
||||||
|
SliceElems elems;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#if 0
|
||||||
/* Realise an fstate expression in the file system. This requires
|
/* Realise an fstate expression in the file system. This requires
|
||||||
execution of all Derive() nodes. */
|
execution of all Derive() nodes. */
|
||||||
FState realiseFState(FState fs, StringSet & paths);
|
FState realiseFState(FState fs, StringSet & paths);
|
||||||
|
@ -74,6 +85,8 @@ string fstatePath(FState fs);
|
||||||
|
|
||||||
/* Return the paths referenced by fstate expression. */
|
/* Return the paths referenced by fstate expression. */
|
||||||
void fstateRefs(FState fs, StringSet & paths);
|
void fstateRefs(FState fs, StringSet & paths);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* Return a canonical textual representation of an expression. */
|
/* Return a canonical textual representation of an expression. */
|
||||||
string printTerm(ATerm t);
|
string printTerm(ATerm t);
|
||||||
|
@ -85,16 +98,22 @@ Error badTerm(const format & f, ATerm t);
|
||||||
/* Hash an aterm. */
|
/* Hash an aterm. */
|
||||||
Hash hashTerm(ATerm t);
|
Hash hashTerm(ATerm t);
|
||||||
|
|
||||||
FState hash2fstate(Hash hash);
|
/* Read an aterm from disk, given its id. */
|
||||||
|
ATerm termFromId(const FSId & id, string * p = 0);
|
||||||
/* Read an aterm from disk, given its hash. */
|
|
||||||
ATerm termFromHash(const Hash & hash, string * p = 0);
|
|
||||||
|
|
||||||
/* Write an aterm to the Nix store directory, and return its hash. */
|
/* Write an aterm to the Nix store directory, and return its hash. */
|
||||||
Hash writeTerm(ATerm t, const string & suffix, string * p = 0);
|
FSId writeTerm(ATerm t, const string & suffix, string * p = 0);
|
||||||
|
|
||||||
/* Register a successor. */
|
/* Register a successor. */
|
||||||
void registerSuccessor(const Hash & fsHash, const Hash & scHash);
|
void registerSuccessor(const FSId & id1, const FSId & id2);
|
||||||
|
|
||||||
|
|
||||||
|
/* Normalise an fstate-expression, that is, return an equivalent
|
||||||
|
Slice. */
|
||||||
|
Slice normaliseFState(FSId id);
|
||||||
|
|
||||||
|
/* Realise a Slice in the file system. */
|
||||||
|
void realiseSlice(Slice slice);
|
||||||
|
|
||||||
|
|
||||||
#endif /* !__FSTATE_H */
|
#endif /* !__FSTATE_H */
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
#include "db.hh"
|
#include "db.hh"
|
||||||
|
|
||||||
|
|
||||||
string dbHash2Paths = "hash2paths";
|
string dbPath2Id = "path2id";
|
||||||
|
string dbId2Paths = "id2paths";
|
||||||
string dbSuccessors = "successors";
|
string dbSuccessors = "successors";
|
||||||
string dbSubstitutes = "substitutes";
|
string dbSubstitutes = "substitutes";
|
||||||
|
|
||||||
|
@ -15,7 +16,8 @@ string nixDB = "/UNINIT";
|
||||||
|
|
||||||
void initDB()
|
void initDB()
|
||||||
{
|
{
|
||||||
createDB(nixDB, dbHash2Paths);
|
createDB(nixDB, dbPath2Id);
|
||||||
|
createDB(nixDB, dbId2Paths);
|
||||||
createDB(nixDB, dbSuccessors);
|
createDB(nixDB, dbSuccessors);
|
||||||
createDB(nixDB, dbSubstitutes);
|
createDB(nixDB, dbSubstitutes);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,33 +8,36 @@ using namespace std;
|
||||||
|
|
||||||
/* Database names. */
|
/* Database names. */
|
||||||
|
|
||||||
/* dbHash2Paths :: Hash -> [Path]
|
/* dbPath2Id :: Path -> FSId
|
||||||
|
|
||||||
Maintains a mapping from hashes to lists of paths. This is what we
|
Each pair (p, id) records that path $p$ contains an expansion of
|
||||||
use to resolve Hash(hash) content descriptors. */
|
$id$. */
|
||||||
extern string dbHash2Paths;
|
extern string dbPath2Id;
|
||||||
|
|
||||||
/* dbSuccessors :: Hash -> Hash
|
|
||||||
|
|
||||||
Each pair (h1, h2) in this mapping records the fact that a
|
/* dbId2Paths :: FSId -> [Path]
|
||||||
successor of an fstate expression with hash h1 is stored in a file
|
|
||||||
with hash h2.
|
A mapping from ids to lists of paths. */
|
||||||
|
extern string dbId2Paths;
|
||||||
|
|
||||||
|
|
||||||
|
/* dbSuccessors :: FSId -> FSId
|
||||||
|
|
||||||
|
Each pair $(id_1, id_2)$ in this mapping records the fact that a
|
||||||
|
successor of an fstate 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
|
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$.
|
||||||
|
|
||||||
Also note that instead of a successor, $y$ can be any term
|
|
||||||
equivalent to $x$, that is, reducing to the same result, as long as
|
|
||||||
$x$ is equal to or a successor of $y$. (This is useful, e.g., for
|
|
||||||
shared derivate caching over the network).
|
|
||||||
*/
|
*/
|
||||||
extern string dbSuccessors;
|
extern string dbSuccessors;
|
||||||
|
|
||||||
/* dbSubstitutes :: Hash -> [Hash]
|
|
||||||
|
|
||||||
Each pair $(h, [hs])$ tells Nix that it can realise any of the
|
/* dbSubstitutes :: FSId -> [FSId]
|
||||||
fstate expressions referenced by the hashes in $hs$ to obtain a Nix
|
|
||||||
archive that, when unpacked, will produce a path with hash $h$.
|
Each pair $(id, [ids])$ tells Nix that it can realise any of the
|
||||||
|
fstate expressions referenced by the identifiers in $ids$ to
|
||||||
|
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 with hash $h$ and put it on a
|
||||||
|
|
91
src/store.cc
91
src/store.cc
|
@ -84,39 +84,36 @@ void copyPath(string src, string dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void registerSubstitute(const Hash & srcHash, const Hash & subHash)
|
void registerSubstitute(const FSId & srcId, const FSId & subId)
|
||||||
{
|
{
|
||||||
Strings subs;
|
Strings subs;
|
||||||
queryListDB(nixDB, dbSubstitutes, srcHash, subs); /* non-existence = ok */
|
queryListDB(nixDB, dbSubstitutes, srcId, subs); /* non-existence = ok */
|
||||||
|
|
||||||
for (Strings::iterator it = subs.begin(); it != subs.end(); it++)
|
for (Strings::iterator it = subs.begin(); it != subs.end(); it++)
|
||||||
if (parseHash(*it) == subHash) return;
|
if (parseHash(*it) == subId) return;
|
||||||
|
|
||||||
subs.push_back(subHash);
|
subs.push_back(subId);
|
||||||
|
|
||||||
setListDB(nixDB, dbSubstitutes, srcHash, subs);
|
setListDB(nixDB, dbSubstitutes, srcId, subs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Hash registerPath(const string & _path, Hash hash)
|
void registerPath(const string & _path, const FSId & id)
|
||||||
{
|
{
|
||||||
string path(canonPath(_path));
|
string path(canonPath(_path));
|
||||||
|
|
||||||
if (hash == Hash()) hash = hashPath(path);
|
setDB(nixDB, dbPath2Id, path, id);
|
||||||
|
|
||||||
Strings paths;
|
Strings paths;
|
||||||
queryListDB(nixDB, dbHash2Paths, hash, paths); /* non-existence = ok */
|
queryListDB(nixDB, dbId2Paths, id, paths); /* non-existence = ok */
|
||||||
|
|
||||||
for (Strings::iterator it = paths.begin();
|
for (Strings::iterator it = paths.begin();
|
||||||
it != paths.end(); it++)
|
it != paths.end(); it++)
|
||||||
if (*it == path) goto exists;
|
if (*it == path) return;
|
||||||
|
|
||||||
paths.push_back(path);
|
paths.push_back(path);
|
||||||
|
|
||||||
setListDB(nixDB, dbHash2Paths, hash, paths);
|
setListDB(nixDB, dbId2Paths, id, paths);
|
||||||
|
|
||||||
exists:
|
|
||||||
return hash;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -124,10 +121,15 @@ void unregisterPath(const string & _path)
|
||||||
{
|
{
|
||||||
string path(canonPath(_path));
|
string path(canonPath(_path));
|
||||||
|
|
||||||
Hash hash = hashPath(path);
|
string _id;
|
||||||
|
if (!queryDB(nixDB, dbPath2Id, path, _id))
|
||||||
|
return;
|
||||||
|
FSId id(parseHash(_id));
|
||||||
|
|
||||||
|
/* begin transaction */
|
||||||
|
|
||||||
Strings paths, paths2;
|
Strings paths, paths2;
|
||||||
queryListDB(nixDB, dbHash2Paths, hash, paths); /* non-existence = ok */
|
queryListDB(nixDB, dbId2Paths, id, paths); /* non-existence = ok */
|
||||||
|
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
for (Strings::iterator it = paths.begin();
|
for (Strings::iterator it = paths.begin();
|
||||||
|
@ -135,7 +137,9 @@ void unregisterPath(const string & _path)
|
||||||
if (*it != path) paths2.push_back(*it); else changed = true;
|
if (*it != path) paths2.push_back(*it); else changed = true;
|
||||||
|
|
||||||
if (changed)
|
if (changed)
|
||||||
setListDB(nixDB, dbHash2Paths, hash, paths2);
|
setListDB(nixDB, dbId2Paths, id, paths2);
|
||||||
|
|
||||||
|
/* end transaction */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -146,7 +150,7 @@ bool isInPrefix(const string & path, const string & _prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string expandHash(const Hash & hash, const string & target,
|
string expandId(const FSId & id, const string & target,
|
||||||
const string & prefix)
|
const string & prefix)
|
||||||
{
|
{
|
||||||
Strings paths;
|
Strings paths;
|
||||||
|
@ -154,9 +158,7 @@ string expandHash(const Hash & hash, const string & target,
|
||||||
if (!target.empty() && !isInPrefix(target, prefix))
|
if (!target.empty() && !isInPrefix(target, prefix))
|
||||||
abort();
|
abort();
|
||||||
|
|
||||||
queryListDB(nixDB, dbHash2Paths, hash, paths);
|
queryListDB(nixDB, dbId2Paths, id, paths);
|
||||||
|
|
||||||
/* !!! we shouldn't check for staleness by default --- too slow */
|
|
||||||
|
|
||||||
/* Pick one equal to `target'. */
|
/* Pick one equal to `target'. */
|
||||||
if (!target.empty()) {
|
if (!target.empty()) {
|
||||||
|
@ -165,16 +167,8 @@ string expandHash(const Hash & hash, const string & target,
|
||||||
i != paths.end(); i++)
|
i != paths.end(); i++)
|
||||||
{
|
{
|
||||||
string path = *i;
|
string path = *i;
|
||||||
try {
|
if (path == target && pathExists(path))
|
||||||
#if 0
|
return path;
|
||||||
if (path == target && hashPath(path) == hash)
|
|
||||||
#endif
|
|
||||||
if (path == target && pathExists(path))
|
|
||||||
return path;
|
|
||||||
} catch (Error & e) {
|
|
||||||
debug(format("stale path: %1%") % e.msg());
|
|
||||||
/* try next one */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -184,28 +178,26 @@ string expandHash(const Hash & hash, const string & target,
|
||||||
it != paths.end(); it++)
|
it != paths.end(); it++)
|
||||||
{
|
{
|
||||||
string path = *it;
|
string path = *it;
|
||||||
try {
|
if (isInPrefix(path, prefix) && pathExists(path)) {
|
||||||
if (isInPrefix(path, prefix) && hashPath(path) == hash) {
|
if (target.empty())
|
||||||
if (target.empty())
|
return path;
|
||||||
return path;
|
else {
|
||||||
else {
|
copyPath(path, target);
|
||||||
copyPath(path, target);
|
registerPath(target, id);
|
||||||
return target;
|
return target;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (Error & e) {
|
|
||||||
debug(format("stale path: %1%") % e.msg());
|
|
||||||
/* try next one */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
/* Try to realise the substitutes. */
|
/* Try to realise the substitutes. */
|
||||||
|
|
||||||
Strings subs;
|
Strings subs;
|
||||||
queryListDB(nixDB, dbSubstitutes, hash, subs); /* non-existence = ok */
|
queryListDB(nixDB, dbSubstitutes, id, subs); /* non-existence = ok */
|
||||||
|
|
||||||
for (Strings::iterator it = subs.begin(); it != subs.end(); it++) {
|
for (Strings::iterator it = subs.begin(); it != subs.end(); it++) {
|
||||||
StringSet dummy;
|
realiseSlice(normaliseFState(*it));
|
||||||
|
|
||||||
FState nf = realiseFState(hash2fstate(parseHash(*it)), dummy);
|
FState nf = realiseFState(hash2fstate(parseHash(*it)), dummy);
|
||||||
string path = fstatePath(nf);
|
string path = fstatePath(nf);
|
||||||
|
|
||||||
|
@ -222,29 +214,30 @@ string expandHash(const Hash & hash, const string & target,
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
throw Error(format("cannot expand hash `%1%'") % (string) hash);
|
throw Error(format("cannot expand id `%1%'") % (string) id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void addToStore(string srcPath, string & dstPath, Hash & hash,
|
void addToStore(string srcPath, string & dstPath, FSId & id,
|
||||||
bool deterministicName)
|
bool deterministicName)
|
||||||
{
|
{
|
||||||
srcPath = absPath(srcPath);
|
srcPath = absPath(srcPath);
|
||||||
hash = hashPath(srcPath);
|
id = hashPath(srcPath);
|
||||||
|
|
||||||
string baseName = baseNameOf(srcPath);
|
string baseName = baseNameOf(srcPath);
|
||||||
dstPath = canonPath(nixStore + "/" + (string) hash + "-" + baseName);
|
dstPath = canonPath(nixStore + "/" + (string) id + "-" + baseName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/* !!! should not use the substitutes! */
|
/* !!! should not use the substitutes! */
|
||||||
dstPath = expandHash(hash, deterministicName ? dstPath : "", nixStore);
|
dstPath = expandId(id, deterministicName ? dstPath : "", nixStore);
|
||||||
return;
|
return;
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
}
|
}
|
||||||
|
|
||||||
copyPath(srcPath, dstPath);
|
copyPath(srcPath, dstPath);
|
||||||
registerPath(dstPath, hash);
|
registerPath(dstPath, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
19
src/store.hh
19
src/store.hh
|
@ -8,29 +8,28 @@
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
|
typedef Hash FSId;
|
||||||
|
|
||||||
|
|
||||||
/* Copy a path recursively. */
|
/* Copy a path recursively. */
|
||||||
void copyPath(string src, string dst);
|
void copyPath(string src, string dst);
|
||||||
|
|
||||||
/* Register a substitute. */
|
/* Register a substitute. */
|
||||||
void registerSubstitute(const Hash & srcHash, const Hash & subHash);
|
void registerSubstitute(const FSId & srcId, const FSId & subId);
|
||||||
|
|
||||||
/* Register a path keyed on its hash. */
|
/* Register a path keyed on its id. */
|
||||||
Hash registerPath(const string & path, Hash hash = Hash());
|
void registerPath(const string & path, const FSId & id);
|
||||||
|
|
||||||
/* Return a path whose contents have the given hash. If target is
|
/* Return a path whose contents have the given hash. If target is
|
||||||
not empty, ensure that such a path is realised in target (if
|
not empty, ensure that such a path is realised in target (if
|
||||||
necessary by copying from another location). If prefix is not
|
necessary by copying from another location). If prefix is not
|
||||||
empty, only return a path that is an descendent of prefix.
|
empty, only return a path that is an descendent of prefix. */
|
||||||
|
string expandId(const FSId & id, const string & target = "",
|
||||||
If no path with the given hash is known to exist in the file
|
|
||||||
system,
|
|
||||||
*/
|
|
||||||
string expandHash(const Hash & hash, const string & target = "",
|
|
||||||
const string & prefix = "/");
|
const string & prefix = "/");
|
||||||
|
|
||||||
/* 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, FSId & id,
|
||||||
bool deterministicName = false);
|
bool deterministicName = false);
|
||||||
|
|
||||||
/* Delete a value from the nixStore directory. */
|
/* Delete a value from the nixStore directory. */
|
||||||
|
|
111
src/test.cc
111
src/test.cc
|
@ -11,14 +11,15 @@
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
|
|
||||||
|
|
||||||
void realise(FState fs)
|
void realise(FSId id)
|
||||||
{
|
{
|
||||||
cout << format("%1% => %2%\n")
|
cout << format("realising %1%\n") % (string) id;
|
||||||
% printTerm(fs)
|
Slice slice = normaliseFState(id);
|
||||||
% printTerm(realiseFState(fs));
|
realiseSlice(slice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if 0
|
||||||
void realiseFail(FState fs)
|
void realiseFail(FState fs)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
@ -28,6 +29,7 @@ void realiseFail(FState fs)
|
||||||
cout << "error (expected): " << e.what() << endl;
|
cout << "error (expected): " << e.what() << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
struct MySink : DumpSink
|
struct MySink : DumpSink
|
||||||
|
@ -111,54 +113,47 @@ void runTests()
|
||||||
|
|
||||||
/* Expression evaluation. */
|
/* Expression evaluation. */
|
||||||
|
|
||||||
#if 0
|
FSId builder1id;
|
||||||
eval(whNormalise,
|
|
||||||
ATmake("Str(\"Hello World\")"));
|
|
||||||
eval(whNormalise,
|
|
||||||
ATmake("Bool(True)"));
|
|
||||||
eval(whNormalise,
|
|
||||||
ATmake("Bool(False)"));
|
|
||||||
eval(whNormalise,
|
|
||||||
ATmake("App(Lam(\"x\", Var(\"x\")), Str(\"Hello World\"))"));
|
|
||||||
eval(whNormalise,
|
|
||||||
ATmake("App(App(Lam(\"x\", Lam(\"y\", Var(\"x\"))), Str(\"Hello World\")), Str(\"Hallo Wereld\"))"));
|
|
||||||
eval(whNormalise,
|
|
||||||
ATmake("App(Lam(\"sys\", Lam(\"x\", [Var(\"x\"), Var(\"sys\")])), Str(\"i686-suse-linux\"))"));
|
|
||||||
|
|
||||||
evalFail(whNormalise,
|
|
||||||
ATmake("Foo(123)"));
|
|
||||||
|
|
||||||
string builder1fn = absPath("./test-builder-1.sh");
|
|
||||||
Hash builder1h = hashPath(builder1fn);
|
|
||||||
|
|
||||||
string fn1 = nixValues + "/builder-1.sh";
|
|
||||||
Expr e1 = ATmake("Path(<str>, ExtFile(<str>, <str>), [])",
|
|
||||||
fn1.c_str(),
|
|
||||||
builder1h.c_str(),
|
|
||||||
builder1fn.c_str());
|
|
||||||
eval(fNormalise, e1);
|
|
||||||
|
|
||||||
string fn2 = nixValues + "/refer.txt";
|
|
||||||
Expr e2 = ATmake("Path(<str>, Regular(<str>), [<term>])",
|
|
||||||
fn2.c_str(),
|
|
||||||
("I refer to " + fn1).c_str(),
|
|
||||||
e1);
|
|
||||||
eval(fNormalise, e2);
|
|
||||||
|
|
||||||
realise(e2);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Hash builder1h;
|
|
||||||
string builder1fn;
|
string builder1fn;
|
||||||
addToStore("./test-builder-1.sh", builder1fn, builder1h);
|
addToStore("./test-builder-1.sh", builder1fn, builder1id);
|
||||||
|
|
||||||
FState fs1 = ATmake(
|
FState fs1 = ATmake(
|
||||||
"Path(<str>, Hash(<str>), [])",
|
"Slice([<str>], [(<str>, <str>, [])])",
|
||||||
|
((string) builder1id).c_str(),
|
||||||
builder1fn.c_str(),
|
builder1fn.c_str(),
|
||||||
((string) builder1h).c_str());
|
((string) builder1id).c_str());
|
||||||
realise(fs1);
|
FSId fs1id = writeTerm(fs1, "", 0);
|
||||||
realise(fs1);
|
|
||||||
|
|
||||||
|
realise(fs1id);
|
||||||
|
realise(fs1id);
|
||||||
|
|
||||||
|
FState fs2 = ATmake(
|
||||||
|
"Slice([<str>], [(<str>, <str>, [])])",
|
||||||
|
((string) builder1id).c_str(),
|
||||||
|
(builder1fn + "_bla").c_str(),
|
||||||
|
((string) builder1id).c_str());
|
||||||
|
FSId fs2id = writeTerm(fs2, "", 0);
|
||||||
|
|
||||||
|
realise(fs2id);
|
||||||
|
realise(fs2id);
|
||||||
|
|
||||||
|
string out1fn = nixStore + "/hello.txt";
|
||||||
|
string out1id = hashString("foo"); /* !!! bad */
|
||||||
|
FState fs3 = ATmake(
|
||||||
|
"Derive([(<str>, <str>)], [<str>], <str>, <str>, [(\"out\", <str>)])",
|
||||||
|
out1fn.c_str(),
|
||||||
|
((string) out1id).c_str(),
|
||||||
|
((string) fs1id).c_str(),
|
||||||
|
((string) builder1fn).c_str(),
|
||||||
|
thisSystem.c_str(),
|
||||||
|
out1fn.c_str());
|
||||||
|
debug(printTerm(fs3));
|
||||||
|
FSId fs3id = writeTerm(fs3, "", 0);
|
||||||
|
|
||||||
|
realise(fs3id);
|
||||||
|
realise(fs3id);
|
||||||
|
|
||||||
|
#if 0
|
||||||
FState fs2 = ATmake(
|
FState fs2 = ATmake(
|
||||||
"Path(<str>, Hash(<str>), [])",
|
"Path(<str>, Hash(<str>), [])",
|
||||||
(builder1fn + "_bla").c_str(),
|
(builder1fn + "_bla").c_str(),
|
||||||
|
@ -175,28 +170,8 @@ void runTests()
|
||||||
out1fn.c_str(),
|
out1fn.c_str(),
|
||||||
out1fn.c_str());
|
out1fn.c_str());
|
||||||
realise(fs3);
|
realise(fs3);
|
||||||
|
|
||||||
#if 0
|
|
||||||
Expr e1 = ATmake("Exec(Str(<str>), Hash(<str>), [])",
|
|
||||||
thisSystem.c_str(), ((string) builder1).c_str());
|
|
||||||
|
|
||||||
eval(e1);
|
|
||||||
|
|
||||||
Hash builder2 = addValue("./test-builder-2.sh");
|
|
||||||
|
|
||||||
Expr e2 = ATmake(
|
|
||||||
"Exec(Str(<str>), Hash(<str>), [Tup(Str(\"src\"), <term>)])",
|
|
||||||
thisSystem.c_str(), ((string) builder2).c_str(), e1);
|
|
||||||
|
|
||||||
eval(e2);
|
|
||||||
|
|
||||||
Hash h3 = addValue("./test-expr-1.nix");
|
|
||||||
Expr e3 = ATmake("Deref(Hash(<str>))", ((string) h3).c_str());
|
|
||||||
|
|
||||||
eval(e3);
|
|
||||||
|
|
||||||
deleteValue(h3);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue