Como dividir o /etc/nixos/configuration.nix em módulos separados?

14

Suponha que eu tenha um arquivo de configuração NixOS muito simples :

{ config, pkgs, ... }:    
{
  imports =
    [ # Include the results of the hardware scan.
      ./hardware-configuration.nix
    ];
  # SOME STUFF
  environment.systemPackages = with pkgs; [ emacs gitFull ];
  # SOME STUFF
}

Eu sei que o NixOS implementa um sistema de módulos e um módulo é um .nixarquivo. Todo .nixarquivo deve conter qualquer expressão Nix válida (por exemplo, uma função ou um conjunto). Isso significa que o arquivo de configuração do NixOS /etc/nixos/configuration.nixé ele próprio um módulo, contendo uma expressão do Nix.

Eu também sei que, para tornar a expressão Nix em outro módulo visível para o módulo com o qual estou trabalhando, posso usar uma importfunção interna .

Quero dividir a declaração dos pacotes do sistema (a lista que contém emacse gitFull) em arquivo packages.nix. Como divido o arquivo de configuração do NixOS em módulos separados?

Mirzhan Irkegulov
fonte

Respostas:

22

Expressões Nix

Uma expressão Nix é como qualquer expressão da linguagem de programação: qualquer coisa que seja avaliada como um valor ou função. Um valor nesse caso também pode ser uma lista ou um conjunto. Como um módulo Nix (arquivo com extensão .nix) pode conter qualquer expressão Nix, você esperaria que o arquivo de configuração NixOS ( /etc/nixos/configuration.nix) contivesse uma única expressão Nix como conteúdo do arquivo.

O arquivo de configuração do NixOS contém uma expressão do Nix no formato:

{config, pkgs, ...}: { /* various configuration options */ }

Se você olhar atentamente, poderá ver que é uma função , porque as funções seguem o formulário pattern: form. Você também pode ver que é uma função que aceita um conjunto e retorna um conjunto. Por exemplo, se você tiver uma função f = {x, y}: {a = x + y;}, poderá chamá-la como f {x=1; y=2;}e recuperar um conjunto {a=3;}.

Então, isso significa que, quando você chama nixos-rebuild switch, algo chama a função dentro do arquivo de configuração do NixOS com o conjunto que deve conter atributos confige pkgs.

importações

Seguindo o exemplo de ./hardware-configuration.nix, a maneira simples de extrair a lista de pacotes em um módulo separado packages.nixé apenas extrair a environment.systemPackagesopção e colocá-la ./packages.nixna importsopção. Você /etc/nixos/configuration.nixficaria assim:

{ config, ... }:    
{
  imports =
    [ # Include the results of the hardware scan.
      ./hardware-configuration.nix
      # Include the package list.
      ./packages.nix
    ];
  # SOME STUFF
  # SOME STUFF
}

Você /etc/nixos/packages.nixficaria assim:

{ pkgs, ... }:
{
  environment.systemPackages = with pkgs; [ emacs gitFull ];
}

Como isso funciona? Quando você executa nixos-rebuild switch, o processo que avalia expressões Nix e decide instalar pacotes e assim por diante chama configuration.nixcom um conjunto de atributos, alguns dos quais são confige pkgs.

Ele encontra atributo importsdentro do conjunto devolvido, de modo que avalia cada expressão Nix nos módulos que importscontém com os mesmos argumentos ( config, pkgs, etc.).

Você deve ter pkgscomo argumento (ou, tecnicamente falando, um atributo de um conjunto, que por si só é um argumento) de uma função packages.nix, porque, da perspectiva da linguagem Nix, o processo pode ou não chamar a função com o conjunto que contém pkgs. Caso contrário, a qual atributo você se referiria ao executar with pkgs?

Você também deve ter reticências, porque a função pode ser chamada com outros atributos, não apenas pkgs.

Por que não existe pkgsem configuration.nix? Você pode obtê-lo, mas se não o consultar em nenhum lugar do arquivo, poderá omiti-lo com segurança, pois as reticências os incluiriam de qualquer maneira.

Atualizando um atributo chamando uma função externa

Outra maneira é apenas criar uma função que retorne um conjunto com algum atributo e o valor desse atributo que você colocaria dentro environment.systemPackages. Este é o seu configuration.nix:

{ config, pkgs, ... }:    
{
  imports =
    [ # Include the results of the hardware scan.
      ./hardware-configuration.nix
    ];
  # SOME STUFF
  environment.systemPackages = import ./packages.nix pkgs;
  # SOME STUFF
}

O seu packages.nix:

pkgs: with pkgs; [ emacs gitFull ]

import ./packages.nix pkgssignifica: carregar e retornar a expressão Nix ./packages.nixe, como é uma função, chame-a com um argumento pkgs. with pkgs; [ emacs gitFull ]é uma expressão com , ele traz o escopo da expressão antes do ponto e vírgula para a expressão após ponto e vírgula. Sem isso, seria [ pkgs.emacs pkgs.gitFull ].

Mirzhan Irkegulov
fonte
1
Como as importações são mescladas? Eles usam recursiveUpdate ou algo parecido?
aij
1
Existe uma maneira de fazer importações condicionais?
CMDragonkai
1
@CMCDragonkai o valor importsé apenas uma lista, assim você pode acrescentar elementos para que condicionalmente, por exemploimports = [ ./foo.nix ./bar.nix ] ++ (if baz then [ ./quux.nix ] else []);
Warbo