hercules-ci / flake-parts

❄️ Simplify Nix Flakes with the module system
https://flake.parts
MIT License
721 stars 41 forks source link

how to share a library without using an overlay #168

Closed samuelrivas closed 1 year ago

samuelrivas commented 1 year ago

What is the best way to share a library as part of a flake, when the perSystem part of the flake uses the library as well?

Say that I have something like this:

{
  inputs = {
    nixpkgs.url = github:NixOS/nixpkgs/nixos-22.11;
    flake-parts.url = "github:hercules-ci/flake-parts";
  };
  outputs =
    inputs@{ flake-parts, nixpkgs, self }:
    flake-parts.lib.mkFlake { inherit inputs; } {
      systems = [ "x86_64-linux" ];
      flake = { };
      imports = [ ./nix/lib.nix ];
      perSystem = { ... }: {
        imports = [ ./nix/my-pkgs.nix ];
      };
    };
}

Where lib provides some functions used by my-pkgs. E.g.

nix/lib.nix

{ config, options, lib, ...}:
{
   flake.lib = { my-function = _: {}; };
}

and nix/my-pkgs

{ config,
  lib,
  self',
  ...
}:
let
  my-lib = ???

I want lib to be somewhere in the flake outputs, so I can use it in other flakes, but also accessible in the my-pkgs module. I don't seem to be able to use self inside perSystem (self(without') is not aperSystemmodule argument, ...), but I don't want to exploselib` as a per system output either...

I know that lib is not a standard flake output as well, the only seemingly standard way that I found to do this so far was to return an overlay that adds lib to the standard nixpkgs structure, but that seems a bit overkill, so I wonder if there is a better way

roberth commented 1 year ago

I know that lib is not a standard flake output as well

It's fine. Nix doesn't complain about it anymore. It doesn't have a flake check logic for it, but that's also fine.

is not a perSystem module argument

It is a top level argument though, so you can actually use it through the top level scope.

Here's how you could re-expose your library (using _module.args; no specialArgs support yet if you need to use the lib in imports; you probably don't?)

toplevel@{ config, ... }:
{
  flake.lib = {
    # sweet functions
  };
  perSystem = { ... }: {
    _module.args.my-lib = toplevel.config.flake.lib;
    imports = [ ./modules/perSystem/foo.nix ];
  };
}

modules/perSystem/foo.nix

{ my-lib, ... }:
{
  # ...
}

In general I would recommend to write top level modules instead of perSystem modules, to keep things consistent, but it's probably no big deal, as long as you don't put perSystem modules in flake outputs. I wouldn't even know what the flake attribute for that would be. flakeSystemModules? modules.perSystem? It'd be a bit confusing anyway because the imports are in a different place than the usual, top level.

samuelrivas commented 1 year ago

Ah great. I got it to work using _module.args. I did something like this, not sure if is exactly the same as you suggested:

  outputs =
    inputs@{ flake-parts, nixpkgs, self }:
    flake-parts.lib.mkFlake { inherit inputs; } {
      systems = [ "x86_64-linux" ];
      imports = [ ./nix/lib.nix ];
      perSystem = { config, ... }: {
        _module.args.my-lib = self.lib;
        imports = [ ./nix/my-pkgs.nix ];
      };
    };

Then, the my-pkgs module gets the library as input.

A couple of follow ups if you don't mind:

  1. By "I would recommend to write top level modules instead of perSystem modules", do you mean to import my-packages at the same level as lib.nix and deal with the systems inside the my-pkgs module (perhaps using moduleWithSystem?
  2. Is _module.args documented somewhere that I missed? (just to check if I am not referring to the right docs)

Thanks for your help!