about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--doc/manual/command-ref/nix-env.xml4
-rw-r--r--doc/manual/command-ref/nix-prefetch-url.xml28
-rw-r--r--local.mk3
-rw-r--r--perl/local.mk4
-rw-r--r--release.nix2
-rw-r--r--scripts/download-from-binary-cache.pl.in7
-rw-r--r--scripts/local.mk1
-rwxr-xr-xscripts/nix-prefetch-url.in132
-rw-r--r--src/libexpr/local.mk2
-rw-r--r--src/libmain/shared.hh2
-rw-r--r--src/libstore/build.cc8
-rw-r--r--src/libstore/local-store.hh58
-rw-r--r--src/libstore/remote-store.hh59
-rw-r--r--src/libutil/hash.cc1
-rw-r--r--src/libutil/util.hh1
-rw-r--r--src/nix-env/nix-env.cc22
-rw-r--r--src/nix-prefetch-url/local.mk7
-rw-r--r--src/nix-prefetch-url/nix-prefetch-url.cc168
19 files changed, 288 insertions, 222 deletions
diff --git a/Makefile b/Makefile
index fe2e88a995aa..3a204de888cc 100644
--- a/Makefile
+++ b/Makefile
@@ -13,6 +13,7 @@ makefiles = \
   src/nix-collect-garbage/local.mk \
   src/download-via-ssh/local.mk \
   src/nix-log2xml/local.mk \
+  src/nix-prefetch-url/local.mk \
   src/bsdiff-4.3/local.mk \
   perl/local.mk \
   scripts/local.mk \
diff --git a/doc/manual/command-ref/nix-env.xml b/doc/manual/command-ref/nix-env.xml
index 5e40317f189d..e9a5f0e097c4 100644
--- a/doc/manual/command-ref/nix-env.xml
+++ b/doc/manual/command-ref/nix-env.xml
@@ -378,7 +378,7 @@ number of possible ways:
 
 <variablelist>
 
-  <varlistentry><term><option>--prebuild-only</option> / <option>-b</option></term>
+  <varlistentry><term><option>--prebuilt-only</option> / <option>-b</option></term>
 
     <listitem><para>Use only derivations for which a substitute is
     registered, i.e., there is a pre-built binary available that can
@@ -1012,7 +1012,7 @@ user environment elements, etc. -->
 
   </varlistentry>
 
-  <varlistentry><term><option>--prebuild-only</option> / <option>-b</option></term>
+  <varlistentry><term><option>--prebuilt-only</option> / <option>-b</option></term>
 
     <listitem><para>Show only derivations for which a substitute is
     registered, i.e., there is a pre-built binary available that can
diff --git a/doc/manual/command-ref/nix-prefetch-url.xml b/doc/manual/command-ref/nix-prefetch-url.xml
index 5d1ab6931cd3..9cbaa42a1b1f 100644
--- a/doc/manual/command-ref/nix-prefetch-url.xml
+++ b/doc/manual/command-ref/nix-prefetch-url.xml
@@ -3,7 +3,7 @@
       xmlns:xi="http://www.w3.org/2001/XInclude"
       version="5.0"
       xml:id="sec-nix-prefetch-url">
-  
+
 <refmeta>
   <refentrytitle>nix-prefetch-url</refentrytitle>
   <manvolnum>1</manvolnum>
@@ -20,6 +20,7 @@
   <cmdsynopsis>
     <command>nix-prefetch-url</command>
     <arg><option>--type</option> <replaceable>hashAlgo</replaceable></arg>
+    <arg><option>--print-path</option></arg>
     <arg choice='plain'><replaceable>url</replaceable></arg>
     <arg><replaceable>hash</replaceable></arg>
   </cmdsynopsis>
@@ -54,8 +55,8 @@ error if signaled if the actual hash of the file does not match the
 specified hash.</para>
 
 <para>This command prints the hash on standard output.  Additionally,
-if the environment variable <envar>PRINT_PATH</envar> is set, the path
-of the downloaded file in the Nix store is also printed.</para>
+if the option <option>--print-path</option> is used, the path of the
+downloaded file in the Nix store is also printed.</para>
 
 </refsection>
 
@@ -63,7 +64,7 @@ of the downloaded file in the Nix store is also printed.</para>
 <refsection><title>Options</title>
 
 <variablelist>
-  
+
   <varlistentry><term><option>--type</option> <replaceable>hashAlgo</replaceable></term>
 
     <listitem><para>Use the specified cryptographic hash algorithm,
@@ -73,6 +74,13 @@ of the downloaded file in the Nix store is also printed.</para>
 
   </varlistentry>
 
