218 lines
4.8 KiB
Markdown
218 lines
4.8 KiB
Markdown
This is my take on a module system used to simplify the definition of a Nix
|
|
flake. [flake-parts](https://github.com/hercules-ci/flake-parts) exists to do basically
|
|
the same thing, and you should check it out, before using this flake.
|
|
|
|
# Quick start
|
|
|
|
Add to your flake input:
|
|
```nix
|
|
# flake.nix
|
|
|
|
inputs.modularFlake.url = "https://git.dzuchun.ing/dzu/modular-flake.git";
|
|
# make it follow your nixpkgs, if you want
|
|
```
|
|
|
|
Use in your flake output:
|
|
```nix
|
|
# flake.nix
|
|
outputs = { modularFlake, ... }: modularFlake.mkFlake { };
|
|
```
|
|
|
|
Well, the code above actually produces an empty flake. For most use-cases, you would
|
|
have to _at least_ provide it with one system to use:
|
|
|
|
```nix
|
|
# flake.nix
|
|
outputs = { modularFlake, ... }: modularFlake.mkFlake { systems = [ "x86_64-linux" ] };
|
|
```
|
|
|
|
# Defining stuff
|
|
|
|
You can export things from your flake, using _nix modules_. Nix module is
|
|
- an attrset of a specific form, or
|
|
- a lambda evaluating to such attrset, or
|
|
- a path to a `.nix` file, containing either of those
|
|
|
|
You should emit your outputs in the `config` attribute, and as-if `<system>` component of
|
|
the path does not exist.
|
|
|
|
Here is a module, defining a check called `mathWorks`:
|
|
```nix
|
|
# math-check.nix
|
|
{
|
|
config.checks.mathWorks =
|
|
assert 2 + 2 == 4;
|
|
null;
|
|
}
|
|
```
|
|
|
|
Note, that unlike in flake output `checks` attribute, you may emit any kind of expressions -- for `checks`, they will be force-evaluated/built.
|
|
|
|
Here is how you can define an app:
|
|
```nix
|
|
# meow-app.nix
|
|
{ lib, hasSystem, pkgs }:
|
|
lib.optionalAttrs hasSystem {
|
|
config.apps.meow.program = pkgs.writeShellScript "${pkgs.coreutils}/bin/echo 'meow'";
|
|
}
|
|
```
|
|
|
|
And here is an overlay:
|
|
```nix
|
|
# my-overlay.nix
|
|
_: {
|
|
config.overlays.myOverlay = final: prev: { /* whatever you like */ };
|
|
}
|
|
```
|
|
|
|
To apply your flake modules to your flake output, put them into `modules` argument of `mkFlake`:
|
|
```nix
|
|
# flake.nix
|
|
outputs = { modularFlake, ... }: modularFlake.lib.mkFlake {
|
|
modules = [
|
|
./math-check.nix
|
|
./meow-app.nix
|
|
./my-overlay.nix
|
|
];
|
|
};
|
|
```
|
|
|
|
# Defining ways to define stuff
|
|
|
|
But what if you don't want to use standard flake outputs? What if you want to define your own outputs, with their own schemas and stuff?
|
|
|
|
But what do you do, if your outputs conflict with the defaults?
|
|
|
|
You define your own outputs, of course!
|
|
|
|
```nix
|
|
# cats-output.nix
|
|
{ lib, ... }:
|
|
let
|
|
petCats =
|
|
{
|
|
lib,
|
|
pkgs,
|
|
cats,
|
|
}:
|
|
pkgs.writeShellScript "pet-cats" (
|
|
lib.concatStringsSep "\n" (
|
|
map (
|
|
cat:
|
|
if cat == "niko" then
|
|
builtins.warn "niko is not a cat!!!" ""
|
|
else
|
|
"${pkgs.coreutils}/bin/printf 'cat %s has been petted!' ${lib.escapeShellArg cat}"
|
|
) (lib.unique (lib.sort (a: b: a < b) cats))
|
|
)
|
|
);
|
|
in
|
|
{
|
|
config.outputKinds = [
|
|
(_: {
|
|
options = with lib; {
|
|
cats = mkOption {
|
|
type = with types; listOf str;
|
|
description = "a list of cats to pet";
|
|
};
|
|
};
|
|
outputs =
|
|
{
|
|
lib,
|
|
hasSystem,
|
|
config,
|
|
system,
|
|
pkgs,
|
|
...
|
|
}:
|
|
lib.optionalAttrs hasSystem {
|
|
apps.${system}.catPetters = {
|
|
type = "app";
|
|
program = petCats {
|
|
cats = config.cats;
|
|
inherit lib pkgs;
|
|
};
|
|
};
|
|
};
|
|
})
|
|
];
|
|
};
|
|
```
|
|
|
|
You can now pet cats, in the modular way:
|
|
```nix
|
|
# cat-definitions.nix
|
|
{
|
|
config = {
|
|
cats = [ "orange" "pink" ];
|
|
};
|
|
}
|
|
```
|
|
|
|
```nix
|
|
# niko-definition.nix
|
|
{
|
|
config = {
|
|
# TODO: add cuteness check
|
|
|
|
cats = [ "niko" ]; # uh oh
|
|
};
|
|
}
|
|
```
|
|
|
|
```nix
|
|
# flake.nix
|
|
outputs = { modularFlake, ... }: modularFlake.lib.mkFlake {
|
|
outputs = [
|
|
./cats-output.nix
|
|
];
|
|
modules = [
|
|
./cat-definitions.nix
|
|
./niko-definition.nix
|
|
];
|
|
};
|
|
```
|
|
|
|
Note, that `outputs` parameter defines a complete set of outputs you use. So (for example)
|
|
any `checks` you emmit with the code above will be ignored.
|
|
|
|
## Using default outputs
|
|
|
|
Default output definitions are available under `modularFlake.defaultOutputs`. Here is how
|
|
you would subset your flake outputs to "apps only":
|
|
|
|
```nix
|
|
# flake.nix
|
|
outputs = { modularFlake, ... }: modularFlake.lib.mkFlake {
|
|
outputs = with modularFlake.defaultOutputs; [ apps ];
|
|
...
|
|
};
|
|
```
|
|
|
|
Of course, you can combine default outputs with your own:
|
|
|
|
```nix
|
|
# flake.nix
|
|
outputs = { modularFlake, ... }: modularFlake.lib.mkFlake {
|
|
outputs = with modularFlake.defaultOutputs; [
|
|
apps
|
|
./cats-output.nix
|
|
];
|
|
...
|
|
};
|
|
```
|
|
|
|
## Using other flake inputs
|
|
|
|
In case you need to use some other flake input in your module definition, and want to keep
|
|
the module system ergonomics, here is the way:
|
|
|
|
```nix
|
|
# flake.nix
|
|
outputs = { modularFlake, ... }@flakeInputs: modularFlake.lib.mkFlake {
|
|
inherit flakeInputs;
|
|
...
|
|
};
|
|
```
|
|
|
|
All of your modules will now see your flake inputs at `flakeInputs` argument.
|