diff --git a/scripts/build-remote.pl.in b/scripts/build-remote.pl.in index 930164599a..53cf536447 100755 --- a/scripts/build-remote.pl.in +++ b/scripts/build-remote.pl.in @@ -192,8 +192,16 @@ my $buildFlags = "--max-silent-time $maxSilentTime"; # connection dies. Without it, the remote process might continue to # run indefinitely (that is, until it next tries to write to # stdout/stderr). -system("ssh -tt $sshOpts $hostName 'nix-store -rvvK $buildFlags $drvPath'") == 0 - or die "remote build on $hostName failed: $?"; +if (system("ssh -tt $sshOpts $hostName 'nix-store -rvvK $buildFlags $drvPath'") != 0) { + # If we couldn't run ssh or there was an ssh problem (indicated by + # exit code 255), then we return exit code 1; otherwise we assume + # that the builder failed, which we indicated to Nix using exit + # code 100. It's important to distinguish between the two because + # the first is a transient failure and the latter is permanent. + my $res = $? == -1 || ($? >> 8) == 255 ? 1 : 100; + print STDERR "remote build on $hostName failed: $?"; + exit $res; +} print "REMOTE BUILD DONE: $drvPath on $hostName\n"; diff --git a/src/libstore/build.cc b/src/libstore/build.cc index db2bed6d01..aea4ccdc43 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -648,6 +648,9 @@ private: /* Pipe for the builder's standard output/error. */ Pipe logPipe; + /* Whether we're building using a build hook. */ + bool usingBuildHook; + /* Pipes for talking to the build hook (if any). */ Pipe toHook; Pipe fromHook; @@ -970,6 +973,7 @@ void DerivationGoal::tryToBuild() try { /* Is the build hook willing to accept this job? */ + usingBuildHook = true; switch (tryBuildHook()) { case rpAccept: /* Yes, it has started doing so. Wait until we get @@ -1003,6 +1007,7 @@ void DerivationGoal::tryToBuild() /* Acquire locks and such. If we then see that the build has been done by somebody else, we're done. */ + usingBuildHook = false; PrepareBuildReply preply = prepareBuild(); if (preply == prDone) { amDone(ecSuccess); @@ -1019,8 +1024,12 @@ void DerivationGoal::tryToBuild() } catch (BuildError & e) { printMsg(lvlError, e.msg()); if (printBuildTrace) { - printMsg(lvlError, format("@ build-failed %1% %2% %3% %4%") - % drvPath % drv.outputs["out"].path % 0 % e.msg()); + if (usingBuildHook) + printMsg(lvlError, format("@ hook-failed %1% %2% %3% %4%") + % drvPath % drv.outputs["out"].path % 0 % e.msg()); + else + printMsg(lvlError, format("@ build-failed %1% %2% %3% %4%") + % drvPath % drv.outputs["out"].path % 0 % e.msg()); } amDone(ecFailed); return; @@ -1122,8 +1131,15 @@ void DerivationGoal::buildDone() } catch (BuildError & e) { printMsg(lvlError, e.msg()); if (printBuildTrace) { - printMsg(lvlError, format("@ build-failed %1% %2% %3% %4%") - % drvPath % drv.outputs["out"].path % status % e.msg()); + /* When using a build hook, the hook will return a + remote build failure using exit code 100. Anything + else is a hook problem. */ + if (usingBuildHook && (!WIFEXITED(status) || WEXITSTATUS(status) != 100)) + printMsg(lvlError, format("@ hook-failed %1% %2% %3% %4%") + % drvPath % drv.outputs["out"].path % status % e.msg()); + else + printMsg(lvlError, format("@ build-failed %1% %2% %3% %4%") + % drvPath % drv.outputs["out"].path % status % e.msg()); } amDone(ecFailed); return;