+  <varlistentry><term><option>--print-path</option></term>
+
+    <listitem><para>Print the store path of the downloaded file on
+    standard output.</para></listitem>
+
+  </varlistentry>
+
 </variablelist>
 
 </refsection>
@@ -81,14 +89,14 @@ of the downloaded file in the Nix store is also printed.</para>
 <refsection><title>Examples</title>
 
 <screen>
-$ nix-prefetch-url ftp://ftp.nluug.nl/pub/gnu/make/make-3.80.tar.bz2
-0bbd1df101bc0294d440471e50feca71
+$ nix-prefetch-url ftp://ftp.gnu.org/pub/gnu/hello/hello-2.10.tar.gz
+0ssi1wpaf7plaswqqjwigppsg5fyh99vdlb9kzl7c9lng89ndq1i
 
-$ PRINT_PATH=1 nix-prefetch-url ftp://ftp.nluug.nl/pub/gnu/make/make-3.80.tar.bz2
-0bbd1df101bc0294d440471e50feca71
-/nix/store/wvyz8ifdn7wyz1p3pqyn0ra45ka2l492-make-3.80.tar.bz2</screen>
+$ nix-prefetch-url --print-path mirror://gnu/hello/hello-2.10.tar.gz
+0ssi1wpaf7plaswqqjwigppsg5fyh99vdlb9kzl7c9lng89ndq1i
+/nix/store/3x7dwzq014bblazs7kq20p9hyzz0qh8g-hello-2.10.tar.gz</screen>
 
 </refsection>
 
-    
+
 </refentry>
diff --git a/local.mk b/local.mk
index 25957d92a659..160057ad2625 100644
--- a/local.mk
+++ b/local.mk
@@ -6,7 +6,8 @@ dist-files += configure config.h.in nix.spec
 
 clean-files += Makefile.config
 
-GLOBAL_CXXFLAGS += -I . -I src -I src/libutil -I src/libstore -I src/libmain -I src/libexpr
+GLOBAL_CXXFLAGS += -I . -I src -I src/libutil -I src/libstore -I src/libmain -I src/libexpr \
+  -Wno-unneeded-internal-declaration
 
 $(foreach i, config.h $(call rwildcard, src/lib*, *.hh) src/nix-store/serve-protocol.hh, \
   $(eval $(call install-file-in, $(i), $(includedir)/nix, 0644)))
diff --git a/perl/local.mk b/perl/local.mk
index 132676f53341..ed49e3e6685e 100644
--- a/perl/local.mk
+++ b/perl/local.mk
@@ -24,7 +24,9 @@ ifeq ($(perlbindings), yes)
 
   Store_CXXFLAGS = \
     -I$(shell $(perl) -e 'use Config; print $$Config{archlibexp};')/CORE \
-    -D_FILE_OFFSET_BITS=64 -Wno-unused-variable -Wno-literal-suffix -Wno-reserved-user-defined-literal
+    -D_FILE_OFFSET_BITS=64 \
+    -Wno-unknown-warning-option -Wno-unused-variable -Wno-literal-suffix \
+    -Wno-reserved-user-defined-literal -Wno-duplicate-decl-specifier -Wno-pointer-bool-conversion
 
   Store_LIBS = libstore libutil
 
diff --git a/release.nix b/release.nix
index cfec7b9e774c..5a5a1226fe2c 100644
--- a/release.nix
+++ b/release.nix
@@ -24,7 +24,7 @@ let
 
         buildInputs =
           [ curl bison flex perl libxml2 libxslt bzip2
-            tetex dblatex nukeReferences pkgconfig sqlite libsodium
+            dblatex (dblatex.tex or tetex) nukeReferences pkgconfig sqlite libsodium
             docbook5 docbook5_xsl
           ] ++ lib.optional (!lib.inNixShell) git;
 
