diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 04d4ec8eb..998a36bcc 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -423,7 +423,7 @@ struct CmdFlakeCheck : FlakeCommand return std::nullopt; }; - std::vector drvPaths; + std::map> attrPathsByDrv; auto checkApp = [&](const std::string & attrPath, Value & v, const PosIdx pos) { try { @@ -621,7 +621,13 @@ struct CmdFlakeCheck : FlakeCommand .drvPath = makeConstantStorePathRef(*drvPath), .outputs = OutputsSpec::All{}, }; - drvPaths.push_back(std::move(path)); + + // Build and store the attribute path for error reporting + AttrPath attrPath; + attrPath.push_back(AttrName(state->symbols.create(name))); + attrPath.push_back(AttrName(attr.name)); + attrPath.push_back(AttrName(attr2.name)); + attrPathsByDrv[path].push_back(std::move(attrPath)); } } } @@ -785,7 +791,9 @@ struct CmdFlakeCheck : FlakeCommand }); } - if (build && !drvPaths.empty()) { + if (build && !attrPathsByDrv.empty()) { + auto keys = std::views::keys(attrPathsByDrv); + std::vector drvPaths(keys.begin(), keys.end()); // TODO: This filtering of substitutable paths is a temporary workaround until // https://github.com/NixOS/nix/issues/5025 (union stores) is implemented. // @@ -811,7 +819,28 @@ struct CmdFlakeCheck : FlakeCommand } Activity act(*logger, lvlInfo, actUnknown, fmt("running %d flake checks", toBuild.size())); - store->buildPaths(toBuild); + auto results = store->buildPathsWithResults(toBuild); + + // Report build failures with attribute paths + for (auto & result : results) { + if (auto * failure = result.tryGetFailure()) { + auto it = attrPathsByDrv.find(result.path); + if (it != attrPathsByDrv.end() && !it->second.empty()) { + for (auto & attrPath : it->second) { + auto attrPathStr = showAttrPath(state->symbols, attrPath); + reportError(Error( + "failed to build attribute '%s', build of '%s' failed: %s", + attrPathStr, + result.path.to_string(*store), + failure->errorMsg)); + } + } else { + // Derivation has no attribute path (e.g., a build dependency) + reportError( + Error("build of '%s' failed: %s", result.path.to_string(*store), failure->errorMsg)); + } + } + } } if (hasErrors) throw Error("some errors were encountered during the evaluation"); diff --git a/tests/functional/flakes/check.sh b/tests/functional/flakes/check.sh index 55cd3805f..cb4e3eeba 100755 --- a/tests/functional/flakes/check.sh +++ b/tests/functional/flakes/check.sh @@ -192,3 +192,24 @@ EOF # shellcheck disable=SC2015 checkRes=$(nix flake check "$flakeDir" 2>&1 && fail "nix flake check should have failed" || true) echo "$checkRes" | grepQuiet -E "builder( for .*)? failed with exit code 1" + +# Test that attribute paths are shown in error messages +cat > "$flakeDir"/flake.nix <&1 && fail "nix flake check should have failed" || true) +echo "$checkRes" | grepQuiet "checks.${system}.failingCheck" +echo "$checkRes" | grepQuiet "checks.${system}.anotherFailingCheck"