pkgs.callPackage
pkgs.callPackage é usado para parametrizar a construção de Nix Derivations. Para entender seu propósito, vamos primeiro considerar como definiríamos um pacote Nix (também conhecido como Derivation) sem usar pkgs.callPackage.
1. Sem pkgs.callPackage
Podemos definir um pacote Nix usando um código como este:
pkgs.writeShellScriptBin "hello" ''echo "hello, ryan!"''Para verificar, você pode usar o nix repl, e verá que o resultado é de fato uma Derivation:
› nix repl -f '<nixpkgs>'
Welcome to Nix 2.13.5. Type :? for help.
Loading installable ''...
Added 19203 variables.
nix-repl> pkgs.writeShellScriptBin "hello" '' echo "hello, xxx!" ''
«derivation /nix/store/zhgar12vfhbajbchj36vbbl3mg6762s8-hello.drv»Embora a definição desta Derivation seja bastante concisa, a maioria das Derivations no nixpkgs são muito mais complexas. Em seções anteriores, introduzimos e usamos extensivamente o método import xxx.nix para importar expressões Nix de outros arquivos Nix, o que pode melhorar a manutenibilidade do código.
- Para melhorar a manutenibilidade, você pode armazenar a definição da Derivation em um arquivo separado, por exemplo,
hello.nix.- No entanto, o contexto dentro do
hello.nixpor si só não inclui a variávelpkgs. Portanto, você precisará modificar seu conteúdo para passarpkgscomo um parâmetro parahello.nix.
- No entanto, o contexto dentro do
- Nos locais onde você precisa usar esta Derivation, você pode usar
import ./hello.nix pkgspara importarhello.nixe usarpkgscomo um parâmetro para executar a função definida dentro dele.
Vamos continuar a verificar isso usando nix repl, e você verá que o resultado ainda é uma Derivation:
› cat hello.nix
pkgs:
pkgs.writeShellScriptBin "hello" '' echo "hello, xxx!" ''
› nix repl -f '<nixpkgs>'
Welcome to Nix 2.13.5. Type :? for help.
warning: Nix search path entry '/nix/var/nix/profiles/per-user/root/channels' does not exist, ignoring
Loading installable ''...
Added 19203 variables.
nix-repl> import ./hello.nix pkgs
«derivation /nix/store/zhgar12vfhbajbchj36vbbl3mg6762s8-hello.drv»2. Usando pkgs.callPackage
No exemplo anterior, sem pkgs.callPackage, passamos pkgs diretamente como um parâmetro para hello.nix. No entanto, esta abordagem tem algumas desvantagens:
- Todas as outras dependências da Derivation
helloestão fortemente acopladas apkgs.- Se precisarmos de dependências customizadas, temos que modificar
pkgsou o conteúdo dehello.nix, o que pode ser complicado.
- Se precisarmos de dependências customizadas, temos que modificar
- Nos casos em que o
hello.nixse torna complexo, é difícil determinar de quais Derivations depkgsele depende, tornando a análise das dependências entre as Derivations difícil.
pkgs.callPackage, como uma ferramenta para parametrizar a construção de Derivations, resolve esses problemas. Vamos dar uma olhada em seu código-fonte e comentários nixpkgs/lib/customisation.nix#L101-L121:
/* Call the package function in the file `fn` with the required
arguments automatically. The function is called with the
arguments `args`, but any missing arguments are obtained from
`autoArgs`. This function is intended to be partially
parameterised, e.g.,
callPackage = callPackageWith pkgs;
pkgs = {
libfoo = callPackage ./foo.nix { };
libbar = callPackage ./bar.nix { };
};
If the `libbar` function expects an argument named `libfoo`, it is
automatically passed as an argument. Overrides or missing
arguments can be supplied in `args`, e.g.
libbar = callPackage ./bar.nix {
libfoo = null;
enableX11 = true;
};
*/
callPackageWith = autoArgs: fn: args:
let
f = if lib.isFunction fn then fn else import fn;
fargs = lib.functionArgs f;
# All arguments that will be passed to the function
# This includes automatic ones and ones passed explicitly
allArgs = builtins.intersectAttrs fargs autoArgs // args;
# ......Em essência, pkgs.callPackage é usado como pkgs.callPackage fn args, wonde o placeholder fn é um arquivo ou função Nix, e args é um attribute set. Veja como funciona:
pkgs.callPackage fn argsprimeiro verifica sefné uma função ou um arquivo. Se for um arquivo, ele importa a função definida dentro dele.- Após esta etapa, você tem uma função, tipicamente com parâmetros como
lib,stdenv,fetchurle, possivelmente, alguns parâmetros customizados.
- Após esta etapa, você tem uma função, tipicamente com parâmetros como
- Em seguida,
pkgs.callPackage fn argsmesclaargscom o attribute setpkgs. Se houver conflitos, os parâmetros emargsirão sobrescrever aqueles empkgs. - Então,
pkgs.callPackage fn argsextrai os parâmetros da funçãofndo attribute set mesclado e os usa para executar a função. - O resultado da execução da função é uma Derivation, que é um pacote Nix.
Como pode ser um arquivo ou função Nix, usado como argumento para pkgs.callPackage? Você pode examinar exemplos que usamos antes em Uso Avançado do Nixpkgs - Introdução: hello.nix, fcitx5-rime.nix, vscode/with-extensions.nix e firefox/common.nix. Todos eles podem ser importados usando pkgs.callPackage.
Por exemplo, se você definiu uma configuração de kernel customizada do NixOS em kernel.nix e tornou o nome do ramo de desenvolvimento e o código-fonte do kernel configuráveis:
{
lib,
stdenv,
linuxManualConfig,
src,
boardName,
...
}:
(linuxManualConfig {
version = "5.10.113-thead-1520";
modDirVersion = "5.10.113";
inherit src lib stdenv;
# caminho do arquivo para o arquivo de configuração de kernel gerado (o `.config` gerado por make menuconfig)
#
# aqui está um uso especial para gerar um caminho de arquivo a partir de uma string
configfile = ./. + "${boardName}_config";
allowImportFromDerivation = true;
})Você pode usar pkgs.callPackage ./hello.nix {} em qualquer módulo Nix para importá-lo e usá-lo, substituindo qualquer um de seus parâmetros conforme necessário:
{ lib, pkgs, pkgsKernel, kernel-src, ... }:
{
# ......
boot = {
# ......
kernelPackages = pkgs.linuxPackagesFor (pkgs.callPackage ./pkgs/kernel {
src = kernel-src; # o código-fonte do kernel é passado como um `specialArgs` e injetado neste módulo.
boardName = "licheepi4a"; # o nome da placa, usado para gerar o caminho do arquivo de configuração do kernel.
});
# ......
}Como mostrado acima, usando pkgs.callPackage, você pode passar src e boardName diferentes para a função definida em kernel.nix, para gerar diferentes pacotes de kernel. Isso permite que você adapte o mesmo kernel.nix a diferentes códigos-fonte de kernel e placas de desenvolvimento.
As vantagens de pkgs.callPackage são:
As definições de Derivation são parametrizadas, e todas as dependências da Derivation são os parâmetros da função em sua definição. Isso facilita a análise das dependências entre as Derivations.
Todas as dependências e outros parâmetros customizados da Derivation podem ser facilmente substituídos usando o segundo parâmetro de
pkgs.callPackage, aprimorando enormemente a reutilização da Derivation.Ao mesmo tempo em que alcança as duas funcionalidades acima, não aumenta a complexidade do código, já que todas as dependências em
pkgspodem ser injetadas automaticamente.
Portanto, é sempre recomendado usar pkgs.callPackage para definir Derivations.