From 9599efedb53c8e17a4be3cfa01bd2337229fa472 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 10 Mar 2026 22:31:04 +0100 Subject: [PATCH 1/5] Add `isLegacy` flake schema output attribute This makes `nix flake check` skip `legacyPackages` again. --- doc/manual/source/protocols/flake-schemas.md | 1 + src/libcmd/builtin-flake-schemas.nix | 1 + src/libcmd/flake-schemas.cc | 14 ++++++++++++-- src/libcmd/include/nix/cmd/flake-schemas.hh | 4 +++- src/nix/flake.cc | 20 +++++++++++++------- tests/functional/flakes/show.sh | 4 ++-- 6 files changed, 32 insertions(+), 12 deletions(-) diff --git a/doc/manual/source/protocols/flake-schemas.md b/doc/manual/source/protocols/flake-schemas.md index 18bffeef1dc1..fa8a9a63ef1b 100644 --- a/doc/manual/source/protocols/flake-schemas.md +++ b/doc/manual/source/protocols/flake-schemas.md @@ -40,6 +40,7 @@ Both leaf and non-leaf nodes can have the following attributes: | Attribute | Description | | :----------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `forSystems` | A list of Nix system types (e.g. `["x86_64-linux"]`) supported by this node. This is used by tools to skip nodes that cannot be built on the user's system. Setting this on a non-leaf node allows all the children to be skipped, regardless of the `forSystems` attributes of the children. If this attribute is not set, the node is never skipped. | +| `isLegacy` | If set to true, this node is skipped unless the `--legacy` CLI flag is set. | # Example diff --git a/src/libcmd/builtin-flake-schemas.nix b/src/libcmd/builtin-flake-schemas.nix index f326b413c9e6..a8a6be4a1e85 100644 --- a/src/libcmd/builtin-flake-schemas.nix +++ b/src/libcmd/builtin-flake-schemas.nix @@ -105,6 +105,7 @@ self.lib.mkChildren ( builtins.mapAttrs (systemType: packagesForSystem: { forSystems = [ systemType ]; + isLegacy = true; children = let recurse = diff --git a/src/libcmd/flake-schemas.cc b/src/libcmd/flake-schemas.cc index 725326c0fd75..8b9f76533dcf 100644 --- a/src/libcmd/flake-schemas.cc +++ b/src/libcmd/flake-schemas.cc @@ -161,13 +161,23 @@ void forEachOutput( void visit( std::optional system, + bool includeLegacy, ref node, std::function visitLeaf, std::function)> visitNonLeaf, - std::function node, const std::vector & systems)> visitFiltered) + std::function node, const std::vector & systems)> visitFiltered, + std::function node)> visitLegacy) { Activity act(*logger, lvlInfo, actUnknown, fmt("evaluating '%s'", node->getAttrPathStr())); + /* Filter out legacy outputs, unless --legacy is enabled. */ + if (!includeLegacy) { + if (auto b = node->maybeGetAttr("isLegacy"); b && b->getBool()) { + visitLegacy(node); + return; + } + } + /* Apply the system type filter. */ if (system) { if (auto forSystems = Node(node).forSystems()) { @@ -185,7 +195,7 @@ void visit( try { f(attrName, children->getAttr(attrName), i + 1 == attrNames.size()); } catch (Error & e) { - // FIXME: make it a flake schema attribute whether to ignore evaluation errors. + // FIXME: use the `isLegacy` attribute. if (node->root->state.symbols[node->getAttrPath()[0]] != "legacyPackages") { e.addTrace( nullptr, "while evaluating the flake output attribute '%s':", node->getAttrPathStr()); diff --git a/src/libcmd/include/nix/cmd/flake-schemas.hh b/src/libcmd/include/nix/cmd/flake-schemas.hh index c1ceefdc35aa..3ac280fd6731 100644 --- a/src/libcmd/include/nix/cmd/flake-schemas.hh +++ b/src/libcmd/include/nix/cmd/flake-schemas.hh @@ -64,10 +64,12 @@ typedef std::function attr, bool isLast)> void visit( std::optional system, + bool includeLegacy, ref node, std::function visitLeaf, std::function)> visitNonLeaf, - std::function node, const std::vector & systems)> visitFiltered); + std::function node, const std::vector & systems)> visitFiltered, + std::function node)> visitLegacy); struct OutputInfo { diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 454e10d4ecf2..08f6e40fdf0f 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -382,6 +382,7 @@ struct CmdFlakeCheck : FlakeCommand, MixFlakeSchemas visit = [&](ref node) { flake_schemas::visit( checkAllSystems ? std::optional() : localSystem, + false, // FIXME: add a --legacy flag? node, [&](const flake_schemas::Leaf & leaf) { @@ -452,7 +453,9 @@ struct CmdFlakeCheck : FlakeCommand, MixFlakeSchemas [&](ref node, const std::vector & systems) { for (auto & s : systems) omittedSystems.lock()->insert(s); - }); + }, + + [&](ref node) {}); }; flake_schemas::forEachOutput( @@ -898,6 +901,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON, MixFlakeSchemas visit = [&](ref node, nlohmann::json & obj) { flake_schemas::visit( showAllSystems ? std::optional() : localSystem, + showLegacy, node, [&](const flake_schemas::Leaf & leaf) { @@ -959,7 +963,9 @@ struct CmdFlakeShow : FlakeCommand, MixJSON, MixFlakeSchemas [&](ref node, const std::vector & systems) { obj.emplace("filtered", true); - }); + }, + + [&](ref node) { obj.emplace("legacy", true); }); }; auto inv = nlohmann::json::object(); @@ -972,9 +978,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON, MixFlakeSchemas bool isLast) { auto & j = inv.emplace(state->symbols[outputName], nlohmann::json::object()).first.value(); - if (!showLegacy && state->symbols[outputName] == "legacyPackages") { - j.emplace("skipped", true); - } else if (output) { + if (output) { j.emplace("doc", doc); auto & j2 = j.emplace("output", nlohmann::json::object()).first.value(); state->spawn(futures, 1, [&visit, output, &j2]() { visit(ref(output), j2); }); @@ -996,6 +1000,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON, MixFlakeSchemas render = [&](nlohmann::json j, const std::string & headerPrefix, const std::string & nextPrefix) { auto what = j.find("what"); auto filtered = j.find("filtered"); + auto legacy = j.find("legacy"); auto derivation = j.find("derivation"); auto s = headerPrefix; @@ -1012,6 +1017,9 @@ struct CmdFlakeShow : FlakeCommand, MixJSON, MixFlakeSchemas if (filtered != j.end() && (bool) *filtered) s += " " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--all-systems' to show)"; + if (legacy != j.end() && (bool) *legacy) + s += " " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--legacy' to show)"; + logger->cout(s); auto children = j.find("children"); @@ -1042,8 +1050,6 @@ struct CmdFlakeShow : FlakeCommand, MixJSON, MixFlakeSchemas render(*output, headerPrefix, nextPrefix); else if (child.value().contains("unknown")) logger->cout(headerPrefix + ANSI_WARNING " unknown flake output" ANSI_NORMAL); - else if (child.value().contains("skipped")) - logger->cout(headerPrefix + ANSI_WARNING " omitted" ANSI_NORMAL " (use '--legacy' to show)"); } } } diff --git a/tests/functional/flakes/show.sh b/tests/functional/flakes/show.sh index 6083a79da2b9..485f746f2d01 100755 --- a/tests/functional/flakes/show.sh +++ b/tests/functional/flakes/show.sh @@ -18,7 +18,7 @@ let show_output = builtins.fromJSON (builtins.readFile ./show-output.json); in assert show_output.inventory.packages.output.children.someOtherSystem.filtered; assert show_output.inventory.packages.output.children.${builtins.currentSystem}.children.default.derivation.name == "simple"; -assert show_output.inventory.legacyPackages.skipped; +assert show_output.inventory.legacyPackages.output.children.${builtins.currentSystem}.legacy; true ' @@ -29,7 +29,7 @@ nix eval --impure --expr ' let show_output = builtins.fromJSON (builtins.readFile ./show-output.json); in assert show_output.inventory.packages.output.children.someOtherSystem.children.default.derivation.name == "simple"; -assert show_output.inventory.legacyPackages.skipped; +assert show_output.inventory.legacyPackages.output.children.${builtins.currentSystem}.legacy; true ' From dddda5b793562b943346eb90a6316c787d07fe92 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 10 Mar 2026 22:40:46 +0100 Subject: [PATCH 2/5] Handle modules that are paths --- src/libcmd/builtin-flake-schemas.nix | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libcmd/builtin-flake-schemas.nix b/src/libcmd/builtin-flake-schemas.nix index a8a6be4a1e85..aa37621d6dc1 100644 --- a/src/libcmd/builtin-flake-schemas.nix +++ b/src/libcmd/builtin-flake-schemas.nix @@ -6,7 +6,12 @@ let mapAttrsToList = f: attrs: map (name: f name attrs.${name}) (builtins.attrNames attrs); - checkModule = module: builtins.isAttrs module || builtins.isFunction module; + checkModule = + module_: + let + module = if builtins.isPath module_ then import module_ else module_; + in + builtins.isAttrs module || builtins.isFunction module; schemasSchema = { version = 1; From 49d77b969b023577c9e5e5cbcc4768d485718fa1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 11 Mar 2026 00:06:32 +0100 Subject: [PATCH 3/5] FutureVector::finishAll(): Change "error (ignored)" to "error" "Ignored" suggests that the command would succeed if it wasn't for the error that finishAll() propagates, but that's not the case. --- src/libexpr/parallel-eval.cc | 2 +- src/libutil/include/nix/util/util.hh | 5 +++++ src/libutil/util.cc | 9 +++++++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/libexpr/parallel-eval.cc b/src/libexpr/parallel-eval.cc index 3dfcc54c40fb..8638608fd5c3 100644 --- a/src/libexpr/parallel-eval.cc +++ b/src/libexpr/parallel-eval.cc @@ -172,7 +172,7 @@ void FutureVector::finishAll() } catch (...) { if (ex) { if (!getInterrupted()) - ignoreExceptionExceptInterrupt(); + logExceptionExceptInterrupt(); } else ex = std::current_exception(); } diff --git a/src/libutil/include/nix/util/util.hh b/src/libutil/include/nix/util/util.hh index 8130c52ed27a..ac14d47a58fc 100644 --- a/src/libutil/include/nix/util/util.hh +++ b/src/libutil/include/nix/util/util.hh @@ -227,6 +227,11 @@ void ignoreExceptionInDestructor(Verbosity lvl = lvlError); */ void ignoreExceptionExceptInterrupt(Verbosity lvl = lvlError); +/** + * Like ignoreExceptionExceptInterrupt(), but specifies the error prefix. + */ +void logExceptionExceptInterrupt(std::string_view prefix = "error: ", Verbosity lvl = lvlError); + /** * Tree formatting. */ diff --git a/src/libutil/util.cc b/src/libutil/util.cc index d75aa4d67d94..7456e511e196 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -238,15 +238,20 @@ void ignoreExceptionInDestructor(Verbosity lvl) } void ignoreExceptionExceptInterrupt(Verbosity lvl) +{ + logExceptionExceptInterrupt("error (ignored):", lvl); +} + +void logExceptionExceptInterrupt(std::string_view prefix, Verbosity lvl) { try { throw; } catch (const Interrupted & e) { throw; } catch (Error & e) { - printMsg(lvl, ANSI_RED "error (ignored):" ANSI_NORMAL " %s", e.info().msg); + printMsg(lvl, ANSI_RED "%s" ANSI_NORMAL "%s", prefix, e.info().msg); } catch (std::exception & e) { - printMsg(lvl, ANSI_RED "error (ignored):" ANSI_NORMAL " %s", e.what()); + printMsg(lvl, ANSI_RED "%s" ANSI_NORMAL "%s", prefix, e.what()); } } From 8e509334b0565d8431dd1c54fbf14d004734fa5a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 11 Mar 2026 17:24:25 +0100 Subject: [PATCH 4/5] legacy -> isLegacy for consistency --- src/nix/flake.cc | 6 +++--- tests/functional/flakes/show.sh | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 08f6e40fdf0f..6cd4ecbd36a7 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -965,7 +965,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON, MixFlakeSchemas obj.emplace("filtered", true); }, - [&](ref node) { obj.emplace("legacy", true); }); + [&](ref node) { obj.emplace("isLegacy", true); }); }; auto inv = nlohmann::json::object(); @@ -1000,7 +1000,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON, MixFlakeSchemas render = [&](nlohmann::json j, const std::string & headerPrefix, const std::string & nextPrefix) { auto what = j.find("what"); auto filtered = j.find("filtered"); - auto legacy = j.find("legacy"); + auto isLegacy = j.find("isLegacy"); auto derivation = j.find("derivation"); auto s = headerPrefix; @@ -1017,7 +1017,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON, MixFlakeSchemas if (filtered != j.end() && (bool) *filtered) s += " " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--all-systems' to show)"; - if (legacy != j.end() && (bool) *legacy) + if (isLegacy != j.end() && (bool) *isLegacy) s += " " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--legacy' to show)"; logger->cout(s); diff --git a/tests/functional/flakes/show.sh b/tests/functional/flakes/show.sh index 485f746f2d01..77f350593654 100755 --- a/tests/functional/flakes/show.sh +++ b/tests/functional/flakes/show.sh @@ -18,7 +18,7 @@ let show_output = builtins.fromJSON (builtins.readFile ./show-output.json); in assert show_output.inventory.packages.output.children.someOtherSystem.filtered; assert show_output.inventory.packages.output.children.${builtins.currentSystem}.children.default.derivation.name == "simple"; -assert show_output.inventory.legacyPackages.output.children.${builtins.currentSystem}.legacy; +assert show_output.inventory.legacyPackages.output.children.${builtins.currentSystem}.isLegacy; true ' @@ -29,7 +29,7 @@ nix eval --impure --expr ' let show_output = builtins.fromJSON (builtins.readFile ./show-output.json); in assert show_output.inventory.packages.output.children.someOtherSystem.children.default.derivation.name == "simple"; -assert show_output.inventory.legacyPackages.output.children.${builtins.currentSystem}.legacy; +assert show_output.inventory.legacyPackages.output.children.${builtins.currentSystem}.isLegacy; true ' From b2b856776b628eae9dbbb3177860aa32149f76b4 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 11 Mar 2026 17:27:41 +0100 Subject: [PATCH 5/5] Fix ignored error --- src/libutil/util.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 7456e511e196..6e84f448edfd 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -239,7 +239,7 @@ void ignoreExceptionInDestructor(Verbosity lvl) void ignoreExceptionExceptInterrupt(Verbosity lvl) { - logExceptionExceptInterrupt("error (ignored):", lvl); + logExceptionExceptInterrupt("error (ignored): ", lvl); } void logExceptionExceptInterrupt(std::string_view prefix, Verbosity lvl)