diff options
Diffstat (limited to 'src/fstate.cc')
-rw-r--r-- | src/fstate.cc | 555 |
1 files changed, 101 insertions, 454 deletions
diff --git a/src/fstate.cc b/src/fstate.cc index 31dd17582584..85f8c75cc5bc 100644 --- a/src/fstate.cc +++ b/src/fstate.cc @@ -1,141 +1,6 @@ -#include <map> -#include <iostream> - -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/wait.h> -#include <unistd.h> -#include <fcntl.h> - #include "fstate.hh" #include "globals.hh" #include "store.hh" -#include "db.hh" -#include "references.hh" - - -/* A Unix environment is a mapping from strings to strings. */ -typedef map<string, string> Environment; - - -class AutoDelete -{ - string path; - bool del; -public: - - AutoDelete(const string & p) : path(p) - { - del = true; - } - - ~AutoDelete() - { - if (del) deletePath(path); - } - - void cancel() - { - del = 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 -a " + logFileName + " >&2").c_str(), "w"); /* !!! escaping */ - if (!logFile) - throw SysError(format("creating log file `%1%'") % logFileName); - - /* Create a temporary directory where the build will take - place. */ - static int counter = 0; - string tmpDir = (format("/tmp/nix-%1%-%2%") % getpid() % counter++).str(); - - if (mkdir(tmpDir.c_str(), 0777) == -1) - throw SysError(format("creating directory `%1%'") % tmpDir); - - AutoDelete delTmpDir(tmpDir); - - /* 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 (chdir(tmpDir.c_str()) == -1) - throw SysError(format("changing into to `%1%'") % tmpDir); - - /* 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) { - delTmpDir.cancel(); - 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) @@ -157,23 +22,16 @@ Hash hashTerm(ATerm t) } -FState hash2fstate(Hash hash) -{ - return ATmake("Include(<str>)", ((string) hash).c_str()); -} - - -ATerm termFromId(const FSId & id, string * p) +ATerm termFromId(const FSId & id) { string path = expandId(id); - if (p) *p = path; ATerm t = ATreadFromNamedFile(path.c_str()); if (!t) throw Error(format("cannot read aterm from `%1%'") % path); return t; } -FSId writeTerm(ATerm t, const string & suffix, string * p) +FSId writeTerm(ATerm t, const string & suffix) { FSId id = hashTerm(t); @@ -182,28 +40,14 @@ FSId writeTerm(ATerm t, const string & suffix, string * p) if (!ATwriteToNamedTextFile(t, path.c_str())) throw Error(format("cannot write aterm %1%") % path); - registerPath(path, id); - if (p) *p = path; +// debug(format("written term %1% = %2%") % (string) id % +// printTerm(t)); + registerPath(path, id); return id; } -void registerSuccessor(const FSId & id1, const FSId & id2) -{ - setDB(nixDB, dbSuccessors, id1, id2); -} - - -static FSId storeSuccessor(const FSId & id1, FState sc, - string * p) -{ - FSId id2 = writeTerm(sc, "-s-" + (string) id1, p); - registerSuccessor(id1, id2); - return id2; -} - - static void parseIds(ATermList ids, FSIds & out) { while (!ATisEmpty(ids)) { @@ -217,17 +61,40 @@ static void parseIds(ATermList ids, FSIds & out) } -static void checkSlice(const Slice & slice); +typedef set<FSId> FSIdSet; + + +static void checkSlice(const Slice & slice) +{ + if (slice.elems.size() == 0) + throw Error("empty slice"); + + FSIdSet decl; + for (SliceElems::const_iterator i = slice.elems.begin(); + i != slice.elems.end(); i++) + decl.insert(i->id); + + for (FSIds::const_iterator i = slice.roots.begin(); + i != slice.roots.end(); i++) + if (decl.find(*i) == decl.end()) + throw Error(format("undefined id: %1%") % (string) *i); + + for (SliceElems::const_iterator i = slice.elems.begin(); + i != slice.elems.end(); i++) + for (FSIds::const_iterator j = i->refs.begin(); + j != i->refs.end(); j++) + if (decl.find(*j) == decl.end()) + throw Error(format("undefined id: %1%") % (string) *j); +} /* Parse a slice. */ -static Slice parseSlice(FState fs) +static bool parseSlice(ATerm t, Slice & slice) { - Slice slice; ATermList roots, elems; - if (!ATmatch(fs, "Slice([<list>], [<list>])", &roots, &elems)) - throw badTerm("not a slice", fs); + if (!ATmatch(t, "Slice([<list>], [<list>])", &roots, &elems)) + return false; parseIds(roots, slice.roots); @@ -246,337 +113,117 @@ static Slice parseSlice(FState fs) } checkSlice(slice); - - return slice; + return true; } -static ATermList unparseIds(const FSIds & ids) +static bool parseDerive(ATerm t, Derive & derive) { - ATermList l = ATempty; - for (FSIds::const_iterator i = ids.begin(); - i != ids.end(); i++) - l = ATinsert(l, - ATmake("<str>", ((string) *i).c_str())); - return ATreverse(l); -} - - -static FState unparseSlice(const Slice & slice) -{ - ATermList roots = unparseIds(slice.roots); - - ATermList elems = ATempty; - for (SliceElems::const_iterator i = slice.elems.begin(); - i != slice.elems.end(); i++) - elems = ATinsert(elems, - ATmake("(<str>, <str>, <term>)", - i->path.c_str(), - ((string) i->id).c_str(), - unparseIds(i->refs))); - - return ATmake("Slice(<term>, <term>)", roots, elems); -} - - -typedef set<FSId> FSIdSet; - - -static Slice normaliseFState2(FSId id, StringSet & usedPaths) -{ - 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. */ - string fsPath; - FState fs = termFromId(id, &fsPath); - - /* Already in normal form (i.e., a slice)? */ - if (ATgetType(fs) == AT_APPL && - (string) ATgetName(ATgetAFun(fs)) == "Slice") - { - usedPaths.insert(fsPath); - 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); - - typedef map<string, SliceElem> ElemMap; - ElemMap inMap; - - for (FSIds::iterator i = inIds.begin(); i != inIds.end(); i++) { - Slice slice = normaliseFState(*i); - realiseSlice(slice); + if (!ATmatch(t, "Derive([<list>], [<list>], <str>, <str>, [<list>])", + &outs, &ins, &builder, &platform, &bnds)) + return false; - for (SliceElems::iterator j = slice.elems.begin(); - j != slice.elems.end(); j++) - inMap[j->path] = *j; + while (!ATisEmpty(outs)) { + char * s1, * s2; + ATerm t = ATgetFirst(outs); + if (!ATmatch(t, "(<str>, <str>)", &s1, &s2)) + throw badTerm("not a derive output", t); + derive.outputs.push_back(DeriveOutput(s1, parseHash(s2))); + outs = ATgetNext(outs); } - Strings inPaths; - for (ElemMap::iterator i = inMap.begin(); i != inMap.end(); i++) - inPaths.push_back(i->second.path); + parseIds(ins, derive.inputs); - /* Build the environment. */ - Environment env; + derive.builder = builder; + derive.platform = platform; + 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; + derive.env.push_back(StringPair(s1, s2)); bnds = ATgetNext(bnds); } - /* Parse the outputs. */ - typedef map<string, FSId> OutPaths; - OutPaths outPaths; - while (!ATisEmpty(outs)) { - ATerm t = ATgetFirst(outs); - char * s1, * s2; - if (!ATmatch(t, "(<str>, <str>)", &s1, &s2)) - throw badTerm("string expected", t); - outPaths[s1] = parseHash(s2); - inPaths.push_back(s1); - outs = ATgetNext(outs); - } - - /* We can skip running the builder if we can expand all output - paths from their ids. */ - bool fastBuild = false; -#if 0 - for (OutPaths::iterator i = outPaths.begin(); - i != outPaths.end(); i++) - { - try { - expandId(i->second, i->first); - } catch (...) { - fastBuild = false; - break; - } - } -#endif - - if (!fastBuild) { - - /* Check that none of the outputs exist. */ - for (OutPaths::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); - - } else - debug(format("skipping build")); - - Slice slice; - - /* Check whether the output paths were created, and register each - one. */ - FSIdSet used; - for (OutPaths::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 refs = filterReferences(path, inPaths); - - SliceElem elem; - elem.path = path; - elem.id = i->second; - - for (Strings::iterator j = refs.begin(); j != refs.end(); j++) { - ElemMap::iterator k; - OutPaths::iterator l; - if ((k = inMap.find(*j)) != inMap.end()) { - elem.refs.push_back(k->second.id); - used.insert(k->second.id); - for (FSIds::iterator m = k->second.refs.begin(); - m != k->second.refs.end(); m++) - used.insert(*m); - } else if ((l = outPaths.find(*j)) != outPaths.end()) { - elem.refs.push_back(l->second); - used.insert(l->second); - } else - throw Error(format("unknown referenced path `%1%'") % *j); - } - - slice.elems.push_back(elem); - } - - for (ElemMap::iterator i = inMap.begin(); - i != inMap.end(); i++) - { - FSIdSet::iterator j = used.find(i->second.id); - if (j == used.end()) - debug(format("NOT referenced: `%1%'") % i->second.path); - else { - debug(format("referenced: `%1%'") % i->second.path); - slice.elems.push_back(i->second); - } - } - - FState nf = unparseSlice(slice); - debug(printTerm(nf)); - storeSuccessor(id, nf, &fsPath); - usedPaths.insert(fsPath); - - parseSlice(nf); /* check */ - - return slice; + return true; } -Slice normaliseFState(FSId id) +FState parseFState(ATerm t) { - StringSet dummy; - return normaliseFState2(id, dummy); + FState fs; + if (parseSlice(t, fs.slice)) + fs.type = FState::fsSlice; + else if (parseDerive(t, fs.derive)) + fs.type = FState::fsDerive; + else throw badTerm("not an fstate-expression", t); + return fs; } -static void checkSlice(const Slice & slice) +static ATermList unparseIds(const FSIds & ids) { - if (slice.elems.size() == 0) - throw Error("empty slice"); - - FSIdSet decl; - for (SliceElems::const_iterator i = slice.elems.begin(); - i != slice.elems.end(); i++) - decl.insert(i->id); - - for (FSIds::const_iterator i = slice.roots.begin(); - i != slice.roots.end(); i++) - if (decl.find(*i) == decl.end()) - throw Error(format("undefined id: %1%") % (string) *i); - - for (SliceElems::const_iterator i = slice.elems.begin(); - i != slice.elems.end(); i++) - for (FSIds::const_iterator j = i->refs.begin(); - j != i->refs.end(); j++) - if (decl.find(*j) == decl.end()) - throw Error(format("undefined id: %1%") % (string) *j); + ATermList l = ATempty; + for (FSIds::const_iterator i = ids.begin(); + i != ids.end(); i++) + l = ATinsert(l, + ATmake("<str>", ((string) *i).c_str())); + return ATreverse(l); } -void realiseSlice(const Slice & slice) +static ATerm unparseSlice(const Slice & slice) { - debug(format("realising slice")); - Nest nest(true); - - /* Perhaps all paths already contain the right id? */ - - bool missing = false; + ATermList roots = unparseIds(slice.roots); + + ATermList elems = ATempty; for (SliceElems::const_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; - } + elems = ATinsert(elems, + ATmake("(<str>, <str>, <term>)", + i->path.c_str(), + ((string) i->id).c_str(), + unparseIds(i->refs))); - /* For each element, expand its id at its path. */ - for (SliceElems::const_iterator i = slice.elems.begin(); - i != slice.elems.end(); i++) - { - SliceElem elem = *i; - expandId(elem.id, elem.path); - } + return ATmake("Slice(<term>, <term>)", roots, elems); } -Strings fstatePaths(const FSId & id, bool normalise) +static ATerm unparseDerive(const Derive & derive) { - Strings paths; - - FState fs = termFromId(id); - - ATermList outs, ins, bnds; - char * builder; - char * platform; - - if (normalise || - (ATgetType(fs) == AT_APPL && - (string) ATgetName(ATgetAFun(fs)) == "Slice")) - { - Slice slice; - if (normalise) - slice = normaliseFState(id); - else - slice = parseSlice(fs); - - /* !!! fix complexity */ - for (FSIds::const_iterator i = slice.roots.begin(); - i != slice.roots.end(); i++) - for (SliceElems::const_iterator j = slice.elems.begin(); - j != slice.elems.end(); j++) - if (*i == j->id) paths.push_back(j->path); - } - - else if (ATmatch(fs, "Derive([<list>], [<list>], <str>, <str>, [<list>])", - &outs, &ins, &builder, &platform, &bnds)) - { - while (!ATisEmpty(outs)) { - ATerm t = ATgetFirst(outs); - char * s1, * s2; - if (!ATmatch(t, "(<str>, <str>)", &s1, &s2)) - throw badTerm("string expected", t); - paths.push_back(s1); - outs = ATgetNext(outs); - } - } + ATermList outs = ATempty; + for (DeriveOutputs::const_iterator i = derive.outputs.begin(); + i != derive.outputs.end(); i++) + outs = ATinsert(outs, + ATmake("(<str>, <str>)", + i->first.c_str(), ((string) i->second).c_str())); - else throw badTerm("in fstatePaths", fs); + ATermList env = ATempty; + for (StringPairs::const_iterator i = derive.env.begin(); + i != derive.env.end(); i++) + env = ATinsert(env, + ATmake("(<str>, <str>)", + i->first.c_str(), i->second.c_str())); - return paths; + return ATmake("Derive(<term>, <term>, <str>, <str>, <term>)", + ATreverse(outs), + unparseIds(derive.inputs), + derive.builder.c_str(), + derive.platform.c_str(), + ATreverse(env)); } -StringSet fstateRefs(const FSId & id) +ATerm unparseFState(const FState & fs) { - StringSet paths; - Slice slice = normaliseFState2(id, paths); - for (SliceElems::const_iterator i = slice.elems.begin(); - i != slice.elems.end(); i++) - paths.insert(i->path); - return paths; + if (fs.type == FState::fsSlice) + return unparseSlice(fs.slice); + else if (fs.type == FState::fsDerive) + return unparseDerive(fs.derive); + else abort(); } |