diff --git a/Makefile.am b/Makefile.am index 83a04399a9..2bed3f3bbc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1 +1 @@ -SUBDIRS = src scripts +SUBDIRS = src scripts corepkgs diff --git a/configure.ac b/configure.ac index e3b0f0c6e0..110d8f0e12 100644 --- a/configure.ac +++ b/configure.ac @@ -13,9 +13,10 @@ AC_PROG_RANLIB # Unix shell scripting should die a slow and painful death. AC_DEFINE_UNQUOTED(NIX_STORE_DIR, "$(eval echo $prefix/store)", Nix store directory.) +AC_DEFINE_UNQUOTED(NIX_DATA_DIR, "$(eval echo $datadir)", Nix data directory.) AC_DEFINE_UNQUOTED(NIX_STATE_DIR, "$(eval echo $localstatedir/nix)", Nix state directory.) AC_DEFINE_UNQUOTED(NIX_LOG_DIR, "$(eval echo $localstatedir/log/nix)", Nix log file directory.) AM_CONFIG_HEADER([config.h]) -AC_CONFIG_FILES([Makefile src/Makefile scripts/Makefile]) +AC_CONFIG_FILES([Makefile src/Makefile scripts/Makefile corepkgs/Makefile]) AC_OUTPUT diff --git a/corepkgs/Makefile.am b/corepkgs/Makefile.am new file mode 100644 index 0000000000..9ce9c8c790 --- /dev/null +++ b/corepkgs/Makefile.am @@ -0,0 +1,8 @@ +install-data-local: + $(INSTALL) -d $(datadir)/fix + $(INSTALL) -d $(datadir)/fix/fetchurl + $(INSTALL_DATA) fetchurl/fetchurl.fix $(datadir)/fix/fetchurl + $(INSTALL_DATA) fetchurl/fetchurl.sh $(datadir)/fix/fetchurl + $(INSTALL) -d $(datadir)/fix/nar + $(INSTALL_DATA) nar/nar.fix $(datadir)/fix/nar + $(INSTALL_DATA) nar/nar.sh $(datadir)/fix/nar diff --git a/corepkgs/nar/nar.fix b/corepkgs/nar/nar.fix new file mode 100644 index 0000000000..3db6a48a08 --- /dev/null +++ b/corepkgs/nar/nar.fix @@ -0,0 +1,8 @@ +Function(["path", "name"], + Package( + [ ("name", Var("name")) + , ("build", Relative("nar/nar.sh")) + , ("path", Var("path")) + ] + ) +) \ No newline at end of file diff --git a/corepkgs/nar/nar.sh b/corepkgs/nar/nar.sh new file mode 100644 index 0000000000..6ffcf63224 --- /dev/null +++ b/corepkgs/nar/nar.sh @@ -0,0 +1,3 @@ +#! /bin/sh + +/tmp/nix/bin/nix --dump --file "$path" > $out || exit 1 diff --git a/scripts/Makefile.am b/scripts/Makefile.am index cf70f15744..e4602f2a1f 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -1,5 +1,5 @@ bin_SCRIPTS = nix-switch nix-collect-garbage \ - nix-pull-prebuilts nix-push-prebuilts + nix-pull nix-push install-exec-local: $(INSTALL) -d $(sysconfdir)/profile.d diff --git a/scripts/nix-pull b/scripts/nix-pull new file mode 100644 index 0000000000..0b09c8e00a --- /dev/null +++ b/scripts/nix-pull @@ -0,0 +1,2 @@ +#! /usr/bin/perl -w + diff --git a/scripts/nix-push b/scripts/nix-push new file mode 100644 index 0000000000..14b7e28346 --- /dev/null +++ b/scripts/nix-push @@ -0,0 +1,60 @@ +#! /usr/bin/perl -w + +my @pushlist; + +foreach my $hash (@ARGV) { + + die unless $hash =~ /^([0-9a-z]{32})$/; + + # Get all paths referenced by the normalisation of the given + # fstate expression. + my @paths; + open PATHS, "nix -qrh $hash 2> /dev/null |" or die "nix -qrh"; + while () { + chomp; + next unless /^\//; + push @paths, $_; + } + close PATHS; + + # For each path, create a Fix expression that turns the path into + # a Nix archive. + foreach my $path (@paths) { + + # Hash the path. + my $phash = `nix-hash $path`; + $? and die "nix-hash"; + chomp $phash; + die unless $phash =~ /^([0-9a-z]{32})$/; + + # Construct a Fix expression that creates a Nar archive. + my $fixexpr = + "App(IncludeFix(\"nar/nar.fix\"), " . + "[ (\"path\", Path(\"$path\", Hash(\"$phash\"), [Include(\"$hash\")]))" . + ", (\"name\", \"$phash.nar\")" . + "])"; + + my $fixfile = "/tmp/nix-push-tmp.fix"; + open FIX, ">$fixfile"; + print FIX $fixexpr; + close FIX; + + # Instantiate a Nix expression from the Fix expression. + my $nhash = `fix $fixfile`; + $? and die "instantiating Nix archive expression"; + chomp $nhash; + die unless $nhash =~ /^([0-9a-z]{32})$/; + + # Realise the Nix expression. + my $npath = `nix -qph $nhash 2> /dev/null`; + $? and die "creating Nix archive"; + chomp $npath; + + push @pushlist, $npath; + + print "$path -> $npath\n"; + } +} + +# Push the prebuilts to the server. !!! FIXME +system "rsync -av -e ssh @pushlist eelco\@losser.st-lab.cs.uu.nl:/home/eelco/public_html/nix-dist/"; diff --git a/src/fix.cc b/src/fix.cc index 9e5b8b5d41..5c4297bfb7 100644 --- a/src/fix.cc +++ b/src/fix.cc @@ -15,6 +15,8 @@ static Strings searchDirs; static string searchPath(string relPath) { + if (string(relPath, 0, 1) == "/") return relPath; + for (Strings::iterator i = searchDirs.begin(); i != searchDirs.end(); i++) { @@ -218,7 +220,10 @@ static Expr evalExpr(Expr e) static Expr evalFile(string relPath) { - Expr e = ATreadFromNamedFile(searchPath(relPath).c_str()); + string path = searchPath(relPath); + Expr e = ATreadFromNamedFile(path.c_str()); + if (!e) + throw Error(format("unable to read a term from `%1%'") % path); return evalExpr(e); } @@ -228,6 +233,7 @@ void run(Strings args) Strings files; searchDirs.push_back("."); + searchDirs.push_back(nixDataDir + "/fix"); for (Strings::iterator it = args.begin(); it != args.end(); ) diff --git a/src/fstate.cc b/src/fstate.cc index e289ca7b19..fdd43d1b13 100644 --- a/src/fstate.cc +++ b/src/fstate.cc @@ -149,7 +149,7 @@ Hash hashTerm(ATerm t) ATerm termFromHash(const Hash & hash, string * p) { - string path = queryPathByHash(hash); + string path = expandHash(hash); if (p) *p = path; ATerm t = ATreadFromNamedFile(path.c_str()); if (!t) throw Error(format("cannot read aterm %1%") % path); @@ -253,26 +253,6 @@ static FState realise(FState fs, StringSet & paths) /* Expand the hash into the target path. */ expandHash(hash, path); -#if 0 - /* Perhaps the path already exists and has the right hash? */ - if (pathExists(path)) { - - if (hash != hashPath(path)) - throw Error(format("path %1% exists, but does not have hash %2%") - % path % (string) hash); - - debug(format("path %1% already has hash %2%") - % path % (string) hash); - - } else { - - /* Do we know a path with that hash? If so, copy it. */ - string path2 = queryPathByHash(hash); - copyPath(path2, path); - - } -#endif - return nf; } diff --git a/src/globals.cc b/src/globals.cc index 40a5a981b8..9893d7ad22 100644 --- a/src/globals.cc +++ b/src/globals.cc @@ -4,8 +4,11 @@ string dbHash2Paths = "hash2paths"; string dbSuccessors = "successors"; +string dbSubstitutes = "substitutes"; + string nixStore = "/UNINIT"; +string nixDataDir = "/UNINIT"; string nixLogDir = "/UNINIT"; string nixDB = "/UNINIT"; @@ -14,4 +17,5 @@ void initDB() { createDB(nixDB, dbHash2Paths); createDB(nixDB, dbSuccessors); + createDB(nixDB, dbSubstitutes); } diff --git a/src/globals.hh b/src/globals.hh index 8d8c63bd75..0668ac40e5 100644 --- a/src/globals.hh +++ b/src/globals.hh @@ -52,6 +52,8 @@ extern string dbSubstitutes; derived files. */ extern string nixStore; +extern string nixDataDir; /* !!! fix */ + /* nixLogDir is the directory where we log various operations. */ extern string nixLogDir; diff --git a/src/nix.cc b/src/nix.cc index d6f2db4fef..4721563fdf 100644 --- a/src/nix.cc +++ b/src/nix.cc @@ -37,7 +37,7 @@ static ArgType argType = atpUnknown; Source selection for --install, --dump: - --file / -f: by file name + --file / -f: by file name !!! -> path --hash / -h: by hash Query flags: @@ -87,6 +87,12 @@ static Hash argToHash(const string & arg) } +static FState hash2fstate(Hash hash) +{ + return ATmake("Include()", ((string) hash).c_str()); +} + + /* Realise (or install) paths from the given Nix fstate expressions. */ static void opInstall(Strings opFlags, Strings opArgs) @@ -98,7 +104,7 @@ static void opInstall(Strings opFlags, Strings opArgs) it != opArgs.end(); it++) { StringSet paths; - realiseFState(termFromHash(argToHash(*it)), paths); + realiseFState(hash2fstate(argToHash(*it)), paths); } } @@ -157,14 +163,16 @@ static void opQuery(Strings opFlags, Strings opArgs) switch (query) { - case qPath: + case qPath: { + StringSet refs; cout << format("%s\n") % - (string) fstatePath(termFromHash(hash)); + (string) fstatePath(realiseFState(termFromHash(hash), refs)); break; + } case qRefs: { StringSet refs; - FState fs = ATmake("Include()", ((string) hash).c_str()); + FState fs = hash2fstate(hash); fstateRefs(realiseFState(fs, refs), refs); for (StringSet::iterator j = refs.begin(); j != refs.end(); j++) @@ -203,10 +211,8 @@ static void opDump(Strings opFlags, Strings opArgs) string arg = *opArgs.begin(); string path; - if (argType == atpHash) - path = queryPathByHash(parseHash(arg)); - else if (argType == atpPath) - path = arg; + if (argType == atpHash) path = expandHash(parseHash(arg)); + else if (argType == atpPath) path = arg; dumpPath(path, sink); } diff --git a/src/shared.cc b/src/shared.cc index bd165ce978..bfd7498de1 100644 --- a/src/shared.cc +++ b/src/shared.cc @@ -16,6 +16,7 @@ static void initAndRun(int argc, char * * argv) { /* Setup Nix paths. */ nixStore = NIX_STORE_DIR; + nixDataDir = NIX_DATA_DIR; nixLogDir = NIX_LOG_DIR; nixDB = (string) NIX_STATE_DIR + "/nixstate.db"; diff --git a/src/store.cc b/src/store.cc index 38e059a294..5a3a4e0678 100644 --- a/src/store.cc +++ b/src/store.cc @@ -131,25 +131,53 @@ bool isInPrefix(const string & path, const string & _prefix) } -static string queryPathByHashPrefix(Hash hash, const string & prefix) +string expandHash(const Hash & hash, const string & target, + const string & prefix) { Strings paths; + if (!target.empty() && !isInPrefix(target, prefix)) + abort(); + if (!queryListDB(nixDB, dbHash2Paths, hash, paths)) throw Error(format("no paths known with hash `%1%'") % (string) hash); - /* Arbitrarily pick the first one that exists and still hash the - right hash. */ + /* !!! we shouldn't check for staleness by default --- too slow */ + /* Pick one equal to `target'. */ + if (!target.empty()) { + + for (Strings::iterator i = paths.begin(); + i != paths.end(); i++) + { + string path = *i; + try { + if (path == target && hashPath(path) == hash) + return path; + } catch (Error & e) { + debug(format("stale path: %1%") % e.msg()); + /* try next one */ + } + } + + } + + /* Arbitrarily pick the first one that exists and isn't stale. */ for (Strings::iterator it = paths.begin(); it != paths.end(); it++) { string path = *it; try { - if (isInPrefix(path, prefix) && hashPath(path) == hash) - return path; + if (isInPrefix(path, prefix) && hashPath(path) == hash) { + if (target.empty()) + return path; + else { + copyPath(path, target); + return target; + } + } } catch (Error & e) { - debug(format("checking hash: %1%") % e.msg()); + debug(format("stale path: %1%") % e.msg()); /* try next one */ } } @@ -157,16 +185,7 @@ static string queryPathByHashPrefix(Hash hash, const string & prefix) throw Error(format("all paths with hash `%1%' are stale") % (string) hash); } - -string expandHash(const Hash & hash, const string & outPath = "") -{ -string queryPathByHash(Hash hash) -{ - return queryPathByHashPrefix(hash, "/"); -} - - void addToStore(string srcPath, string & dstPath, Hash & hash) { srcPath = absPath(srcPath); @@ -174,7 +193,7 @@ void addToStore(string srcPath, string & dstPath, Hash & hash) hash = hashPath(srcPath); try { - dstPath = queryPathByHashPrefix(hash, nixStore); + dstPath = expandHash(hash, "", nixStore); return; } catch (...) { } diff --git a/src/store.hh b/src/store.hh index f747b7ee3a..8b02cba996 100644 --- a/src/store.hh +++ b/src/store.hh @@ -13,15 +13,15 @@ void copyPath(string src, string dst); /* Register a path keyed on its hash. */ Hash registerPath(const string & path, Hash hash = Hash()); -/* Return a path whose contents have the given hash. If outPath is - not empty, ensure that such a path is realised in outPath (if +/* Return a path whose contents have the given hash. If target is + not empty, ensure that such a path is realised in target (if necessary by copying from another location). If prefix is not empty, only return a path that is an descendent of prefix. If no path with the given hash is known to exist in the file - system, ... + system, */ -string expandHash(const Hash & hash, const string & outPath = "", +string expandHash(const Hash & hash, const string & target = "", const string & prefix = "/"); /* Copy a file to the nixStore directory and register it in dbRefs.