Possibility for subflakes?

Update

I believe the best solution, long term, would be to use a self reference for inputs, just as in outputs.

Original Post

I’d like to use a flake.nix in a subdir of an existing flake as an input. This is mainly because of the evaluation restrictions on the inputs attribute.

Since you can’t do any real computation on inputs, without evaluation complaining about thunks instead of values, I was wanting to manage a series of sources for overlays via another flake in a subdirectory.
This is mainly so I no longer have to specify revisions and hashes manually, but can rely on nix flake update to update derivation sources when desired.

The only other alternative is to put the inputs directly in the main flake, which mixes concerns a little bit. I originally was going to simply specify the inputs in a separate file and import and merge it with the inputs attribute set, but that’s when I discovered that you can’t really run any computation over the inputs.

Problem is, input urls expect a full path. So I can’t just say inputs.pkgSrcs.url = "./pkgs" like I want to. Is there a way to solve this? Possibly an input type I am not aware of? If not, is this behavior desirable enough to others to open an issue on nix issue tracker.

I think this behavior should still be pure as long as we disallow files from the top flakes root.

1 Like

I think this would be a good alternative to passing arguments to flakes, that’s currently missing.

1 Like

Indeed, we could liken it to private interfaces from the world of OOP.

Really, anything that would allow us to specify inputs in more than one place would solve my immediate problem. I just feel like private subflakes would be a good way to organize projects into the various concerns.

Perhaps we could add some logic that disallows them to be referenced directly from the outside world, ensuring they are only available internally.

perhaps:

{
  inputs.<flake>.private = true;
}

And if this is set, then the flake ref must resolve to a flake directly inside the toplevel flake

I just tried this out and it works:

johannes@longitude-j testflake % tree .        
.
├── flake.nix
└── subflake
    └── flake.nix

1 directory, 2 files
johannes@longitude-j testflake % nix run                      
Hello, world!

flake.nix:

flake.nix       
{
  description = "A flake with a subflake";

  inputs.subflake.url =  "path:./subflake"; # edit: use url path syntax

  outputs = { self, subflake }:
  let
    inherit (subflake.inputs) nixpkgs;
  in {

    packages.x86_64-linux.hello = nixpkgs.legacyPackages.x86_64-linux.hello;

    defaultPackage.x86_64-linux = self.packages.x86_64-linux.hello;

  };
}

subflake/flake.nix:

{
  description = "A Subflake";

  inputs.nixpkgs.url = "github:NixOS/nixpkgs";

  outputs = inputs@{ self, nixpkgs }: {

    inherit inputs;

  };
}

Is this what you are looking for, @nrdxp?
The possible fecher (=input) types are defined in https://github.com/NixOS/nix/tree/master/src/libfetchers.

Edit:

The top-level flake.lock pins also the inputs for the subflake. If the subflake has its own flake.lock, updates on its inputs only propagate to the top-level flake using nix flake update --update-input subflake. A simple nix flake update doesn’t do it. Is this probably a bug or does it make sense (i.e. shall I open an issue if there exists none)?

5 Likes

This is perfect. Thanks a ton! I did notice that the lock didn’t update when changing the revision, so perhaps this is a bug.

edit

Unfortunately, this doesn’t actually solve the problem, it pushes the error down the chain. After actually attempting to use them from within my configuration I get the following error:

    41|     else if info.type == "path" then
    42|       { outPath = builtins.path { path = info.path; };
      |                                   ^
    43|         narHash = info.narHash;

string './pkgs' doesn't represent an absolute path

Related

https://github.com/NixOS/nix/issues/4218
https://github.com/NixOS/nix/issues/3978

Your error only appears when using flake-compat, doesn’t it? The path fetcher in Nix allows relative paths, but coerceToPath, which builtins.path uses does not. So flake-compat is more restrictive than flakes are. Maybe this is a bug in flake-compat, but according to line 87 in https://github.com/NixOS/nix/blob/6548b89cc4eb214cb4632fd4332c610f2d1f0a9d/src/libfetchers/path.cc#L87 maybe this restriction is just not yet implemented in the path fetcher.

1 Like

Thanks for this, I usually pull the flake into a repl using flake-compat (less typing than builtins.getFlake "/full/path/to/flake"). Never would have guess it was less compatible, but your right, the error does not occur in the flake proper. So guess this is a workable solution after all. Thanks you!

Maybe as a workaround you can also wrap flake.nix for flake-compat with a function which changes relative path strings to paths like ./. + "/" + path.

1 Like

I usually have a repl.nix with builtins.getFlake (toString ./.) around, so i just nix repl ./repl.nix
Really wish there was a better way to do that though.

1 Like

A PR exists to solve this in flake-compat