Rework a few things with the experimental features list

- Use struct not `std::pair`, designated initializers

- Use `constexpr` array that we can index by enum tag

- It no longer segfaults; not sure why.
This commit is contained in:
John Ericson 2023-04-02 18:11:16 -04:00
parent b2c9315bf2
commit 2585bcaa50
3 changed files with 142 additions and 82 deletions

View file

@ -5,75 +5,131 @@
namespace nix { namespace nix {
std::map<ExperimentalFeature, std::pair<std::string, std::string>> stringifiedXpFeatures = { struct ExperimentalFeatureDetails
{ Xp::CaDerivations, {"ca-derivations", R"( {
Allows derivations to be content-addressed in order to prevent rebuilds ExperimentalFeature tag;
when changes to the derivation do not result in changes to the std::string_view name;
derivation's output. See std::string_view description;
[__contentAddressed](../language/advanced-attributes.md#adv-attr-__contentAddressed)
for more info.
)"} },
{ Xp::ImpureDerivations, {"impure-derivations", R"(
Allows derivations to produce non-fixed outputs by setting the `__impure`
derivation attribute to `true`. See [these release
notes](../release-notes/rl-2.8.md) for an example.
)"} },
{ Xp::Flakes, {"flakes", R"(
Allows for derivations to be packaged in flakes. See the manual entry for
[`nix flake`](../command-ref/new-cli/nix3-flake.md) or this [detailed
introduction](https://www.tweag.io/blog/2020-05-25-flakes/) for more info.
)"} },
{ Xp::NixCommand, {"nix-command", R"(
Allows the usage of the new `nix` CLI subcommands, such as `nix build`, `nix
develop`, `nix run`, etc. See the manual for
[`nix`](../command-ref/new-cli/nix.md) for more info.
)"} },
{ Xp::RecursiveNix, {"recursive-nix", R"(
Allow Nix derivations to call Nix in order to recursively build derivations.
See [this
commit](https://github.com/edolstra/nix/commit/1a27aa7d64ffe6fc36cfca4d82bdf51c4d8cf717)
for more info.
)"} },
{ Xp::NoUrlLiterals, {"no-url-literals", R"(
Disallows unquoted URLs as part of the Nix language syntax. See [RFC
45](https://github.com/NixOS/rfcs/pull/45) for more info.
)"} },
{ Xp::FetchClosure, {"fetch-closure", R"(
Enables the use of the `fetchClosure` function in the standard library. See
the docs for [`fetchClosure`](../language/builtins.md#builtins-fetchClosure)
for more info.
)"} },
{ Xp::ReplFlake, {"repl-flake", R"(
Allows the user to enter a Nix REPL within a flake, e.g. `nix repl nixpkgs`
or `nix repl .#foo`.
)"} },
{ Xp::AutoAllocateUids, {"auto-allocate-uids", R"(
Allows Nix to automatically pick UIDs for builds, rather than creating
`nixbld*` user accounts. See [here](#conf-auto-allocate-uids) for more info.
)"} },
{ Xp::Cgroups, {"cgroups", R"(
Allows Nix to execute builds inside cgroups. See
[`use-cgroups`](#conf-use-cgroups) for more info.
)"} },
{ Xp::DiscardReferences, {"discard-references", R"(
Enables the use of the `unsafeDiscardReferences` attribute in derivations
that use structured attributes. This disables scanning of outputs for
runtime dependencies.
)"} },
}; };
constexpr std::array<ExperimentalFeatureDetails, 11> xpFeatureDetails = {{
{
.tag = Xp::CaDerivations,
.name = "ca-derivations",
.description = R"(
Allows derivations to be content-addressed in order to prevent rebuilds
when changes to the derivation do not result in changes to the
derivation's output. See
[__contentAddressed](../language/advanced-attributes.md#adv-attr-__contentAddressed)
for more info.
)",
},
{
.tag = Xp::ImpureDerivations,
.name = "impure-derivations",
.description = R"(
Allows derivations to produce non-fixed outputs by setting the `__impure`
derivation attribute to `true`. See [these release
notes](../release-notes/rl-2.8.md) for an example.
)",
},
{
.tag = Xp::Flakes,
.name = "flakes",
.description = R"(
Allows for derivations to be packaged in flakes. See the manual entry for
[`nix flake`](../command-ref/new-cli/nix3-flake.md) or this [detailed
introduction](https://www.tweag.io/blog/2020-05-25-flakes/) for more info.
)",
},
{
.tag = Xp::NixCommand,
.name = "nix-command",
.description = R"(
Allows the usage of the new `nix` CLI subcommands, such as `nix build`, `nix
develop`, `nix run`, etc. See the manual for
[`nix`](../command-ref/new-cli/nix.md) for more info.
)",
},
{
.tag = Xp::RecursiveNix,
.name = "recursive-nix",
.description = R"(
Allow Nix derivations to call Nix in order to recursively build derivations.
See [this
commit](https://github.com/edolstra/nix/commit/1a27aa7d64ffe6fc36cfca4d82bdf51c4d8cf717)
for more info.
)",
},
{
.tag = Xp::NoUrlLiterals,
.name = "no-url-literals",
.description = R"(
Disallows unquoted URLs as part of the Nix language syntax. See [RFC
45](https://github.com/NixOS/rfcs/pull/45) for more info.
)",
},
{
.tag = Xp::FetchClosure,
.name = "fetch-closure",
.description = R"(
Enables the use of the `fetchClosure` function in the standard library. See
the docs for [`fetchClosure`](../language/builtins.md#builtins-fetchClosure)
for more info.
)",
},
{
.tag = Xp::ReplFlake,
.name = "repl-flake",
.description = R"(
Allows the user to enter a Nix REPL within a flake, e.g. `nix repl nixpkgs`
or `nix repl .#foo`.
)",
},
{
.tag = Xp::AutoAllocateUids,
.name = "auto-allocate-uids",
.description = R"(
Allows Nix to automatically pick UIDs for builds, rather than creating
`nixbld*` user accounts. See [here](#conf-auto-allocate-uids) for more info.
)",
},
{
.tag = Xp::Cgroups,
.name = "cgroups",
.description = R"(
Allows Nix to execute builds inside cgroups. See
[`use-cgroups`](#conf-use-cgroups) for more info.
)",
},
{
.tag = Xp::DiscardReferences,
.name = "discard-references",
.description = R"(
Enables the use of the `unsafeDiscardReferences` attribute in derivations
that use structured attributes. This disables scanning of outputs for
runtime dependencies.
)",
},
}};
static_assert(
[]() constexpr {
for (auto [index, feature] : enumerate(xpFeatureDetails))
if (index != (size_t)feature.tag)
return false;
return true;
}(),
"array order does not match enum tag order");
const std::optional<ExperimentalFeature> parseExperimentalFeature(const std::string_view & name) const std::optional<ExperimentalFeature> parseExperimentalFeature(const std::string_view & name)
{ {
using ReverseXpMap = std::map<std::string_view, ExperimentalFeature>; using ReverseXpMap = std::map<std::string_view, ExperimentalFeature>;
static auto reverseXpMap = []() static std::unique_ptr<ReverseXpMap> reverseXpMap = [](){
{
auto reverseXpMap = std::make_unique<ReverseXpMap>(); auto reverseXpMap = std::make_unique<ReverseXpMap>();
std::string_view name; for (auto & xpFeature : xpFeatureDetails)
for (auto & [feature, featureStringPair] : stringifiedXpFeatures) { (*reverseXpMap)[xpFeature.name] = xpFeature.tag;
name = featureStringPair.first;
(*reverseXpMap)[name] = feature;
}
return reverseXpMap; return reverseXpMap;
}(); }();
@ -83,25 +139,29 @@ const std::optional<ExperimentalFeature> parseExperimentalFeature(const std::str
return std::nullopt; return std::nullopt;
} }
std::string_view showExperimentalFeature(const ExperimentalFeature feature) std::string_view showExperimentalFeature(const ExperimentalFeature tag)
{ {
const auto ret = get(stringifiedXpFeatures, feature); assert((size_t)tag < xpFeatureDetails.size());
assert(ret); return xpFeatureDetails[(size_t)tag].name;
return ret->first;
} }
std::string getExperimentalFeaturesList() { std::string getExperimentalFeaturesList() {
std::string experimentalFeaturesList = R"( std::string experimentalFeaturesList = R"(
Experimental Nix features to enable. Experimental Nix features to enable.
Current experimental features are the following: Current experimental features are the following:
)"; )";
std::string experimentalFeatureString; for (auto & xpFeature : xpFeatureDetails) {
for (auto& [feature, featureStringPair] : stringifiedXpFeatures) { experimentalFeaturesList += std::string {}
experimentalFeatureString = " - `" + featureStringPair.first + "`\n"; /* length of this first string must be 12, matching the indent of
experimentalFeatureString += featureStringPair.second + "\n\n"; the descriptions in the xpFeatureDetails literal. FIXME compute
experimentalFeaturesList += experimentalFeatureString; markdown in a less hacky way. */
+ " - "
+ "`" + xpFeature.name + "`"
+ "\n"
+ xpFeature.description
+ "\n\n";
} }
return experimentalFeaturesList; return experimentalFeaturesList;
@ -110,10 +170,9 @@ std::string getExperimentalFeaturesList() {
std::set<ExperimentalFeature> parseFeatures(const std::set<std::string> & rawFeatures) std::set<ExperimentalFeature> parseFeatures(const std::set<std::string> & rawFeatures)
{ {
std::set<ExperimentalFeature> res; std::set<ExperimentalFeature> res;
for (auto & rawFeature : rawFeatures) { for (auto & rawFeature : rawFeatures)
if (auto feature = parseExperimentalFeature(rawFeature)) if (auto feature = parseExperimentalFeature(rawFeature))
res.insert(*feature); res.insert(*feature);
}
return res; return res;
} }

View file

@ -11,8 +11,9 @@ namespace nix {
/** /**
* The list of available experimental features. * The list of available experimental features.
* *
* If you update this, dont forget to also change the map defining their string * If you update this, dont forget to also change the map defining
* representation and documentation in the corresponding `.cc` file as well. * their string representation and documentation in the corresponding
* `.cc` file as well.
*/ */
enum struct ExperimentalFeature enum struct ExperimentalFeature
{ {

View file

@ -730,16 +730,16 @@ constexpr auto enumerate(T && iterable)
{ {
size_t i; size_t i;
TIter iter; TIter iter;
bool operator != (const iterator & other) const { return iter != other.iter; } constexpr bool operator != (const iterator & other) const { return iter != other.iter; }
void operator ++ () { ++i; ++iter; } constexpr void operator ++ () { ++i; ++iter; }
auto operator * () const { return std::tie(i, *iter); } constexpr auto operator * () const { return std::tie(i, *iter); }
}; };
struct iterable_wrapper struct iterable_wrapper
{ {
T iterable; T iterable;
auto begin() { return iterator{ 0, std::begin(iterable) }; } constexpr auto begin() { return iterator{ 0, std::begin(iterable) }; }
auto end() { return iterator{ 0, std::end(iterable) }; } constexpr auto end() { return iterator{ 0, std::end(iterable) }; }
}; };
return iterable_wrapper{ std::forward<T>(iterable) }; return iterable_wrapper{ std::forward<T>(iterable) };