From aa7ae84bc74c7a6509f5117e38f85b81f1e50276 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 13 Nov 2025 16:10:12 +0100 Subject: [PATCH 1/7] BuildResult::Failure::rethrow(): Show status --- src/libstore/build-result.cc | 8 ++++---- src/libstore/include/nix/store/build-result.hh | 6 +++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/libstore/build-result.cc b/src/libstore/build-result.cc index e0eb1899722..95c0fd807f7 100644 --- a/src/libstore/build-result.cc +++ b/src/libstore/build-result.cc @@ -13,7 +13,7 @@ std::strong_ordering BuildResult::Success::operator<=>(const BuildResult::Succes bool BuildResult::Failure::operator==(const BuildResult::Failure &) const noexcept = default; std::strong_ordering BuildResult::Failure::operator<=>(const BuildResult::Failure &) const noexcept = default; -static std::string_view statusToString(BuildResult::Success::Status status) +std::string_view BuildResult::Success::statusToString(BuildResult::Success::Status status) { switch (status) { case BuildResult::Success::Built: @@ -29,7 +29,7 @@ static std::string_view statusToString(BuildResult::Success::Status status) } } -static std::string_view statusToString(BuildResult::Failure::Status status) +std::string_view BuildResult::Failure::statusToString(BuildResult::Failure::Status status) { switch (status) { case BuildResult::Failure::PermanentFailure: @@ -66,9 +66,9 @@ void to_json(nlohmann::json & json, const BuildResult & buildResult) json = nlohmann::json::object(); // FIXME: change this to have `success` and `failure` objects. if (auto success = buildResult.tryGetSuccess()) { - json["status"] = statusToString(success->status); + json["status"] = BuildResult::Success::statusToString(success->status); } else if (auto failure = buildResult.tryGetFailure()) { - json["status"] = statusToString(failure->status); + json["status"] = BuildResult::Failure::statusToString(failure->status); if (failure->errorMsg != "") json["errorMsg"] = failure->errorMsg; if (failure->isNonDeterministic) diff --git a/src/libstore/include/nix/store/build-result.hh b/src/libstore/include/nix/store/build-result.hh index 52bd179cb4a..c6141cb2c00 100644 --- a/src/libstore/include/nix/store/build-result.hh +++ b/src/libstore/include/nix/store/build-result.hh @@ -31,6 +31,8 @@ struct BuildResult ResolvesToAlreadyValid = 13, } status; + static std::string_view statusToString(Status status); + /** * For derivations, a mapping from the names of the wanted outputs * to actual paths. @@ -77,6 +79,8 @@ struct BuildResult HashMismatch = 15, } status = MiscFailure; + static std::string_view statusToString(Status status); + /** * Information about the error if the build failed. * @@ -98,7 +102,7 @@ struct BuildResult [[noreturn]] void rethrow() const { - throw Error("%s", errorMsg); + throw Error("%s", errorMsg.empty() ? statusToString(status) : errorMsg); } }; From e0fdfa87baca2dae2aae24156144b11c6c9e5165 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 13 Nov 2025 16:41:40 +0100 Subject: [PATCH 2/7] BuildResult: Add a "Cancelled" status This denotes the result of a build that didn't succeed or fail, but was cancelled because some other goal failed and --keep-going was not enabled. --- src/libstore/build-result.cc | 2 ++ src/libstore/include/nix/store/build-result.hh | 1 + src/libstore/include/nix/store/build/goal.hh | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libstore/build-result.cc b/src/libstore/build-result.cc index 95c0fd807f7..3e96d585235 100644 --- a/src/libstore/build-result.cc +++ b/src/libstore/build-result.cc @@ -56,6 +56,8 @@ std::string_view BuildResult::Failure::statusToString(BuildResult::Failure::Stat return "NoSubstituters"; case BuildResult::Failure::HashMismatch: return "HashMismatch"; + case BuildResult::Failure::Cancelled: + return "Cancelled"; default: unreachable(); } diff --git a/src/libstore/include/nix/store/build-result.hh b/src/libstore/include/nix/store/build-result.hh index c6141cb2c00..a21c6a89d80 100644 --- a/src/libstore/include/nix/store/build-result.hh +++ b/src/libstore/include/nix/store/build-result.hh @@ -77,6 +77,7 @@ struct BuildResult /// know about this one, so change it back to `OutputRejected` /// before serialization. HashMismatch = 15, + Cancelled = 16, } status = MiscFailure; static std::string_view statusToString(Status status); diff --git a/src/libstore/include/nix/store/build/goal.hh b/src/libstore/include/nix/store/build/goal.hh index 52700d12ea9..f009b60a10b 100644 --- a/src/libstore/include/nix/store/build/goal.hh +++ b/src/libstore/include/nix/store/build/goal.hh @@ -109,7 +109,7 @@ public: /** * Build result. */ - BuildResult buildResult; + BuildResult buildResult = {.inner = BuildResult::Failure{.status = BuildResult::Failure::Cancelled}}; /** * Suspend our goal and wait until we get `work`-ed again. From 508c09aef31a0c34b2770bc6485fbc73793eb086 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 26 Nov 2025 18:47:52 +0100 Subject: [PATCH 3/7] Introduce InstallableWithBuildResult --- src/libcmd/include/nix/cmd/installables.hh | 8 +++- src/libcmd/installables.cc | 51 ++++++++++++---------- src/nix/profile.cc | 10 ++--- 3 files changed, 40 insertions(+), 29 deletions(-) diff --git a/src/libcmd/include/nix/cmd/installables.hh b/src/libcmd/include/nix/cmd/installables.hh index 530334e037b..07cfd3d23b4 100644 --- a/src/libcmd/include/nix/cmd/installables.hh +++ b/src/libcmd/include/nix/cmd/installables.hh @@ -96,6 +96,12 @@ typedef std::vector DerivedPathsWithInfo; struct Installable; +struct InstallableWithBuildResult +{ + ref installable; + BuiltPathWithResult builtPath; +}; + /** * Shorthand, for less typing and helping us keep the choice of * collection in sync. @@ -160,7 +166,7 @@ struct Installable const Installables & installables, BuildMode bMode = bmNormal); - static std::vector, BuiltPathWithResult>> build2( + static std::vector build2( ref evalStore, ref store, Realise mode, diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 37959431a18..c414117b450 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -629,7 +629,7 @@ static void throwBuildErrors(std::vector & buildResults, const } } -std::vector, BuiltPathWithResult>> Installable::build2( +std::vector Installable::build2( ref evalStore, ref store, Realise mode, const Installables & installables, BuildMode bMode) { if (mode == Realise::Nothing) @@ -651,7 +651,7 @@ std::vector, BuiltPathWithResult>> Installable::build } } - std::vector, BuiltPathWithResult>> res; + std::vector res; switch (mode) { @@ -666,17 +666,20 @@ std::vector, BuiltPathWithResult>> Installable::build [&](const DerivedPath::Built & bfd) { auto outputs = resolveDerivedPath(*store, bfd, &*evalStore); res.push_back( - {aux.installable, - {.path = - BuiltPath::Built{ - .drvPath = - make_ref(getBuiltPath(evalStore, store, *bfd.drvPath)), - .outputs = outputs, - }, - .info = aux.info}}); + {.installable = aux.installable, + .builtPath = { + .path = + BuiltPath::Built{ + .drvPath = make_ref( + getBuiltPath(evalStore, store, *bfd.drvPath)), + .outputs = outputs, + }, + .info = aux.info}}); }, [&](const DerivedPath::Opaque & bo) { - res.push_back({aux.installable, {.path = BuiltPath::Opaque{bo.path}, .info = aux.info}}); + res.push_back( + {.installable = aux.installable, + .builtPath = {.path = BuiltPath::Opaque{bo.path}, .info = aux.info}}); }, }, path.raw()); @@ -692,7 +695,7 @@ std::vector, BuiltPathWithResult>> Installable::build auto buildResults = store->buildPathsWithResults(pathsToBuild, bMode, evalStore); throwBuildErrors(buildResults, *store); for (auto & buildResult : buildResults) { - // If we didn't throw, they must all be sucesses + // If we didn't throw, they must all be successes. auto & success = std::get(buildResult.inner); for (auto & aux : backmap[buildResult.path]) { std::visit( @@ -702,20 +705,22 @@ std::vector, BuiltPathWithResult>> Installable::build for (auto & [outputName, realisation] : success.builtOutputs) outputs.emplace(outputName, realisation.outPath); res.push_back( - {aux.installable, - {.path = - BuiltPath::Built{ - .drvPath = - make_ref(getBuiltPath(evalStore, store, *bfd.drvPath)), - .outputs = outputs, - }, - .info = aux.info, - .result = buildResult}}); + {.installable = aux.installable, + .builtPath = { + .path = + BuiltPath::Built{ + .drvPath = make_ref( + getBuiltPath(evalStore, store, *bfd.drvPath)), + .outputs = outputs, + }, + .info = aux.info, + .result = buildResult}}); }, [&](const DerivedPath::Opaque & bo) { res.push_back( - {aux.installable, - {.path = BuiltPath::Opaque{bo.path}, .info = aux.info, .result = buildResult}}); + {.installable = aux.installable, + .builtPath = { + .path = BuiltPath::Opaque{bo.path}, .info = aux.info, .result = buildResult}}); }, }, buildResult.path.raw()); diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 702633d96b0..aed06727504 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -313,11 +313,11 @@ struct ProfileManifest }; static std::map>> -builtPathsPerInstallable(const std::vector, BuiltPathWithResult>> & builtPaths) +builtPathsPerInstallable(const std::vector & builtPaths) { std::map>> res; - for (auto & [installable, builtPath] : builtPaths) { - auto & r = res.insert({&*installable, + for (auto & b : builtPaths) { + auto & r = res.insert({&*b.installable, { {}, make_ref(), @@ -327,8 +327,8 @@ builtPathsPerInstallable(const std::vector, BuiltPath (e.g. meta.priority fields) if the installable returned multiple derivations. So pick one arbitrarily. FIXME: print a warning? */ - r.first.push_back(builtPath.path); - r.second = builtPath.info; + r.first.push_back(b.builtPath.path); + r.second = b.builtPath.info; } return res; } From c6d07c4226e0d2f0899e6c7c516129636fb93855 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 26 Nov 2025 20:22:29 +0100 Subject: [PATCH 4/7] InstallableWithBuildResult: Store build failures --- src/libcmd/include/nix/cmd/installables.hh | 12 ++++++++++- src/libcmd/installables.cc | 23 ++++++++++++++++------ src/nix/profile.cc | 5 +++-- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/libcmd/include/nix/cmd/installables.hh b/src/libcmd/include/nix/cmd/installables.hh index 07cfd3d23b4..8516b688811 100644 --- a/src/libcmd/include/nix/cmd/installables.hh +++ b/src/libcmd/include/nix/cmd/installables.hh @@ -99,7 +99,17 @@ struct Installable; struct InstallableWithBuildResult { ref installable; - BuiltPathWithResult builtPath; + + using Success = BuiltPathWithResult; + + using Failure = BuildResult; // must be a `BuildResult::Failure` + + std::variant result; + + /** + * Throw an exception if this represents a failure, otherwise returns a `BuiltPathWithResult`. + */ + const BuiltPathWithResult & getSuccess() const; }; /** diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index c414117b450..8988743340e 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -590,12 +590,22 @@ static SingleBuiltPath getBuiltPath(ref evalStore, ref store, cons b.raw()); } +const BuiltPathWithResult & InstallableWithBuildResult::getSuccess() const +{ + if (auto * failure = std::get_if(&result)) { + auto failure2 = failure->tryGetFailure(); + assert(failure2); + failure2->rethrow(); + } else + return *std::get_if(&result); +} + std::vector Installable::build( ref evalStore, ref store, Realise mode, const Installables & installables, BuildMode bMode) { std::vector res; - for (auto & [_, builtPathWithResult] : build2(evalStore, store, mode, installables, bMode)) - res.push_back(builtPathWithResult); + for (auto & b : build2(evalStore, store, mode, installables, bMode)) + res.push_back(b.getSuccess()); return res; } @@ -667,7 +677,7 @@ std::vector Installable::build2( auto outputs = resolveDerivedPath(*store, bfd, &*evalStore); res.push_back( {.installable = aux.installable, - .builtPath = { + .result = InstallableWithBuildResult::Success{ .path = BuiltPath::Built{ .drvPath = make_ref( @@ -679,7 +689,8 @@ std::vector Installable::build2( [&](const DerivedPath::Opaque & bo) { res.push_back( {.installable = aux.installable, - .builtPath = {.path = BuiltPath::Opaque{bo.path}, .info = aux.info}}); + .result = InstallableWithBuildResult::Success{ + .path = BuiltPath::Opaque{bo.path}, .info = aux.info}}); }, }, path.raw()); @@ -706,7 +717,7 @@ std::vector Installable::build2( outputs.emplace(outputName, realisation.outPath); res.push_back( {.installable = aux.installable, - .builtPath = { + .result = InstallableWithBuildResult::Success{ .path = BuiltPath::Built{ .drvPath = make_ref( @@ -719,7 +730,7 @@ std::vector Installable::build2( [&](const DerivedPath::Opaque & bo) { res.push_back( {.installable = aux.installable, - .builtPath = { + .result = InstallableWithBuildResult::Success{ .path = BuiltPath::Opaque{bo.path}, .info = aux.info, .result = buildResult}}); }, }, diff --git a/src/nix/profile.cc b/src/nix/profile.cc index aed06727504..7c70f54444b 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -327,8 +327,9 @@ builtPathsPerInstallable(const std::vector & builtPa (e.g. meta.priority fields) if the installable returned multiple derivations. So pick one arbitrarily. FIXME: print a warning? */ - r.first.push_back(b.builtPath.path); - r.second = b.builtPath.info; + auto builtPath = b.getSuccess(); + r.first.push_back(builtPath.path); + r.second = builtPath.info; } return res; } From ee75a669dc00c4df40bb734e4c31cd0f2b2a0622 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 26 Nov 2025 22:04:40 +0100 Subject: [PATCH 5/7] Installable::build(): Report succeeding/failing installables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If any installable fails to build, it will print something like ✅ flake:nixpkgs#hello ❌ git+file:///home/eelco/Dev/patchelf#packages.x86_64-linux.default error: Cannot build '/nix/store/nh2dx9cqcy9lw4d4rvd0dbsflwdsbzdy-patchelf-0.18.0.drv'. Reason: builder failed with exit code 2. ... ❓ git+file:///home/eelco/Dev/patchelf#hydraJobs.coverage (cancelled) It doesn't print anything if all installables succeed. --- src/libcmd/include/nix/cmd/installables.hh | 2 + src/libcmd/installables.cc | 78 +++++++++++++--------- src/nix/profile.cc | 12 ++-- tests/functional/build.sh | 13 ++-- 4 files changed, 61 insertions(+), 44 deletions(-) diff --git a/src/libcmd/include/nix/cmd/installables.hh b/src/libcmd/include/nix/cmd/installables.hh index 8516b688811..2ea35261c7f 100644 --- a/src/libcmd/include/nix/cmd/installables.hh +++ b/src/libcmd/include/nix/cmd/installables.hh @@ -183,6 +183,8 @@ struct Installable const Installables & installables, BuildMode bMode = bmNormal); + static void throwBuildErrors(std::vector & buildResults, const Store & store); + static std::set toStorePathSet( ref evalStore, ref store, Realise mode, OperateOn operateOn, const Installables & installables); diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 8988743340e..7f1ea4d5cc0 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -21,6 +21,7 @@ #include "nix/util/url.hh" #include "nix/fetchers/registry.hh" #include "nix/store/build-result.hh" +#include "nix/util/exit.hh" #include #include @@ -600,45 +601,54 @@ const BuiltPathWithResult & InstallableWithBuildResult::getSuccess() const return *std::get_if(&result); } -std::vector Installable::build( - ref evalStore, ref store, Realise mode, const Installables & installables, BuildMode bMode) +void Installable::throwBuildErrors(std::vector & buildResults, const Store & store) { - std::vector res; - for (auto & b : build2(evalStore, store, mode, installables, bMode)) - res.push_back(b.getSuccess()); - return res; -} - -static void throwBuildErrors(std::vector & buildResults, const Store & store) -{ - std::vector> failed; for (auto & buildResult : buildResults) { - if (auto * failure = buildResult.tryGetFailure()) { - failed.push_back({&buildResult, failure}); - } - } + if (std::get_if(&buildResult.result)) { + // Report success first. + for (auto & buildResult : buildResults) { + if (std::get_if(&buildResult.result)) + notice("✅ " ANSI_BOLD "%s" ANSI_NORMAL, buildResult.installable->what()); + } - auto failedResult = failed.begin(); - if (failedResult != failed.end()) { - if (failed.size() == 1) { - failedResult->second->rethrow(); - } else { - StringSet failedPaths; - for (; failedResult != failed.end(); failedResult++) { - if (!failedResult->second->errorMsg.empty()) { - logError( - ErrorInfo{ - .level = lvlError, - .msg = failedResult->second->errorMsg, - }); + // Then failures. + for (auto & buildResult : buildResults) { + if (auto failure = std::get_if(&buildResult.result)) { + auto failure2 = failure->tryGetFailure(); + assert(failure2); + if (failure2->status == BuildResult::Failure::Cancelled + // FIXME: remove MiscFailure eventually + || failure2->status == BuildResult::Failure::MiscFailure) + notice( + "❓ " ANSI_BOLD "%s" ANSI_NORMAL ANSI_FAINT " (cancelled)", + buildResult.installable->what()); + else { + printError("❌ " ANSI_RED "%s" ANSI_NORMAL, buildResult.installable->what()); + try { + failure2->rethrow(); + } catch (Error & e) { + logError(e.info()); + } + } } - failedPaths.insert(failedResult->first->path.to_string(store)); } - throw Error("build of %s failed", concatStringsSep(", ", quoteStrings(failedPaths))); + + throw Exit(1); } } } +std::vector Installable::build( + ref evalStore, ref store, Realise mode, const Installables & installables, BuildMode bMode) +{ + auto results = build2(evalStore, store, mode, installables, bMode); + throwBuildErrors(results, *store); + std::vector res; + for (auto & b : results) + res.push_back(b.getSuccess()); + return res; +} + std::vector Installable::build2( ref evalStore, ref store, Realise mode, const Installables & installables, BuildMode bMode) { @@ -704,9 +714,13 @@ std::vector Installable::build2( printMissing(store, pathsToBuild, lvlInfo); auto buildResults = store->buildPathsWithResults(pathsToBuild, bMode, evalStore); - throwBuildErrors(buildResults, *store); for (auto & buildResult : buildResults) { - // If we didn't throw, they must all be successes. + if (buildResult.tryGetFailure()) { + for (auto & aux : backmap[buildResult.path]) { + res.push_back({.installable = aux.installable, .result = buildResult}); + } + continue; + } auto & success = std::get(buildResult.inner); for (auto & aux : backmap[buildResult.path]) { std::visit( diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 7c70f54444b..42a8f9dcfec 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -364,8 +364,10 @@ struct CmdProfileAdd : InstallablesCommand, MixDefaultProfile { ProfileManifest manifest(*getEvalState(), *profile); - auto builtPaths = builtPathsPerInstallable( - Installable::build2(getEvalStore(), store, Realise::Outputs, installables, bmNormal)); + auto buildResults = Installable::build2(getEvalStore(), store, Realise::Outputs, installables, bmNormal); + Installable::throwBuildErrors(buildResults, *store); + + auto builtPaths = builtPathsPerInstallable(buildResults); for (auto & installable : installables) { ProfileElement element; @@ -767,8 +769,10 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf return; } - auto builtPaths = builtPathsPerInstallable( - Installable::build2(getEvalStore(), store, Realise::Outputs, installables, bmNormal)); + auto buildResults = Installable::build2(getEvalStore(), store, Realise::Outputs, installables, bmNormal); + Installable::throwBuildErrors(buildResults, *store); + + auto builtPaths = builtPathsPerInstallable(buildResults); for (size_t i = 0; i < installables.size(); ++i) { auto & installable = installables.at(i); diff --git a/tests/functional/build.sh b/tests/functional/build.sh index 0a19ff7dabb..c9e04056da8 100755 --- a/tests/functional/build.sh +++ b/tests/functional/build.sh @@ -161,20 +161,17 @@ printf "%s\n" "$drv^*" | nix build --no-link --stdin --json | jq --exit-status ' 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 -cE '^error:')" = 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" +test "$(<<<"$out" grep -cE '^error:')" = 1 +test "$(<<<"$out" grep -cE '(cancelled)')" = 3 +<<<"$out" grepQuiet -E "hash mismatch in fixed-output derivation" 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 -cE '^error:')" = 4 +# three "hash mismatch" errors - for each failing fod +test "$(<<<"$out" grep -cE '^error:')" = 3 <<<"$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 From 706fb076c82ac1d50852b41a3f383b77224056fc Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 4 Dec 2025 15:35:28 +0100 Subject: [PATCH 6/7] Print cancelled builds before failed builds --- src/libcmd/installables.cc | 30 +++++++++++-------- .../include/nix/store/build-result.hh | 7 +++++ 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 7f1ea4d5cc0..8293254db08 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -611,24 +611,28 @@ void Installable::throwBuildErrors(std::vector & bui notice("✅ " ANSI_BOLD "%s" ANSI_NORMAL, buildResult.installable->what()); } - // Then failures. + // Then cancelled builds. for (auto & buildResult : buildResults) { if (auto failure = std::get_if(&buildResult.result)) { - auto failure2 = failure->tryGetFailure(); - assert(failure2); - if (failure2->status == BuildResult::Failure::Cancelled - // FIXME: remove MiscFailure eventually - || failure2->status == BuildResult::Failure::MiscFailure) + if (failure->isCancelled()) notice( "❓ " ANSI_BOLD "%s" ANSI_NORMAL ANSI_FAINT " (cancelled)", buildResult.installable->what()); - else { - printError("❌ " ANSI_RED "%s" ANSI_NORMAL, buildResult.installable->what()); - try { - failure2->rethrow(); - } catch (Error & e) { - logError(e.info()); - } + } + } + + // Then failures. + for (auto & buildResult : buildResults) { + if (auto failure = std::get_if(&buildResult.result)) { + if (failure->isCancelled()) + continue; + auto failure2 = failure->tryGetFailure(); + assert(failure2); + printError("❌ " ANSI_RED "%s" ANSI_NORMAL, buildResult.installable->what()); + try { + failure2->rethrow(); + } catch (Error & e) { + logError(e.info()); } } } diff --git a/src/libstore/include/nix/store/build-result.hh b/src/libstore/include/nix/store/build-result.hh index a21c6a89d80..3333132884d 100644 --- a/src/libstore/include/nix/store/build-result.hh +++ b/src/libstore/include/nix/store/build-result.hh @@ -147,6 +147,13 @@ struct BuildResult bool operator==(const BuildResult &) const noexcept; std::strong_ordering operator<=>(const BuildResult &) const noexcept; + + bool isCancelled() const + { + auto failure = tryGetFailure(); + // FIXME: remove MiscFailure eventually. + return failure && (failure->status == Failure::Cancelled || failure->status == Failure::MiscFailure); + } }; /** From ed90b251c1e4fe6efe5ab5c2c40e43b8d0926bc6 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 4 Dec 2025 15:37:56 +0100 Subject: [PATCH 7/7] Fix comment --- tests/functional/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/build.sh b/tests/functional/build.sh index c9e04056da8..4b13eb7ebc0 100755 --- a/tests/functional/build.sh +++ b/tests/functional/build.sh @@ -160,7 +160,7 @@ printf "%s\n" "$drv^*" | nix build --no-link --stdin --json | jq --exit-status ' # --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" +# one "hash mismatch" error, one cancelled build test "$(<<<"$out" grep -cE '^error:')" = 1 test "$(<<<"$out" grep -cE '(cancelled)')" = 3 <<<"$out" grepQuiet -E "hash mismatch in fixed-output derivation"