Nix: [flake]: provide builtins.currentSystem as a input to flakes

Created on 21 Jul 2020  路  11Comments  路  Source: NixOS/nix

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:

outputs = { self } : let
   # ...
   forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system);
{
  defaultPackage = forAllSystems (system: self.packages.${system}.nix);;  
};

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

outputs = { self, system } : {
  defaultPackage.${system} = self.packages.${system};  
};

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.

flakes improvement

Most helpful comment

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.

All 11 comments

cc @zimbatm who is involved in flake-utils

Not passing in system (or any other arguments) is intentional:

  • It breaks hermetic evaluation: it makes the evaluation result depend on something external.
  • It makes it impossible to enumerate the contents of the flake, since Nix doesn't know what the permissible values of the arguments are.
  • The packages output should provide packages that work, not packages that might work.
  • Evaluation caching becomes harder (not a huge problem for system, but would be a problem for arbitrary arguments).

Also, it's not clear that a system argument should be at the outputs level, since you may want to use outputs for different platforms in the same evaluation (e.g. on x86_64-linux, you might want to use some i686-linux derivations like wine).

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

Was this page helpful?
0 / 5 - 0 ratings

Related issues

edolstra picture edolstra  路  99Comments

vcunat picture vcunat  路  36Comments

domenkozar picture domenkozar  路  53Comments

Profpatsch picture Profpatsch  路  73Comments

rrnewton picture rrnewton  路  34Comments