Skip to content

Commit bb6c8a1

Browse files
authored
Merge pull request #285 from DeterminateSystems/nix-flake-check-results
nix flake check: Show which outputs failed or succeeded
2 parents 1dacc20 + 256efe2 commit bb6c8a1

File tree

4 files changed

+70
-23
lines changed

4 files changed

+70
-23
lines changed

src/libexpr/eval-cache.cc

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -379,14 +379,19 @@ std::vector<Symbol> AttrCursor::getAttrPath(Symbol name) const
379379
return attrPath;
380380
}
381381

382+
std::string toAttrPathStr(EvalState & state, const AttrPath & attrPath)
383+
{
384+
return dropEmptyInitThenConcatStringsSep(".", state.symbols.resolve(attrPath));
385+
}
386+
382387
std::string AttrCursor::getAttrPathStr() const
383388
{
384-
return dropEmptyInitThenConcatStringsSep(".", root->state.symbols.resolve(getAttrPath()));
389+
return toAttrPathStr(root->state, getAttrPath());
385390
}
386391

387392
std::string AttrCursor::getAttrPathStr(Symbol name) const
388393
{
389-
return dropEmptyInitThenConcatStringsSep(".", root->state.symbols.resolve(getAttrPath(name)));
394+
return toAttrPathStr(root->state, getAttrPath(name));
390395
}
391396

392397
Value & AttrCursor::forceValue()