diff --git a/scripts/download-from-binary-cache.pl.in b/scripts/download-from-binary-cache.pl.in
index bb63eafca522..ea053bf14da4 100644
--- a/scripts/download-from-binary-cache.pl.in
+++ b/scripts/download-from-binary-cache.pl.in
@@ -80,7 +80,12 @@ sub addRequest {
     $curl->setopt(CURLOPT_WRITEDATA, $fh);
     $curl->setopt(CURLOPT_FOLLOWLOCATION, 1);
     $curl->setopt(CURLOPT_CAINFO, $caBundle) if defined $caBundle;
-    $curl->setopt(CURLOPT_SSL_VERIFYPEER, 0) unless isTrue($Nix::Config::config{"verify-https-binary-caches"} // "1");
+
+    unless (isTrue($Nix::Config::config{"verify-https-binary-caches"} // "1")) {
+        $curl->setopt(CURLOPT_SSL_VERIFYPEER, 0);
+        $curl->setopt(CURLOPT_SSL_VERIFYHOST, 0);
+    }
+
     $curl->setopt(CURLOPT_USERAGENT, $userAgent);
     $curl->setopt(CURLOPT_NOBODY, 1) if $head;
     $curl->setopt(CURLOPT_FAILONERROR, 1);
diff --git a/scripts/local.mk b/scripts/local.mk
index 39e1df611c5c..3fb47676fa54 100644
--- a/scripts/local.mk
+++ b/scripts/local.mk
@@ -4,7 +4,6 @@ nix_bin_scripts := \
   $(d)/nix-copy-closure \
   $(d)/nix-generate-patches \
   $(d)/nix-install-package \
-  $(d)/nix-prefetch-url \
   $(d)/nix-pull \
   $(d)/nix-push
 
diff --git a/scripts/nix-prefetch-url.in b/scripts/nix-prefetch-url.in
deleted file mode 100755
index 6effbe208146..000000000000
--- a/scripts/nix-prefetch-url.in
+++ /dev/null
@@ -1,132 +0,0 @@
-#! @perl@ -w @perlFlags@
-
-use utf8;
-use strict;
-use File::Basename;
-use File::stat;
-use Nix::Store;
-use Nix::Config;
-use Nix::Utils;
-
-binmode STDERR, ":encoding(utf8)";
-
-
-my $hashType = $ENV{'NIX_HASH_ALGO'} || "sha256"; # obsolete
-my $cacheDir = $ENV{'NIX_DOWNLOAD_CACHE'};
-
-my @args;
-my $arg;
-while ($arg = shift) {
-    if ($arg eq "--help") {
-        exec "man nix-prefetch-url" or die;
-    } elsif ($arg eq "--type") {
-        $hashType = shift;
-        die "$0: ‘$arg’ requires an argument\n" unless defined $hashType;
-    } elsif (substr($arg, 0, 1) eq "-") {
-        die "$0: unknown flag ‘$arg’\n";
-    } else {
-        push @args, $arg;
-    }
-}
-
-my $url = $args[0];
-my $expHash = $args[1];
-
-
-if (!defined $url || $url eq "") {
-    print STDERR <<EOF
-Usage: nix-prefetch-url URL [EXPECTED-HASH]
-EOF
-    ;
-    exit 1;
-}
-
-my $tmpDir = mkTempDir("nix-prefetch-url");
-
-# Hack to support the mirror:// scheme from Nixpkgs.
-if ($url =~ /^mirror:\/\//) {
-    system("$Nix::Config::binDir/nix-build '<nixpkgs>' -A resolveMirrorURLs --argstr url '$url' -o $tmpDir/urls > /dev/null") == 0
-        or die "$0: nix-build failed; maybe \$NIX_PATH is not set properly\n";
-    my @expanded = split ' ', readFile("$tmpDir/urls");
-    die "$0: cannot resolve ‘$url’" unless scalar @expanded > 0;
-    print STDERR "$url expands to $expanded[0]\n";
-    $url = $expanded[0];
-}
-
-# Handle escaped characters in the URI.  `+', `=' and `?' are the only
-# characters that are valid in Nix store path names but have a special
-# meaning in URIs.
-my $name = basename $url;
-die "cannot figure out file name for ‘$url’\n" if $name eq ""; 
-$name =~ s/%2b/+/g;
-$name =~ s/%3d/=/g;
-$name =~ s/%3f/?/g;
-
-my $finalPath;
-my $hash;
-
-# If the hash was given, a file with that hash may already be in the
-# store.
-if (defined $expHash) {
-    $finalPath = makeFixedOutputPath(0, $hashType, $expHash, $name);
-    if (isValidPath($finalPath)) { $hash = $expHash; } else { $finalPath = undef; }
-}
-
-# If we don't know the hash or a file with that hash doesn't exist,
-# download the file and add it to the store.
-if (!defined $finalPath) {
-
-    my $tmpFile = "$tmpDir/$name";
-    
-    # Optionally do timestamp-based caching of the download.
-    # Actually, the only thing that we cache in $NIX_DOWNLOAD_CACHE is
-    # the hash and the timestamp of the file at $url.  The caching of
-    # the file *contents* is done in Nix store, where it can be
-    # garbage-collected independently.
-    my ($cachedTimestampFN, $cachedHashFN, @cacheFlags);
-    if (defined $cacheDir) {
-        my $urlHash = hashString("sha256", 1, $url);
-        writeFile "$cacheDir/$urlHash.url", $url;
-        $cachedHashFN = "$cacheDir/$urlHash.$hashType";
-        $cachedTimestampFN = "$cacheDir/$urlHash.stamp";
-        @cacheFlags = ("--time-cond", $cachedTimestampFN) if -f $cachedHashFN && -f $cachedTimestampFN;
-    }
-    
-    # Perform the download.
-    my @curlFlags = ("curl", $url, "-o", $tmpFile, "--fail", "--location", "--max-redirs", "20", "--disable-epsv", "--cookie-jar", "$tmpDir/cookies", "--remote-time", (split " ", ($ENV{NIX_CURL_FLAGS} || "")));
-    (system $Nix::Config::curl @curlFlags, @cacheFlags) == 0 or die "$0: download of ‘$url’ failed\n";
-
-    if (defined $cacheDir && ! -e $tmpFile) {
-        # Curl didn't create $tmpFile, so apparently there's no newer
-        # file on the server.
-        $hash = readFile $cachedHashFN or die;
-        $finalPath = makeFixedOutputPath(0, $hashType, $hash, $name);
-        unless (isValidPath $finalPath) {
-            print STDERR "cached contents of ‘$url’ disappeared, redownloading...\n";
-            $finalPath = undef;
-            (system $Nix::Config::curl @curlFlags) == 0 or die "$0: download of ‘$url’ failed\n";
-        }
-    }
-
-    if (!defined $finalPath) {
-        
-        # Compute the hash.
-        $hash = hashFile($hashType, $hashType ne "md5", $tmpFile);
-
-        if (defined $cacheDir) {
-            writeFile $cachedHashFN, $hash;
-            my $st = stat($tmpFile) or die;
-            open STAMP, ">$cachedTimestampFN" or die; close STAMP;
-            utime($st->atime, $st->mtime, $cachedTimestampFN) or die;
-        }
-    
-        # Add the downloaded file to the Nix store.
-        $finalPath = addToStore($tmpFile, 0, $hashType);
-    }
-
-    die "$0: hash mismatch for ‘$url’\n" if defined $expHash && $expHash ne $hash;
-}
-
-print STDERR "path is ‘$finalPath’\n" unless $ENV{'QUIET'};
-print "$hash\n";
-print "$finalPath\n" if $ENV{'PRINT_PATH'};
diff --git a/src/libexpr/local.mk b/src/libexpr/local.mk
index 4c1f4de19187..d1b1987fb037 100644
--- a/src/libexpr/local.mk
+++ b/src/libexpr/local.mk
@@ -6,6 +6,8 @@ libexpr_DIR := $(d)
 
 libexpr_SOURCES := $(wildcard $(d)/*.cc) $(d)/lexer-tab.cc $(d)/parser-tab.cc
 
+libexpr_CXXFLAGS := -Wno-deprecated-register
+
 libexpr_LIBS = libutil libstore libformat
 
 libexpr_LDFLAGS = -ldl
diff --git a/src/libmain/shared.hh b/src/libmain/shared.hh
index a7e6ef762120..65b288e1ff3e 100644
--- a/src/libmain/shared.hh
+++ b/src/libmain/shared.hh
@@ -88,7 +88,7 @@ extern volatile ::sig_atomic_t blockInt;
 
 string showBytes(unsigned long long bytes);
 
-class GCResults;
+struct GCResults;
 
 struct PrintFreed
 {
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index c1163e63d90f..70278a8785d6 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -806,7 +806,7 @@ public:
 
     void timedOut() override;
 
-    string key()
+    string key() override
     {
         /* Ensure that derivations get built in order of their name,
            i.e. a derivation named "aardvark" always comes before
@@ -815,7 +815,7 @@ public:
         return "b$" + storePathToName(drvPath) + "$" + drvPath;
     }
 
-    void work();
+    void work() override;
 
     Path getDrvPath()
     {
@@ -863,8 +863,8 @@ private:
     void deleteTmpDir(bool force);
 
     /* Callback used by the worker to write to the log. */
-    void handleChildOutput(int fd, const string & data);
-    void handleEOF(int fd);
+    void handleChildOutput(int fd, const string & data) override;
+    void handleEOF(int fd) override;
 
     /* Return the set of (in)valid paths. */
     PathSet checkPathValidity(bool returnValid, bool checkHash);
diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh
index 105f3592c685..ebdf19bf1359 100644
--- a/src/libstore/local-store.hh
+++ b/src/libstore/local-store.hh
@@ -98,41 +98,41 @@ public:
 
     /* Implementations of abstract store API methods. */
 
-    bool isValidPath(const Path & path);
+    bool isValidPath(const Path & path) override;
 
-    PathSet queryValidPaths(const PathSet & paths);
+    PathSet queryValidPaths(const PathSet & paths) override;
 
-    PathSet queryAllValidPaths();
+    PathSet queryAllValidPaths() override;
 
-    ValidPathInfo queryPathInfo(const Path & path);
+    ValidPathInfo queryPathInfo(const Path & path) override;
 
-    Hash queryPathHash(const Path & path);
+    Hash queryPathHash(const Path & path) override;
 
-    void queryReferences(const Path & path, PathSet & references);
+    void queryReferences(const Path & path, PathSet & references) override;
 
-    void queryReferrers(const Path & path, PathSet & referrers);
+    void queryReferrers(const Path & path, PathSet & referrers) override;
 
-    Path queryDeriver(const Path & path);
+    Path queryDeriver(const Path & path) override;
 
-    PathSet queryValidDerivers(const Path & path);
+    PathSet queryValidDerivers(const Path & path) override;
 
-    PathSet queryDerivationOutputs(const Path & path);
+    PathSet queryDerivationOutputs(const Path & path) override;
 
-    StringSet queryDerivationOutputNames(const Path & path);
+    StringSet queryDerivationOutputNames(const Path & path) override;
 
-    Path queryPathFromHashPart(const string & hashPart);
+    Path queryPathFromHashPart(const string & hashPart) override;
 
-    PathSet querySubstitutablePaths(const PathSet & paths);
+    PathSet querySubstitutablePaths(const PathSet & paths) override;
 
     void querySubstitutablePathInfos(const Path & substituter,
         PathSet & paths, SubstitutablePathInfos & infos);
 
     void querySubstitutablePathInfos(const PathSet & paths,
-        SubstitutablePathInfos & infos);
+        SubstitutablePathInfos & infos) override;
 
     Path addToStore(const string & name, const Path & srcPath,
         bool recursive = true, HashType hashAlgo = htSHA256,
-        PathFilter & filter = defaultPathFilter, bool repair = false);
+        PathFilter & filter = defaultPathFilter, bool repair = false) override;
 
     /* Like addToStore(), but the contents of the path are contained
        in `dump', which is either a NAR serialisation (if recursive ==
@@ -142,43 +142,43 @@ public:
         bool recursive = true, HashType hashAlgo = htSHA256, bool repair = false);
 
     Path addTextToStore(const string & name, const string & s,
-        const PathSet & references, bool repair = false);
+        const PathSet & references, bool repair = false) override;
 
     void exportPath(const Path & path, bool sign,
-        Sink & sink);
+        Sink & sink) override;
 
-    Paths importPaths(bool requireSignature, Source & source);
+    Paths importPaths(bool requireSignature, Source & source) override;
 
-    void buildPaths(const PathSet & paths, BuildMode buildMode);
+    void buildPaths(const PathSet & paths, BuildMode buildMode) override;
 
     BuildResult buildDerivation(const Path & drvPath, const BasicDerivation & drv,
         BuildMode buildMode) override;
 
-    void ensurePath(const Path & path);
+    void ensurePath(const Path & path) override;
 
-    void addTempRoot(const Path & path);
+    void addTempRoot(const Path & path) override;
 
-    void addIndirectRoot(const Path & path);
+    void addIndirectRoot(const Path & path) override;
 
-    void syncWithGC();
+    void syncWithGC() override;
 
-    Roots findRoots();
+    Roots findRoots() override;
 
-    void collectGarbage(const GCOptions & options, GCResults & results);
+    void collectGarbage(const GCOptions & options, GCResults & results) override;
 
     /* Optimise the disk space usage of the Nix store by hard-linking
        files with the same contents. */
     void optimiseStore(OptimiseStats & stats);
 
     /* Generic variant of the above method.  */
-    void optimiseStore();
+    void optimiseStore() override;
 
     /* Optimise a single store path. */
     void optimisePath(const Path & path);
 
     /* Check the integrity of the Nix store.  Returns true if errors
        remain. */
-    bool verifyStore(bool checkContents, bool repair);
+    bool verifyStore(bool checkContents, bool repair) override;
 
     /* Register the validity of a path, i.e., that `path' exists, that
        the paths referenced by it exists, and in the case of an output
@@ -197,9 +197,9 @@ public:
     /* Query whether `path' previously failed to build. */
     bool hasPathFailed(const Path & path);
 
-    PathSet queryFailedPaths();
+    PathSet queryFailedPaths() override;
 
-    void clearFailedPaths(const PathSet & paths);
+    void clearFailedPaths(const PathSet & paths) override;
 
     void vacuumDB();
 
diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh
index 09e250386c5c..b0787c072904 100644
--- a/src/libstore/remote-store.hh
+++ b/src/libstore/remote-store.hh
@@ -24,71 +24,72 @@ public:
 
     /* Implementations of abstract store API methods. */
 
-    bool isValidPath(const Path & path);
+    bool isValidPath(const Path & path) override;
 
-    PathSet queryValidPaths(const PathSet & paths);
+    PathSet queryValidPaths(const PathSet & paths) override;
 
-    PathSet queryAllValidPaths();
+    PathSet queryAllValidPaths() override;
 
-    ValidPathInfo queryPathInfo(const Path & path);
+    ValidPathInfo queryPathInfo(const Path & path) override;
 
-    Hash queryPathHash(const Path & path);
+    Hash queryPathHash(const Path & path) override;
 
-    void queryReferences(const Path & path, PathSet & references);
+    void queryReferences(const Path & path, PathSet & references) override;
 
-    void queryReferrers(const Path & path, PathSet & referrers);
+    void queryReferrers(const Path & path, PathSet & referrers) override;
 
-    Path queryDeriver(const Path & path);
+    Path queryDeriver(const Path & path) override;
 
-    PathSet queryValidDerivers(const Path & path);
+    PathSet queryValidDerivers(const Path & path) override;
 
-    PathSet queryDerivationOutputs(const Path & path);
+    PathSet queryDerivationOutputs(const Path & path) override;
 
-    StringSet queryDerivationOutputNames(const Path & path);
+    StringSet queryDerivationOutputNames(const Path & path) override;
 
-    Path queryPathFromHashPart(const string & hashPart);
+    Path queryPathFromHashPart(const string & hashPart) override;
 
-    PathSet querySubstitutablePaths(const PathSet & paths);
+    PathSet querySubstitutablePaths(const PathSet & paths) override;
 
     void querySubstitutablePathInfos(const PathSet & paths,
-        SubstitutablePathInfos & infos);
+        SubstitutablePathInfos & infos) override;
 
     Path addToStore(const string & name, const Path & srcPath,
         bool recursive = true, HashType hashAlgo = htSHA256,
-        PathFilter & filter = defaultPathFilter, bool repair = false);
+        PathFilter & filter = defaultPathFilter, bool repair = false) override;
 
     Path addTextToStore(const string & name, const string & s,
-        const PathSet & references, bool repair = false);
+        const PathSet & references, bool repair = false) override;
 
     void exportPath(const Path & path, bool sign,
-        Sink & sink);
+        Sink & sink) override;
 
