Skip to content

Commit

Permalink
Remove nix-build --hash
Browse files Browse the repository at this point in the history
Instead, if a fixed-output derivation produces has an incorrect output
hash, we now unconditionally move the outputs to the path
corresponding with the actual hash and register it as valid. Thus,
after correcting the hash in the Nix expression (e.g. in a fetchurl
call), the fixed-output derivation doesn't have to be built again.

It would still be good to have a command for reporting the actual hash
of a fixed-output derivation (instead of throwing an error), but
"nix-build --hash" didn't do that.
  • Loading branch information
edolstra committed Feb 3, 2018
1 parent de96daf commit 84722d6
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 33 deletions.
10 changes: 5 additions & 5 deletions doc/manual/release-notes/rl-2.0.xml
Expand Up @@ -99,11 +99,11 @@
</listitem>

<listitem>
<para>New build mode <command>nix-build --hash</command> that
builds a derivation, computes the hash of the output, and moves
the output to the store path corresponding to what a fixed-output
derivation with that hash would produce.
(Add docs and examples; see d367b8e7875161e655deaa96bf8a5dd0bcf8229e)</para>
<para>If a fixed-output derivation produces a result with an
incorrect hash, the output path will be moved to the location
corresponding to the actual hash and registered as valid. Thus, a
subsequent build of the fixed-output derivation with the correct
hash is unnecessary.</para>
</listitem>

<listitem>
Expand Down
47 changes: 27 additions & 20 deletions src/libstore/build.cc
Expand Up @@ -1124,11 +1124,6 @@ void DerivationGoal::haveDerivation()
return;
}

/* Reject doing a hash build of anything other than a fixed-output
derivation. */
if (buildMode == bmHash && !drv->isFixedOutput())
throw Error("cannot do a hash build of non-fixed-output derivation '%1%'", drvPath);

/* We are first going to try to create the invalid output paths
through substitutes. If that doesn't work, we'll build
them. */
Expand Down Expand Up @@ -1320,9 +1315,7 @@ void DerivationGoal::inputsRealised()
allPaths.insert(inputPaths.begin(), inputPaths.end());

/* Is this a fixed-output derivation? */
fixedOutput = true;
for (auto & i : drv->outputs)
if (i.second.hash == "") fixedOutput = false;
fixedOutput = drv->isFixedOutput();

/* Don't repeat fixed-output derivations since they're already
verified by their output hash.*/
Expand Down Expand Up @@ -3019,6 +3012,8 @@ void DerivationGoal::registerOutputs()
bool runDiffHook = settings.runDiffHook;
bool keepPreviousRound = settings.keepFailed || runDiffHook;

std::exception_ptr delayedException;

