aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Hensing <robert@roberthensing.nl>2023-01-31 18:16:31 +0100
committerRobert Hensing <robert@roberthensing.nl>2023-01-31 18:20:26 +0100
commit60d48eda2354dfa6d23ed3feb7b23ac25b34edcf (patch)
treed6cbc5e1da134f6b565f01e01105aa6747e3e547
parentc9b9260f348299aad70ea2010db6e291ee1e8114 (diff)
nix flake show: Ignore empty attrsets
For frameworks it's important that structures are as lazy as possible to prevent infinite recursions, performance issues and errors that aren't related to the thing to evaluate. As a consequence, they have to emit more attributes than strictly (sic) necessary. However, these attributes with empty values are not useful to the user so we omit them.
-rw-r--r--src/nix/flake.cc62
-rw-r--r--tests/flakes/show.sh27
2 files changed, 88 insertions, 1 deletions
diff --git a/src/nix/flake.cc b/src/nix/flake.cc
index d2f68c37c..c025bc7a6 100644
--- a/src/nix/flake.cc
+++ b/src/nix/flake.cc
@@ -1002,6 +1002,61 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
auto flake = std::make_shared<LockedFlake>(lockFlake());
auto localSystem = std::string(settings.thisSystem.get());
+ std::function<bool(
+ eval_cache::AttrCursor & visitor,
+ const std::vector<Symbol> &attrPath,
+ const Symbol &attr)> hasContent;
+
+ // For frameworks it's important that structures are as lazy as possible
+ // to prevent infinite recursions, performance issues and errors that
+ // aren't related to the thing to evaluate. As a consequence, they have
+ // to emit more attributes than strictly (sic) necessary.
+ // However, these attributes with empty values are not useful to the user
+ // so we omit them.
+ hasContent = [&](
+ eval_cache::AttrCursor & visitor,
+ const std::vector<Symbol> &attrPath,
+ const Symbol &attr) -> bool
+ {
+ auto attrPath2(attrPath);
+ attrPath2.push_back(attr);
+ auto attrPathS = state->symbols.resolve(attrPath2);
+ const auto & attrName = state->symbols[attr];
+
+ auto visitor2 = visitor.getAttr(attrName);
+
+ if ((attrPathS[0] == "apps"
+ || attrPathS[0] == "checks"
+ || attrPathS[0] == "devShells"
+ || attrPathS[0] == "legacyPackages"
+ || attrPathS[0] == "packages")
+ && (attrPathS.size() == 1 || attrPathS.size() == 2)) {
+ for (const auto &subAttr : visitor2->getAttrs()) {
+ if (hasContent(*visitor2, attrPath2, subAttr)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ if ((attrPathS.size() == 1)
+ && (attrPathS[0] == "formatter"
+ || attrPathS[0] == "nixosConfigurations"
+ || attrPathS[0] == "nixosModules"
+ || attrPathS[0] == "overlays"
+ )) {
+ for (const auto &subAttr : visitor2->getAttrs()) {
+ if (hasContent(*visitor2, attrPath2, subAttr)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // If we don't recognize it, it's probably content
+ return true;
+ };
+
std::function<nlohmann::json(
eval_cache::AttrCursor & visitor,
const std::vector<Symbol> & attrPath,
@@ -1027,7 +1082,12 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
{
if (!json)
logger->cout("%s", headerPrefix);
- auto attrs = visitor.getAttrs();
+ std::vector<Symbol> attrs;
+ for (const auto &attr : visitor.getAttrs()) {
+ if (hasContent(visitor, attrPath, attr))
+ attrs.push_back(attr);
+ }
+
for (const auto & [i, attr] : enumerate(attrs)) {
const auto & attrName = state->symbols[attr];
bool last = i + 1 == attrs.size();
diff --git a/tests/flakes/show.sh b/tests/flakes/show.sh
index 995de8dc3..dd13264b9 100644
--- a/tests/flakes/show.sh
+++ b/tests/flakes/show.sh
@@ -37,3 +37,30 @@ in
assert show_output.legacyPackages.${builtins.currentSystem}.hello.name == "simple";
true
'
+
+# Test that attributes are only reported when they have actual content
+cat >flake.nix <<EOF
+{
+ description = "Bla bla";
+
+ outputs = inputs: rec {
+ apps.$system = { };
+ checks.$system = { };
+ devShells.$system = { };
+ legacyPackages.$system = { };
+ packages.$system = { };
+ packages.someOtherSystem = { };
+
+ formatter = { };
+ nixosConfigurations = { };
+ nixosModules = { };
+ };
+}
+EOF
+nix flake show --json --all-systems > show-output.json
+nix eval --impure --expr '
+let show_output = builtins.fromJSON (builtins.readFile ./show-output.json);
+in
+assert show_output == { };
+true
+'