-    Paths importPaths(bool requireSignature, Source & source);
+    Paths importPaths(bool requireSignature, Source & source) override;
 
-    void buildPaths(const PathSet & paths, BuildMode buildMode);
+    void buildPaths(const PathSet & paths, BuildMode buildMode) override;
 
     BuildResult buildDerivation(const Path & drvPath, const BasicDerivation & drv,
         BuildMode buildMode) override;
 
-    void ensurePath(const Path & path);
+    void ensurePath(const Path & path) override;
 
-    void addTempRoot(const Path & path);
+    void addTempRoot(const Path & path) override;
 
-    void addIndirectRoot(const Path & path);
+    void addIndirectRoot(const Path & path) override;
 
-    void syncWithGC();
+    void syncWithGC() override;
 
-    Roots findRoots();
+    Roots findRoots() override;
 
-    void collectGarbage(const GCOptions & options, GCResults & results);
+    void collectGarbage(const GCOptions & options, GCResults & results) override;
 
-    PathSet queryFailedPaths();
+    PathSet queryFailedPaths() override;
 
-    void clearFailedPaths(const PathSet & paths);
+    void clearFailedPaths(const PathSet & paths) override;
 
-    void optimiseStore();
+    void optimiseStore() override;
+
+    bool verifyStore(bool checkContents, bool repair) override;
 
