From 224c585aba5e450fa47e41c4cc19dac2d0c6fe2a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 7 Jul 2003 07:43:58 +0000 Subject: * Refactoring on the file names. --- src/Makefile.am | 2 +- src/eval.cc | 340 -------------------------------------------------------- src/eval.hh | 80 ------------- src/fix.cc | 4 +- src/fstate.cc | 340 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/fstate.hh | 80 +++++++++++++ src/nix.cc | 4 +- src/store.cc | 132 ++++++++++++++++++++++ src/store.hh | 24 ++++ src/values.cc | 175 ----------------------------- src/values.hh | 24 ---- 11 files changed, 581 insertions(+), 624 deletions(-) delete mode 100644 src/eval.cc delete mode 100644 src/eval.hh create mode 100644 src/fstate.cc create mode 100644 src/fstate.hh create mode 100644 src/store.cc create mode 100644 src/store.hh delete mode 100644 src/values.cc delete mode 100644 src/values.hh diff --git a/src/Makefile.am b/src/Makefile.am index 22b27d88a8a7..5488e133a142 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,7 +20,7 @@ test_LDADD = libnix.a -ldb_cxx-4 -lATerm noinst_LIBRARIES = libnix.a libnix_a_SOURCES = util.cc hash.cc archive.cc md5.c \ - eval.cc values.cc globals.cc db.cc + fstate.cc store.cc globals.cc db.cc install-data-local: $(INSTALL) -d $(localstatedir)/nix diff --git a/src/eval.cc b/src/eval.cc deleted file mode 100644 index 354ecb6cc7fa..000000000000 --- a/src/eval.cc +++ /dev/null @@ -1,340 +0,0 @@ -#include -#include - -#include -#include -#include -#include -#include - -#include "eval.hh" -#include "globals.hh" -#include "values.hh" -#include "db.hh" - - -/* A Unix environment is a mapping from strings to strings. */ -typedef map Environment; - - -/* Return true iff the given path exists. */ -bool pathExists(const string & path) -{ - int res; - struct stat st; - res = stat(path.c_str(), &st); - if (!res) return true; - if (errno != ENOENT) - throw SysError(format("getting status of %1%") % path); - return false; -} - - -/* Run a program. */ -static void runProgram(const string & program, Environment env) -{ - /* Create a log file. */ - string logFileName = nixLogDir + "/run.log"; - /* !!! auto-pclose on exit */ - FILE * logFile = popen(("tee " + logFileName + " >&2").c_str(), "w"); /* !!! escaping */ - if (!logFile) - throw SysError(format("unable to create log file %1%") % logFileName); - - /* Fork a child to build the package. */ - pid_t pid; - switch (pid = fork()) { - - case -1: - throw SysError("unable to fork"); - - case 0: - - try { /* child */ - -#if 0 - /* Try to use a prebuilt. */ - string prebuiltHashS, prebuiltFile; - if (queryDB(nixDB, dbPrebuilts, hash, prebuiltHashS)) { - - try { - prebuiltFile = getFile(parseHash(prebuiltHashS)); - } catch (Error e) { - cerr << "cannot obtain prebuilt (ignoring): " << e.what() << endl; - goto build; - } - - cerr << "substituting prebuilt " << prebuiltFile << endl; - - int res = system(("tar xfj " + prebuiltFile + " 1>&2").c_str()); // !!! escaping - if (WEXITSTATUS(res) != 0) - /* This is a fatal error, because path may now - have clobbered. */ - throw Error("cannot unpack " + prebuiltFile); - - _exit(0); - } -#endif - - // build: - - /* Fill in the environment. We don't bother freeing - the strings, since we'll exec or die soon - anyway. */ - const char * env2[env.size() + 1]; - int i = 0; - for (Environment::iterator it = env.begin(); - it != env.end(); it++, i++) - env2[i] = (new string(it->first + "=" + it->second))->c_str(); - env2[i] = 0; - - /* Dup the log handle into stderr. */ - if (dup2(fileno(logFile), STDERR_FILENO) == -1) - throw SysError("cannot pipe standard error into log file"); - - /* Dup stderr to stdin. */ - if (dup2(STDERR_FILENO, STDOUT_FILENO) == -1) - throw SysError("cannot dup stderr into stdout"); - - /* Make the program executable. !!! hack. */ - if (chmod(program.c_str(), 0755)) - throw SysError("cannot make program executable"); - - /* Execute the program. This should not return. */ - execle(program.c_str(), baseNameOf(program).c_str(), 0, env2); - - throw SysError(format("unable to execute %1%") % program); - - } catch (exception & e) { - cerr << format("build error: %1%\n") % e.what(); - } - _exit(1); - - } - - /* parent */ - - /* Close the logging pipe. Note that this should not cause - the logger to exit until builder exits (because the latter - has an open file handle to the former). */ - pclose(logFile); - - /* Wait for the child to finish. */ - int status; - if (waitpid(pid, &status, 0) != pid) - throw Error("unable to wait for child"); - - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - throw Error("unable to build package"); -} - - -/* Throw an exception if the given platform string is not supported by - the platform we are executing on. */ -static void checkPlatform(const string & platform) -{ - if (platform != thisSystem) - throw Error(format("a `%1%' is required, but I am a `%2%'") - % platform % thisSystem); -} - - -string printTerm(ATerm t) -{ - char * s = ATwriteToString(t); - return s; -} - - -Error badTerm(const format & f, ATerm t) -{ - return Error(format("%1%, in `%2%'") % f.str() % printTerm(t)); -} - - -Hash hashTerm(ATerm t) -{ - return hashString(printTerm(t)); -} - - -struct RStatus -{ - /* !!! the comparator of this hash should match the semantics of - the file system */ -// map paths; -}; - - -static ATerm termFromHash(const Hash & hash) -{ - string path = queryFromStore(hash); - ATerm t = ATreadFromNamedFile(path.c_str()); - if (!t) throw Error(format("cannot read aterm %1%") % path); - return t; -} - - -Hash writeTerm(ATerm t) -{ - string path = nixStore + "/tmp.nix"; /* !!! */ - if (!ATwriteToNamedTextFile(t, path.c_str())) - throw Error(format("cannot write aterm %1%") % path); - Hash hash = hashPath(path); - string path2 = nixStore + "/" + (string) hash + ".nix"; - if (rename(path.c_str(), path2.c_str()) == -1) - throw SysError(format("renaming %1% to %2%") % path % path2); - setDB(nixDB, dbRefs, hash, path2); - return hash; -} - - -static FState realise(RStatus & status, FState fs) -{ - char * s1, * s2, * s3; - Content content; - ATermList refs, ins, bnds; - - /* First repeatedly try to substitute $fs$ by any known successors - in order to speed up the rewrite process. */ - { - string fsHash, scHash; - while (queryDB(nixDB, dbSuccessors, fsHash = hashTerm(fs), scHash)) { - debug(format("successor %1% -> %2%") % (string) fsHash % scHash); - FState fs2 = termFromHash(parseHash(scHash)); - if (fs == fs2) { - debug(format("successor cycle detected in %1%") % printTerm(fs)); - break; - } - fs = fs2; - } - } - - /* Fall through. */ - - if (ATmatch(fs, "Include()", &s1)) { - return realise(status, termFromHash(parseHash(s1))); - } - - else if (ATmatch(fs, "Path(, , [])", &s1, &content, &refs)) { - string path(s1); - - msg(format("realising atomic path %1%") % path); - Nest nest(true); - - if (path[0] != '/') - throw Error(format("path `%1% is not absolute") % path); - - /* Realise referenced paths. */ - ATermList refs2 = ATempty; - while (!ATisEmpty(refs)) { - refs2 = ATinsert(refs2, realise(status, ATgetFirst(refs))); - refs = ATgetNext(refs); - } - refs2 = ATreverse(refs2); - - if (!ATmatch(content, "Hash()", &s1)) - throw badTerm("hash expected", content); - Hash hash = parseHash(s1); - - /* Normal form. */ - ATerm nf = ATmake("Path(, , )", - path.c_str(), content, refs2); - - /* Register the normal form. */ - if (fs != nf) { - Hash nfHash = writeTerm(nf); - setDB(nixDB, dbSuccessors, hashTerm(fs), nfHash); - } - - /* Perhaps the path already exists and has the right hash? */ - if (pathExists(path)) { - if (hash == hashPath(path)) { - debug(format("path %1% already has hash %2%") - % path % (string) hash); - return nf; - } - - throw Error(format("path %1% exists, but does not have hash %2%") - % path % (string) hash); - } - - /* Do we know a path with that hash? If so, copy it. */ - string path2 = queryFromStore(hash); - copyPath(path2, path); - - return nf; - } - - else if (ATmatch(fs, "Derive(, , [], , [])", - &s1, &s2, &ins, &s3, &bnds)) - { - string platform(s1), builder(s2), outPath(s3); - - msg(format("realising derivate path %1%") % outPath); - Nest nest(true); - - checkPlatform(platform); - - /* Realise inputs. */ - ATermList ins2 = ATempty; - while (!ATisEmpty(ins)) { - ins2 = ATinsert(ins2, realise(status, ATgetFirst(ins))); - ins = ATgetNext(ins); - } - ins2 = ATreverse(ins2); - - /* Build the environment. */ - Environment env; - while (!ATisEmpty(bnds)) { - ATerm bnd = ATgetFirst(bnds); - if (!ATmatch(bnd, "(, )", &s1, &s2)) - throw badTerm("tuple of strings expected", bnd); - env[s1] = s2; - bnds = ATgetNext(bnds); - } - - /* Check whether the target already exists. */ - if (pathExists(outPath)) - deleteFromStore(outPath); -// throw Error(format("path %1% already exists") % outPath); - - /* Run the builder. */ - runProgram(builder, env); - - /* Check whether the result was created. */ - if (!pathExists(outPath)) - throw Error(format("program %1% failed to create a result in %2%") - % builder % outPath); - -#if 0 - /* Remove write permission from the value. */ - int res = system(("chmod -R -w " + targetPath).c_str()); // !!! escaping - if (WEXITSTATUS(res) != 0) - throw Error("cannot remove write permission from " + targetPath); -#endif - - /* Hash the result. */ - Hash outHash = hashPath(outPath); - - /* Register targetHash -> targetPath. !!! this should be in - values.cc. */ - setDB(nixDB, dbRefs, outHash, outPath); - - /* Register the normal form of fs. */ - FState nf = ATmake("Path(, Hash(), )", - outPath.c_str(), ((string) outHash).c_str(), ins2); - Hash nfHash = writeTerm(nf); - setDB(nixDB, dbSuccessors, hashTerm(fs), nfHash); - - return nf; - } - - throw badTerm("bad fstate expression", fs); -} - - -FState realiseFState(FState fs) -{ - RStatus status; - return realise(status, fs); -} diff --git a/src/eval.hh b/src/eval.hh deleted file mode 100644 index b04588e7b35b..000000000000 --- a/src/eval.hh +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef __EVAL_H -#define __EVAL_H - -extern "C" { -#include -} - -#include "hash.hh" - -using namespace std; - - -/* \section{Abstract syntax of Nix file system state expressions} - - A Nix file system state expression, or FState, describes a - (partial) state of the file system. - - Path : Path * Content * [FState] -> FState - - Path(path, content, refs) specifies a file object (its full path - and contents), along with all file objects referenced by it (that - is, that it has pointers to). We assume that all files are - self-referential. This prevents us from having to deal with - cycles. - - Derive : String * Path * [FState] * Path * [(String, String)] -> FState - - Derive(platform, builder, ins, outs, env) specifies the creation of - new file objects (in paths declared by `outs') by the execution of - a program `builder' on a platform `platform'. This execution takes - place in a file system state given by `ins'. `env' specifies a - 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 - have been reduced to File nodes. - - DISCUSSION: the idea is that a Regular/Directory is interchangeable - with its CHash. This would appear to break referential - transparency, e.g., Derive(..., ..., [...CHash(h)...], ...) can - only be reduced in a context were the Regular/Directory equivalent - of Hash(h) is known. However, CHash should be viewed strictly as a - shorthand; that is, when we export an expression containing a - CHash, we should also export the file object referenced by that - CHash. - -*/ - -typedef ATerm FState; -typedef ATerm Content; - - -/* Realise a $f$-normalised expression in the file system. */ -FState realiseFState(FState fs); - -/* Return a canonical textual representation of an expression. */ -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 hashTerm(ATerm t); - -/* Write an aterm to the Nix store directory, and return its hash. */ -Hash writeTerm(ATerm t); - - -#endif /* !__EVAL_H */ diff --git a/src/fix.cc b/src/fix.cc index 33bcdd9a1372..fdf12ffef5fe 100644 --- a/src/fix.cc +++ b/src/fix.cc @@ -2,8 +2,8 @@ #include #include "globals.hh" -#include "eval.hh" -#include "values.hh" +#include "fstate.hh" +#include "store.hh" #include "shared.hh" diff --git a/src/fstate.cc b/src/fstate.cc new file mode 100644 index 000000000000..8003a1b38105 --- /dev/null +++ b/src/fstate.cc @@ -0,0 +1,340 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include "fstate.hh" +#include "globals.hh" +#include "store.hh" +#include "db.hh" + + +/* A Unix environment is a mapping from strings to strings. */ +typedef map Environment; + + +/* Return true iff the given path exists. */ +bool pathExists(const string & path) +{ + int res; + struct stat st; + res = stat(path.c_str(), &st); + if (!res) return true; + if (errno != ENOENT) + throw SysError(format("getting status of %1%") % path); + return false; +} + + +/* Run a program. */ +static void runProgram(const string & program, Environment env) +{ + /* Create a log file. */ + string logFileName = nixLogDir + "/run.log"; + /* !!! auto-pclose on exit */ + FILE * logFile = popen(("tee " + logFileName + " >&2").c_str(), "w"); /* !!! escaping */ + if (!logFile) + throw SysError(format("unable to create log file %1%") % logFileName); + + /* Fork a child to build the package. */ + pid_t pid; + switch (pid = fork()) { + + case -1: + throw SysError("unable to fork"); + + case 0: + + try { /* child */ + +#if 0 + /* Try to use a prebuilt. */ + string prebuiltHashS, prebuiltFile; + if (queryDB(nixDB, dbPrebuilts, hash, prebuiltHashS)) { + + try { + prebuiltFile = getFile(parseHash(prebuiltHashS)); + } catch (Error e) { + cerr << "cannot obtain prebuilt (ignoring): " << e.what() << endl; + goto build; + } + + cerr << "substituting prebuilt " << prebuiltFile << endl; + + int res = system(("tar xfj " + prebuiltFile + " 1>&2").c_str()); // !!! escaping + if (WEXITSTATUS(res) != 0) + /* This is a fatal error, because path may now + have clobbered. */ + throw Error("cannot unpack " + prebuiltFile); + + _exit(0); + } +#endif + + // build: + + /* Fill in the environment. We don't bother freeing + the strings, since we'll exec or die soon + anyway. */ + const char * env2[env.size() + 1]; + int i = 0; + for (Environment::iterator it = env.begin(); + it != env.end(); it++, i++) + env2[i] = (new string(it->first + "=" + it->second))->c_str(); + env2[i] = 0; + + /* Dup the log handle into stderr. */ + if (dup2(fileno(logFile), STDERR_FILENO) == -1) + throw SysError("cannot pipe standard error into log file"); + + /* Dup stderr to stdin. */ + if (dup2(STDERR_FILENO, STDOUT_FILENO) == -1) + throw SysError("cannot dup stderr into stdout"); + + /* Make the program executable. !!! hack. */ + if (chmod(program.c_str(), 0755)) + throw SysError("cannot make program executable"); + + /* Execute the program. This should not return. */ + execle(program.c_str(), baseNameOf(program).c_str(), 0, env2); + + throw SysError(format("unable to execute %1%") % program); + + } catch (exception & e) { + cerr << format("build error: %1%\n") % e.what(); + } + _exit(1); + + } + + /* parent */ + + /* Close the logging pipe. Note that this should not cause + the logger to exit until builder exits (because the latter + has an open file handle to the former). */ + pclose(logFile); + + /* Wait for the child to finish. */ + int status; + if (waitpid(pid, &status, 0) != pid) + throw Error("unable to wait for child"); + + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + throw Error("unable to build package"); +} + + +/* Throw an exception if the given platform string is not supported by + the platform we are executing on. */ +static void checkPlatform(const string & platform) +{ + if (platform != thisSystem) + throw Error(format("a `%1%' is required, but I am a `%2%'") + % platform % thisSystem); +} + + +string printTerm(ATerm t) +{ + char * s = ATwriteToString(t); + return s; +} + + +Error badTerm(const format & f, ATerm t) +{ + return Error(format("%1%, in `%2%'") % f.str() % printTerm(t)); +} + + +Hash hashTerm(ATerm t) +{ + return hashString(printTerm(t)); +} + + +struct RStatus +{ + /* !!! the comparator of this hash should match the semantics of + the file system */ +// map paths; +}; + + +static ATerm termFromHash(const Hash & hash) +{ + string path = queryFromStore(hash); + ATerm t = ATreadFromNamedFile(path.c_str()); + if (!t) throw Error(format("cannot read aterm %1%") % path); + return t; +} + + +Hash writeTerm(ATerm t) +{ + string path = nixStore + "/tmp.nix"; /* !!! */ + if (!ATwriteToNamedTextFile(t, path.c_str())) + throw Error(format("cannot write aterm %1%") % path); + Hash hash = hashPath(path); + string path2 = nixStore + "/" + (string) hash + ".nix"; + if (rename(path.c_str(), path2.c_str()) == -1) + throw SysError(format("renaming %1% to %2%") % path % path2); + setDB(nixDB, dbRefs, hash, path2); + return hash; +} + + +static FState realise(RStatus & status, FState fs) +{ + char * s1, * s2, * s3; + Content content; + ATermList refs, ins, bnds; + + /* First repeatedly try to substitute $fs$ by any known successors + in order to speed up the rewrite process. */ + { + string fsHash, scHash; + while (queryDB(nixDB, dbSuccessors, fsHash = hashTerm(fs), scHash)) { + debug(format("successor %1% -> %2%") % (string) fsHash % scHash); + FState fs2 = termFromHash(parseHash(scHash)); + if (fs == fs2) { + debug(format("successor cycle detected in %1%") % printTerm(fs)); + break; + } + fs = fs2; + } + } + + /* Fall through. */ + + if (ATmatch(fs, "Include()", &s1)) { + return realise(status, termFromHash(parseHash(s1))); + } + + else if (ATmatch(fs, "Path(, , [])", &s1, &content, &refs)) { + string path(s1); + + msg(format("realising atomic path %1%") % path); + Nest nest(true); + + if (path[0] != '/') + throw Error(format("path `%1% is not absolute") % path); + + /* Realise referenced paths. */ + ATermList refs2 = ATempty; + while (!ATisEmpty(refs)) { + refs2 = ATinsert(refs2, realise(status, ATgetFirst(refs))); + refs = ATgetNext(refs); + } + refs2 = ATreverse(refs2); + + if (!ATmatch(content, "Hash()", &s1)) + throw badTerm("hash expected", content); + Hash hash = parseHash(s1); + + /* Normal form. */ + ATerm nf = ATmake("Path(, , )", + path.c_str(), content, refs2); + + /* Register the normal form. */ + if (fs != nf) { + Hash nfHash = writeTerm(nf); + setDB(nixDB, dbSuccessors, hashTerm(fs), nfHash); + } + + /* Perhaps the path already exists and has the right hash? */ + if (pathExists(path)) { + if (hash == hashPath(path)) { + debug(format("path %1% already has hash %2%") + % path % (string) hash); + return nf; + } + + throw Error(format("path %1% exists, but does not have hash %2%") + % path % (string) hash); + } + + /* Do we know a path with that hash? If so, copy it. */ + string path2 = queryFromStore(hash); + copyPath(path2, path); + + return nf; + } + + else if (ATmatch(fs, "Derive(, , [], , [])", + &s1, &s2, &ins, &s3, &bnds)) + { + string platform(s1), builder(s2), outPath(s3); + + msg(format("realising derivate path %1%") % outPath); + Nest nest(true); + + checkPlatform(platform); + + /* Realise inputs. */ + ATermList ins2 = ATempty; + while (!ATisEmpty(ins)) { + ins2 = ATinsert(ins2, realise(status, ATgetFirst(ins))); + ins = ATgetNext(ins); + } + ins2 = ATreverse(ins2); + + /* Build the environment. */ + Environment env; + while (!ATisEmpty(bnds)) { + ATerm bnd = ATgetFirst(bnds); + if (!ATmatch(bnd, "(, )", &s1, &s2)) + throw badTerm("tuple of strings expected", bnd); + env[s1] = s2; + bnds = ATgetNext(bnds); + } + + /* Check whether the target already exists. */ + if (pathExists(outPath)) + deleteFromStore(outPath); +// throw Error(format("path %1% already exists") % outPath); + + /* Run the builder. */ + runProgram(builder, env); + + /* Check whether the result was created. */ + if (!pathExists(outPath)) + throw Error(format("program %1% failed to create a result in %2%") + % builder % outPath); + +#if 0 + /* Remove write permission from the value. */ + int res = system(("chmod -R -w " + targetPath).c_str()); // !!! escaping + if (WEXITSTATUS(res) != 0) + throw Error("cannot remove write permission from " + targetPath); +#endif + + /* Hash the result. */ + Hash outHash = hashPath(outPath); + + /* Register targetHash -> targetPath. !!! this should be in + values.cc. */ + setDB(nixDB, dbRefs, outHash, outPath); + + /* Register the normal form of fs. */ + FState nf = ATmake("Path(, Hash(), )", + outPath.c_str(), ((string) outHash).c_str(), ins2); + Hash nfHash = writeTerm(nf); + setDB(nixDB, dbSuccessors, hashTerm(fs), nfHash); + + return nf; + } + + throw badTerm("bad fstate expression", fs); +} + + +FState realiseFState(FState fs) +{ + RStatus status; + return realise(status, fs); +} diff --git a/src/fstate.hh b/src/fstate.hh new file mode 100644 index 000000000000..b04588e7b35b --- /dev/null +++ b/src/fstate.hh @@ -0,0 +1,80 @@ +#ifndef __EVAL_H +#define __EVAL_H + +extern "C" { +#include +} + +#include "hash.hh" + +using namespace std; + + +/* \section{Abstract syntax of Nix file system state expressions} + + A Nix file system state expression, or FState, describes a + (partial) state of the file system. + + Path : Path * Content * [FState] -> FState + + Path(path, content, refs) specifies a file object (its full path + and contents), along with all file objects referenced by it (that + is, that it has pointers to). We assume that all files are + self-referential. This prevents us from having to deal with + cycles. + + Derive : String * Path * [FState] * Path * [(String, String)] -> FState + + Derive(platform, builder, ins, outs, env) specifies the creation of + new file objects (in paths declared by `outs') by the execution of + a program `builder' on a platform `platform'. This execution takes + place in a file system state given by `ins'. `env' specifies a + 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 + have been reduced to File nodes. + + DISCUSSION: the idea is that a Regular/Directory is interchangeable + with its CHash. This would appear to break referential + transparency, e.g., Derive(..., ..., [...CHash(h)...], ...) can + only be reduced in a context were the Regular/Directory equivalent + of Hash(h) is known. However, CHash should be viewed strictly as a + shorthand; that is, when we export an expression containing a + CHash, we should also export the file object referenced by that + CHash. + +*/ + +typedef ATerm FState; +typedef ATerm Content; + + +/* Realise a $f$-normalised expression in the file system. */ +FState realiseFState(FState fs); + +/* Return a canonical textual representation of an expression. */ +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 hashTerm(ATerm t); + +/* Write an aterm to the Nix store directory, and return its hash. */ +Hash writeTerm(ATerm t); + + +#endif /* !__EVAL_H */ diff --git a/src/nix.cc b/src/nix.cc index f2d00394f9d7..dc9d04148d14 100644 --- a/src/nix.cc +++ b/src/nix.cc @@ -1,8 +1,8 @@ #include #include "globals.hh" -#include "values.hh" -#include "eval.hh" +#include "store.hh" +#include "fstate.hh" #include "archive.hh" #include "shared.hh" diff --git a/src/store.cc b/src/store.cc new file mode 100644 index 000000000000..68a1cd1e2879 --- /dev/null +++ b/src/store.cc @@ -0,0 +1,132 @@ +#include + +#include +#include + +#include "store.hh" +#include "globals.hh" +#include "db.hh" +#include "archive.hh" + + +struct CopySink : DumpSink +{ + int fd; + virtual void operator () (const unsigned char * data, unsigned int len) + { + if (write(fd, (char *) data, len) != (ssize_t) len) + throw SysError("writing to child"); + } +}; + + +struct CopySource : RestoreSource +{ + int fd; + virtual void operator () (const unsigned char * data, unsigned int len) + { + ssize_t res = read(fd, (char *) data, len); + if (res == -1) + throw SysError("reading from parent"); + if (res != (ssize_t) len) + throw Error("not enough data available on parent"); + } +}; + + +void copyPath(string src, string dst) +{ + /* Unfortunately C++ doesn't support coprocedures, so we have no + nice way to chain CopySink and CopySource together. Instead we + fork off a child to run the sink. (Fork-less platforms should + use a thread). */ + + /* Create a pipe. */ + int fds[2]; + if (pipe(fds) == -1) throw SysError("creating pipe"); + + /* Fork. */ + pid_t pid; + switch (pid = fork()) { + + case -1: + throw SysError("unable to fork"); + + case 0: /* child */ + try { + close(fds[1]); + CopySource source; + source.fd = fds[0]; + restorePath(dst, source); + _exit(0); + } catch (exception & e) { + cerr << "error: " << e.what() << endl; + } + _exit(1); + } + + close(fds[0]); + + /* Parent. */ + + CopySink sink; + sink.fd = fds[1]; + dumpPath(src, sink); + + /* Wait for the child to finish. */ + int status; + if (waitpid(pid, &status, 0) != pid) + throw SysError("waiting for child"); + + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + throw Error("cannot copy file: child died"); +} + + +void addToStore(string srcPath, string & dstPath, Hash & hash) +{ + srcPath = absPath(srcPath); + + hash = hashPath(srcPath); + + string path; + if (queryDB(nixDB, dbRefs, hash, path)) { + debug((string) hash + " already known"); + dstPath = path; + return; + } + + string baseName = baseNameOf(srcPath); + dstPath = nixStore + "/" + (string) hash + "-" + baseName; + + copyPath(srcPath, dstPath); + + setDB(nixDB, dbRefs, hash, dstPath); +} + + +void deleteFromStore(const string & path) +{ + string prefix = nixStore + "/"; + if (string(path, 0, prefix.size()) != prefix) + throw Error(format("path %1% is not in the store") % path); + deletePath(path); +// 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); +} diff --git a/src/store.hh b/src/store.hh new file mode 100644 index 000000000000..b96fa30ba8e9 --- /dev/null +++ b/src/store.hh @@ -0,0 +1,24 @@ +#ifndef __VALUES_H +#define __VALUES_H + +#include + +#include "hash.hh" + +using namespace std; + + +void copyPath(string src, string dst); + +/* 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, Hash & hash); + +/* Delete a value from the nixStore directory. */ +void deleteFromStore(const string & path); + +/* !!! */ +string queryFromStore(Hash hash); + + +#endif /* !__VALUES_H */ diff --git a/src/values.cc b/src/values.cc deleted file mode 100644 index 5db04036c4de..000000000000 --- a/src/values.cc +++ /dev/null @@ -1,175 +0,0 @@ -#include - -#include -#include - -#include "values.hh" -#include "globals.hh" -#include "db.hh" -#include "archive.hh" - - -struct CopySink : DumpSink -{ - int fd; - virtual void operator () (const unsigned char * data, unsigned int len) - { - if (write(fd, (char *) data, len) != (ssize_t) len) - throw SysError("writing to child"); - } -}; - - -struct CopySource : RestoreSource -{ - int fd; - virtual void operator () (const unsigned char * data, unsigned int len) - { - ssize_t res = read(fd, (char *) data, len); - if (res == -1) - throw SysError("reading from parent"); - if (res != (ssize_t) len) - throw Error("not enough data available on parent"); - } -}; - - -void copyPath(string src, string dst) -{ - /* Unfortunately C++ doesn't support coprocedures, so we have no - nice way to chain CopySink and CopySource together. Instead we - fork off a child to run the sink. (Fork-less platforms should - use a thread). */ - - /* Create a pipe. */ - int fds[2]; - if (pipe(fds) == -1) throw SysError("creating pipe"); - - /* Fork. */ - pid_t pid; - switch (pid = fork()) { - - case -1: - throw SysError("unable to fork"); - - case 0: /* child */ - try { - close(fds[1]); - CopySource source; - source.fd = fds[0]; - restorePath(dst, source); - _exit(0); - } catch (exception & e) { - cerr << "error: " << e.what() << endl; - } - _exit(1); - } - - close(fds[0]); - - /* Parent. */ - - CopySink sink; - sink.fd = fds[1]; - dumpPath(src, sink); - - /* Wait for the child to finish. */ - int status; - if (waitpid(pid, &status, 0) != pid) - throw SysError("waiting for child"); - - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - throw Error("cannot copy file: child died"); -} - - -void addToStore(string srcPath, string & dstPath, Hash & hash) -{ - srcPath = absPath(srcPath); - - hash = hashPath(srcPath); - - string path; - if (queryDB(nixDB, dbRefs, hash, path)) { - debug((string) hash + " already known"); - dstPath = path; - return; - } - - string baseName = baseNameOf(srcPath); - dstPath = nixStore + "/" + (string) hash + "-" + baseName; - - copyPath(srcPath, dstPath); - - setDB(nixDB, dbRefs, hash, dstPath); -} - - -#if 0 -/* Download object referenced by the given URL into the sources - directory. Return the file name it was downloaded to. */ -string fetchURL(string url) -{ - string filename = baseNameOf(url); - string fullname = nixSourcesDir + "/" + filename; - struct stat st; - if (stat(fullname.c_str(), &st)) { - cerr << "fetching " << url << endl; - /* !!! quoting */ - string shellCmd = - "cd " + nixSourcesDir + " && wget --quiet -N \"" + url + "\""; - int res = system(shellCmd.c_str()); - if (WEXITSTATUS(res) != 0) - throw Error("cannot fetch " + url); - } - return fullname; -} -#endif - - -void deleteFromStore(const string & path) -{ - string prefix = nixStore + "/"; - if (string(path, 0, prefix.size()) != prefix) - throw Error(format("path %1% is not in the store") % path); - deletePath(path); -// delDB(nixDB, dbRefs, hash); -} - - -string queryFromStore(Hash hash) -{ - bool checkedNet = false; - - while (1) { - - 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("a file with hash " + (string) hash + " is required, " - "but it is not known to exist locally or on the network"); -#if 0 - if (checkedNet) - throw Error("consistency problem: file fetched from " + url + - " should have hash " + (string) hash + ", but it doesn't"); - - if (!queryDB(nixDB, dbNetSources, hash, url)) - throw Error("a file with hash " + (string) hash + " is required, " - "but it is not known to exist locally or on the network"); - - checkedNet = true; - - fn = fetchURL(url); - - setDB(nixDB, dbRefs, hash, fn); -#endif - } -} diff --git a/src/values.hh b/src/values.hh deleted file mode 100644 index b96fa30ba8e9..000000000000 --- a/src/values.hh +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef __VALUES_H -#define __VALUES_H - -#include - -#include "hash.hh" - -using namespace std; - - -void copyPath(string src, string dst); - -/* 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, Hash & hash); - -/* Delete a value from the nixStore directory. */ -void deleteFromStore(const string & path); - -/* !!! */ -string queryFromStore(Hash hash); - - -#endif /* !__VALUES_H */ -- cgit 1.4.1