Skip to content

Commit 055dfbc

Browse files
authored
Merge pull request #281 from DeterminateSystems/show-build-result
nix build, nix profile: Report failing/succeeding installables
2 parents 40abd9e + ed90b25 commit 055dfbc

7 files changed

Lines changed: 142 additions & 74 deletions

File tree

src/libcmd/include/nix/cmd/installables.hh

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,22 @@ typedef std::vector<DerivedPathWithInfo> DerivedPathsWithInfo;
9696

9797
struct Installable;
9898

99+
struct InstallableWithBuildResult
100+
{
101+
ref<Installable> installable;
102+
103+
using Success = BuiltPathWithResult;
104+
105+
using Failure = BuildResult; // must be a `BuildResult::Failure`
106+
107+
std::variant<Success, Failure> result;
108+
109+
/**
110+
* Throw an exception if this represents a failure, otherwise returns a `BuiltPathWithResult`.
111+
*/
112+
const BuiltPathWithResult & getSuccess() const;
113+
};
114+
99115
/**
100116
* Shorthand, for less typing and helping us keep the choice of
101117
* collection in sync.
@@ -160,13 +176,15 @@ struct Installable
160176
const Installables & installables,
161177
BuildMode bMode = bmNormal);
162178

163-
static std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> build2(
179+
static std::vector<InstallableWithBuildResult> build2(
164180
ref<Store> evalStore,
165181
ref<Store> store,
166182
Realise mode,
167183
const Installables & installables,
168184
BuildMode bMode = bmNormal);
169185

186+
static void throwBuildErrors(std::vector<InstallableWithBuildResult> & buildResults, const Store & store);
187+
170188
static std::set<StorePath> toStorePathSet(
171189
ref<Store> evalStore, ref<Store> store, Realise mode, OperateOn operateOn, const Installables & installables);
172190

src/libcmd/installables.cc

Lines changed: 85 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "nix/util/url.hh"
2222
#include "nix/fetchers/registry.hh"
2323
#include "nix/store/build-result.hh"
24+
#include "nix/util/exit.hh"
2425

2526
#include <regex>
2627
#include <queue>
@@ -590,46 +591,69 @@ static SingleBuiltPath getBuiltPath(ref<Store> evalStore, ref<Store> store, cons
590591
b.raw());
591592
}
592593

593-
std::vector<BuiltPathWithResult> Installable::build(
594-
ref<Store> evalStore, ref<Store> store, Realise mode, const Installables & installables, BuildMode bMode)
594+
const BuiltPathWithResult & InstallableWithBuildResult::getSuccess() const
595595
{
596-
std::vector<BuiltPathWithResult> res;
597-
for (auto & [_, builtPathWithResult] : build2(evalStore, store, mode, installables, bMode))
598-
res.push_back(builtPathWithResult);
599-
return res;
596+
if (auto * failure = std::get_if<Failure>(&result)) {
597+
auto failure2 = failure->tryGetFailure();
598+
assert(failure2);
599+
failure2->rethrow();
600+
} else
601+
return *std::get_if<Success>(&result);
600602
}
601603

602-
static void throwBuildErrors(std::vector<KeyedBuildResult> & buildResults, const Store & store)
604+
void Installable::throwBuildErrors(std::vector<InstallableWithBuildResult> & buildResults, const Store & store)
603605
{
604-
std::vector<std::pair<const KeyedBuildResult *, const KeyedBuildResult::Failure *>> failed;
605606
for (auto & buildResult : buildResults) {
606-
if (auto * failure = buildResult.tryGetFailure()) {
607-
failed.push_back({&buildResult, failure});
608-
}
609-
}
607+
if (std::get_if<InstallableWithBuildResult::Failure>(&buildResult.result)) {
608+
// Report success first.
609+
for (auto & buildResult : buildResults) {
610+
if (std::get_if<InstallableWithBuildResult::Success>(&buildResult.result))
611+
notice("" ANSI_BOLD "%s" ANSI_NORMAL, buildResult.installable->what());
612+
}
610613

611-
auto failedResult = failed.begin();
612-
if (failedResult != failed.end()) {
613-
if (failed.size() == 1) {
614-
failedResult->second->rethrow();
615-
} else {
616-
StringSet failedPaths;
617-
for (; failedResult != failed.end(); failedResult++) {
618-
if (!failedResult->second->errorMsg.empty()) {
619-
logError(
620-
ErrorInfo{
621-
.level = lvlError,
622-
.msg = failedResult->second->errorMsg,
623-
});
614+
// Then cancelled builds.
615+
for (auto & buildResult : buildResults) {
616+
if (auto failure = std::get_if<InstallableWithBuildResult::Failure>(&buildResult.result)) {
617+
if (failure->isCancelled())
618+
notice(
619+
"" ANSI_BOLD "%s" ANSI_NORMAL ANSI_FAINT " (cancelled)",
620+
buildResult.installable->what());
621+
}
622+
}
623+
624+
// Then failures.
625+
for (auto & buildResult : buildResults) {
626+
if (auto failure = std::get_if<InstallableWithBuildResult::Failure>(&buildResult.result)) {
627+
if (failure->isCancelled())
628+
continue;
629+
auto failure2 = failure->tryGetFailure();
630+
assert(failure2);
631+
printError("" ANSI_RED "%s" ANSI_NORMAL, buildResult.installable->what());
632+
try {
633+
failure2->rethrow();
634+
} catch (Error & e) {
635+
logError(e.info());
636+
}
624637
}
625-
failedPaths.insert(failedResult->first->path.to_string(store));
626638
}
627-
throw Error("build of %s failed", concatStringsSep(", ", quoteStrings(failedPaths)));
639+
640+
throw Exit(1);
628641
}
629642
}
630643
}
631644

632-
std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> Installable::build2(
645+
std::vector<BuiltPathWithResult> Installable::build(
646+
ref<Store> evalStore, ref<Store> store, Realise mode, const Installables & installables, BuildMode bMode)
647+
{
648+
auto results = build2(evalStore, store, mode, installables, bMode);
649+
throwBuildErrors(results, *store);
650+
std::vector<BuiltPathWithResult> res;
651+
for (auto & b : results)
652+
res.push_back(b.getSuccess());
653+
return res;
654+
}
655+
656+
std::vector<InstallableWithBuildResult> Installable::build2(
633657
ref<Store> evalStore, ref<Store> store, Realise mode, const Installables & installables, BuildMode bMode)
634658
{
635659
if (mode == Realise::Nothing)
@@ -651,7 +675,7 @@ std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> Installable::build
651675
}
652676
}
653677

654-
std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> res;
678+
std::vector<InstallableWithBuildResult> res;
655679

656680
switch (mode) {
657681

@@ -666,17 +690,21 @@ std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> Installable::build
666690
[&](const DerivedPath::Built & bfd) {
667691
auto outputs = resolveDerivedPath(*store, bfd, &*evalStore);
668692
res.push_back(
669-
{aux.installable,
670-
{.path =
671-
BuiltPath::Built{
672-
.drvPath =
673-
make_ref<SingleBuiltPath>(getBuiltPath(evalStore, store, *bfd.drvPath)),
674-
.outputs = outputs,
675-
},
676-
.info = aux.info}});
693+
{.installable = aux.installable,
694+
.result = InstallableWithBuildResult::Success{
695+
.path =
696+
BuiltPath::Built{
697+
.drvPath = make_ref<SingleBuiltPath>(
698+
getBuiltPath(evalStore, store, *bfd.drvPath)),
699+
.outputs = outputs,
700+
},
701+
.info = aux.info}});
677702
},
678703
[&](const DerivedPath::Opaque & bo) {
679-
res.push_back({aux.installable, {.path = BuiltPath::Opaque{bo.path}, .info = aux.info}});
704+
res.push_back(
705+
{.installable = aux.installable,
706+
.result = InstallableWithBuildResult::Success{
707+
.path = BuiltPath::Opaque{bo.path}, .info = aux.info}});
680708
},
681709
},
682710
path.raw());
@@ -690,9 +718,13 @@ std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> Installable::build
690718
printMissing(store, pathsToBuild, lvlInfo);
691719

692720
auto buildResults = store->buildPathsWithResults(pathsToBuild, bMode, evalStore);
693-
throwBuildErrors(buildResults, *store);
694721
for (auto & buildResult : buildResults) {
695-
// If we didn't throw, they must all be sucesses
722+
if (buildResult.tryGetFailure()) {
723+
for (auto & aux : backmap[buildResult.path]) {
724+
res.push_back({.installable = aux.installable, .result = buildResult});
725+
}
726+
continue;
727+
}
696728
auto & success = std::get<nix::BuildResult::Success>(buildResult.inner);
697729
for (auto & aux : backmap[buildResult.path]) {
698730
std::visit(
@@ -702,20 +734,22 @@ std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> Installable::build
702734
for (auto & [outputName, realisation] : success.builtOutputs)
703735
outputs.emplace(outputName, realisation.outPath);
704736
res.push_back(
705-
{aux.installable,
706-
{.path =
707-
BuiltPath::Built{
708-
.drvPath =
709-
make_ref<SingleBuiltPath>(getBuiltPath(evalStore, store, *bfd.drvPath)),
710-
.outputs = outputs,
711-
},
712-
.info = aux.info,
713-
.result = buildResult}});
737+
{.installable = aux.installable,
738+
.result = InstallableWithBuildResult::Success{
739+
.path =
740+
BuiltPath::Built{
741+
.drvPath = make_ref<SingleBuiltPath>(
742+
getBuiltPath(evalStore, store, *bfd.drvPath)),
743+
.outputs = outputs,
744+
},
745+
.info = aux.info,
746+
.result = buildResult}});
714747
},
715748
[&](const DerivedPath::Opaque & bo) {
716749
res.push_back(
717-
{aux.installable,
718-
{.path = BuiltPath::Opaque{bo.path}, .info = aux.info, .result = buildResult}});
750+
{.installable = aux.installable,
751+
.result = InstallableWithBuildResult::Success{
752+
.path = BuiltPath::Opaque{bo.path}, .info = aux.info, .result = buildResult}});
719753
},
720754
},
721755
buildResult.path.raw());

src/libstore/build-result.cc

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ std::strong_ordering BuildResult::Success::operator<=>(const BuildResult::Succes
1313
bool BuildResult::Failure::operator==(const BuildResult::Failure &) const noexcept = default;
1414
std::strong_ordering BuildResult::Failure::operator<=>(const BuildResult::Failure &) const noexcept = default;
1515

16-
static std::string_view statusToString(BuildResult::Success::Status status)
16+
std::string_view BuildResult::Success::statusToString(BuildResult::Success::Status status)
1717
{
1818
switch (status) {
1919
case BuildResult::Success::Built:
@@ -29,7 +29,7 @@ static std::string_view statusToString(BuildResult::Success::Status status)
2929
}
3030
}
3131

32-
static std::string_view statusToString(BuildResult::Failure::Status status)
32+
std::string_view BuildResult::Failure::statusToString(BuildResult::Failure::Status status)
3333
{
3434
switch (status) {
3535
case BuildResult::Failure::PermanentFailure:
@@ -56,6 +56,8 @@ static std::string_view statusToString(BuildResult::Failure::Status status)
5656
return "NoSubstituters";
5757
case BuildResult::Failure::HashMismatch:
5858
return "HashMismatch";
59+
case BuildResult::Failure::Cancelled:
60+
return "Cancelled";
5961
default:
6062
unreachable();
6163
}
@@ -66,9 +68,9 @@ void to_json(nlohmann::json & json, const BuildResult & buildResult)
6668
json = nlohmann::json::object();
6769
// FIXME: change this to have `success` and `failure` objects.
6870
if (auto success = buildResult.tryGetSuccess()) {
69-
json["status"] = statusToString(success->status);
71+
json["status"] = BuildResult::Success::statusToString(success->status);
7072
} else if (auto failure = buildResult.tryGetFailure()) {
71-
json["status"] = statusToString(failure->status);
73+
json["status"] = BuildResult::Failure::statusToString(failure->status);
7274
if (failure->errorMsg != "")
7375
json["errorMsg"] = failure->errorMsg;
7476
if (failure->isNonDeterministic)

src/libstore/include/nix/store/build-result.hh

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ struct BuildResult
3131
ResolvesToAlreadyValid = 13,
3232
} status;
3333

34+
static std::string_view statusToString(Status status);
35+
3436
/**
3537
* For derivations, a mapping from the names of the wanted outputs
3638
* to actual paths.
@@ -75,8 +77,11 @@ struct BuildResult
7577
/// know about this one, so change it back to `OutputRejected`
7678
/// before serialization.
7779
HashMismatch = 15,
80+
Cancelled = 16,
7881
} status = MiscFailure;
7982

83+
static std::string_view statusToString(Status status);
84+
8085
/**
8186
* Information about the error if the build failed.
8287
*
@@ -98,7 +103,7 @@ struct BuildResult
98103

99104
[[noreturn]] void rethrow() const
100105
{
101-
throw Error("%s", errorMsg);
106+
throw Error("%s", errorMsg.empty() ? statusToString(status) : errorMsg);
102107
}
103108
};
104109

@@ -142,6 +147,13 @@ struct BuildResult
142147

143148
bool operator==(const BuildResult &) const noexcept;
144149
std::strong_ordering operator<=>(const BuildResult &) const noexcept;
150+
151+
bool isCancelled() const
152+
{
153+
auto failure = tryGetFailure();
154+
// FIXME: remove MiscFailure eventually.
155+
return failure && (failure->status == Failure::Cancelled || failure->status == Failure::MiscFailure);
156+
}
145157
};
146158

147159
/**

src/libstore/include/nix/store/build/goal.hh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ public:
109109
/**
110110
* Build result.
111111
*/
112-
BuildResult buildResult;
112+
BuildResult buildResult = {.inner = BuildResult::Failure{.status = BuildResult::Failure::Cancelled}};
113113

