Open Mic92 opened 4 years ago
cc @zimbatm who is involved in flake-utils
Not passing in system
(or any other arguments) is intentional:
packages
output should provide packages that work, not packages that might work.system
, but would be a problem for arbitrary arguments).foo.bar
will produce a different result on every system. Having a single name to specify flake outputs (rather than a tuple (attrname, system type, args)
) is very useful, e.g. for Hydra job names (you can count on hydraJobs.foo
to produce the same result for everybody).system
argument, you cannot use flake outputs for different systems in the same evaluation. (E.g. on x86_64-linux, you might want to use some i686-linux derivations like wine
; and a Hydra aggregate job needs to be able to depend on jobs for different systems.)There are multiple usability issues with the current design:
The list of supported systems is currently internal to the flake. As a flake author, it is useful to be able to specify which systems are supported. As a flake user, it means that it is not possible to change that list without forking the repository. Especially once the flake ecosystem starts to grow, it will quickly become prohibitive to introduce new platforms.
The amount of boilerplate that is needed to specify all the permutations of systems for all the derivations. That's why I wrote flake-utils, to make that usage a bit less cumbersome. The outputs are mixing both system-dependent and independent keys so it's not as easy as passing the system
as an argument.
Typing x86_64-linux
is error-prone. When a typo happens, the feedback system is not able to point to the typo directly. The user will see that their system is not supported, and then have to make the mental connection to look for a typo.
Each flake ends-up re-initializing nixpkgs. This adds to the boilerplate and evaluation time.
One possibility would be to introduce a new systems
attribute to the top-level that lists all the supported systems by the flake. This has the advantage of making that list discoverable with tooling. And introduce typo checks for common archs. Then split the outputs to generic and system-dependent:
{
description = "Usability flakes";
systems = [ "x86_64-linux" "x86_64-darwin"];
inputs = {}; # nothing changes here
# system-independent outputs
exports = inputs: {
nixosConfiguration = {};
overlay = final: prev: {};
lib = {};
};
# system-dependent outputs
outputs = system: { self, nixpkgs, ... }@inputs:
let
# I don't have a good idea to avoid that boilerplate
pkgs = import nixpkgs {
# Notice that the overlay is accessible via `self`
overlays = [ self.overlay ];
inherit system;
};
in
{
packages.hello = pkgs.hello;
apps = {};
};
}
One of the use-case that this design prevents is combining the derivations of multiple systems. This is for example if you want to create a release tarball that includes multiple architectures. In practice, that kind of construct is quite difficult to build outside of CI systems which have all the archs attached as remote builders.
Another possibility is that systems
could inherit the list from the nixpkgs flake if left unspecified. That way, if nixpkgs gain a new architecture it will be easier to upgrade all of the flakes.
Could we recognise the usefulness of ternary logic known-good/known-broken/unsure? The less overhead there is in checking whether something never-tried works due to upstream efforts, the better information we will have available.
The original RFC states:
Flakes provide an attribute set of values, such as packages, Nixpkgs overlays, NixOS modules, library functions, Hydra jobs, nix-shell definitions, etc.
Is my assumption correct, that the idea of exposinglibrary functions
has been abandoned? Since it's not possible to pass any arguments, it will never be possible to use a function of a flake?
I'm looking for exactly that use case. I'd like to expose a function that produces a python derivation, given a set of constraints. Currently I have to use a really weird hack where I generate an infinitely deep attribute set, to allow the user to select packages:
nix run github:davhau/mach-nix#with.requests.tensorflow.aiohttp
With this trick, it's already possible to build constructs which allow passing arguments (kind of). Why not support it properly?
No, you can definitely have library functions as flake outputs. You just can't call them from the command line.
Why not support it properly?
See my comment above.
Having said that, the configs
branch does add experimental support for setting Nix module options from the command line (dc4a280318650762a79447dbb299aef887f61d2e).
This issue has been mentioned on NixOS Discourse. There might be relevant details there:
https://discourse.nixos.org/t/flakes-with-unfree-licenses/9405/1
I have similar problem as @DavHau, I'd like to give users of my flake the ability to compile firmware from the command line. The ideal UX is that one would write a config file, describing the build for the build system of the original project and then a derivation would be built, which would contain just the resulting .bin
. This is currently impossible with flakes as I can't pass arguments into my flake from the command line.
What's the alternative?
What format is the config file in? nix run github:user/repo#compile --config ./myconfig.toml
would work fine for example. nix run
and nix develop
use the current system to find the attribute. If the user wants to compose Nix files then they need another flake.
it's Kconfig, the kernel like build config
I wrote a proposal without knowing about this issue.
@Mic92 pointed me to it later on and it's interesting to observe that I've come to the same conclusions.
I strongly support the idea of making the system
handling simpler − esp. given that 80% of the time people just don’t care about it.
I think the current design has a really good thing though, in that it’s conceptually very simple (the only part of the Nix codebase that has to know about the notion of a flake “system” is the cli shortcut that expands foo#bar
into foo#packages.x86_64-linux.bar
). So although it’s painful and verbose, it’s explicit and doesn’t have any hidden magic.
(commenting here on @domenkozar's doc because I can't comment inline on it)
Flake would still pass the
system
implicitly by default (as it already does today when selecting the output attribute) but it would be explicit inflake.nix
inputs. There's already--system XXX
argument tonix
command line that can be used to override the default value matching host.
I guess you'd need a way to override the system
arguments of the input flakes too (and separately from --system
) for building multi-arch systems (Edit: I see this is handled by still having a .{system}
attribute in the flake outputs, but that makes things slightly weird as it means that the system becomes both an input and an output)
We would also need to add top-level attribute
supportedSystems = [ ... ]
that would allow buildinghydraJobs
andchecks
for all systems.
Then that leaves the problem that “The list of supported systems is currently internal to the flake” (Edit: you use inputs.nixpkgs.supportedSystems = [ ... ];
in a code snippet, which seems to imply that this could be overriden. In that case it's worth pointing out explicitely).
It is hermetic, the outputs would depend on the
system
input. Flakes caching can take the system into the account when calculating the key cache.
Maybe it’s the case (need to think it a bit more), but this only holds as long as Nix can generate a system-independent lockfile (because it would be pretty bad if the lockfile couldn’t be shared between two different systems)
There could be
--local-system
argument to flake commands to restrict evaluation to only one system.Alternatively, that could be the default with
--all-system
providing the current default.
That bit isn't really clear to me: If system
is an input, then evaluating the flake will only evaluate it for the system that's given as input. Or do you mean that a flake would actually evaluate to something like map (system: outputs (inputs // { inherit system; }) supportedSystems
?
You'll have to evaluate each input with a different system. That can now be done in parallel.
I don’t think it’s really easier to parallelize this than the current version. The blocker in both cases is that the evaluator isn’t reentrant.
Maybe a (relatively) small change to solve the problem of systems being hardcoded in a flake (which might also solve other issues) would be to allow raw Nix values as input for a flake (this shouldn’t mess-up caching as long as these values end-up in the lockfile). Then we could have a convention that flakes accept a supportedSystems
input − in which case it could be overriden without having to fork the flake.
That does in a way add yet-more-convention and doesn’t do anything for reducing the boilerplate so I’m not sure it’s a good idea as-it-is, but there might be something to build on top of that.
I strongly support the idea of making the system handling simpler − esp. given that 80% of the time people just don’t care about it.
Thanks for putting it so succinctly!
I think the current design has a really good thing though, in that it’s conceptually very simple (the only part of the Nix codebase that has to know about the notion of a flake “system” is the cli shortcut that expands foo#bar into foo#packages.x86_64-linux.bar). So although it’s painful and verbose, it’s explicit and doesn’t have any hidden magic.
That's because it pushes most of the questions to the writer of the flake. I consider that a con, because things have to be implemented over and over again.
I guess you'd need a way to override the system arguments of the input flakes too (and separately from --system) for building multi-arch systems (Edit: I see this is handled by still having a .{system} attribute in the flake outputs, but that makes things slightly weird as it means that the system becomes both an input and an output)
It wouldn't be an output, but you'd declare it as a setting for the input itself - evaluating the same input for different systems. I'll update the document to clarify this.
Then that leaves the problem that “The list of supported systems is currently internal to the flake”
It gives the space for warning the user that the system isn't supported by the flake, instead of getting an attribute error. That seems quite an improvement for the experience.
Maybe it’s the case (need to think it a bit more), but this only holds as long as Nix can generate a system-independent lockfile (because it would be pretty bad if the lockfile couldn’t be shared between two different systems)
Looking at the lockfiles (there's, unfortunately, no documentation about them), what does narHash
consist of?
That bit isn't really clear to me: If system is an input, then evaluating the flake will only evaluate it for the system that's given as input. Or do you mean that a flake would actually evaluate to something like map (system: outputs (inputs // { inherit system; }) supportedSystems?
It would mean you can evaluate the flake for all given systems, for example on a CI to make sure everything evaluates.
I don’t think it’s really easier to parallelize this than the current version. The blocker in both cases is that the evaluator isn’t reentrant.
It's parallelizable because one needs to pass system as an input, so when evaluating for more than one system, that's one evaluation per system.
Maybe a (relatively) small change to solve the problem of systems being hardcoded in a flake (which might also solve other issues) would be to allow raw Nix values as input for a flake (this shouldn’t mess-up caching as long as these values end-up in the lockfile). Then we could have a convention that flakes accept a supportedSystems input − in which case it could be overriden without having to fork the flake.
That will relative flakes would really go a long way with modularity. I'd still make supportedSystem
a special meaning for warnings, etc as mentioned above.
That does in a way add yet-more-convention and doesn’t do anything for reducing the boilerplate so I’m not sure it’s a good idea as-it-is, but there might be something to build on top of that.
Yes, this design change is "convention over configuration" applied to dealing with system
. Why do you think more conventions is bad?
This issue has been mentioned on NixOS Discourse. There might be relevant details there:
I agree with the gist of this change. The larger issue though is Flakes is constantly lagging behind idioms in Nixpkgs that have been polished over the years. For example:
stdenv.{build,host,target}Platform
which provided much more detailed information and supports cross compilation.lib.platforms
used to be not a list of system
s (again, too crude) but an arbitrary predicate on the host platform, which allowed intent to be conveyed much more precisely. This was reverted because it broke some (IMO layer-violating) feature of Hydra or nix-env
.We could incorporate all this stuff in Nix for Flakes, but that leaves Flakes quite brittle when we can and do refine this stuff from time to time in Nixpkgs. Or maybe we should implement more of Flakes in Nix. Factoring out lib/
and such things to make a standard library of sorts so we still have some agility. But that that point, what are flakes? Just something to manage inputs / lockfile without much extra structure?
The larger issue though is Flakes is constantly lagging behind idioms in Nixpkgs that have been polished over the years.
Maybe the nix
UI, but not flakes, because...
Just something to manage inputs / lockfile without much extra structure?
That's basically correct. The flake file format doesn't impose a lot of requirements on what the output attributes of a flake are. Thus it can accommodate whatever idioms regarding systems/platforms we may come up with in the future. (This is also why we shouldn't have a system
input attribute, because that would lock us into a particular idiom.)
OTOH, various tools can define "well-known" flake output attributes, e.g. nix build
looks for packages.<name>.<system>
by default, as a convenience. These can of course evolve, e.g. we could have a packages_v2
output that uses a different encoding for system types.
This was reverted because it broke some (IMO layer-violating) feature of Hydra or nix-env.
The semantics of those attributes are determined by the tools that operate on them. You can't just change the meaning of platforms
and then complain that nix-env
barfs :-)
Looking at the lockfiles (there's, unfortunately, no documentation about them), what does narHash consist of?
Lock files are documented here: https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-flake.html#lock-files
It's parallelizable because one needs to pass system as an input, so when evaluating for more than one system, that's one evaluation per system.
The current hydra-evaluator is already parallel. Having to deal with a system
input type would just make it more complex since it then has two axes of parallelism to deal with: the system type and the jobs for each system type.
That's basically correct. The flake file format doesn't impose a lot of requirements on what the output attributes of a flake are. Thus it can accommodate whatever idioms regarding systems/platforms we may come up with in the future. (This is also why we shouldn't have a system input attribute, because that would lock us into a particular idiom.)
I understand what you are arguing for, but I am confused because to me it seems like this might at odds with the goals of nix flakes as declared in the abstract of the RFC
Abstract: This RFC proposes a mechanism to package Nix expressions into composable entities called "flakes". Flakes allow hermetic, reproducible evaluation of multi-repository Nix projects; impose a discoverable, standard structure on Nix projects; and replace previous mechanisms such as Nix channels and the Nix search path.
So "standard structure" is indeed only to be understood as "something to manage inputs / lockfile" as @Ericson2314 put it above?
No, the standard interface to Nix projects is on the one hand the file format and hermetic evaluation model, and on the other hand having some standard flake output attributes recognized by tools (so that e.g. running nix develop
in a project does the right thing). But the latter is not required by the file format.
(With the caveat that most people on this thread have thought about this much longer than I have:)
The flake file format doesn't impose a lot of requirements on what the output attributes of a flake are. Thus it can accommodate whatever idioms regarding systems/platforms we may come up with in the future. (This is also why we shouldn't have a
system
input attribute, because that would lock us into a particular idiom.)
It sounds like this is drawing a distinction between two levels of schema: the flake file format, and the well-known output attributes, and suggesting that the latter is easier to evolve, and the former should be basically locked and never evolve.
As a user, it seems like there's not actually a practical difference between the two levels. Most of the implementation of both is in C++ that I'm not going to look at or modify (and would take a big effort to understand if I needed to).
If we did move from packages
to packages_v2
with different schema, almost all tools in the ecosystem would have to change, because pretty much everything wants to know about packages. But moving to outputs_v2
with a different signature would require pretty much the same set of tools to change. If the changes required to evolve outputs
instead of packages
would be in different source code files in the nix binary, and even if they were more extensive, that's all pretty much irrelevant to me, since I consider the nix binary a monolithic thing.
Another good example is nix-darwin, which calls nixpkgs inside the darwin configuration.
This is where the design has to be different to NixOS, because in NixOS nixpkgs is evaluated outside the supplied configuration.
So the output interface starts to differ, introducing cognitive overhead.
NixOS:
nixosConfigurations.<hostname> = {};
nix-darwin:
darwinConfigurations.<system>.<name> = {};
The other alternative is to tell people to hardcode the system, but that's another way to wrestle with the design.
Consider also the case where aarch64-darwin was introduced to only one system in nix-darwin, if system was an input this wouldn't be a breaking change.
In my experience the evaluation cache is also not very useful as any changed bit will invalidate the cache - it's only useful for searching flakes, where one could have a way simpler implementation.
This issue has been mentioned on NixOS Discourse. There might be relevant details there:
https://discourse.nixos.org/t/passing-options-to-flakes/7579/3
Having each and every project enumerate systems
leads to nonsense churn prs like this https://github.com/cachix/pre-commit-hooks.nix/pull/122.
Having each and every project enumerate systems leads to nonsense churn prs like this cachix/pre-commit-hooks.nix#122.
Isn't this exactly the opposite of what you describe? That project was relying on an external list of systems (defaultSystems
in flake-utils
) rather than enumerating supported systems itself.
There's no reason for it not to support any system really. If a system is not supported, that's a problem in a dependency, not in the flake itself.
No, if a flake exposes an attribute like packages.x86_64-linux.foo
, it advertises that the foo
package works on x86_64-linux
. If it doesn't work then it shouldn't expose that attribute.
Sure, that's nice, but not something flakes can "guarantee" without preemptively breaking support for less-used systems.
Heck, attributes like overlays
and nixosModules
can never be guaranteed to work.
Requiring the enumeration of systems is too simplistic of an approach.
This is where honestly flakes are a regression in my book. In projects I had worked on, there was a default.nix
which was just the reusable part, with no whitelist of supported systems or anything like that. and then a release.nix
that enumerated the known-good arguments (be they the system, or other sorts of arguments) to the default.nix
for CI , and the release.nix
wasn't meant to be used downstream,
Flakes trying to put all that in one file is an unavoidably a mistake, in my book. It's like combing the foo.cabal
and cabal.project
into one file (Like Cargo does really awkwardly).
Also, enumerating the system
s is not even enough. https://github.com/NixOS/nix/pull/4996 shows we often need more grunularity. Seem thing if we remember that the localSystem
and crossSystem
arguments to Nixpkgs also contain far more info. I think while Flakes conflate the two concerns I described, there is a pressure to make it all work with just builtin.system
to cut down on complexity, but if we had too files, we would feel more comfortable making the CI one as complex as it needs to be, since it won't complicate anything downstream.
They wouldn't have to be two files for the separation of concerns to be achieved. The same can be done with two attributes (or groups of attributes), so flake.nix
can be a single file, like in the current design.
A short story: A while ago, I had an armv7l system running Arch. I wanted to build a package from the AUR, so I downloaded the PKGBUILD and tried to build it, and it didn't work, because armv7l was missing from the arch list. I manually added it, and it built and ran perfectly. But it felt kind of weird: why shouldn't it just let me build it and see what happens?
This feels like a similar situation: what if I'm using a flake that declares a few x86 systems because that's all the author bothered to test, but I want to build it on my ARM system? I have to fork it and make changes to the list? (And the list isn't even guaranteed to be static.. in the worse case I could have to read and understand code that derives it, dig into dependencies, etc.)
It's one thing for the author to provide lists of known-working, or supported, systems (which might be different lists!), and it would be great to get a warning if I try to build on a system that isn't known to work, but the tools should certainly let me just try it.
They wouldn't have to be two files for the separation of concerns to be achieved. The same can be done with two attributes (or groups of attributes), so
flake.nix
can be a single file, like in the current design.
That is true, I just would want to make sure the other stuff is inaccessable downstream so it is very separated "by construction".
(Trying this comment again, since it has proved challenging to get right 😔)
I’d like to offer a slight reframing of the challenge here to see if it unlocks any new solution directions.
Thus: maybe part of what’s hard here is that while it’s completely reasonable at the nix CLI UX level to want to be able to show a list of packages that a developer has asserted should work, IMO at the level of the flake ecosystem it seems to be really beneficial:
a. that the build recipes encoded into flakes be able to be easily re-used by downstream flake authors, and
b. that these build recipes be able to be re-used by those downstream flake authors with minimal (and IMO preferably ~overriding-based rather than source-modification-based) changes on systems not tested, foreseen, or even not existing at the time the original flake was written.
Now, how can this be achieved?
Honestly, I’m not sure yet.
That said, what I think I do see is that the outputs
well-known attr we have today only solves this combined description of the problem partially because while it does give a convenient interface to use to combine inputs with build recipes and to advertise packages for supported platforms, it does do in ways that interfere with both the “build recipe visibility” and “build recipe tweakability” goals that I’ve sketched above.
Thus, some questions:
@domenkozar
Another good example is nix-darwin, which calls nixpkgs inside the darwin configuration.
This is where the design has to be different to NixOS, because in NixOS nixpkgs is evaluated outside the supplied configuration.
This isn't quite true. The nixpkgs.nix
in NixOS gives you the flexibility to set pkgs
directly from outside using the lexical scope, but this is somewhat unusual.
nix-darwin and NixOS are quite similar with respect to system
. The main difference is that nix-darwin needs to get its nixpkgs from somewhere, but that is not the problem here.
So the output interface starts to differ, introducing cognitive overhead.
NixOS:
nixosConfigurations.<hostname> = {};
nix-darwin:
darwinConfigurations.<system>.<name> = {};
Isn't it already darwinConfigurations.<hostname>
?
The other alternative is to tell people to hardcode the system, but that's another way to wrestle with the design.
Unlike darwinModules
, aren't darwinConfigurations
supposed to be configurations for real-world systems? Those have a native system
which makes sense to specify. On NixOS I'd expect to find it in hardware-configuration.nix
. Nix-darwin doesn't have that.
Consider also the case where aarch64-darwin was introduced to only one system in nix-darwin, if system was an input this wouldn't be a breaking change.
If you want to do remote deployments, you can not assume the remote host's system
to be the same as the currentSystem
.
It could be argued that if you intend your darwin configuration to be system
-independent, you should use darwinModules
. Migrating to a new machine is handled by adding and removing darwinConfiguration
s. Migrating from Rosetta to native amounts to changing darwinConfiguration
's system
config value (presumably; no experience with this specifically).
This shows that we need two functions in a flake to represent the two contexts: one where system
is passed in and one where it isn't. It does not rely on an implicit currentSystem
. I've described this idea here as well https://github.com/NixOS/nix/issues/5256#issuecomment-930038843.
It could be generalized not just to system but to cross compilation configs as well.
Not allowing users to easily build outputs for unsupported platforms will just force maintainers into a dilemma, which will eventually lead to an inconsistency across the ecosystem. Why is that?
Example: I develop something on linux and I'm collaborating with people who develop on darwin. Whenever I release something, I'm in the following dilemma:
supportedSystems
, not allowing macos users to build anything
orsupportedSystems
, despite I cannot test it, because I don't want to make them go through the annoyance of having to make another PR.It does not matter what my personal opinion is about which of the two approaches is the correct one. The community will inevitable split into two camps, ones who decide to signal platform support by the existence of flake attributes, and ones who do not.
This results in an inconsistency across the ecosystem.
However, if we allowed users to signal support for platforms without forcing them to restrict building, there wouldn't be a reason for this inconsistency to evolve..
This focuses on the motivation and not the solutions, so that we can understand what we are trying to solve before jumping into implementation details
Motivation:
Reduce boilerplate in 66% of real world flakes (flake-utils, forEachSystem, ...)
Avoid extra concepts (system) to new people (UX)
Decouple reusable code (default.nix) from known-valid configuration (release.nix):
So that you can try building the flake for any system, even if it fails
So that you don't have to fork just to add a new system
And more generically so that you can re-use other people (default.nix) and reconfigure as you wish in your own release.nix
(to discuss) you can encourage putting reusable code in lib
and this would solve the reusability problem
Criticism of current design:
Two tuple system is not enough, a system in Nixpkgs is a complex data structure
Evaluation cache is fragile and thus not useful, a tiny bit change invalidates it, so maybe it's not worth sacrificing other features in the name of having a cache.
Great summary. Thanks @kamadorueda!
- Evaluation cache is fragile and thus not useful, a tiny bit change invalidates it, so maybe it's not worth sacrificing other features in the name of having a cache.
This is even a false dilemma, although I'll be happy to see the flake cache replaced by something more general and fine-grained like #6228. system
is not a problem for an evaluation cache. It just means that the lookup key should include system
. You can already simulate this by creating a flake that defines a single system string in lib
and use it as an input
in other flakes. Voila, a cached, overridable system
(inputs.*.follows
).
@roberth is exactly right. Sorry I missed the call today but looks like all good points were made!
Let's get this discussion back on track. By the looks of things, everyone except Eelco are in favor of this feature but that definitely does not mean that @edolstra is wrong, if anything he is protecting us from being caught with our pants down
@edolstra there must be some middle ground that we could agree on, here are a couple of ideas:
flake.nix
schema versioning flake.nix
schemas that get added/deprecated as the Nix ecosystem evolves. Template type could be passed as part of the file name (for example: xyz.flake.nix
)Passing system
as an attribute set instead of a string. This should keep the schema flexible and resolve this issue. Example:
{
inputs.nixpkgs.url = github:NixOS/nixpkgs;
nixConfig.systems = inputs: [
{ __toString = it: it.hostSystem; targetSystem = "x86_64-linux"; hostSystem = "x86_64-linux"; buildSystem = "x86_64-linux";}
];
outputs = { nixpkgs, system, ...}@inputs {
};
}
The following schema is more flexible and is self-contained within outputs
, which is great for flakes that rely on libraries to define their outputs.
{
outputs = inputs@{ nixpkgs, ... }: {
perSystem = system: {
# old style legacyPackages just for reference
packages.hello = nixpkgs.legacyPackages.${system}.hello;
packages.default = (self.perSystem system).packages.hello;
};
# This allows Nix to memoize `perSystem` as part of the `self` fixpoint
# using a `genAttrs` call. To be added in `call-flake.nix`
systems = [ "x86_64-linux" "aarch64-darwin" ];
};
nixosConfigurations.zeus = let self' = self.perSystem "x86_64-linux"; in .....;
}
call-flake.nix
line where perSystem
memoization can be implemented: https://github.com/NixOS/nix/blob/694b12052a2f3c830daa3acc7696b31a04afe329/src/libexpr/flake/call-flake.nix#L44
-self = result;
+self = memoize result;
where
memoize = outputs:
let
allSystems = genAttrs outputs.systems flake.perSystem;
perSystem = system: allSystems.${system};
# potentially: allSystems.${system} or (warn "system ${system} unsupported; trying to continue; ymmv" perSystem system);
# might want to have more flexible memoization for that; see memoise branch
in
flake // { inherit perSystem; }; # perhaps also inherit allSystems
This also leaves room for attributes that are not generic across system
s, such as configurations for specific machines. Those have a fixed system
and it wouldn't make sense to build them for all systems. Same for nixpkgs.lib
, nixosModules
, etc.
Side note: Not coincidentally, this is very close to the original flake-parts
schema when the perSystem
type was an explicit functionTo submodule
. Recent versions use deferredModule
instead, so that system
can be a module argument instead; a cosmetic change really. Users seem quite happy with the schema. You can find it here, but it's diluted by the original flake schema, which is also supported, and the back and forth conversion machinery. (and apologies for the primitive site as of writing)
I guess what I was trying to say is that this schema has proven to be valid and useful.
In my example, the system
is still a string, but I don't see a fundamental reason why it'd have to be.
genAttrs
goes out of the window, but could be replaced by a fancier memoization scheme, or a proper function memoization builtin as exists in the memoise branch.
This makes the type of system
a mostly orthogonal problem that can be addressed independently after implementing the flake schema outlined here.
flake.nix schema versioning
Technically avoidable while flakes are experimental. Might piss some people off. flake-parts
can probably offer compatibility; haven't tried though. flake-utils
might be able to provide a simple migration path too, as long as its users don't do a bunch of recursiveUpdate
-esque processing on the returned outputs.
@thufschmitt do you have an opinion about this?
@thufschmitt do you have an opinion about this?
I must confess that I haven't followed the last updates here. I still agree with the fact that there's a problem but I would need more time to really get an opinion on the solution
Addressing this is more of a Flakes 2.0 feature, we should get the initial version of flakes stabilized first. Then we can think about potential solutions like configurable derivations (#6583) that would allow flakes to declare options (like system
) in a discoverable way.
I wish there was something I could do, but it didn't work out that way.
@edolstra could you give a quick summary (Maybe with an example config) how configurable derivations would solve this compared to the other suggestions here? Mostly so that it's clear from the discussion why it was concluded to not fix this now.
Too bad, I hope that Flake 2.0 will quickly be on the road as it seems to me a crucial point to address (and we mostly agree on that with the @NixOS/documentation-team, as it's unclear for now what we should recommend to users, what template we should provide… as numtide/flake-utils
has also multiple issues, see e.g. below).
One of the problem I have with the current system (beside the dirty and complex boilerplate, lack of flexibility and more…) is that it could easily lead to an important security issue : for now most of the flakes rely on numtide/flake-utils
, which is not directly maintained by the nixpkgs team. If the github accound of any numtide's member is corrupted or hacked, it can really easily be easy to introduce a malware into every single flake based on it, that would be introduced on the next software update. This issue https://github.com/numtide/flake-utils/issues/51 was meant to transfer this repository back to nixpkgs, but it's wanting… mostly because people believe that we should, once for all, sort this issue properly.
Otherwise, just a few random ideas:
numtide/flake-utils
and implicitely say that the system is supported on all plateforms.nixosConfiguration
can't depend on the system? It is a pain to maintain multiple identical configurations that represent the exact same configuration… For now I need to give names like myconfig-x86_64-linux
, or consider them as packages (but then the integration with CLI is not great)… but it's higly impractical.features.forAllSystems = true;
:
{
description = "A very basic flake";
features.forAllSystems = true;
outputs = { self, system, nixpkgs }: { # Or certainly better, use the structure provided by roberth
packages.hello = nixpkgs.legacyPackages.${system}.hello;
packages.default = self.packages.${system}.hello;
};
}
This issue has been mentioned on NixOS Discourse. There might be relevant details there:
https://discourse.nixos.org/t/why-is-arg-and-argstr-incompatible-with-flakes/27800/2
This issue has been mentioned on NixOS Discourse. There might be relevant details there:
Is your feature request related to a problem? Please describe.
Right now one has to explicitly define system in flake outputs. The nix flake itself already comes with boiler code like this:
I think having to use that much code hurts portability of the ecosystem because people will only specify the minimum and there seems no easy way of overriding it without changing the flake itself.
Describe the solution you'd like
Describe alternatives you've considered
Use boilercode in every project or rely on external libraries like https://github.com/numtide/flake-utils
If someone needs to explicitly specify platforms i.e. to build packages for different architectures with hydra this should be still possible.