diff --git a/doc/manual/rl-next/consistent-nix-build.md b/doc/manual/rl-next/consistent-nix-build.md new file mode 100644 index 000000000..d5929dc8e --- /dev/null +++ b/doc/manual/rl-next/consistent-nix-build.md @@ -0,0 +1,6 @@ +--- +synopsis: Show all FOD errors with `nix build --keep-going` +--- + +`nix build --keep-going` now behaves consistently with `nix-build --keep-going`. This means +that if e.g. multiple FODs fail to build, all hash mismatches are displayed. diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index dca17555a..2c18653e4 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -547,6 +547,37 @@ std::vector Installable::build( return res; } +static void throwBuildErrors( + std::vector & buildResults, + const Store & store) +{ + std::vector failed; + for (auto & buildResult : buildResults) { + if (!buildResult.success()) { + failed.push_back(buildResult); + } + } + + auto failedResult = failed.begin(); + if (failedResult != failed.end()) { + if (failed.size() == 1) { + failedResult->rethrow(); + } else { + StringSet failedPaths; + for (; failedResult != failed.end(); failedResult++) { + if (!failedResult->errorMsg.empty()) { + logError(ErrorInfo{ + .level = lvlError, + .msg = failedResult->errorMsg, + }); + } + failedPaths.insert(failedResult->path.to_string(store)); + } + throw Error("build of %s failed", concatStringsSep(", ", quoteStrings(failedPaths))); + } + } +} + std::vector, BuiltPathWithResult>> Installable::build2( ref evalStore, ref store, @@ -608,10 +639,9 @@ std::vector, BuiltPathWithResult>> Installable::build if (settings.printMissing) printMissing(store, pathsToBuild, lvlInfo); - for (auto & buildResult : store->buildPathsWithResults(pathsToBuild, bMode, evalStore)) { - if (!buildResult.success()) - buildResult.rethrow(); - + auto buildResults = store->buildPathsWithResults(pathsToBuild, bMode, evalStore); + throwBuildErrors(buildResults, *store); + for (auto & buildResult : buildResults) { for (auto & aux : backmap[buildResult.path]) { std::visit(overloaded { [&](const DerivedPath::Built & bfd) { diff --git a/tests/functional/build.sh b/tests/functional/build.sh index 7fbdb0f07..95a20dc6a 100644 --- a/tests/functional/build.sh +++ b/tests/functional/build.sh @@ -133,3 +133,35 @@ nix build --impure -f multiple-outputs.nix --json e --no-link | jq --exit-status # Make sure that `--stdin` works and does not apply any defaults printf "" | nix build --no-link --stdin --json | jq --exit-status '. == []' printf "%s\n" "$drv^*" | nix build --no-link --stdin --json | jq --exit-status '.[0]|has("drvPath")' + +# --keep-going and FOD +out="$(nix build -f fod-failing.nix -L 2>&1)" && status=0 || status=$? +test "$status" = 1 +# one "hash mismatch" error, one "build of ... failed" +test "$(<<<"$out" grep -E '^error:' | wc -l)" = 2 +<<<"$out" grepQuiet -E "hash mismatch in fixed-output derivation '.*-x1\\.drv'" +<<<"$out" grepQuiet -vE "hash mismatch in fixed-output derivation '.*-x3\\.drv'" +<<<"$out" grepQuiet -vE "hash mismatch in fixed-output derivation '.*-x2\\.drv'" +<<<"$out" grepQuiet -E "error: build of '.*-x[1-4]\\.drv\\^out', '.*-x[1-4]\\.drv\\^out', '.*-x[1-4]\\.drv\\^out', '.*-x[1-4]\\.drv\\^out' failed" + +out="$(nix build -f fod-failing.nix -L x1 x2 x3 --keep-going 2>&1)" && status=0 || status=$? +test "$status" = 1 +# three "hash mismatch" errors - for each failing fod, one "build of ... failed" +test "$(<<<"$out" grep -E '^error:' | wc -l)" = 4 +<<<"$out" grepQuiet -E "hash mismatch in fixed-output derivation '.*-x1\\.drv'" +<<<"$out" grepQuiet -E "hash mismatch in fixed-output derivation '.*-x3\\.drv'" +<<<"$out" grepQuiet -E "hash mismatch in fixed-output derivation '.*-x2\\.drv'" +<<<"$out" grepQuiet -E "error: build of '.*-x[1-3]\\.drv\\^out', '.*-x[1-3]\\.drv\\^out', '.*-x[1-3]\\.drv\\^out' failed" + +out="$(nix build -f fod-failing.nix -L x4 2>&1)" && status=0 || status=$? +test "$status" = 1 +test "$(<<<"$out" grep -E '^error:' | wc -l)" = 2 +<<<"$out" grepQuiet -E "error: 1 dependencies of derivation '.*-x4\\.drv' failed to build" +<<<"$out" grepQuiet -E "hash mismatch in fixed-output derivation '.*-x2\\.drv'" + +out="$(nix build -f fod-failing.nix -L x4 --keep-going 2>&1)" && status=0 || status=$? +test "$status" = 1 +test "$(<<<"$out" grep -E '^error:' | wc -l)" = 3 +<<<"$out" grepQuiet -E "error: 2 dependencies of derivation '.*-x4\\.drv' failed to build" +<<<"$out" grepQuiet -vE "hash mismatch in fixed-output derivation '.*-x3\\.drv'" +<<<"$out" grepQuiet -vE "hash mismatch in fixed-output derivation '.*-x2\\.drv'" diff --git a/tests/functional/fod-failing.nix b/tests/functional/fod-failing.nix new file mode 100644 index 000000000..37c04fe12 --- /dev/null +++ b/tests/functional/fod-failing.nix @@ -0,0 +1,39 @@ +with import ./config.nix; +rec { + x1 = mkDerivation { + name = "x1"; + builder = builtins.toFile "builder.sh" + '' + echo $name > $out + ''; + outputHashMode = "recursive"; + outputHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; + }; + x2 = mkDerivation { + name = "x2"; + builder = builtins.toFile "builder.sh" + '' + echo $name > $out + ''; + outputHashMode = "recursive"; + outputHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; + }; + x3 = mkDerivation { + name = "x3"; + builder = builtins.toFile "builder.sh" + '' + echo $name > $out + ''; + outputHashMode = "recursive"; + outputHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; + }; + x4 = mkDerivation { + name = "x4"; + inherit x2 x3; + builder = builtins.toFile "builder.sh" + '' + echo $x2 $x3 + exit 1 + ''; + }; +}