diff --git a/scripts/nix-prefetch-url.in b/scripts/nix-prefetch-url.in index 409c3f992d..b33aa8a850 100644 --- a/scripts/nix-prefetch-url.in +++ b/scripts/nix-prefetch-url.in @@ -25,7 +25,7 @@ if test -z "$hash"; then tmpPath1=/tmp/nix-prefetch-url-$$ # !!! security? fi - # Perform the checkout. + # Perform the download. @curl@ --fail --location --max-redirs 20 "$url" > $tmpPath1 # Compute the hash. @@ -47,7 +47,7 @@ storeExpr=$( \ | @bindir@/nix-instantiate -) # Realise it. -finalPath=$(@bindir@/nix-store -q --force-realise $storeExpr) +finalPath=$(@bindir@/nix-store -r $storeExpr) if ! test -n "$QUIET"; then echo "path is $finalPath" >&2; fi diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index da9d1e6358..a2f27b4dc8 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -194,6 +194,7 @@ static Expr primDerivation(EvalState & state, const ATermVector & _args) string outputHash; string outputHashAlgo; + bool outputHashRecursive = false; for (ATermIterator i(attrs.keys()); i; ++i) { string key = aterm2String(*i); @@ -228,6 +229,11 @@ static Expr primDerivation(EvalState & state, const ATermVector & _args) else if (key == "name") drvName = s; else if (key == "outputHash") outputHash = s; else if (key == "outputHashAlgo") outputHashAlgo = s; + else if (key == "outputHashMode") { + if (s == "recursive") outputHashRecursive = true; + else if (s == "flat") outputHashRecursive = false; + else throw Error(format("invalid value `%1%' for `outputHashMode' attribute") % s); + } } } @@ -255,6 +261,7 @@ static Expr primDerivation(EvalState & state, const ATermVector & _args) h = parseHash32(ht, outputHash); string s = outputHash; outputHash = printHash(h); + if (outputHashRecursive) outputHashAlgo = "r:" + outputHashAlgo; } /* Check the derivation name. It shouldn't contain whitespace, diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 1ba521eb37..f6b081aafc 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -1082,25 +1082,37 @@ void DerivationGoal::computeClosure() outputs (i.e., the content hash should match the specified hash). */ if (i->second.hash != "") { - HashType ht = parseHashType(i->second.hashAlgo); + + bool recursive = false; + string algo = i->second.hashAlgo; + + if (string(algo, 0, 2) == "r:") { + recursive = true; + algo = string(algo, 2); + } + + if (!recursive) { + /* The output path should be a regular file without + execute permission. */ + struct stat st; + if (lstat(path.c_str(), &st)) + throw SysError(format("getting attributes of path `%1%'") % path); + if (!S_ISREG(st.st_mode) || (st.st_mode & S_IXUSR) != 0) + throw Error( + format("output path `%1% should be a non-executable regular file") + % path); + } + + /* Check the hash. */ + HashType ht = parseHashType(algo); if (ht == htUnknown) - throw Error(format("unknown hash algorithm `%1%'") % i->second.hashAlgo); + throw Error(format("unknown hash algorithm `%1%'") % algo); Hash h = parseHash(ht, i->second.hash); - Hash h2 = hashFile(ht, path); + Hash h2 = recursive ? hashPath(ht, path) : hashFile(ht, path); if (h != h2) throw Error( format("output path `%1% should have %2% hash `%3%', instead has `%4%'") - % path % i->second.hashAlgo % printHash(h) % printHash(h2)); - - /* Also, the output path should be a regular file withouth - execute permission. */ - struct stat st; - if (lstat(path.c_str(), &st)) - throw SysError(format("getting attributes of path `%1%'") % path); - if (!S_ISREG(st.st_mode) || (st.st_mode & S_IXUSR) != 0) - throw Error( - format("output path `%1% should be a non-executable regular file") - % path); + % path % algo % printHash(h) % printHash(h2)); } canonicalisePathMetaData(path);