diff --git a/src/fix.cc b/src/fix.cc index 87ce7c775a..508a441167 100644 --- a/src/fix.cc +++ b/src/fix.cc @@ -13,29 +13,6 @@ typedef ATerm Expr; static Expr evalFile(string fileName); -static bool isFState(Expr e, string & path) -{ - char * s1, * s2, * s3; - Expr e1, e2; - if (ATmatch(e, "Path(, , [])", &s1, &e1, &e2)) { - path = s1; - return true; - } - else if (ATmatch(e, "Derive(, , [], , [])", - &s1, &s2, &e1, &s3, &e2)) - { - path = s3; - return true; - } - else if (ATmatch(e, "Include()", &s1)) - { - string fn = queryPathByHash(parseHash(s1)); - return isFState(evalFile(fn), path); - } - else return false; -} - - static Expr substExpr(string x, Expr rep, Expr e) { char * s; @@ -113,8 +90,7 @@ static Expr evalExpr(Expr e) ATmatch(e, "Function([], )", &e1, &e2)) return e; - string dummy; - if (isFState(e, dummy)) return e; + if (fstatePath(e) != "") return e; /* !!! hack */ /* Application. */ if (ATmatch(e, "App(, [])", &e1, &e2)) { @@ -165,8 +141,8 @@ static Expr evalExpr(Expr e) string key = it->first; ATerm value = it->second; - string path; - if (isFState(value, path)) { + string path = fstatePath(value); + if (path != "") { ins = ATinsert(ins, value); env = ATinsert(env, ATmake("(, )", key.c_str(), path.c_str())); diff --git a/src/fstate.cc b/src/fstate.cc index 2e3ffd639c..fa677a257a 100644 --- a/src/fstate.cc +++ b/src/fstate.cc @@ -1,4 +1,5 @@ #include +#include #include #include @@ -17,17 +18,20 @@ typedef map Environment; -/* Return true iff the given path exists. */ -bool pathExists(const string & path) +class AutoDelete { - 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; -} + string path; +public: + + AutoDelete(const string & p) : path(p) + { + } + + ~AutoDelete() + { + deletePath(path); + } +}; /* Run a program. */ @@ -36,9 +40,19 @@ 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 " + logFileName + " >&2").c_str(), "w"); /* !!! escaping */ + FILE * logFile = popen(("tee -a " + logFileName + " >&2").c_str(), "w"); /* !!! escaping */ 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. */ pid_t pid; @@ -51,31 +65,8 @@ static void runProgram(const string & program, Environment env) 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; - } - - 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: + 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 @@ -157,15 +148,7 @@ Hash hashTerm(ATerm t) } -struct RStatus -{ - /* !!! the comparator of this hash should match the semantics of - the file system */ -// map paths; -}; - - -static ATerm termFromHash(const Hash & hash) +ATerm termFromHash(const Hash & hash) { string path = queryPathByHash(hash); 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; Content content; @@ -212,7 +195,7 @@ static FState realise(RStatus & status, FState fs) /* Fall through. */ if (ATmatch(fs, "Include()", &s1)) { - return realise(status, termFromHash(parseHash(s1))); + return realise(termFromHash(parseHash(s1))); } else if (ATmatch(fs, "Path(, , [])", &s1, &content, &refs)) { @@ -227,7 +210,7 @@ static FState realise(RStatus & status, FState fs) /* Realise referenced paths. */ ATermList refs2 = ATempty; while (!ATisEmpty(refs)) { - refs2 = ATinsert(refs2, realise(status, ATgetFirst(refs))); + refs2 = ATinsert(refs2, realise(ATgetFirst(refs))); refs = ATgetNext(refs); } refs2 = ATreverse(refs2); @@ -278,7 +261,7 @@ static FState realise(RStatus & status, FState fs) /* Realise inputs. */ ATermList ins2 = ATempty; while (!ATisEmpty(ins)) { - ins2 = ATinsert(ins2, realise(status, ATgetFirst(ins))); + ins2 = ATinsert(ins2, realise(ATgetFirst(ins))); ins = ATgetNext(ins); } ins2 = ATreverse(ins2); @@ -335,6 +318,67 @@ static FState realise(RStatus & status, FState fs) FState realiseFState(FState fs) { - RStatus status; - return realise(status, fs); + return realise(fs); +} + + +string fstatePath(FState fs) +{ + char * s1, * s2, * s3; + FState e1, e2; + if (ATmatch(fs, "Path(, , [])", &s1, &e1, &e2)) + return s1; + else if (ATmatch(fs, "Derive(, , [], , [])", + &s1, &s2, &e1, &s3, &e2)) + return s3; + else if (ATmatch(fs, "Include()", &s1)) + return fstatePath(termFromHash(parseHash(s1))); + else + return ""; +} + + +typedef set StringSet; + + +void fstateRefs2(FState fs, StringSet & paths) +{ + char * s1, * s2, * s3; + FState e1, e2; + ATermList refs, ins; + + if (ATmatch(fs, "Path(, , [])", &s1, &e1, &refs)) { + paths.insert(s1); + + while (!ATisEmpty(refs)) { + fstateRefs2(ATgetFirst(refs), paths); + refs = ATgetNext(refs); + } + } + + else if (ATmatch(fs, "Derive(, , [], , [])", + &s1, &s2, &ins, &s3, &e2)) + { + paths.insert(s3); + + while (!ATisEmpty(ins)) { + fstateRefs2(ATgetFirst(ins), paths); + ins = ATgetNext(ins); + } + } + + else if (ATmatch(fs, "Include()", &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; } diff --git a/src/fstate.hh b/src/fstate.hh index b04588e7b3..de6303dca1 100644 --- a/src/fstate.hh +++ b/src/fstate.hh @@ -60,9 +60,17 @@ typedef ATerm FState; 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); +/* 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. */ string printTerm(ATerm t); @@ -73,6 +81,9 @@ Error badTerm(const format & f, ATerm t); /* Hash an aterm. */ 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. */ Hash writeTerm(ATerm t); diff --git a/src/nix.cc b/src/nix.cc index 0e51545724..ec4c91788e 100644 --- a/src/nix.cc +++ b/src/nix.cc @@ -21,9 +21,10 @@ static ArgType argType = atpUnknown; Operations: - --install / -i: realise a Nix expression + --install / -i: realise an fstate --delete / -d: delete paths from the Nix store --add / -A: copy a path to the Nix store + --query / -q: query information --dump: dump a path as a Nix archive --restore: restore a path from a Nix archive @@ -39,6 +40,11 @@ static ArgType argType = atpUnknown; --file / -f: by file name --hash / -h: by hash + Query flags: + + --path / -p: query the path of an fstate + --refs / -r: query paths referenced by an fstate + Options: --verbose / -v: verbose operation @@ -54,10 +60,8 @@ static void getArgType(Strings & flags) { string arg = *it; ArgType tp; - if (arg == "--hash" || arg == "-h") - tp = atpHash; - else if (arg == "--file" || arg == "-f") - tp = atpPath; + if (arg == "--hash" || arg == "-h") tp = atpHash; + else if (arg == "--file" || arg == "-f") tp = atpPath; else { it++; continue; } if (argType != atpUnknown) 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 expressions. */ static void opInstall(Strings opFlags, Strings opArgs) @@ -78,20 +96,11 @@ static void opInstall(Strings opFlags, Strings opArgs) for (Strings::iterator it = opArgs.begin(); it != opArgs.end(); it++) - { - Hash hash; - if (argType == atpHash) - hash = parseHash(*it); - else if (argType == atpPath) { - string path; - addToStore(*it, path, hash); - } - FState fs = ATmake("Include()", ((string) hash).c_str()); - realiseFState(fs); - } + realiseFState(termFromHash(argToHash(*it))); } +/* Delete a path in the Nix store directory. */ static void opDelete(Strings opFlags, Strings opArgs) { 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. */ struct StdoutSink : DumpSink { @@ -208,8 +262,10 @@ void run(Strings args) op = opInstall; else if (arg == "--delete" || arg == "-d") op = opDelete; - else if (arg == "--add") + else if (arg == "--add" || arg == "-A") op = opAdd; + else if (arg == "--query" || arg == "-q") + op = opQuery; else if (arg == "--dump") op = opDump; else if (arg == "--restore") diff --git a/src/util.cc b/src/util.cc index 2f9c43e55c..8ccd3c1524 100644 --- a/src/util.cc +++ b/src/util.cc @@ -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) { struct stat st; diff --git a/src/util.hh b/src/util.hh index a8f801b30c..684bafbb55 100644 --- a/src/util.hh +++ b/src/util.hh @@ -60,6 +60,8 @@ string dirOf(string path); the final `/'. */ 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 recursively. Don't use this at home, kids. */