diff options
Diffstat (limited to 'src/eval.cc')
-rw-r--r-- | src/eval.cc | 285 |
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); } |