Add build mode to compute fixed-output derivation hashes

For example,

  $ nix-build --hash -A nix-repl.src

will build the fixed-output derivation nix-repl.src (a fetchFromGitHub
call), but instead of *verifying* the hash given in the Nix
expression, it prints out the resulting hash, and then moves the
result to its content-addressed location in the Nix store. E.g

  build produced path ‘/nix/store/504a4k6zi69dq0yjc0bm12pa65bccxam-nix-repl-8a2f5f0607540ffe56b56d52db544373e1efb980-src’ with sha256 hash ‘0cjablz01i0g9smnavhf86imwx1f9mnh5flax75i615ml71gsr88’

The goal of this is to make all nix-prefetch-* scripts unnecessary: we
can just let Nix run the real thing (i.e., the corresponding fetch*
derivation).

Another example:

  $ nix-build --hash -E 'with import <nixpkgs> {}; fetchgit { url = "https://github.com/NixOS/nix.git"; sha256 = "ffffffffffffffffffffffffffffffffffffffffffffffffffff"; }'
  ...
  git revision is 9e7c1a4bbd
  ...
  build produced path ‘/nix/store/gmsnh9i7x4mb7pyd2ns7n3c9l90jfsi1-nix’ with sha256 hash ‘1188xb621diw89n25rifqg9lxnzpz7nj5bfh4i1y3dnis0dmc0zp’

(Having to specify a fake sha256 hash is a bit annoying...)
This commit is contained in:
Eelco Dolstra 2016-01-31 12:06:45 +01:00
parent 9e7c1a4bbd
commit d367b8e787
3 changed files with 34 additions and 7 deletions

View file

@ -1045,6 +1045,15 @@ void DerivationGoal::haveDerivation()
for (auto & i : invalidOutputs) for (auto & i : invalidOutputs)
if (pathFailed(i)) return; if (pathFailed(i)) return;
/* Reject doing a hash build of anything other than a fixed-output
derivation. */
if (buildMode == bmHash) {
if (drv->outputs.size() != 1 ||
drv->outputs.find("out") == drv->outputs.end() ||
drv->outputs["out"].hashAlgo == "")
throw Error(format("cannot do a hash build of non-fixed-output derivation %1%") % drvPath);
}
/* We are first going to try to create the invalid output paths /* We are first going to try to create the invalid output paths
through substitutes. If that doesn't work, we'll build through substitutes. If that doesn't work, we'll build
them. */ them. */
@ -2727,13 +2736,30 @@ void DerivationGoal::registerOutputs()
format("output path %1% should be a non-executable regular file") % path); format("output path %1% should be a non-executable regular file") % path);
} }
/* Check the hash. */ /* Check the hash. In hash mode, move the path produced by
the derivation to its content-addressed location. */
Hash h2 = recursive ? hashPath(ht, actualPath).first : hashFile(ht, actualPath); Hash h2 = recursive ? hashPath(ht, actualPath).first : hashFile(ht, actualPath);
if (buildMode == bmHash) {
Path dest = makeFixedOutputPath(recursive, ht, h2, drv->env["name"]);
printMsg(lvlError, format("build produced path %1% with %2% hash %3%")
% dest % printHashType(ht) % printHash16or32(h2));
if (worker.store.isValidPath(dest))
return;
if (actualPath != dest) {
PathLocks outputLocks({dest});
if (pathExists(dest))
deletePath(dest);
if (rename(actualPath.c_str(), dest.c_str()) == -1)
throw SysError(format("moving %1% to %2%") % actualPath % dest);
}
path = actualPath = dest;
} else {
if (h != h2) if (h != h2)
throw BuildError( throw BuildError(
format("output path %1% has %2% hash %3% when %4% was expected") format("output path %1% has %2% hash %3% when %4% was expected")
% path % i.second.hashAlgo % printHash16or32(h2) % printHash16or32(h)); % path % i.second.hashAlgo % printHash16or32(h2) % printHash16or32(h));
} }
}
/* Get rid of all weird permissions. This also checks that /* Get rid of all weird permissions. This also checks that
all files are owned by the build user, if applicable. */ all files are owned by the build user, if applicable. */
@ -2748,7 +2774,7 @@ void DerivationGoal::registerOutputs()
PathSet references = scanForReferences(actualPath, allPaths, hash); PathSet references = scanForReferences(actualPath, allPaths, hash);
if (buildMode == bmCheck) { if (buildMode == bmCheck) {
if (!store->isValidPath(path)) continue; if (!worker.store.isValidPath(path)) continue;
ValidPathInfo info = worker.store.queryPathInfo(path); ValidPathInfo info = worker.store.queryPathInfo(path);
if (hash.first != info.hash) { if (hash.first != info.hash) {
if (settings.keepFailed) { if (settings.keepFailed) {

View file

@ -103,7 +103,7 @@ struct ValidPathInfo
typedef list<ValidPathInfo> ValidPathInfos; typedef list<ValidPathInfo> ValidPathInfos;
enum BuildMode { bmNormal, bmRepair, bmCheck }; enum BuildMode { bmNormal, bmRepair, bmCheck, bmHash };
struct BuildResult struct BuildResult

View file

@ -117,6 +117,7 @@ static void opRealise(Strings opFlags, Strings opArgs)
if (i == "--dry-run") dryRun = true; if (i == "--dry-run") dryRun = true;
else if (i == "--repair") buildMode = bmRepair; else if (i == "--repair") buildMode = bmRepair;
else if (i == "--check") buildMode = bmCheck; else if (i == "--check") buildMode = bmCheck;
else if (i == "--hash") buildMode = bmHash;
else if (i == "--ignore-unknown") ignoreUnknown = true; else if (i == "--ignore-unknown") ignoreUnknown = true;
else throw UsageError(format("unknown flag %1%") % i); else throw UsageError(format("unknown flag %1%") % i);