-    bool verifyStore(bool checkContents, bool repair);
 private:
     AutoCloseFD fdSocket;
     FdSink to;
diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc
index a83ba0a81817..1d973e7c8f14 100644
--- a/src/libutil/hash.cc
+++ b/src/libutil/hash.cc
@@ -115,7 +115,6 @@ const string base32Chars = "0123456789abcdfghijklmnpqrsvwxyz";
 
 string printHash32(const Hash & hash)
 {
-    Hash hash2(hash);
     unsigned int len = hashLength32(hash);
 
     string s;
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index b2fb59d6f2d7..a05a4cb88093 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -202,6 +202,7 @@ public:
     AutoDelete(const Path & p, bool recursive = true);
     ~AutoDelete();
     void cancel();
+    operator Path() const { return path; }
 };
 
 
diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc
index 97a2bbdb7de0..313f8a8a8f35 100644
--- a/src/nix-env/nix-env.cc
+++ b/src/nix-env/nix-env.cc
@@ -570,14 +570,16 @@ static void upgradeDerivations(Globals & globals,
                    constraints specified by upgradeType.  If there are
                    multiple matches, take the one with the highest
                    priority.  If there are still multiple matches,
-                   take the one with the highest version. */
+                   take the one with the highest version.
+                   Do not upgrade if it would decrease the priority. */
                 DrvInfos::iterator bestElem = availElems.end();
-                DrvName bestName;
+                string bestVersion;
                 for (auto j = availElems.begin(); j != availElems.end(); ++j) {
+                    if (comparePriorities(*globals.state, i, *j) > 0)
+                        continue;
                     DrvName newName(j->name);
                     if (newName.name == drvName.name) {
-                        int d = comparePriorities(*globals.state, i, *j);
-                        if (d == 0) d = compareVersions(drvName.version, newName.version);
+                        int d = compareVersions(drvName.version, newName.version);
                         if ((upgradeType == utLt && d < 0) ||
                             (upgradeType == utLeq && d <= 0) ||
                             (upgradeType == utEq && d == 0) ||
@@ -586,11 +588,11 @@ static void upgradeDerivations(Globals & globals,
                             int d2 = -1;
                             if (bestElem != availElems.end()) {
                                 d2 = comparePriorities(*globals.state, *bestElem, *j);
-                                if (d2 == 0) d2 = compareVersions(bestName.version, newName.version);
+                                if (d2 == 0) d2 = compareVersions(bestVersion, newName.version);
                             }
                             if (d2 < 0 && (!globals.prebuiltOnly || isPrebuilt(*globals.state, *j))) {
                                 bestElem = j;
-                                bestName = newName;
+                                bestVersion = newName.version;
                             }
                         }
                     }
@@ -600,9 +602,11 @@ static void upgradeDerivations(Globals & globals,
                     i.queryOutPath() !=
                     bestElem->queryOutPath())
                 {
+                    const char * action = compareVersions(drvName.version, bestVersion) <= 0
+                        ? "upgrading" : "downgrading";
                     printMsg(lvlInfo,
-                        format("upgrading ‘%1%’ to ‘%2%’")
-                        % i.name % bestElem->name);
+                        format("%1% ‘%2%’ to ‘%3%’")
+                        % action % i.name % bestElem->name);
                     newElems.push_back(*bestElem);
                 } else newElems.push_back(i);
 
@@ -1273,7 +1277,7 @@ static void opDeleteGenerations(Globals & globals, Strings opFlags, Strings opAr
         std::set<unsigned int> gens;
         for (auto & i : opArgs) {
             unsigned int n;
-            if (!string2Int(i, n) || n < 0)
+            if (!string2Int(i, n))
                 throw UsageError(format("invalid generation number ‘%1%’") % i);
             gens.insert(n);
         }
diff --git a/src/nix-prefetch-url/local.mk b/src/nix-prefetch-url/local.mk
new file mode 100644
index 000000000000..3e7735406af0
--- /dev/null
+++ b/src/nix-prefetch-url/local.mk
@@ -0,0 +1,7 @@
+programs += nix-prefetch-url
+
+nix-prefetch-url_DIR := $(d)
+
+nix-prefetch-url_SOURCES := $(d)/nix-prefetch-url.cc
+
+nix-prefetch-url_LIBS = libmain libexpr libstore libutil libformat
diff --git a/src/nix-prefetch-url/nix-prefetch-url.cc b/src/nix-prefetch-url/nix-prefetch-url.cc
new file mode 100644
index 000000000000..112d303e0c16
--- /dev/null
+++ b/src/nix-prefetch-url/nix-prefetch-url.cc
@@ -0,0 +1,168 @@
+#include "hash.hh"
+#include "shared.hh"
+#include "download.hh"
+#include "store-api.hh"
+#include "eval.hh"
+#include "eval-inline.hh"
+#include "common-opts.hh"
+#include "attr-path.hh"
+
+#include <iostream>
+
+using namespace nix;
+
+
+/* If ‘uri’ starts with ‘mirror://’, then resolve it using the list of
+   mirrors defined in Nixpkgs. */
+string resolveMirrorUri(EvalState & state, string uri)
+{
+    if (string(uri, 0, 9) != "mirror://") return uri;
+
+    string s(uri, 9);
+    auto p = s.find('/');
+    if (p == string::npos) throw Error("invalid mirror URI");
+    string mirrorName(s, 0, p);
+
+    Value vMirrors;
+    state.eval(state.parseExprFromString("import <nixpkgs/pkgs/build-support/fetchurl/mirrors.nix>", "."), vMirrors);
+    state.forceAttrs(vMirrors);
+
+    auto mirrorList = vMirrors.attrs->find(state.symbols.create(mirrorName));
+    if (mirrorList == vMirrors.attrs->end())
+        throw Error(format("unknown mirror name ‘%1%’") % mirrorName);
+    state.forceList(*mirrorList->value);
+
+    if (mirrorList->value->listSize() < 1)
+        throw Error(format("mirror URI ‘%1%’ did not expand to anything") % uri);
+
+    string mirror = state.forceString(*mirrorList->value->listElems()[0]);
+    return mirror + (hasSuffix(mirror, "/") ? "" : "/") + string(s, p + 1);
+}
+
+
+int main(int argc, char * * argv)
+{
+    return handleExceptions(argv[0], [&]() {
+        initNix();
+        initGC();
+
+        HashType ht = htSHA256;
+        std::vector<string> args;
+        Strings searchPath;
+        bool printPath = getEnv("PRINT_PATH") != "";
+        bool fromExpr = false;
+        string attrPath;
+        std::map<string, string> autoArgs_;
+
+        parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) {
+            if (*arg == "--help")
+                showManPage("nix-prefetch-url");
+            else if (*arg == "--version")
+                printVersion("nix-prefetch-url");
+            else if (*arg == "--type") {
+                string s = getArg(*arg, arg, end);
+                ht = parseHashType(s);
+                if (ht == htUnknown)
+                    throw UsageError(format("unknown hash type ‘%1%’") % s);
+            }
+            else if (*arg == "--print-path")
+                printPath = true;
+            else if (*arg == "--attr" || *arg == "-A") {
+                fromExpr = true;
+                attrPath = getArg(*arg, arg, end);
+            }
+            else if (parseAutoArgs(arg, end, autoArgs_))
+                ;
+            else if (parseSearchPathArg(arg, end, searchPath))
+                ;
+            else if (*arg != "" && arg->at(0) == '-')
+                return false;
+            else
+                args.push_back(*arg);
+            return true;
+        });
+
+        if (args.size() > 2)
+            throw UsageError("too many arguments");
+
+        store = openStore();
+        EvalState state(searchPath);
+
+        Bindings & autoArgs(*evalAutoArgs(state, autoArgs_));
+
+        /* If -A is given, get the URI from the specified Nix
+           expression. */
+        string uri;
+        if (!fromExpr) {
+            if (args.empty())
+                throw UsageError("you must specify a URI");
+            uri = args[0];
+        } else {
+            Path path = resolveExprPath(lookupFileArg(state, args.empty() ? "." : args[0]));
+            Value vRoot;
+            state.evalFile(path, vRoot);
+            Value & v(*findAlongAttrPath(state, attrPath, autoArgs, vRoot));
+            state.forceAttrs(v);
+            auto urls = v.attrs->find(state.symbols.create("urls"));
+            if (urls == v.attrs->end())
+                throw Error("attribute set does not contain a ‘urls’ attribute");
+            state.forceList(*urls->value);
+            if (urls->value->listSize() < 1)
+                throw Error("‘urls’ list is empty");
+            uri = state.forceString(*urls->value->listElems()[0]);
+        }
+
+        /* Figure out a name in the Nix store. */
+        auto name = baseNameOf(uri);
+        if (name.empty())
+            throw Error(format("cannot figure out file name for ‘%1%’") % uri);
+
+        /* If an expected hash is given, the file may already exist in
+           the store. */
+        Hash hash, expectedHash(ht);
+        Path storePath;
+        if (args.size() == 2) {
+            expectedHash = parseHash16or32(ht, args[1]);
+            storePath = makeFixedOutputPath(false, ht, expectedHash, name);
+            if (store->isValidPath(storePath))
+                hash = expectedHash;
+            else
+                storePath.clear();
+        }
+
+        if (storePath.empty()) {
+
+            auto actualUri = resolveMirrorUri(state, uri);
+
+            if (uri != actualUri)
+                printMsg(lvlInfo, format("‘%1%’ expands to ‘%2%’") % uri % actualUri);
+
+            /* Download the file. */
+            auto result = downloadFile(actualUri);
+
+            /* Copy the file to the Nix store. FIXME: if RemoteStore
+               implemented addToStoreFromDump() and downloadFile()
+               supported a sink, we could stream the download directly
+               into the Nix store. */
+            AutoDelete tmpDir(createTempDir(), true);
+            Path tmpFile = (Path) tmpDir + "/tmp";
+            writeFile(tmpFile, result.data);
+
+            /* FIXME: inefficient; addToStore() will also hash
+               this. */
+            hash = hashString(ht, result.data);
+
+            if (expectedHash != Hash(ht) && expectedHash != hash)
+                throw Error(format("hash mismatch for ‘%1%’") % uri);
+
+            storePath = store->addToStore(name, tmpFile, false, ht);
+        }
+
+        if (!printPath)
+            printMsg(lvlInfo, format("path is ‘%1%’") % storePath);
+
+        std::cout << printHash16or32(hash) << std::endl;
+        if (printPath)
+            std::cout << storePath << std::endl;
+    });
+}