* Got Fix working again.

This commit is contained in:
Eelco Dolstra 2003-07-06 14:20:47 +00:00
parent f826e432aa
commit 82e3d8fafe
7 changed files with 298 additions and 23 deletions

View file

@ -1,4 +1,4 @@
bin_PROGRAMS = nix bin_PROGRAMS = nix fix
check_PROGRAMS = test check_PROGRAMS = test
AM_CXXFLAGS = -DSYSTEM=\"@host@\" -Wall -I.. AM_CXXFLAGS = -DSYSTEM=\"@host@\" -Wall -I..
@ -6,6 +6,9 @@ AM_CXXFLAGS = -DSYSTEM=\"@host@\" -Wall -I..
nix_SOURCES = nix.cc shared.cc nix_SOURCES = nix.cc shared.cc
nix_LDADD = libnix.a -ldb_cxx-4 -lATerm nix_LDADD = libnix.a -ldb_cxx-4 -lATerm
fix_SOURCES = fix.cc shared.cc
fix_LDADD = libnix.a -ldb_cxx-4 -lATerm
TESTS = test TESTS = test
test_SOURCES = test.cc test_SOURCES = test.cc

View file

@ -145,9 +145,7 @@ string printTerm(ATerm t)
} }
/* Throw an exception with an error message containing the given Error badTerm(const format & f, ATerm t)
aterm. */
static Error badTerm(const format & f, ATerm t)
{ {
return Error(format("%1%, in `%2%'") % f.str() % printTerm(t)); return Error(format("%1%, in `%2%'") % f.str() % printTerm(t));
} }
@ -176,7 +174,7 @@ static ATerm termFromHash(const Hash & hash)
} }
static Hash writeTerm(ATerm t) Hash writeTerm(ATerm t)
{ {
string path = nixStore + "/tmp.nix"; /* !!! */ string path = nixStore + "/tmp.nix"; /* !!! */
if (!ATwriteToNamedTextFile(t, path.c_str())) if (!ATwriteToNamedTextFile(t, path.c_str()))
@ -217,18 +215,19 @@ static FState realise(RStatus & status, FState fs)
return realise(status, termFromHash(parseHash(s1))); return realise(status, termFromHash(parseHash(s1)));
} }
else if (ATmatch(fs, "File(<str>, <term>, [<list>])", &s1, &content, &refs)) { else if (ATmatch(fs, "Path(<str>, <term>, [<list>])", &s1, &content, &refs)) {
string path(s1); string path(s1);
msg(format("realising atomic path %1%") % path); msg(format("realising atomic path %1%") % path);
Nest nest(true); Nest nest(true);
if (path[0] != '/') throw Error("absolute path expected: " + path); if (path[0] != '/')
throw Error(format("path `%1% is not absolute") % path);
/* Realise referenced paths. */ /* Realise referenced paths. */
ATermList refs2 = ATempty; ATermList refs2 = ATempty;
while (!ATisEmpty(refs)) { while (!ATisEmpty(refs)) {
refs2 = ATappend(refs2, realise(status, ATgetFirst(refs))); refs2 = ATinsert(refs2, realise(status, ATgetFirst(refs)));
refs = ATgetNext(refs); refs = ATgetNext(refs);
} }
refs2 = ATreverse(refs2); refs2 = ATreverse(refs2);
@ -238,7 +237,7 @@ static FState realise(RStatus & status, FState fs)
Hash hash = parseHash(s1); Hash hash = parseHash(s1);
/* Normal form. */ /* Normal form. */
ATerm nf = ATmake("File(<str>, <term>, <term>)", ATerm nf = ATmake("Path(<str>, <term>, <term>)",
path.c_str(), content, refs2); path.c_str(), content, refs2);
/* Register the normal form. */ /* Register the normal form. */
@ -261,7 +260,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 = queryFromStore(hash);
copyFile(path2, path); copyPath(path2, path);
return nf; return nf;
} }
@ -279,7 +278,7 @@ static FState realise(RStatus & status, FState fs)
/* Realise inputs. */ /* Realise inputs. */
ATermList ins2 = ATempty; ATermList ins2 = ATempty;
while (!ATisEmpty(ins)) { while (!ATisEmpty(ins)) {
ins2 = ATappend(ins2, realise(status, ATgetFirst(ins))); ins2 = ATinsert(ins2, realise(status, ATgetFirst(ins)));
ins = ATgetNext(ins); ins = ATgetNext(ins);
} }
ins2 = ATreverse(ins2); ins2 = ATreverse(ins2);
@ -289,7 +288,7 @@ static FState realise(RStatus & status, FState fs)
while (!ATisEmpty(bnds)) { while (!ATisEmpty(bnds)) {
ATerm bnd = ATgetFirst(bnds); ATerm bnd = ATgetFirst(bnds);
if (!ATmatch(bnd, "(<str>, <str>)", &s1, &s2)) if (!ATmatch(bnd, "(<str>, <str>)", &s1, &s2))
throw badTerm("string expected", bnd); throw badTerm("tuple of strings expected", bnd);
env[s1] = s2; env[s1] = s2;
bnds = ATgetNext(bnds); bnds = ATgetNext(bnds);
} }
@ -322,7 +321,7 @@ static FState realise(RStatus & status, FState fs)
setDB(nixDB, dbRefs, outHash, outPath); setDB(nixDB, dbRefs, outHash, outPath);
/* Register the normal form of fs. */ /* Register the normal form of fs. */
FState nf = ATmake("File(<str>, Hash(<str>), <term>)", FState nf = ATmake("Path(<str>, Hash(<str>), <term>)",
outPath.c_str(), ((string) outHash).c_str(), ins2); outPath.c_str(), ((string) outHash).c_str(), ins2);
Hash nfHash = writeTerm(nf); Hash nfHash = writeTerm(nf);
setDB(nixDB, dbSuccessors, hashTerm(fs), nfHash); setDB(nixDB, dbSuccessors, hashTerm(fs), nfHash);

