How can we pass argument to flake.nix?

$ nix build --arg cl true
error: '--arg' and '--argstr' are incompatible with flakes
Try 'nix --help' for more information.
$ nix build --argstr cl true
error: '--arg' and '--argstr' are incompatible with flakes
Try 'nix --help' for more information.

For example, how can we pass arguments to a flake.nix to change its build behaviour?

2 Likes

Oh, I notice https://github.com/NixOS/nix/issues/2861:

Currently, flakes are evaluated in pure mode and have no function arguments (other than the computed set of flake dependencies), so it’s not possible to do things like:

Use builtins.currentSystem.
Access ~/.config/nixpkgs/{config.nix,overlays}.
Access command line arguments passed via --arg.

It cannot be done, right?

Some discussion can be seen in https://github.com/NixOS/nix/issues/5663.

1 Like

Yeah, I don’t think that nix is able to take arguments right now. Bat people have been (ab)using the system to encode arguments in the name of the package, like in mach-nix that allows a syntax like:

nix (build|shell) mach-nix#gen.(python|docker).package1.package2...

Depending on your use case it might be enough. So you could for instance create a package mypackage, a package mypackage.arg1.true, a package mypackage.arg1.false, a package mypackage.arg2.2 etc… and I guess this can be done automatically to avoid copy/pasting the expression (I’m sure that mach nix does not create exponentially many expressions manually).

3 Likes

Not what you are asking but in case helpful, a way I personally use to allow having options in my local flakes which is similar to having arguments, is to check for the existence of files. For example, I have expressions of the type:

let has_foo = builtins.pathExists ./config/foo;
in {
  imports = [] ++ (lib.optional has_foo ./foo.nix);
}

Then I use .gitignore to ignore the config directory. Because the files are ignored by git, to build this flake I have to run commands passing path:, like: sudo nixos-rebuild switch --flake path:$(pwd) or nix build path:$(pwd).

This allows also to read the file and compare it if more granular options are needed. It works because when running with path:$(pwd), the whole directory path is included in the flake, not only the files tracked by git.

1 Like

Do you have an example repo of using this approach? I’m trying this exact thing here with no success: Add option to direnv shell.nix - #2 by akarypid

I think --apply works

Could you please provide more context? I edited my direnv to:

$ cat .envrc   
use flake . --apply

But got an error:

direnv: using flake . --apply
error: unrecognised flag '--apply'
Try '/nix/var/nix/profiles/default/bin/nix --help' for more information.

Was this what you were suggesting?

The trick is to pass the path parameter to nix when the configuration files are not tracked by the git repository, as I do in my approach. If using a nix shell with flakes and direnv, you can use this in the .envrc:

use flake path:$PWD

Or if the path is always the same, e.g. /home/myuser/foo/bar, can also put it directly:

use flake path:/home/myuser/foo/bar

However, this approach also means that the whole directory is copied to the nix store for evaluation, regardless if the files are tracked by git or not. This can take some time in repositories with many dependencies (e.g.node_modules). It will only happen when the .envrc file or the .nix files have changed so it is ok in my case, it just takes a bit the first time.

You can also move the .nix and config files into a subdirectory and only evaluate that if there are frequent changes.

In my initial post I’ve put the way for NixOS, you can also use this with home-manager where there is no need to pass the path keyword, for example:

home-manager switch --flake $(pwd)
1 Like

Indeed, after changing my .envrc to use flake path:$PWD I can now:

touch .devtools flake.nix # adds the dev tools to path
rm .devtools ; touch flake.nix # removes the dev tools from path

Touching flake.nix causes direnv to re-evaluate and add/remove the extra packages from the path…