mirror of
https://github.com/NixOS/nix.git
synced 2025-12-06 00:51:00 +01:00
* 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
7 changed files with 367 additions and 194 deletions
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;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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()))
|
||||
throw Error(format("cannot write aterm %1%") % path);
|
||||
Hash hash = hashPath(path);
|
||||
string path2 = canonPath(nixStore + "/" +
|
||||
(string) hash + suffix + ".nix");
|
||||
if (rename(path.c_str(), path2.c_str()) == -1)
|
||||
throw SysError(format("renaming %1% to %2%") % path % path2);
|
||||
registerPath(path2, hash);
|
||||
if (p) *p = path2;
|
||||
return hash;
|
||||
|
||||
registerPath(path, id);
|
||||
if (p) *p = path;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
string path;
|
||||
Hash fsHash = hashTerm(fs);
|
||||
Hash scHash = writeTerm(sc, "-s-" + (string) fsHash, &path);
|
||||
registerSuccessor(fsHash, scHash);
|
||||
paths.insert(path);
|
||||
FSId id2 = writeTerm(sc, "-s-" + (string) id1, 0);
|
||||
registerSuccessor(id1, id2);
|
||||
return id2;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
return ATmake("Include(<str>)", ((string) scHash).c_str());
|
||||
#endif
|
||||
return sc;
|
||||
}
|
||||
|
||||
|
||||
static FState realise(FState fs, StringSet & paths)
|
||||
{
|
||||
char * s1, * s2, * s3;
|
||||
|
|
@ -421,3 +413,193 @@ void fstateRefs(FState fs, StringSet & 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue