* A command to query the paths referenced by an fstate expression.

* Use a temporary directory for build actions.
This commit is contained in:
Eelco Dolstra 2003-07-08 13:22:08 +00:00
parent a279137327
commit 40274c1f4f
6 changed files with 198 additions and 97 deletions

View file

@ -13,29 +13,6 @@ typedef ATerm Expr;
static Expr evalFile(string fileName); static Expr evalFile(string fileName);
static bool isFState(Expr e, string & path)
{
char * s1, * s2, * s3;
Expr e1, e2;
if (ATmatch(e, "Path(<str>, <term>, [<list>])", &s1, &e1, &e2)) {
path = s1;
return true;
}
else if (ATmatch(e, "Derive(<str>, <str>, [<list>], <str>, [<list>])",
&s1, &s2, &e1, &s3, &e2))
{
path = s3;
return true;
}
else if (ATmatch(e, "Include(<str>)", &s1))
{
string fn = queryPathByHash(parseHash(s1));
return isFState(evalFile(fn), path);
}
else return false;
}
static Expr substExpr(string x, Expr rep, Expr e) static Expr substExpr(string x, Expr rep, Expr e)
{ {
char * s; char * s;
@ -113,8 +90,7 @@ static Expr evalExpr(Expr e)
ATmatch(e, "Function([<list>], <term>)", &e1, &e2)) ATmatch(e, "Function([<list>], <term>)", &e1, &e2))
return e; return e;
string dummy; if (fstatePath(e) != "") return e; /* !!! hack */
if (isFState(e, dummy)) return e;
/* Application. */ /* Application. */
if (ATmatch(e, "App(<term>, [<list>])", &e1, &e2)) { if (ATmatch(e, "App(<term>, [<list>])", &e1, &e2)) {
@ -165,8 +141,8 @@ static Expr evalExpr(Expr e)
string key = it->first; string key = it->first;
ATerm value = it->second; ATerm value = it->second;
string path; string path = fstatePath(value);
if (isFState(value, path)) { if (path != "") {
ins = ATinsert(ins, value); ins = ATinsert(ins, value);
env = ATinsert(env, ATmake("(<str>, <str>)", env = ATinsert(env, ATmake("(<str>, <str>)",
key.c_str(), path.c_str())); key.c_str(), path.c_str()));

View file

@ -1,4 +1,5 @@
#include <map> #include <map>
#include <set>
#include <iostream> #include <iostream>
#include <sys/types.h> #include <sys/types.h>
@ -17,17 +18,20 @@
typedef map<string, string> Environment; typedef map<string, string> Environment;
/* Return true iff the given path exists. */ class AutoDelete
bool pathExists(const string & path)
{ {
int res; string path;
struct stat st; public:
res = stat(path.c_str(), &st);
if (!res) return true; AutoDelete(const string & p) : path(p)
if (errno != ENOENT) {
throw SysError(format("getting status of %1%") % path); }
return false;
} ~AutoDelete()
{
deletePath(path);
}
};
/* Run a program. */ /* Run a program. */
@ -36,9 +40,19 @@ static void runProgram(const string & program, Environment env)
/* Create a log file. */ /* Create a log file. */
string logFileName = nixLogDir + "/run.log"; string logFileName = nixLogDir + "/run.log";
/* !!! auto-pclose on exit */ /* !!! auto-pclose on exit */
FILE * logFile = popen(("tee " + logFileName + " >&2").c_str(), "w"); /* !!! escaping */ FILE * logFile = popen(("tee -a " + logFileName + " >&2").c_str(), "w"); /* !!! escaping */
if (!logFile) if (!logFile)
throw SysError(format("unable to create log file %1%") % logFileName); 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. */ /* Fork a child to build the package. */
pid_t pid; pid_t pid;
@ -51,31 +65,8 @@ static void runProgram(const string & program, Environment env)
try { /* child */ try { /* child */
#if 0 if (chdir(tmpDir.c_str()) == -1)
/* Try to use a prebuilt. */ throw SysError(format("changing into to `%1%'") % tmpDir);
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;
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);
}
#endif
// build:
/* Fill in the environment. We don't bother freeing /* Fill in the environment. We don't bother freeing
the strings, since we'll exec or die soon the strings, since we'll exec or die soon
@ -157,15 +148,7 @@ Hash hashTerm(ATerm t)
} }
struct RStatus ATerm termFromHash(const Hash & hash)
{
/* !!! the comparator of this hash should match the semantics of
the file system */
// map<string, Hash> paths;
};
static ATerm termFromHash(const Hash & hash)
{ {
string path = queryPathByHash(hash); string path = queryPathByHash(hash);
ATerm t = ATreadFromNamedFile(path.c_str()); ATerm t = ATreadFromNamedFile(path.c_str());
@ -188,7 +171,7 @@ Hash writeTerm(ATerm t)
} }
static FState realise(RStatus & status, FState fs) static FState realise(FState fs)
{ {
char * s1, * s2, * s3; char * s1, * s2, * s3;
Content content; Content content;
@ -212,7 +195,7 @@ static FState realise(RStatus & status, FState fs)
/* Fall through. */ /* Fall through. */
if (ATmatch(fs, "Include(<str>)", &s1)) { if (ATmatch(fs, "Include(<str>)", &s1)) {
return realise(status, termFromHash(parseHash(s1))); return realise(termFromHash(parseHash(s1)));
} }
else if (ATmatch(fs, "Path(<str>, <term>, [<list>])", &s1, &content, &refs)) { else if (ATmatch(fs, "Path(<str>, <term>, [<list>])", &s1, &content, &refs)) {
@ -227,7 +210,7 @@ static FState realise(RStatus & status, FState fs)
/* Realise referenced paths. */ /* Realise referenced paths. */
ATermList refs2 = ATempty; ATermList refs2 = ATempty;
while (!ATisEmpty(refs)) { while (!ATisEmpty(refs)) {
refs2 = ATinsert(refs2, realise(status, ATgetFirst(refs))); refs2 = ATinsert(refs2, realise(ATgetFirst(refs)));
refs = ATgetNext(refs); refs = ATgetNext(refs);
} }
refs2 = ATreverse(refs2); refs2 = ATreverse(refs2);
@ -278,7 +261,7 @@ static FState realise(RStatus & status, FState fs)
/* Realise inputs. */ /* Realise inputs. */
ATermList ins2 = ATempty; ATermList ins2 = ATempty;
while (!ATisEmpty(ins)) { while (!ATisEmpty(ins)) {
ins2 = ATinsert(ins2, realise(status, ATgetFirst(ins))); ins2 = ATinsert(ins2, realise(ATgetFirst(ins)));
ins = ATgetNext(ins); ins = ATgetNext(ins);
} }
ins2 = ATreverse(ins2); ins2 = ATreverse(ins2);
@ -335,6 +318,67 @@ static FState realise(RStatus & status, FState fs)
FState realiseFState(FState fs) FState realiseFState(FState fs)
{ {
RStatus status; return realise(fs);
return realise(status, fs); }
string fstatePath(FState fs)
{
char * s1, * s2, * s3;
FState e1, e2;
if (ATmatch(fs, "Path(<str>, <term>, [<list>])", &s1, &e1, &e2))
return s1;
else if (ATmatch(fs, "Derive(<str>, <str>, [<list>], <str>, [<list>])",
&s1, &s2, &e1, &s3, &e2))
return s3;
else if (ATmatch(fs, "Include(<str>)", &s1))
return fstatePath(termFromHash(parseHash(s1)));
else
return "";
}
typedef set<string> StringSet;
void fstateRefs2(FState fs, StringSet & paths)
{
char * s1, * s2, * s3;
FState e1, e2;
ATermList refs, ins;
if (ATmatch(fs, "Path(<str>, <term>, [<list>])", &s1, &e1, &refs)) {
paths.insert(s1);
while (!ATisEmpty(refs)) {
fstateRefs2(ATgetFirst(refs), paths);
refs = ATgetNext(refs);
}
}
else if (ATmatch(fs, "Derive(<str>, <str>, [<list>], <str>, [<list>])",
&s1, &s2, &ins, &s3, &e2))
{
paths.insert(s3);
while (!ATisEmpty(ins)) {
fstateRefs2(ATgetFirst(ins), paths);
ins = ATgetNext(ins);
}
}
else if (ATmatch(fs, "Include(<str>)", &s1))
fstateRefs2(termFromHash(parseHash(s1)), paths);
else throw badTerm("bad fstate expression", fs);
}
Strings fstateRefs(FState fs)
{
StringSet paths;
fstateRefs2(fs, paths);
Strings paths2(paths.size());
copy(paths.begin(), paths.end(), paths2.begin());
return paths2;
} }

View file

@ -60,9 +60,17 @@ typedef ATerm FState;
typedef ATerm Content; typedef ATerm Content;
/* Realise a $f$-normalised expression in the file system. */ /* Realise an fstate expression in the file system. This requires
execution of all Derive() nodes. */
FState realiseFState(FState fs); FState realiseFState(FState fs);
/* Return the path of an fstate expression. An empty string is
returned if the term is not a valid fstate expression. (!!!) */
string fstatePath(FState fs);
/* Return the paths referenced by fstate expression. */
Strings fstateRefs(FState fs);
/* Return a canonical textual representation of an expression. */ /* Return a canonical textual representation of an expression. */
string printTerm(ATerm t); string printTerm(ATerm t);
@ -73,6 +81,9 @@ Error badTerm(const format & f, ATerm t);
/* Hash an aterm. */ /* Hash an aterm. */
Hash hashTerm(ATerm t); Hash hashTerm(ATerm t);
/* Read an aterm from disk, given its hash. */
ATerm termFromHash(const Hash & hash);
/* Write an aterm to the Nix store directory, and return its hash. */ /* Write an aterm to the Nix store directory, and return its hash. */
Hash writeTerm(ATerm t); Hash writeTerm(ATerm t);

View file

@ -21,9 +21,10 @@ static ArgType argType = atpUnknown;
Operations: Operations:
--install / -i: realise a Nix expression --install / -i: realise an fstate
--delete / -d: delete paths from the Nix store --delete / -d: delete paths from the Nix store
--add / -A: copy a path to the Nix store --add / -A: copy a path to the Nix store
--query / -q: query information
--dump: dump a path as a Nix archive --dump: dump a path as a Nix archive
--restore: restore a path from a Nix archive --restore: restore a path from a Nix archive
@ -39,6 +40,11 @@ static ArgType argType = atpUnknown;
--file / -f: by file name --file / -f: by file name
--hash / -h: by hash --hash / -h: by hash
Query flags:
--path / -p: query the path of an fstate
--refs / -r: query paths referenced by an fstate
Options: Options:
--verbose / -v: verbose operation --verbose / -v: verbose operation
@ -54,10 +60,8 @@ static void getArgType(Strings & flags)
{ {
string arg = *it; string arg = *it;
ArgType tp; ArgType tp;
if (arg == "--hash" || arg == "-h") if (arg == "--hash" || arg == "-h") tp = atpHash;
tp = atpHash; else if (arg == "--file" || arg == "-f") tp = atpPath;
else if (arg == "--file" || arg == "-f")
tp = atpPath;
else { it++; continue; } else { it++; continue; }
if (argType != atpUnknown) if (argType != atpUnknown)
throw UsageError("only one argument type specified may be specified"); throw UsageError("only one argument type specified may be specified");
@ -69,6 +73,20 @@ static void getArgType(Strings & flags)
} }
static Hash argToHash(const string & arg)
{
if (argType == atpHash)
return parseHash(arg);
else if (argType == atpPath) {
string path;
Hash hash;
addToStore(arg, path, hash);
return hash;
}
else abort();
}
/* Realise (or install) paths from the given Nix fstate /* Realise (or install) paths from the given Nix fstate
expressions. */ expressions. */
static void opInstall(Strings opFlags, Strings opArgs) static void opInstall(Strings opFlags, Strings opArgs)
@ -78,20 +96,11 @@ static void opInstall(Strings opFlags, Strings opArgs)
for (Strings::iterator it = opArgs.begin(); for (Strings::iterator it = opArgs.begin();
it != opArgs.end(); it++) it != opArgs.end(); it++)
{ realiseFState(termFromHash(argToHash(*it)));
Hash hash;
if (argType == atpHash)
hash = parseHash(*it);
else if (argType == atpPath) {
string path;
addToStore(*it, path, hash);
}
FState fs = ATmake("Include(<str>)", ((string) hash).c_str());
realiseFState(fs);
}
} }
/* Delete a path in the Nix store directory. */
static void opDelete(Strings opFlags, Strings opArgs) static void opDelete(Strings opFlags, Strings opArgs)
{ {
if (!opFlags.empty()) throw UsageError("unknown flag"); if (!opFlags.empty()) throw UsageError("unknown flag");
@ -120,6 +129,51 @@ static void opAdd(Strings opFlags, Strings opArgs)
} }
/* Perform various sorts of queries. */
static void opQuery(Strings opFlags, Strings opArgs)
{
enum { qPath, qRefs, qUnknown } query = qPath;
for (Strings::iterator it = opFlags.begin();
it != opFlags.end(); )
{
string arg = *it;
if (arg == "--path" || arg == "-p") query = qPath;
else if (arg == "--refs" || arg == "-r") query = qRefs;
else { it++; continue; }
it = opFlags.erase(it);
}
getArgType(opFlags);
if (!opFlags.empty()) throw UsageError("unknown flag");
for (Strings::iterator it = opArgs.begin();
it != opArgs.end(); it++)
{
Hash hash = argToHash(*it);
switch (query) {
case qPath:
cout << format("%s\n") %
(string) fstatePath(termFromHash(hash));
break;
case qRefs: {
Strings refs = fstateRefs(termFromHash(hash));
for (Strings::iterator j = refs.begin();
j != refs.end(); j++)
cout << format("%s\n") % *j;
break;
}
default:
abort();
}
}
}
/* A sink that writes dump output to stdout. */ /* A sink that writes dump output to stdout. */
struct StdoutSink : DumpSink struct StdoutSink : DumpSink
{ {
@ -208,8 +262,10 @@ void run(Strings args)
op = opInstall; op = opInstall;
else if (arg == "--delete" || arg == "-d") else if (arg == "--delete" || arg == "-d")
op = opDelete; op = opDelete;
else if (arg == "--add") else if (arg == "--add" || arg == "-A")
op = opAdd; op = opAdd;
else if (arg == "--query" || arg == "-q")
op = opQuery;
else if (arg == "--dump") else if (arg == "--dump")
op = opDump; op = opDump;
else if (arg == "--restore") else if (arg == "--restore")

View file

@ -66,6 +66,18 @@ string baseNameOf(string path)
} }
bool pathExists(const string & path)
{
int res;
struct stat st;
res = stat(path.c_str(), &st);
if (!res) return true;
if (errno != ENOENT)
throw SysError(format("getting status of %1%") % path);
return false;
}
void deletePath(string path) void deletePath(string path)
{ {
struct stat st; struct stat st;

View file

@ -60,6 +60,8 @@ string dirOf(string path);
the final `/'. */ the final `/'. */
string baseNameOf(string path); string baseNameOf(string path);
/* Return true iff the given path exists. */
bool pathExists(const string & path);
/* Delete a path; i.e., in the case of a directory, it is deleted /* Delete a path; i.e., in the case of a directory, it is deleted
recursively. Don't use this at home, kids. */ recursively. Don't use this at home, kids. */