/* Check whether the output paths were created, and grep each
output path to determine what other paths it references. Also make all
output paths read-only. */
Expand Down Expand Up @@ -3093,7 +3088,7 @@ void DerivationGoal::registerOutputs()
/* Check that fixed-output derivations produced the right
outputs (i.e., the content hash should match the specified
hash). */
if (i.second.hash != "") {
if (fixedOutput) {

bool recursive; Hash h;
i.second.parseHashInfo(recursive, h);
Expand All @@ -3109,27 +3104,34 @@ void DerivationGoal::registerOutputs()
/* Check the hash. In hash mode, move the path produced by
the derivation to its content-addressed location. */
Hash h2 = recursive ? hashPath(h.type, actualPath).first : hashFile(h.type, actualPath);
if (buildMode == bmHash) {
Path dest = worker.store.makeFixedOutputPath(recursive, h2, drv->env["name"]);
printError(format("build produced path '%1%' with %2% hash '%3%'")
% dest % printHashType(h.type) % printHash16or32(h2));
if (worker.store.isValidPath(dest))
return;

Path dest = worker.store.makeFixedOutputPath(recursive, h2, drv->env["name"]);

if (h != h2) {

/* Throw an error after registering the path as
valid. */
delayedException = std::make_exception_ptr(
BuildError("fixed-output derivation produced path '%s' with %s hash '%s' instead of the expected hash '%s'",
dest, printHashType(h.type), printHash16or32(h2), printHash16or32(h)));

Path actualDest = worker.store.toRealPath(dest);

if (worker.store.isValidPath(dest))
std::rethrow_exception(delayedException);

if (actualPath != actualDest) {
PathLocks outputLocks({actualDest});
deletePath(actualDest);
if (rename(actualPath.c_str(), actualDest.c_str()) == -1)
throw SysError(format("moving '%1%' to '%2%'") % actualPath % dest);
}

path = dest;
actualPath = actualDest;
} else {
if (h != h2)
throw BuildError(
format("output path '%1%' has %2% hash '%3%' when '%4%' was expected")
% path % i.second.hashAlgo % printHash16or32(h2) % printHash16or32(h));
}
else
assert(path == dest);

info.ca = makeFixedOutputCA(recursive, h2);
}
Expand Down Expand Up @@ -3306,6 +3308,11 @@ void DerivationGoal::registerOutputs()
paths referenced by each of them. If there are cycles in the
outputs, this will fail. */
worker.store.registerValidPaths(infos);

/* In case of a fixed-output derivation hash mismatch, throw an
exception now that we have registered the output as valid. */
if (delayedException)
std::rethrow_exception(delayedException);
}


Expand Down
2 changes: 1 addition & 1 deletion src/libstore/store-api.hh
Expand Up @@ -192,7 +192,7 @@ struct ValidPathInfo
typedef list<ValidPathInfo> ValidPathInfos;


enum BuildMode { bmNormal, bmRepair, bmCheck, bmHash };
enum BuildMode { bmNormal, bmRepair, bmCheck };


struct BuildResult
Expand Down
3 changes: 0 additions & 3 deletions src/nix-build/nix-build.cc
Expand Up @@ -167,9 +167,6 @@ void mainWrapped(int argc, char * * argv)
buildMode = bmRepair;
}

else if (*arg == "--hash")
buildMode = bmHash;

else if (*arg == "--run-env") // obsolete
runEnv = true;

Expand Down
1 change: 0 additions & 1 deletion src/nix-store/nix-store.cc
Expand Up @@ -122,7 +122,6 @@ static void opRealise(Strings opFlags, Strings opArgs)
if (i == "--dry-run") dryRun = true;
else if (i == "--repair") buildMode = bmRepair;
else if (i == "--check") buildMode = bmCheck;
else if (i == "--hash") buildMode = bmHash;
else if (i == "--ignore-unknown") ignoreUnknown = true;
else throw UsageError(format("unknown flag '%1%'") % i);

Expand Down
13 changes: 10 additions & 3 deletions tests/fixed.sh
Expand Up @@ -5,15 +5,22 @@ clearStore
export IMPURE_VAR1=foo
export IMPURE_VAR2=bar

path=$(nix-store -q $(nix-instantiate fixed.nix -A good.0))

echo 'testing bad...'
nix-build fixed.nix -A bad --no-out-link && fail "should fail"

# Building with the bad hash should produce the "good" output path as
# a side-effect.
[[ -e $path ]]
nix path-info --json $path | grep fixed:md5:2qk15sxzzjlnpjk9brn7j8ppcd

echo 'testing good...'
nix-build fixed.nix -A good --no-out-link

echo 'testing good2...'
nix-build fixed.nix -A good2 --no-out-link

echo 'testing bad...'
nix-build fixed.nix -A bad --no-out-link && fail "should fail"

echo 'testing reallyBad...'
nix-instantiate fixed.nix -A reallyBad && fail "should fail"

Expand Down

4 comments on commit 84722d6

@shlevy
Copy link
Member

@shlevy shlevy commented on 84722d6 Feb 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice if there was some interface (e.g. explicitly setting sha256 = null) to say "do this build fixed-output and tell me the hash" without needing a fake one of the right length

@yurrriq
Copy link
Member

@yurrriq yurrriq commented on 84722d6 Feb 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+:100: to sha256 = null !!

@copumpkin
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I seem to remember that some of the fixed output wrappers did their own validation on the hash format didn't they? Otherwise null would be pretty cool

@shlevy
Copy link
Member

@shlevy shlevy commented on 84722d6 Feb 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copumpkin sure, we can change them though...

Please sign in to comment.