about summary refs log tree commit diff
path: root/src/eval.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/eval.cc')
-rw-r--r--src/eval.cc285
1 files changed, 150 insertions, 135 deletions
diff --git a/src/eval.cc b/src/eval.cc
index 4f59bcc21c4e..a1b8db6e0732 100644
--- a/src/eval.cc
+++ b/src/eval.cc
@@ -18,7 +18,7 @@ typedef map<string, string> Environment;
 
 
 /* Return true iff the given path exists. */
-bool pathExists(string path)
+bool pathExists(const string & path)
 {
     int res;
     struct stat st;
@@ -30,158 +30,107 @@ bool pathExists(string path)
 }
 
 
-#if 0
-/* Compute a derived value by running a program. */
-static Hash computeDerived(Hash sourceHash, string targetName,
-    string platform, Hash prog, Environment env)
+/* Run a program. */
+static void runProgram(const string & program, Environment env)
 {
-    string targetPath = nixValues + "/" + 
-        (string) sourceHash + "-nf";
-
-    /* Check whether the target already exists. */
-    if (pathExists(targetPath)) 
-        throw Error("derived value in " + targetPath + " already exists");
-
-    /* Find the program corresponding to the hash `prog'. */
-    string progPath = queryValuePath(prog);
-
-    /* Finalize the environment. */
-    env["out"] = targetPath;
-
     /* Create a log file. */
-    string logFileName = 
-        nixLogDir + "/" + baseNameOf(targetPath) + ".log";
+    string logFileName = nixLogDir + "/run.log";
     /* !!! auto-pclose on exit */
     FILE * logFile = popen(("tee " + logFileName + " >&2").c_str(), "w"); /* !!! escaping */
     if (!logFile)
-        throw SysError("unable to create log file " + logFileName);
-
-    try {
+        throw SysError(format("unable to create log file %1%") % logFileName);
 
-        /* Fork a child to build the package. */
-        pid_t pid;
-        switch (pid = fork()) {
+    /* Fork a child to build the package. */
+    pid_t pid;
+    switch (pid = fork()) {
             
-        case -1:
-            throw SysError("unable to fork");
+    case -1:
+        throw SysError("unable to fork");
 
-        case 0: 
+    case 0: 
 
-            try { /* child */
+        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;
-                    }
+            /* 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;
+                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);
+                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);
-                }
+                _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 Error("cannot pipe standard error into log file: " + string(strerror(errno)));
+            //             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 Error("cannot dup stderr into stdout");
+            /* 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(progPath.c_str(), 0755))
-                    throw Error("cannot make program executable");
+            /* 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(progPath.c_str(), baseNameOf(progPath).c_str(), 0, env2);
+            /* Execute the program.  This should not return. */
+            execle(program.c_str(), baseNameOf(program).c_str(), 0, env2);
 
-                throw Error("unable to execute builder: " +
-                    string(strerror(errno)));
+            throw SysError(format("unable to execute %1%") % program);
             
-            } catch (exception & e) {
-                cerr << "build error: " << e.what() << endl;
-            }
-            _exit(1);
-
+        } catch (exception & e) {
+            cerr << "build error: " << e.what() << endl;
         }
+        _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");
-
-        /* Check whether the result was created. */
-        if (!pathExists(targetPath))
-            throw Error("program " + progPath + 
-                " failed to create a result in " + targetPath);
-
-#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
-
-    } catch (exception &) {
-//         system(("rm -rf " + targetPath).c_str());
-        throw;
     }
 
-    /* Hash the result. */
-    Hash targetHash = hashPath(targetPath);
-
-    /* Register targetHash -> targetPath.  !!! this should be in
-       values.cc. */
-    setDB(nixDB, dbRefs, targetHash, targetName);
+    /* parent */
 
-    /* Register that targetHash was produced by evaluating
-       sourceHash; i.e., that targetHash is a normal form of
-       sourceHash. !!! this shouldn't be here */
-    setDB(nixDB, dbNFs, sourceHash, targetHash);
-
-    return targetHash;
+    /* 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");
 }
-#endif
 
 
 /* Throw an exception if the given platform string is not supported by
    the platform we are executing on. */
-static void checkPlatform(string platform)
+static void checkPlatform(const string & platform)
 {
     if (platform != thisSystem)
         throw Error(format("a `%1%' is required, but I am a `%2%'")
@@ -250,33 +199,39 @@ struct RStatus
 };
 
 
-static void realise(RStatus & status, FState fs)
+static FState realise(RStatus & status, FState fs)
 {
-    char * s;
+    char * s1, * s2, * s3;
     Content content;
-    ATermList refs;
+    ATermList refs, ins, outs, bnds;
     
-    if (ATmatch(fs, "File(<str>, <term>, [<list>])", &s, &content, &refs)) {
-        string path(s);
+    if (ATmatch(fs, "File(<str>, <term>, [<list>])", &s1, &content, &refs)) {
+        string path(s1);
 
         if (path[0] != '/') throw Error("absolute path expected: " + path);
 
         /* Realise referenced paths. */
+        ATermList refs2 = ATempty;
         while (!ATisEmpty(refs)) {
-            realise(status, ATgetFirst(refs));
+            refs2 = ATappend(refs2, realise(status, ATgetFirst(refs)));
             refs = ATgetNext(refs);
         }
+        refs2 = ATreverse(refs2);
 
-        if (!ATmatch(content, "Hash(<str>)", &s))
+        if (!ATmatch(content, "Hash(<str>)", &s1))
             throw badTerm("hash expected", content);
-        Hash hash = parseHash(s);
+        Hash hash = parseHash(s1);
+
+        /* Normal form. */
+        ATerm nf = ATmake("File(<str>, <term>, <list>)",
+            path.c_str(), content, refs2);
 
         /* 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;
+                return nf;
             }
 
             throw Error(format("path %1% exists, but does not have hash %2%")
@@ -286,19 +241,79 @@ static void realise(RStatus & status, FState fs)
         /* Do we know a path with that hash?  If so, copy it. */
         string path2 = queryFromStore(hash);
         copyFile(path2, path);
+
+        return nf;
     }
 
-    else if (ATmatch(fs, "Derive()")) {
+    else if (ATmatch(fs, "Derive(<str>, <str>, [<list>], <str>, [<list>])",
+                 &s1, &s2, &ins, &s3, &bnds)) 
+    {
+        string platform(s1), builder(s2), outPath(s3);
+
+        checkPlatform(platform);
         
+        /* Realise inputs. */
+        ATermList ins2 = ATempty;
+        while (!ATisEmpty(ins)) {
+            ins2 = ATappend(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, "(<str>, <str>)", &s1, &s2))
+                throw badTerm("string 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);
+
+#if 0
+        /* Register that targetHash was produced by evaluating
+           sourceHash; i.e., that targetHash is a normal form of
+           sourceHash. !!! this shouldn't be here */
+        setDB(nixDB, dbNFs, sourceHash, targetHash);
+#endif
+
+        return ATmake("File(<str>, Hash(<str>), <list>)",
+            outPath.c_str(), ((string) outHash).c_str(), ins2);
     }
 
-    else throw badTerm("bad file system state expression", fs);
+    throw badTerm("bad file system state expression", fs);
 }
 
 
-void realiseFState(FState fs)
+FState realiseFState(FState fs)
 {
     RStatus status;
-    realise(status, fs);
+    return realise(status, fs);
 }