aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--scripts/local.mk1
-rwxr-xr-xscripts/nix-prefetch-url.in132
-rw-r--r--src/libutil/util.hh1
-rw-r--r--src/nix-prefetch-url/local.mk7
-rw-r--r--src/nix-prefetch-url/nix-prefetch-url.cc132
6 files changed, 141 insertions, 133 deletions
diff --git a/Makefile b/Makefile
index fe2e88a99..3a204de88 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/scripts/local.mk b/scripts/local.mk
index 39e1df611..3fb47676f 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 6effbe208..000000000
--- 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/libutil/util.hh b/src/libutil/util.hh
index b2fb59d6f..a05a4cb88 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-prefetch-url/local.mk b/src/nix-prefetch-url/local.mk
new file mode 100644
index 000000000..3e7735406
--- /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 000000000..bd52df1c7
--- /dev/null
+++ b/src/nix-prefetch-url/nix-prefetch-url.cc
@@ -0,0 +1,132 @@
+#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 <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;
+
+ 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 (parseSearchPathArg(arg, end, searchPath))
+ ;
+ else if (*arg != "" && arg->at(0) == '-')
+ return false;
+ else
+ args.push_back(*arg);
+ return true;
+ });
+
+ if (args.size() < 1 || args.size() > 2)
+ throw UsageError("nix-prefetch-url expects one argument");
+
+ store = openStore();
+
+ EvalState state(searchPath);
+
+ /* Figure out a name in the Nix store. */
+ auto uri = args[0];
+ 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);
+ }
+
+ printMsg(lvlInfo, format("path is ‘%1%’") % storePath);
+
+ std::cout << printHash16or32(hash) << std::endl;
+ if (getEnv("PRINT_PATH") != "")
+ std::cout << storePath << std::endl;
+ });
+}