about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2003-06-27T14·56+0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2003-06-27T14·56+0000
commit40b5936691fe2448dea0080e2319cc340bc7c65c (patch)
tree66144e3af56bc1d7035134b85f5e00641ab305e0
parent3da9687854e029e9df3b612fd592d2d5a622bb20 (diff)
* Realisation of Derive(...) expressions.
-rw-r--r--src/eval.cc285
-rw-r--r--src/eval.hh4
-rw-r--r--src/test.cc22
-rw-r--r--src/util.cc11
-rw-r--r--src/util.hh9
-rw-r--r--src/values.cc15
-rw-r--r--src/values.hh2
7 files changed, 188 insertions, 160 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);
 }
diff --git a/src/eval.hh b/src/eval.hh
index f90d5ba02b4f..553c7c40b246 100644
--- a/src/eval.hh
+++ b/src/eval.hh
@@ -23,7 +23,7 @@ using namespace std;
    self-referential.  This prevents us from having to deal with
    cycles.
 
-     Derive : String * Path * [FState] * [Path] * [(String, String)] -> [FState]
+     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
@@ -61,7 +61,7 @@ typedef ATerm Content;
 
 
 /* Realise a $f$-normalised expression in the file system. */
-void realiseFState(FState fs);
+FState realiseFState(FState fs);
 
 /* Return a canonical textual representation of an expression. */
 string printTerm(ATerm t);
diff --git a/src/test.cc b/src/test.cc
index aafae8ee37ff..639bd5ccf5ad 100644
--- a/src/test.cc
+++ b/src/test.cc
@@ -13,7 +13,9 @@
 
 void realise(FState fs)
 {
-    realiseFState(fs);
+    cout << format("%1% => %2%\n")
+        % printTerm(fs)
+        % printTerm(realiseFState(fs));
 }
 
 
@@ -145,15 +147,25 @@ void runTests()
         "File(<str>, Hash(<str>), [])", 
         builder1fn.c_str(),
         ((string) builder1h).c_str());
-    realiseFState(fs1);
-    realiseFState(fs1);
+    realise(fs1);
+    realise(fs1);
 
     FState fs2 = ATmake(
         "File(<str>, Hash(<str>), [])", 
         (builder1fn + "_bla").c_str(),
         ((string) builder1h).c_str());
-    realiseFState(fs2);
-    realiseFState(fs2);
+    realise(fs2);
+    realise(fs2);
+
+    string out1fn = nixStore + "/hello.txt";
+    FState fs3 = ATmake(
+        "Derive(<str>, <str>, [<term>], <str>, [(\"out\", <str>)])",
+        thisSystem.c_str(),
+        builder1fn.c_str(),
+        fs1,
+        out1fn.c_str(),
+        out1fn.c_str());
+    realise(fs3);
 
 #if 0
     Expr e1 = ATmake("Exec(Str(<str>), Hash(<str>), [])",
diff --git a/src/util.cc b/src/util.cc
index c6a0c1199770..a042a65b075c 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -11,10 +11,15 @@
 string thisSystem = SYSTEM;
 
 
-SysError::SysError(string msg)
+Error::Error(const format & f)
+{
+    err = f.str();
+}
+
+
+SysError::SysError(const format & f)
+    : Error(format("%1%: %2%") % f.str() % strerror(errno))
 {
-    char * sysMsg = strerror(errno);
-    err = msg + ": " + sysMsg;
 }
 
 
diff --git a/src/util.hh b/src/util.hh
index 3efac928b54e..cf6f7d0c1c65 100644
--- a/src/util.hh
+++ b/src/util.hh
@@ -18,22 +18,21 @@ class Error : public exception
 protected:
     string err;
 public:
-    Error() { }
-    Error(format f) { err = f.str(); }
-    ~Error() throw () { }
+    Error(const format & f);
+    ~Error() throw () { };
     const char * what() const throw () { return err.c_str(); }
 };
 
 class SysError : public Error
 {
 public:
-    SysError(string msg);
+    SysError(const format & f);
 };
 
 class UsageError : public Error
 {
 public:
-    UsageError(string _err) : Error(_err) { };
+    UsageError(const format & f) : Error(f) { };
 };
 
 
diff --git a/src/values.cc b/src/values.cc
index e23624ce5f43..fe65b977eaec 100644
--- a/src/values.cc
+++ b/src/values.cc
@@ -127,16 +127,13 @@ string fetchURL(string url)
 #endif
 
 
-void deleteFromStore(Hash hash)
+void deleteFromStore(const string & path)
 {
-    string fn;
-    if (queryDB(nixDB, dbRefs, hash, fn)) {
-        string prefix = nixStore + "/";
-        if (string(fn, prefix.size()) != prefix)
-            throw Error("path " + fn + " is not in the store");
-        deletePath(fn);
-        delDB(nixDB, dbRefs, hash);
-    }
+    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);
 }
 
 
diff --git a/src/values.hh b/src/values.hh
index 1bb00a9dd0d4..79ef48671f6c 100644
--- a/src/values.hh
+++ b/src/values.hh
@@ -15,7 +15,7 @@ void copyFile(string src, string dst);
 void addToStore(string srcPath, string & dstPath, Hash & hash);
 
 /* Delete a value from the nixStore directory. */
-void deleteFromStore(Hash hash);
+void deleteFromStore(const string & path);
 
 /* !!! */
 string queryFromStore(Hash hash);