src/libexpr/include/nix/expr/eval-cache.hh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ namespace nix::eval_cache {
1313
struct AttrDb;
1414
class AttrCursor;
1515

16+
using AttrPath = std::vector<Symbol>;
17+
18+
std::string toAttrPathStr(EvalState & state, const AttrPath & attrPath);
19+
1620
struct CachedEvalError : EvalError
1721
{
1822
const ref<AttrCursor> cursor;

src/libstore/include/nix/store/derived-path.hh

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,10 +234,6 @@ struct DerivedPath : _DerivedPathRaw
234234
return static_cast<const Raw &>(*this);
235235
}
236236

237-
bool operator==(const DerivedPath &) const = default;
238-
// TODO libc++ 16 (used by darwin) missing `std::set::operator <=>`, can't do yet.
239-
// auto operator <=> (const DerivedPath &) const = default;
240-
241237
/**
242238
* Get the store path this is ultimately derived from (by realising
243239
* and projecting outputs).

src/nix/flake.cc

Lines changed: 59 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "nix/store/local-fs-store.hh"
1919
#include "nix/store/globals.hh"
2020
#include "nix/expr/parallel-eval.hh"
21+
#include "nix/util/exit.hh"
2122

2223
#include <filesystem>
2324
#include <nlohmann/json.hpp>
@@ -385,7 +386,9 @@ struct CmdFlakeCheck : FlakeCommand
385386
}
386387
};
387388

388-
Sync<StringSet> omittedSystems;
389+
Sync<std::vector<DerivedPath>> drvPaths_;
390+
Sync<std::set<std::string>> omittedSystems;
391+
Sync<std::map<DerivedPath, std::vector<eval_cache::AttrPath>>> derivedPathToAttrPaths_;
389392

390393
// FIXME: rewrite to use EvalCache.
391394

@@ -434,8 +437,6 @@ struct CmdFlakeCheck : FlakeCommand
434437
return std::nullopt;
435438
};
436439

437-
std::vector<DerivedPath> drvPaths;
438-
439440
FutureVector futures(*state->executor);
440441

441442
auto checkApp = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
@@ -633,11 +634,13 @@ struct CmdFlakeCheck : FlakeCommand
633634
*attr2.value,
634635
attr2.pos);
635636
if (drvPath && attr_name == settings.thisSystem.get()) {
636-
auto path = DerivedPath::Built{
637+
auto derivedPath = DerivedPath::Built{
637638
.drvPath = makeConstantStorePathRef(*drvPath),
638639
.outputs = OutputsSpec::All{},
639640
};
640-
drvPaths.push_back(std::move(path));
641+
(*derivedPathToAttrPaths_.lock())[derivedPath].push_back(
642+
{state->symbols.create("checks"), attr.name, attr2.name});
643+
drvPaths_.lock()->push_back(std::move(derivedPath));
641644
}
642645
}
643646
}
@@ -806,7 +809,10 @@ struct CmdFlakeCheck : FlakeCommand
806809
futures.spawn(1, checkFlake);
807810
futures.finishAll();
808811

809-
if (build && !drvPaths.empty()) {
812+
auto drvPaths(drvPaths_.lock());
813+
auto derivedPathToAttrPaths(derivedPathToAttrPaths_.lock());
814+
815+
if (build && !drvPaths->empty()) {
810816
// TODO: This filtering of substitutable paths is a temporary workaround until
811817
// https://github.com/NixOS/nix/issues/5025 (union stores) is implemented.
812818
//
@@ -819,32 +825,68 @@ struct CmdFlakeCheck : FlakeCommand
819825
// via substitution, as `nix flake check` only needs to verify buildability,
820826
// not actually produce the outputs.
821827
state->waitForAllPaths();
822-
auto missing = store->queryMissing(drvPaths);
828+
auto missing = store->queryMissing(*drvPaths);
823829

824830
std::vector<DerivedPath> toBuild;
831+
std::set<DerivedPath> toBuildSet;
825832
for (auto & path : missing.willBuild) {
826-
toBuild.emplace_back(
827-
DerivedPath::Built{
828-
.drvPath = makeConstantStorePathRef(path),
829-
.outputs = OutputsSpec::All{},
830-
});
833+
auto derivedPath = DerivedPath::Built{
834+
.drvPath = makeConstantStorePathRef(path),
835+
.outputs = OutputsSpec::All{},
836+
};
837+
toBuild.emplace_back(derivedPath);
838+
toBuildSet.insert(std::move(derivedPath));
831839
}
832840

841+
for (auto & [derivedPath, attrPaths] : *derivedPathToAttrPaths)
842+
if (!toBuildSet.contains(derivedPath))
843+
for (auto & attrPath : attrPaths)
844+
notice(
845+
"" ANSI_BOLD "%s" ANSI_NORMAL ANSI_ITALIC ANSI_FAINT " (previously built)" ANSI_NORMAL,
846+
eval_cache::toAttrPathStr(*state, attrPath));
847+
833848
// FIXME: should start building while evaluating.
834849
Activity act(*logger, lvlInfo, actUnknown, fmt("running %d flake checks", toBuild.size()));
835-
store->buildPaths(toBuild);
850+
auto buildResults = store->buildPathsWithResults(toBuild);
851+
assert(buildResults.size() == toBuild.size());
852+
853+
// Report successes first.
854+
for (auto & buildResult : buildResults)
855+
if (buildResult.tryGetSuccess())
856+
for (auto & attrPath : (*derivedPathToAttrPaths)[buildResult.path])
857+
notice("" ANSI_BOLD "%s" ANSI_NORMAL, eval_cache::toAttrPathStr(*state, attrPath));
858+
859+
// Then cancelled builds.
860+
for (auto & buildResult : buildResults)
861+
if (buildResult.isCancelled())
862+
for (auto & attrPath : (*derivedPathToAttrPaths)[buildResult.path])
863+
notice(
864+
"" ANSI_BOLD "%s" ANSI_NORMAL ANSI_FAINT " (cancelled)",
865+
eval_cache::toAttrPathStr(*state, attrPath));
866+
867+
// Then failures.
868+
for (auto & buildResult : buildResults)
869+
if (auto failure = buildResult.tryGetFailure(); failure && !buildResult.isCancelled())
870+
try {
871+
hasErrors = true;
872+
for (auto & attrPath : (*derivedPathToAttrPaths)[buildResult.path])
873+
printError("" ANSI_RED "%s" ANSI_NORMAL, eval_cache::toAttrPathStr(*state, attrPath));
874+
failure->rethrow();
875+
} catch (Error & e) {
876+
logError(e.info());
877+
}
836878
}
837879

838-
if (hasErrors)
839-
throw Error("some errors were encountered during the evaluation");
840-
841880
if (!omittedSystems.lock()->empty()) {
842881
// TODO: empty system is not visible; render all as nix strings?
843882
warn(
844883
"The check omitted these incompatible systems: %s\n"
845884
"Use '--all-systems' to check all.",
846885
concatStringsSep(", ", *omittedSystems.lock()));
847-
};
886+
}
887+
888+
if (hasErrors)
889+
throw Exit(1);
848890
};
849891
};
850892

0 commit comments

Comments
 (0)