View file

@ -15,9 +15,9 @@ 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.
File : Path * Content * [FState] -> FState Path : Path * Content * [FState] -> FState
File(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
@ -66,8 +66,15 @@ FState realiseFState(FState fs);
/* Return a canonical textual representation of an expression. */ /* Return a canonical textual representation of an expression. */
string printTerm(ATerm t); string printTerm(ATerm t);
/* Throw an exception with an error message containing the given
aterm. */
Error badTerm(const format & f, ATerm t);
/* Hash an aterm. */ /* Hash an aterm. */
Hash hashTerm(ATerm t); Hash hashTerm(ATerm t);
/* Write an aterm to the Nix store directory, and return its hash. */
Hash writeTerm(ATerm t);
#endif /* !__EVAL_H */ #endif /* !__EVAL_H */

266
src/fix.cc Normal file
View file

@ -0,0 +1,266 @@
#include <map>
#include <iostream>
#include "globals.hh"
#include "eval.hh"
#include "values.hh"
#include "shared.hh"
typedef ATerm Expr;
static Expr evalFile(string fileName);
static bool isFState(Expr e, string & path)
{
char * s1, * s2, * s3;
Expr e1, e2;
if (ATmatch(e, "Path(<str>, <term>, [<list>])", &s1, &e1, &e2)) {
path = s1;
return true;
}
else if (ATmatch(e, "Derive(<str>, <str>, [<list>], <str>, [<list>])",
&s1, &s2, &e1, &s3, &e2))
{
path = s3;
return true;
}
else if (ATmatch(e, "Include(<str>)", &s1))
{
string fn = queryFromStore(parseHash(s1));
return isFState(evalFile(fn), path);
}
else return false;
}
static Expr substExpr(string x, Expr rep, Expr e)
{
char * s;
Expr e2;
if (ATmatch(e, "Var(<str>)", &s))
if (x == s)
return rep;
else
return e;
if (ATmatch(e, "Lam(<str>, <term>)", &s, &e2))
if (x == s)
return e;
/* !!! unfair substitutions */
/* Generically substitute in subterms. */
if (ATgetType(e) == AT_APPL) {
AFun fun = ATgetAFun(e);
int arity = ATgetArity(fun);
ATermList args = ATempty;
for (int i = arity - 1; i >= 0; i--)
args = ATinsert(args, substExpr(x, rep, ATgetArgument(e, i)));
return (ATerm) ATmakeApplList(fun, args);
}
if (ATgetType(e) == AT_LIST) {
ATermList in = (ATermList) e;
ATermList out = ATempty;
while (!ATisEmpty(in)) {
out = ATinsert(out, substExpr(x, rep, ATgetFirst(in)));
in = ATgetNext(in);
}
return (ATerm) ATreverse(out);
}
throw badTerm("do not know how to substitute", e);
}
static Expr substExprMany(ATermList formals, ATermList args, Expr body)
{
char * s;
Expr e;
/* !!! check args against formals */
while (!ATisEmpty(args)) {
ATerm tup = ATgetFirst(args);
if (!ATmatch(tup, "(<str>, <term>)", &s, &e))
throw badTerm("expected an argument tuple", tup);
body = substExpr(s, e, body);
args = ATgetNext(args);
}
return body;
}
static Expr evalExpr(Expr e)
{
char * s1;
Expr e1, e2, e3, e4;
ATermList bnds;
/* Normal forms. */
if (ATmatch(e, "<str>", &s1) ||
ATmatch(e, "Function([<list>], <term>)", &e1, &e2))
return e;
string dummy;
if (isFState(e, dummy)) return e;
/* Application. */
if (ATmatch(e, "App(<term>, [<list>])", &e1, &e2)) {
e1 = evalExpr(e1);
if (!ATmatch(e1, "Function([<list>], <term>)", &e3, &e4))
throw badTerm("expecting a function", e1);
return evalExpr(substExprMany((ATermList) e3, (ATermList) e2, e4));
}
/* Fix inclusion. */
if (ATmatch(e, "IncludeFix(<str>)", &s1)) {
string fileName(s1);
return evalFile(s1);
}
/* Relative files. */
if (ATmatch(e, "Relative(<str>)", &s1)) {
string srcPath = s1;
string dstPath;
Hash hash;
addToStore(srcPath, dstPath, hash);
return ATmake("Path(<str>, Hash(<str>), [])",
dstPath.c_str(), ((string) hash).c_str());
}
/* Packages are transformed into Derive fstate expressions. */
if (ATmatch(e, "Package([<list>])", &bnds)) {
/* Evaluate the bindings and put them in a map. */
map<string, ATerm> bndMap;
bndMap["platform"] = ATmake("<str>", SYSTEM);
while (!ATisEmpty(bnds)) {
ATerm bnd = ATgetFirst(bnds);
if (!ATmatch(bnd, "(<str>, <term>)", &s1, &e1))
throw badTerm("binding expected", bnd);
bndMap[s1] = evalExpr(e1);
bnds = ATgetNext(bnds);
}
/* Gather information for building the Derive expression. */
ATermList ins = ATempty, env = ATempty;
string builder, id;
bnds = ATempty;
for (map<string, ATerm>::iterator it = bndMap.begin();
it != bndMap.end(); it++)
{
string key = it->first;
ATerm value = it->second;
string path;
if (isFState(value, path)) {
ins = ATinsert(ins, value);
env = ATinsert(env, ATmake("(<str>, <str>)",
key.c_str(), path.c_str()));
if (key == "build") builder = path;
}
else if (ATmatch(value, "<str>", &s1)) {
if (key == "id") id = s1;
env = ATinsert(env,
ATmake("(<str>, <str>)", key.c_str(), s1));
}
else throw badTerm("invalid package argument", value);
bnds = ATinsert(bnds,
ATmake("(<str>, <term>)", key.c_str(), value));
}
/* Hash the normal form to produce a unique but deterministic
path name for this package. */
ATerm nf = ATmake("Package(<term>)", ATreverse(bnds));
debug(printTerm(nf));
Hash hash = hashTerm(nf);
if (builder == "")
throw badTerm("no builder specified", nf);
if (id == "")
throw badTerm("no package identifier specified", nf);
string out = nixStore + "/" + ((string) hash).c_str() + "-" + id;
env = ATinsert(env, ATmake("(<str>, <str>)", "out", out.c_str()));
/* Construct the result. */
e = ATmake("Derive(<str>, <str>, <term>, <str>, <term>)",
SYSTEM, builder.c_str(), ins, out.c_str(), env);
debug(printTerm(e));
/* Write the resulting term into the Nix store directory. */
Hash eHash = writeTerm(e);
return ATmake("Include(<str>)", ((string) eHash).c_str());
}
/* Barf. */
throw badTerm("invalid expression", e);
}
static Strings searchPath;
static Expr evalFile(string fileName)
{
Expr e = ATreadFromNamedFile(fileName.c_str());
if (!e) throw Error(format("cannot read aterm `%1%'") % fileName);
return evalExpr(e);
}
void run(Strings args)
{
Strings files;
searchPath.push_back(".");
for (Strings::iterator it = args.begin();
it != args.end(); )
{
string arg = *it++;
if (arg == "--includedir" || arg == "-I") {
if (it == args.end())
throw UsageError(format("argument required in `%1%'") % arg);
searchPath.push_back(*it++);
}
else if (arg[0] == '-')
throw UsageError(format("unknown flag `%1%`") % arg);
else
files.push_back(arg);
}
if (files.empty()) throw UsageError("no files specified");
for (Strings::iterator it = files.begin();
it != files.end(); it++)
{
Expr e = evalFile(*it);
char * s;
if (ATmatch(e, "Include(<str>)", &s)) {
cout << format("%1%\n") % s;
}
else throw badTerm("top level is not a package", e);
}
}
string programId = "fix";

View file

@ -120,17 +120,17 @@ void runTests()
ATmake("Foo(123)")); ATmake("Foo(123)"));
string builder1fn = absPath("./test-builder-1.sh"); string builder1fn = absPath("./test-builder-1.sh");
Hash builder1h = hashFile(builder1fn); Hash builder1h = hashPath(builder1fn);
string fn1 = nixValues + "/builder-1.sh"; string fn1 = nixValues + "/builder-1.sh";
Expr e1 = ATmake("File(<str>, ExtFile(<str>, <str>), [])", Expr e1 = ATmake("Path(<str>, ExtFile(<str>, <str>), [])",
fn1.c_str(), fn1.c_str(),
builder1h.c_str(), builder1h.c_str(),
builder1fn.c_str()); builder1fn.c_str());
eval(fNormalise, e1); eval(fNormalise, e1);
string fn2 = nixValues + "/refer.txt"; string fn2 = nixValues + "/refer.txt";
Expr e2 = ATmake("File(<str>, Regular(<str>), [<term>])", Expr e2 = ATmake("Path(<str>, Regular(<str>), [<term>])",
fn2.c_str(), fn2.c_str(),
("I refer to " + fn1).c_str(), ("I refer to " + fn1).c_str(),
e1); e1);
@ -144,14 +144,14 @@ void runTests()
addToStore("./test-builder-1.sh", builder1fn, builder1h); addToStore("./test-builder-1.sh", builder1fn, builder1h);
FState fs1 = ATmake( FState fs1 = ATmake(
"File(<str>, Hash(<str>), [])", "Path(<str>, Hash(<str>), [])",
builder1fn.c_str(), builder1fn.c_str(),
((string) builder1h).c_str()); ((string) builder1h).c_str());
realise(fs1); realise(fs1);
realise(fs1); realise(fs1);
FState fs2 = ATmake( FState fs2 = ATmake(
"File(<str>, Hash(<str>), [])", "Path(<str>, Hash(<str>), [])",
(builder1fn + "_bla").c_str(), (builder1fn + "_bla").c_str(),
((string) builder1h).c_str()); ((string) builder1h).c_str());
realise(fs2); realise(fs2);

View file

@ -34,7 +34,7 @@ struct CopySource : RestoreSource
}; };
void copyFile(string src, string dst) void copyPath(string src, string dst)
{ {
/* Unfortunately C++ doesn't support coprocedures, so we have no /* Unfortunately C++ doesn't support coprocedures, so we have no
nice way to chain CopySink and CopySource together. Instead we nice way to chain CopySink and CopySource together. Instead we
@ -99,7 +99,7 @@ void addToStore(string srcPath, string & dstPath, Hash & hash)
string baseName = baseNameOf(srcPath); string baseName = baseNameOf(srcPath);
dstPath = nixStore + "/" + (string) hash + "-" + baseName; dstPath = nixStore + "/" + (string) hash + "-" + baseName;
copyFile(srcPath, dstPath); copyPath(srcPath, dstPath);
setDB(nixDB, dbRefs, hash, dstPath); setDB(nixDB, dbRefs, hash, dstPath);
} }

View file

@ -8,7 +8,7 @@
using namespace std; using namespace std;
void copyFile(string src, string dst); void copyPath(string src, string dst);
/* 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. */