From c5fd34a14e2a271b66839b35f500caeae3368be2 Mon Sep 17 00:00:00 2001 From: Graham Bennett Date: Sat, 22 Oct 2022 14:24:25 +0100 Subject: [PATCH 01/10] Build with C++20 --- Makefile | 2 +- perl/Makefile | 2 +- src/libcmd/nix-cmd.pc.in | 2 +- src/libexpr/nix-expr.pc.in | 2 +- src/libmain/nix-main.pc.in | 2 +- src/libstore/nix-store.pc.in | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index c1a1ce2c7..42d11638b 100644 --- a/Makefile +++ b/Makefile @@ -36,4 +36,4 @@ endif include mk/lib.mk -GLOBAL_CXXFLAGS += -g -Wall -include config.h -std=c++17 -I src +GLOBAL_CXXFLAGS += -g -Wall -include config.h -std=c++20 -I src diff --git a/perl/Makefile b/perl/Makefile index 708f86882..2d759e6fc 100644 --- a/perl/Makefile +++ b/perl/Makefile @@ -1,6 +1,6 @@ makefiles = local.mk -GLOBAL_CXXFLAGS += -g -Wall -std=c++17 -I ../src +GLOBAL_CXXFLAGS += -g -Wall -std=c++20 -I ../src -include Makefile.config diff --git a/src/libcmd/nix-cmd.pc.in b/src/libcmd/nix-cmd.pc.in index 1761a9f41..a21d93f1d 100644 --- a/src/libcmd/nix-cmd.pc.in +++ b/src/libcmd/nix-cmd.pc.in @@ -6,4 +6,4 @@ Name: Nix Description: Nix Package Manager Version: @PACKAGE_VERSION@ Libs: -L${libdir} -lnixcmd -Cflags: -I${includedir}/nix -std=c++17 +Cflags: -I${includedir}/nix -std=c++20 diff --git a/src/libexpr/nix-expr.pc.in b/src/libexpr/nix-expr.pc.in index 80f7a492b..95d452ca8 100644 --- a/src/libexpr/nix-expr.pc.in +++ b/src/libexpr/nix-expr.pc.in @@ -7,4 +7,4 @@ Description: Nix Package Manager Version: @PACKAGE_VERSION@ Requires: nix-store bdw-gc Libs: -L${libdir} -lnixexpr -Cflags: -I${includedir}/nix -std=c++17 +Cflags: -I${includedir}/nix -std=c++20 diff --git a/src/libmain/nix-main.pc.in b/src/libmain/nix-main.pc.in index 37b03dcd4..b46ce1990 100644 --- a/src/libmain/nix-main.pc.in +++ b/src/libmain/nix-main.pc.in @@ -6,4 +6,4 @@ Name: Nix Description: Nix Package Manager Version: @PACKAGE_VERSION@ Libs: -L${libdir} -lnixmain -Cflags: -I${includedir}/nix -std=c++17 +Cflags: -I${includedir}/nix -std=c++20 diff --git a/src/libstore/nix-store.pc.in b/src/libstore/nix-store.pc.in index 6d67b1e03..385169a13 100644 --- a/src/libstore/nix-store.pc.in +++ b/src/libstore/nix-store.pc.in @@ -6,4 +6,4 @@ Name: Nix Description: Nix Package Manager Version: @PACKAGE_VERSION@ Libs: -L${libdir} -lnixstore -lnixutil -Cflags: -I${includedir}/nix -std=c++17 +Cflags: -I${includedir}/nix -std=c++20 From 4563e803633d44ec57dfc4515f0c42c8855c018b Mon Sep 17 00:00:00 2001 From: Graham Bennett Date: Sat, 22 Oct 2022 14:25:35 +0100 Subject: [PATCH 02/10] Fix C++20 warnings --- src/libstore/binary-cache-store.cc | 2 +- src/libstore/build/worker.cc | 2 +- src/libstore/globals.cc | 6 +++--- src/libutil/args.cc | 2 +- src/libutil/config.cc | 8 ++++---- src/libutil/config.hh | 12 ++++++++---- 6 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc index a26770c79..9ab941075 100644 --- a/src/libstore/binary-cache-store.cc +++ b/src/libstore/binary-cache-store.cc @@ -378,7 +378,7 @@ void BinaryCacheStore::queryPathInfoUncached(const StorePath & storePath, auto callbackPtr = std::make_shared(std::move(callback)); getFile(narInfoFile, - {[=](std::future> fut) { + {[=,this](std::future> fut) { try { auto data = fut.get(); diff --git a/src/libstore/build/worker.cc b/src/libstore/build/worker.cc index b192fbc77..75406bec9 100644 --- a/src/libstore/build/worker.cc +++ b/src/libstore/build/worker.cc @@ -276,7 +276,7 @@ void Worker::run(const Goals & _topGoals) if (!children.empty() || !waitingForAWhile.empty()) waitForInput(); else { - if (awake.empty() && 0 == settings.maxBuildJobs) + if (awake.empty() && 0U == settings.maxBuildJobs) { if (getMachines().empty()) throw Error("unable to start any build; either increase '--max-jobs' " diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index ff658c428..c264014be 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -218,19 +218,19 @@ template<> void BaseSetting::convertToArg(Args & args, const std::s .longName = name, .description = "Enable sandboxing.", .category = category, - .handler = {[=]() { override(smEnabled); }} + .handler = {[this]() { override(smEnabled); }} }); args.addFlag({ .longName = "no-" + name, .description = "Disable sandboxing.", .category = category, - .handler = {[=]() { override(smDisabled); }} + .handler = {[this]() { override(smDisabled); }} }); args.addFlag({ .longName = "relaxed-" + name, .description = "Enable sandboxing, but allow builds to disable it.", .category = category, - .handler = {[=]() { override(smRelaxed); }} + .handler = {[this]() { override(smRelaxed); }} }); } diff --git a/src/libutil/args.cc b/src/libutil/args.cc index 753980fd4..2930913d6 100644 --- a/src/libutil/args.cc +++ b/src/libutil/args.cc @@ -324,7 +324,7 @@ MultiCommand::MultiCommand(const Commands & commands_) expectArgs({ .label = "subcommand", .optional = true, - .handler = {[=](std::string s) { + .handler = {[=,this](std::string s) { assert(!command); auto i = commands.find(s); if (i == commands.end()) { diff --git a/src/libutil/config.cc b/src/libutil/config.cc index 9bb412b4f..b349f2d80 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -209,7 +209,7 @@ void BaseSetting::convertToArg(Args & args, const std::string & category) .description = fmt("Set the `%s` setting.", name), .category = category, .labels = {"value"}, - .handler = {[=](std::string s) { overridden = true; set(s); }}, + .handler = {[this](std::string s) { overridden = true; set(s); }}, }); if (isAppendable()) @@ -218,7 +218,7 @@ void BaseSetting::convertToArg(Args & args, const std::string & category) .description = fmt("Append to the `%s` setting.", name), .category = category, .labels = {"value"}, - .handler = {[=](std::string s) { overridden = true; set(s, true); }}, + .handler = {[this](std::string s) { overridden = true; set(s, true); }}, }); } @@ -270,13 +270,13 @@ template<> void BaseSetting::convertToArg(Args & args, const std::string & .longName = name, .description = fmt("Enable the `%s` setting.", name), .category = category, - .handler = {[=]() { override(true); }} + .handler = {[this]() { override(true); }} }); args.addFlag({ .longName = "no-" + name, .description = fmt("Disable the `%s` setting.", name), .category = category, - .handler = {[=]() { override(false); }} + .handler = {[this]() { override(false); }} }); } diff --git a/src/libutil/config.hh b/src/libutil/config.hh index 79ec0f9cf..7ac43c854 100644 --- a/src/libutil/config.hh +++ b/src/libutil/config.hh @@ -250,11 +250,15 @@ public: operator const T &() const { return value; } operator T &() { return value; } const T & get() const { return value; } - bool operator ==(const T & v2) const { return value == v2; } - bool operator !=(const T & v2) const { return value != v2; } - void operator =(const T & v) { assign(v); } + template + bool operator ==(const U & v2) const { return value == v2; } + template + bool operator !=(const U & v2) const { return value != v2; } + template + void operator =(const U & v) { assign(v); } virtual void assign(const T & v) { value = v; } - void setDefault(const T & v) { if (!overridden) value = v; } + template + void setDefault(const U & v) { if (!overridden) value = v; } void set(const std::string & str, bool append = false) override; From 0b3464a107dab7aea00c3a9a0f91e240e6d8b851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Na=C3=AFm=20Favier?= Date: Mon, 30 Jan 2023 14:49:45 +0100 Subject: [PATCH 03/10] doc: add `__structuredAttrs`, `outputChecks`, `unsafeDiscardReferences` --- .../src/language/advanced-attributes.md | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/doc/manual/src/language/advanced-attributes.md b/doc/manual/src/language/advanced-attributes.md index f02425b13..a164b3997 100644 --- a/doc/manual/src/language/advanced-attributes.md +++ b/doc/manual/src/language/advanced-attributes.md @@ -255,3 +255,67 @@ Derivations can declare some infrequently used optional attributes. > substituted. Thus it is usually a good idea to align `system` with > `builtins.currentSystem` when setting `allowSubstitutes` to > `false`. For most trivial derivations this should be the case. + + - [`__structuredAttrs`]{#adv-attr-structuredAttrs}\ + If the special attribute `__structuredAttrs` is set to `true`, the other derivation + attributes are serialised in JSON format and made available to the + builder via the file `.attrs.json` in the builder’s temporary + directory. This obviates the need for `passAsFile` since JSON files + have no size restrictions, unlike process environments. + + It also makes it possible to tweak derivation settings in a structured way; see + [`outputChecks`](#adv-attr-outputChecks) for example. + + As a convenience to Bash builders, + Nix writes a script named `.attrs.sh` to the builder’s directory + that initialises shell variables corresponding to all attributes + that are representable in Bash. This includes non-nested + (associative) arrays. For example, the attribute `hardening.format = true` + ends up as the Bash associative array element `${hardening[format]}`. + + - [`outputChecks`]{#adv-attr-outputChecks}\ + When using [structured attributes](#adv-attr-structuredAttrs), the `outputChecks` + attribute allows defining checks per-output. + + In addition to + [`allowedReferences`](#adv-attr-allowedReferences), [`allowedRequisites`](#adv-attr-allowedRequisites), + [`disallowedReferences`](#adv-attr-disallowedReferences) and [`disallowedRequisites`](#adv-attr-disallowedRequisites), + the following attributes are available: + + - `maxSize` defines the maximum size of the output path. + - `maxClosureSize` defines the maximum size of the output's closure. + - `ignoreSelfRefs` controls whether self-references should be considered when + checking for allowed references/requisites. + + ```nix + __structuredAttrs = true; + + outputChecks.out = { + # The closure of 'out' must not be larger than 256 MiB. + maxClosureSize = 256 * 1024 * 1024; + + # It must not refer to the C compiler or to the 'dev' output. + disallowedRequisites = [ stdenv.cc "dev" ]; + }; + + outputChecks.dev = { + # The 'dev' output must not be larger than 128 KiB. + maxSize = 128 * 1024; + }; + ``` + + - [`unsafeDiscardReferences`]{#adv-attr-unsafeDiscardReferences}\ + When using [structured attributes](#adv-attr-structuredAttrs), the **experimental** + attribute `unsafeDiscardReferences` is a per-output boolean which, if set to `true`, + disables scanning the build output for runtime dependencies altogether. + + ```nix + __structuredAttrs = true; + unsafeDiscardReferences.out = true; + ``` + + This is useful, for example, when generating self-contained filesystem images with + their own embedded Nix store: hashes found inside such an image refer + to the embedded store and not to the host's Nix store. + + This is only allowed if the `discard-references` experimental feature is enabled. From b26562c629604b76c58b789ee520581cc5549433 Mon Sep 17 00:00:00 2001 From: dramforever Date: Mon, 23 Jan 2023 23:06:57 +0800 Subject: [PATCH 04/10] InstallableFlake: Handle missing attr in getCursors Handle the case where none of getActualAttrPaths() actually exists, in which case instead of returning an empty vector. This fixes the case where the user misspells the attribute name in nix search. Instead of getting no search results, now it shows an error with suggestions. Also remove InstallableFlake::getCursor() override since it's now equivalent to the base class version. --- src/libcmd/installables.cc | 44 +++++++++++--------------------------- src/libcmd/installables.hh | 4 ---- 2 files changed, 13 insertions(+), 35 deletions(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 5090ea6d2..f8632a535 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -696,46 +696,28 @@ InstallableFlake::getCursors(EvalState & state) std::vector> res; - for (auto & attrPath : getActualAttrPaths()) { - auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath)); - if (attr) res.push_back(ref(*attr)); - } - - return res; -} - -ref InstallableFlake::getCursor(EvalState & state) -{ - auto lockedFlake = getLockedFlake(); - - auto cache = openEvalCache(state, lockedFlake); - auto root = cache->getRoot(); - Suggestions suggestions; - auto attrPaths = getActualAttrPaths(); for (auto & attrPath : attrPaths) { debug("trying flake output attribute '%s'", attrPath); - auto attrOrSuggestions = root->findAlongAttrPath( - parseAttrPath(state, attrPath), - true - ); - - if (!attrOrSuggestions) { - suggestions += attrOrSuggestions.getSuggestions(); - continue; + auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath)); + if (attr) { + res.push_back(ref(*attr)); + } else { + suggestions += attr.getSuggestions(); } - - return *attrOrSuggestions; } - throw Error( - suggestions, - "flake '%s' does not provide attribute %s", - flakeRef, - showAttrPaths(attrPaths)); + if (res.size() == 0) + throw Error( + suggestions, + "flake '%s' does not provide attribute %s", + flakeRef, + showAttrPaths(attrPaths)); + + return res; } std::shared_ptr InstallableFlake::getLockedFlake() const diff --git a/src/libcmd/installables.hh b/src/libcmd/installables.hh index 3d12639b0..d489315df 100644 --- a/src/libcmd/installables.hh +++ b/src/libcmd/installables.hh @@ -198,10 +198,6 @@ struct InstallableFlake : InstallableValue std::vector> getCursors(EvalState & state) override; - /* Get a cursor to the first attrpath in getActualAttrPaths() that - exists, or throw an exception with suggestions if none exists. */ - ref getCursor(EvalState & state) override; - std::shared_ptr getLockedFlake() const; FlakeRef nixpkgsFlakeRef() const override; From 377d5eb38815d8290ab444c81c7826f46ba79844 Mon Sep 17 00:00:00 2001 From: dramforever Date: Mon, 30 Jan 2023 23:56:27 +0800 Subject: [PATCH 05/10] Installable::getCursors: Cleanup - Clarify doc comments, Installables::getCursors returns non-empty vector - Use vector::at in Installable::getCursor instead of checking for empty vector and throwing an exception with error message. --- src/libcmd/installables.cc | 7 +++---- src/libcmd/installables.hh | 8 ++++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index f8632a535..24f458f1a 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -379,10 +379,9 @@ Installable::getCursors(EvalState & state) ref Installable::getCursor(EvalState & state) { - auto cursors = getCursors(state); - if (cursors.empty()) - throw Error("cannot find flake attribute '%s'", what()); - return cursors[0]; + /* Although getCursors should return at least one element, in case it doesn't, + bound check to avoid an undefined behavior for vector[0] */ + return getCursors(state).at(0); } static StorePath getDeriver( diff --git a/src/libcmd/installables.hh b/src/libcmd/installables.hh index d489315df..da6a3addd 100644 --- a/src/libcmd/installables.hh +++ b/src/libcmd/installables.hh @@ -103,9 +103,13 @@ struct Installable return {}; } + /* Get a cursor to each value this Installable could refer to. However + if none exists, throw exception instead of returning empty vector. */ virtual std::vector> getCursors(EvalState & state); + /* Get the first and most preferred cursor this Installable could refer + to, or throw an exception if none exists. */ virtual ref getCursor(EvalState & state); @@ -193,8 +197,8 @@ struct InstallableFlake : InstallableValue std::pair toValue(EvalState & state) override; - /* Get a cursor to every attrpath in getActualAttrPaths() that - exists. */ + /* Get a cursor to every attrpath in getActualAttrPaths() + that exists. However if none exists, throw an exception. */ std::vector> getCursors(EvalState & state) override; From 6b779e4b07c42e5780feda5daf6dedc6c49e50d7 Mon Sep 17 00:00:00 2001 From: dramforever Date: Mon, 23 Jan 2023 23:07:34 +0800 Subject: [PATCH 06/10] Fix extra "." in CmdSearch::getDefaultFlakeAttrPaths No other getDefaultFlakeAttrPaths implementation has this trailing dot, and the dot can show up in error messages like: error: flake '...' does not provide attribute 'packages.x86_64-linux.', ... --- src/nix/search.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nix/search.cc b/src/nix/search.cc index d2a31607d..4fa1e7837 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -56,8 +56,8 @@ struct CmdSearch : InstallableCommand, MixJSON Strings getDefaultFlakeAttrPaths() override { return { - "packages." + settings.thisSystem.get() + ".", - "legacyPackages." + settings.thisSystem.get() + "." + "packages." + settings.thisSystem.get(), + "legacyPackages." + settings.thisSystem.get() }; } From e21aa43212a4326631ecb14a673b68f0b3bbc58e Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 30 Jan 2023 11:14:25 -0500 Subject: [PATCH 07/10] Delete dead code The references set seems to have been unused since `LegacySSHStore` references were first created in caa5793b4a74049ee37dd88eb1c5b785456ce40d. The method decls never were upstream, and accidentally added by me in 062533f7cdb74026096ca8c7d5b6e393893d59ef (probably due to `git rerere`). Sorry! This reduces the diff from #3746. --- src/libstore/legacy-ssh-store.cc | 1 - src/libstore/path-info.hh | 6 ------ 2 files changed, 7 deletions(-) diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index e1a4e13a3..2c9dd2680 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -134,7 +134,6 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor /* Hash will be set below. FIXME construct ValidPathInfo at end. */ auto info = std::make_shared(path, Hash::dummy); - PathSet references; auto deriver = readString(conn->from); if (deriver != "") info->deriver = parseStorePath(deriver); diff --git a/src/libstore/path-info.hh b/src/libstore/path-info.hh index b4b54e593..a7fcbd232 100644 --- a/src/libstore/path-info.hh +++ b/src/libstore/path-info.hh @@ -81,12 +81,6 @@ struct ValidPathInfo /* Return true iff the path is verifiably content-addressed. */ bool isContentAddressed(const Store & store) const; - /* Functions to view references + hasSelfReference as one set, mainly for - compatibility's sake. */ - StorePathSet referencesPossiblyToSelf() const; - void insertReferencePossiblyToSelf(StorePath && ref); - void setReferencesPossiblyToSelf(StorePathSet && refs); - static const size_t maxSigs = std::numeric_limits::max(); /* Return the number of signatures on this .narinfo that were From 16fa8c2e0e1c03c5cdf759358574a7030bb0dbf7 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 30 Jan 2023 12:08:57 -0500 Subject: [PATCH 08/10] Fix configure.ac rapidcheck tests - `AC_LANG_PUSH(C++)` is needed for the header check - The library check is hopeless (without lots of third-party macros I don't feel like getting) because name mangling Pkg-config would make all this easier. I previously opened https://github.com/emil-e/rapidcheck/issues/302, I should write a PR too. --- configure.ac | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 0066bc389..09b3651b9 100644 --- a/configure.ac +++ b/configure.ac @@ -276,8 +276,11 @@ PKG_CHECK_MODULES([GTEST], [gtest_main]) # Look for rapidcheck. # No pkg-config yet, https://github.com/emil-e/rapidcheck/issues/302 +AC_LANG_PUSH(C++) AC_CHECK_HEADERS([rapidcheck/gtest.h], [], [], [#include ]) -AC_CHECK_LIB([rapidcheck], []) +dnl No good for C++ libs with mangled symbols +dnl AC_CHECK_LIB([rapidcheck], []) +AC_LANG_POP(C++) # Look for nlohmann/json. From c5d4c50abaa5977db7dd7da2112f9ac289d4171e Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Mon, 30 Jan 2023 23:34:35 -0800 Subject: [PATCH 09/10] manual: Document that the store timestamp is now 1, not 0 Commit 14bc3ce3d6d5745717fa19b8b43b5fdd117ff757 (0.13~43) changed the timestamps in the Nix store from 0 to 1. Update the nix-store man page to match. Signed-off-by: Anders Kaseorg --- doc/manual/src/command-ref/nix-store.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/command-ref/nix-store.md b/doc/manual/src/command-ref/nix-store.md index 403cf285d..31fdd7806 100644 --- a/doc/manual/src/command-ref/nix-store.md +++ b/doc/manual/src/command-ref/nix-store.md @@ -633,7 +633,7 @@ written to standard output. A NAR archive is like a TAR or Zip archive, but it contains only the information that Nix considers important. For instance, timestamps are -elided because all files in the Nix store have their timestamp set to 0 +elided because all files in the Nix store have their timestamp set to 1 anyway. Likewise, all permissions are left out except for the execute bit, because all files in the Nix store have 444 or 555 permission. From 60d48eda2354dfa6d23ed3feb7b23ac25b34edcf Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 31 Jan 2023 18:16:31 +0100 Subject: [PATCH 10/10] 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. --- src/nix/flake.cc | 62 +++++++++++++++++++++++++++++++++++++++++++- tests/flakes/show.sh | 27 +++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) 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(lockFlake()); auto localSystem = std::string(settings.thisSystem.get()); + std::function &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 &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 & attrPath, @@ -1027,7 +1082,12 @@ struct CmdFlakeShow : FlakeCommand, MixJSON { if (!json) logger->cout("%s", headerPrefix); - auto attrs = visitor.getAttrs(); + std::vector 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 < show-output.json +nix eval --impure --expr ' +let show_output = builtins.fromJSON (builtins.readFile ./show-output.json); +in +assert show_output == { }; +true +'