Allow flake `nixConfig` attribute to contain architecture-specific options
Is your feature request related to a problem? Please describe.
I want to have my flake specify e.g. nixConfig.aarch64-darwin.extra-platforms = [ "x86_64-darwin" ] without setting this for other platforms.
Describe the solution you'd like
Right now nixConfig doesn't allow nested attribute sets (if I'm reading the code correctly). It could support nested attribute sets by assuming the key is a platform and recursing only if the platform matches.
This issue has been mentioned on NixOS Discourse. There might be relevant details there:
https://discourse.nixos.org/t/nix-2-6-0-released/17324/12
I'm not sure we should do this. It gives the flake file format a hard dependency on our current simplistic concept of systems (i.e. simple strings like "x86_64-linux"), which doesn't account for cross-compilation and doesn't match the richer definition in lib/systems in nixpkgs.
It also seems ugly/ambiguous to put system types in the same namespace as options (i.e. in nixConfig.aarch64-darwin it's not clear without domain knowledge that aarch64-darwin is a system type rather than an option name). While we currently don't have attrset-typed options, that's something I've wanted for a while (e.g. remote builders could be expressed as attrsets rather than our current ad-hoc format). This could be addressed by renaming it to nixConfig.perSystem.<system> or something like that.
I agree with @edolstra. I think I see why you’d want that, but it’s a bit too ad-hoc and rigid to be freezed forever in the flake schema.
I think this kind of “twisted” use-case would be better served by a custom wrapper that would be more flexible imho
Regarding freezing in the flake schema, Nix already faces challenges with the idea of changing the system strings as builtins.currentSystem returns just such a string and changing this is likely to break a lot. And of course the current strings are used in a lot of the outputs already, with this use directly understood by the nix tools, meaning we've already baked this into the flake schema.
As for cross-compilation, we're talking about the options that affect Nix, not anything involved in the actual building of packages.
I have no objection to using nixConfig.perSystem.<system> for this. We should make sure list-typed config values will merge together, though I'm not sure right now if it already does that for any nixConfig entries that match things from nix.conf (I hope it does).
@thufschmitt I don't know what sort of "custom wrapper" you mean. I'm writing a flake, not writing a build system. My flake should work as-is with existing Nix tooling, and in particular with any existing wrappers around Nix tooling.
I have basically the same opinions and questions as @lilyball: I do agree that we're kind of already stuck with host triples in the schema, and I don't quite see the relevancy of cross-compilation, either.
I have no objection to using
nixConfig.perSystem.<system>for this.
+1; I'd be happy to write that into my PR :)
We should make sure list-typed config values will merge together, though I'm not sure right now if it already does that for any
nixConfigentries that match things fromnix.conf(I hope it does).
Ah, I overlooked this, thanks for pointing it out! I'll take a look at implementing that (and of course the other feedback) soon.
@thufschmitt I don't know what sort of "custom wrapper" you mean.
+1. I think an example would go a long way for my understanding here if it's not too much effort :).
I don't know what sort of "custom wrapper" you mean
I don’t know either tbh :) I’d say that depends on why exactly you need this.
My point is that 99% of the cases setting extra-platforms = [ "x86_64-darwin" ] is something that has to be specified at the machine level, not in the flake (If I have an aarch64-darwin machine but don’t want it to build x86_64-darwin stuff, I don’t want a flake that I get from the internet to override that for me. And conversely, if it makes sense for my machine to build x86_64-darwin stuff then I should add that to my global nix.conf − or Nix should do it automatically like it does for i686).
The remaining 1% makes it a very niche thing for which it’s reasonable to tell the users of the flake, that they might want to run Nix with --option extra-platforms x86_64-darwin or set NIX_CONFIG="$NIX_CONFIG\nextra-platforms = x86_64-darwin.
In particular, I don’t see a good reason why there should be a nixConfig.perSystem field that would discriminate on the system type and not a nixConfig.perSystemFeature that would discriminate on the system features of the machine or nixConfig.perHostname.
(Obviously maybe there’s a very good reason that would make this much more common than what I imagine and worth special-casing this in Nix. In which case feel free to correct me)
The use-case that prompted this suggestion was my nix-darwin setup, which has a separate file for each host that uses the setup, and each host then imports a common file based on the arch (which then imports a common file for the os e.g. darwin, which then imports the root common file). My problem is I have an M1 machine (which is why I have the per-arch files now on top of the per-os file) which wants to set extra-platforms = [ "x86_64-darwin" ]. It currently does so with the nix.extra-options config, but the problem is I need to activate the setup before that takes effect, meaning I had to do a two-phase setup when I first configured this (where phase 1 just added that config, and then phase 2 added the cross-compiled packages).
So what I was hoping to do right now is to use the nixConfig attribute to set this extra platform only for aarch64-darwin[^1], such that I can do a fresh install of my config on an M1 machine without needing this two-phase setup. I do realize this means it will enable x86_64-darwin packages on all M1 machines regardless of whether they have Rosetta, but the actual use of x86_64-darwin packages can be controlled on a per-host basis if needed, I just need the capability to be there. And honestly, I don't have any reason to disable that, as my shared setup includes some packages via Rosetta and so I'm committing to using Rosetta on all M1 machines that use this config. This is for personal use, not for servers, so this is a choice I've made for myself.
[^1]: I believe Nix 2.5 actually attempts to detect Rosetta and enable the platform, but I want to support Nix 2.4 as well and I don't particularly want to rely on this behavior, especially since the packages I'm using that require Rosetta are not used as dependencies of anything, and so I believe that the install should actually work without Rosetta given that the shared binary cache should include the packages and therefore not require actually running anything under Rosetta as part of the install. I am willing to accept the scenario where I activate my config on an M1 machine without Rosetta, and then at some point in the future I attempt to invoke such a package and get prompted by the OS to install Rosetta.
Oh and to answer the question more broadly, Nix already has the notion of "what's the current system?", both in the builtin (for impure evaluations) and in how flakes use the system to select the output. Nix does not have any equivalent for hostname. Until now I didn't realize that Nix had functionality for system features, although at first glance that does not appear to be exposed while evaluating Nix code but is instead like extra-platforms in that it allows derivations to fail to build under various conditions, and this is something that is specified in the config[^1] so having it also be used to affect the config would be curious.
I do admit that I'm not sure what configuration other than extra-platforms I would want to set on a per-system basis, but the sandbox-related options seem like good candidates too (e.g. setting sandbox=relaxed on darwin, setting sandbox-paths,
[^1]: Technically the config also specifies system but the documentation says you probably shouldn't change that value. I didn't realize this was even in the config. So this raises the question, what happens if I write nixConfig.perSystem.x86_64-darwin.system = "aarch64-darwin"? I'm inclined to just say the system key should be disallowed in per-system config
Regarding merging conflicting settings, it looks like settings with the prefix extra- are merged - however, (src/libexpr/flake/config.cc, in the declaration of baseName) this is explicitly opted-out of for all nixConfig settings.
I imagine we wouldn't want to merge flake settings with system settings, as this can produce unpredictable results from the pov of the flake author, but it would make sense to merge system specific extra-settings with system-agnostic nixConfig settings. Assuming this is desired, I've pushed ~~bb4e54c~~ (rebased, now 553206d).