114114
/**
115115
* Suspend our goal and wait until we get `work`-ed again.

src/nix/profile.cc

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -313,11 +313,11 @@ struct ProfileManifest
313313
};
314314

315315
static std::map<Installable *, std::pair<BuiltPaths, ref<ExtraPathInfo>>>
316-
builtPathsPerInstallable(const std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> & builtPaths)
316+
builtPathsPerInstallable(const std::vector<InstallableWithBuildResult> & builtPaths)
317317
{
318318
std::map<Installable *, std::pair<BuiltPaths, ref<ExtraPathInfo>>> res;
319-
for (auto & [installable, builtPath] : builtPaths) {
320-
auto & r = res.insert({&*installable,
319+
for (auto & b : builtPaths) {
320+
auto & r = res.insert({&*b.installable,
321321
{
322322
{},
323323
make_ref<ExtraPathInfo>(),
@@ -327,6 +327,7 @@ builtPathsPerInstallable(const std::vector<std::pair<ref<Installable>, BuiltPath
327327
(e.g. meta.priority fields) if the installable returned
328328
multiple derivations. So pick one arbitrarily. FIXME:
329329
print a warning? */
330+
auto builtPath = b.getSuccess();
330331
r.first.push_back(builtPath.path);
331332
r.second = builtPath.info;
332333
}
@@ -363,8 +364,10 @@ struct CmdProfileAdd : InstallablesCommand, MixDefaultProfile
363364
{
364365
ProfileManifest manifest(*getEvalState(), *profile);
365366

366-
auto builtPaths = builtPathsPerInstallable(
367-
Installable::build2(getEvalStore(), store, Realise::Outputs, installables, bmNormal));
367+
auto buildResults = Installable::build2(getEvalStore(), store, Realise::Outputs, installables, bmNormal);
368+
Installable::throwBuildErrors(buildResults, *store);
369+
370+
auto builtPaths = builtPathsPerInstallable(buildResults);
368371

369372
for (auto & installable : installables) {
370373
ProfileElement element;
@@ -766,8 +769,10 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
766769
return;
767770
}
768771

769-
auto builtPaths = builtPathsPerInstallable(
770-
Installable::build2(getEvalStore(), store, Realise::Outputs, installables, bmNormal));
772+
auto buildResults = Installable::build2(getEvalStore(), store, Realise::Outputs, installables, bmNormal);
773+
Installable::throwBuildErrors(buildResults, *store);
774+
775+
auto builtPaths = builtPathsPerInstallable(buildResults);
771776

772777
for (size_t i = 0; i < installables.size(); ++i) {
773778
auto & installable = installables.at(i);

0 commit comments

Comments
 (0)