about summary refs log tree commit diff
path: root/src/fstate.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/fstate.cc')
-rw-r--r--src/fstate.cc555
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();
 }