From bf448deab9e96fc4dd1f6a0ef2fa66b414bea40e Mon Sep 17 00:00:00 2001 From: Ivan Trubach Date: Wed, 4 Oct 2023 06:07:53 +0300 Subject: [PATCH] Rename nixosConfigurations to configurations. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change makes it possible to define cross-compiled configurations. We add `configurations` attribute because changing `nixosConfigurations.` to `nixosConfigurations..` is a breaking change, i.e. we cannot efficiently differentiate between two schemas. Limitations: - Using this reliably requires content-addressed derivations (that are curently available as an experimental feature). - Even with content-addressed derivations, a lot of work has to be done in NixOS/Nixpkgs so that `build ≠ host` does not change the output. While not directly related, also keep in mind that NixOS configurations currently support only a single host platform. That is, the resulting system contains executables and bootloader only for a single CPU architecture. E.g. it is not possible to install systemd-boot with [per-architecture boot entries] to boot the configuration for the specific CPU architecture. [per-architecture boot entries]: https://uapi-group.org/specifications/specs/boot_loader_specification/#boot-loader-entries --- doc/manual/rl-next/nixos-configurations.md | 27 +++++++ src/nix/build.md | 2 +- src/nix/flake-check.md | 8 +- src/nix/flake.cc | 88 ++++++++++++++++++++-- tests/functional/flakes/show.sh | 2 + 5 files changed, 118 insertions(+), 9 deletions(-) create mode 100644 doc/manual/rl-next/nixos-configurations.md diff --git a/doc/manual/rl-next/nixos-configurations.md b/doc/manual/rl-next/nixos-configurations.md new file mode 100644 index 00000000000..12b8165420c --- /dev/null +++ b/doc/manual/rl-next/nixos-configurations.md @@ -0,0 +1,27 @@ +--- +synopsis: "Renamed `nixosConfigurations.`*name* to + `configurations.`*system*`.`*name*" +prs: 10291 +--- + +The `nixosConfigurations.`*name* output is deprecated in favor of +`configurations.`*system*`.`*name* where *system* the name of the system that +would be used to build the configuration, similar to `packages`, `checks` and +other system-dependent flake outputs that produce derivations. + +The old output will continue to work, but `nix flake check` will issue a +deprecation warning about it. + +It is strongly recommended to keep configurations identical when defined under +the same name with multiple systems, e.g. + +* `configurations.aarch64-linux.webserver` defines a webserver configuration + built on and for `aarch64-linux` system. +* `configurations.x86_64-linux.webserver` describes the same configuration, but + cross-compiled from `x86_64-linux` system. + +Note though that bit-wise identical cross-compilation is currently not +possible without experimental [content-addressed derivations] (and even then a +lot of effort is necessary on the Nixpkgs side to make that work). + +[content-addressed derivations]: @docroot@/contributing/experimental-features.md#xp-feature-ca-derivations diff --git a/src/nix/build.md b/src/nix/build.md index 5dfdd44a71f..e4207a56d61 100644 --- a/src/nix/build.md +++ b/src/nix/build.md @@ -64,7 +64,7 @@ R""( ```console # nix build --profile /nix/var/nix/profiles/system \ - ~/my-configurations#nixosConfigurations.machine.config.system.build.toplevel + ~/my-configurations#.configurations.x86_64-linux.machine.config.system.build.toplevel ``` (This is essentially what `nixos-rebuild` does.) diff --git a/src/nix/flake-check.md b/src/nix/flake-check.md index c8307f8d85b..6bb0043ab28 100644 --- a/src/nix/flake-check.md +++ b/src/nix/flake-check.md @@ -34,8 +34,10 @@ The following flake output attributes must be derivations: * `defaultPackage.`*system* * `devShell.`*system* * `devShells.`*system*`.`*name* -* `nixosConfigurations.`*name*`.config.system.build.toplevel` * `packages.`*system*`.`*name* +* `nixosConfigurations.`*name*`.config.system.build.toplevel` +* `configurations.`*system*`.`*name*`.config.system.build.toplevel` if + `configurations.`*system*`.`*name*`.class` is `nixos` The following flake output attributes must be [app definitions](./nix3-run.md): @@ -49,6 +51,10 @@ definitions](./nix3-flake-init.md): * `defaultTemplate` * `templates.`*name* +The following flake output attributes must be *configurations*: + +* `configurations.`*system*`.`*name* + The following flake output attributes must be *Nixpkgs overlays*: * `overlay` diff --git a/src/nix/flake.cc b/src/nix/flake.cc index a846f637114..82048bd8a41 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -506,21 +506,55 @@ struct CmdFlakeCheck : FlakeCommand } }; + auto checkNixOSConfigurationToplevel = [&](Value & v, const PosIdx pos) { + Bindings & bindings(*state->allocBindings(0)); + auto vToplevel = findAlongAttrPath(*state, "config.system.build.toplevel", bindings, v).first; + state->forceValue(*vToplevel, pos); + if (!state->isDerivation(*vToplevel)) + throw Error("attribute 'config.system.build.toplevel' is not a derivation"); + }; + auto checkNixOSConfiguration = [&](const std::string & attrPath, Value & v, const PosIdx pos) { try { Activity act(*logger, lvlInfo, actUnknown, fmt("checking NixOS configuration '%s'", attrPath)); - Bindings & bindings(*state->allocBindings(0)); - auto vToplevel = findAlongAttrPath(*state, "config.system.build.toplevel", bindings, v).first; - state->forceValue(*vToplevel, pos); - if (!state->isDerivation(*vToplevel)) - throw Error("attribute 'config.system.build.toplevel' is not a derivation"); + checkNixOSConfigurationToplevel(v, pos); } catch (Error & e) { e.addTrace(resolve(pos), HintFmt("while checking the NixOS configuration '%s'", attrPath)); reportError(e); } }; + auto checkConfiguration = [&](const std::string & attrPath, Value & v, const PosIdx pos) { + try { + Activity act(*logger, lvlInfo, actUnknown, + fmt("checking configuration '%s'", attrPath)); + state->forceAttrs(v, pos, ""); + auto sType = state->symbols.create("_type"); + auto aType = v.attrs->get(sType); + if (aType && aType->name == sType) { + auto aTypeValue = state->forceStringNoCtx(*aType->value, aType->pos, ""); + if (aTypeValue == "configuration") { + auto sClass = state->symbols.create("class"); + auto aClass = v.attrs->get(sClass); + if (aClass && aClass->name == sClass) { + auto aClassValue = state->forceStringNoCtx(*aClass->value, aClass->pos, ""); + // Check for v.class == "nixos". This exists mostly for compatibility + // with configurations migrated from nixosConfigurations. + if (aClassValue == "nixos") { + checkNixOSConfigurationToplevel(v, pos); + } + } + return; // OK + } + } + throw Error("attribute set is not a configuration"); + } catch (Error & e) { + e.addTrace(resolve(pos), HintFmt("while checking the configuration '%s'", attrPath)); + reportError(e); + } + }; + auto checkTemplate = [&](const std::string & attrPath, Value & v, const PosIdx pos) { try { Activity act(*logger, lvlInfo, actUnknown, @@ -594,6 +628,7 @@ struct CmdFlakeCheck : FlakeCommand name == "overlay" ? "overlays.default" : name == "devShell" ? "devShells..default" : name == "nixosModule" ? "nixosModules.default" : + name == "nixosConfigurations" ? "configurations.." : ""; if (replacement != "") warn("flake output attribute '%s' is deprecated; use '%s' instead", name, replacement); @@ -725,6 +760,21 @@ struct CmdFlakeCheck : FlakeCommand *attr.value, attr.pos); } + else if (name == "configurations") { + state->forceAttrs(vOutput, pos, ""); + for (auto & attr : *vOutput.attrs) { + const auto & attr_name = state->symbols[attr.name]; + checkSystemName(attr_name, attr.pos); + if (checkSystemType(attr_name, attr.pos)) { + state->forceAttrs(*attr.value, attr.pos, ""); + for (auto & attr2 : *attr.value->attrs) + checkConfiguration( + fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]), + *attr2.value, attr2.pos); + }; + } + } + else if (name == "hydraJobs") checkHydraJobs(name, vOutput, pos); @@ -1154,6 +1204,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON || attrPathS[0] == "checks" || attrPathS[0] == "devShells" || attrPathS[0] == "legacyPackages" + || attrPathS[0] == "configurations" || attrPathS[0] == "packages") && (attrPathS.size() == 1 || attrPathS.size() == 2)) { for (const auto &subAttr : visitor2->getAttrs()) { @@ -1257,6 +1308,26 @@ struct CmdFlakeShow : FlakeCommand, MixJSON } }; + auto showConfiguration = [&]() + { + auto aType = visitor.maybeGetAttr("_type"); + if (!aType || aType->getString() != "configuration") + state->error("not a configuration definition").debugThrow(); + + std::optional optClass; + if (auto aClass = visitor.maybeGetAttr("class")) + optClass = aClass->getString(); + + if (json) { + j.emplace("type", "configuration"); + if (optClass) + j.emplace("class", *optClass); + } else if (optClass) + logger->cout("%s: %s configuration", headerPrefix, *optClass); + else + logger->cout("%s: configuration", headerPrefix); + }; + if (attrPath.size() == 0 || (attrPath.size() == 1 && ( attrPathS[0] == "defaultPackage" @@ -1270,6 +1341,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON || ((attrPath.size() == 1 || attrPath.size() == 2) && (attrPathS[0] == "checks" || attrPathS[0] == "packages" + || attrPathS[0] == "configurations" || attrPathS[0] == "devShells" || attrPathS[0] == "apps")) ) @@ -1279,7 +1351,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON else if ( (attrPath.size() == 2 && (attrPathS[0] == "defaultPackage" || attrPathS[0] == "devShell" || attrPathS[0] == "formatter")) - || (attrPath.size() == 3 && (attrPathS[0] == "checks" || attrPathS[0] == "packages" || attrPathS[0] == "devShells")) + || (attrPath.size() == 3 && (attrPathS[0] == "checks" || attrPathS[0] == "packages" || attrPathS[0] == "configurations" || attrPathS[0] == "devShells")) ) { if (!showAllSystems && std::string(attrPathS[1]) != localSystem) { @@ -1289,7 +1361,9 @@ struct CmdFlakeShow : FlakeCommand, MixJSON logger->warn(fmt("%s omitted (use '--all-systems' to show)", concatStringsSep(".", attrPathS))); } } else { - if (visitor.isDerivation()) + if (attrPathS[0] == "configurations") + showConfiguration(); + else if (visitor.isDerivation()) showDerivation(); else throw Error("expected a derivation"); diff --git a/tests/functional/flakes/show.sh b/tests/functional/flakes/show.sh index a3d30055233..165d805cd6f 100644 --- a/tests/functional/flakes/show.sh +++ b/tests/functional/flakes/show.sh @@ -50,6 +50,8 @@ cat >flake.nix <