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.nix
por si só não inclui a variávelpkgs
. Portanto, você precisará modificar seu conteúdo para passarpkgs
como 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 pkgs
para importarhello.nix
e usarpkgs
como 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
hello
estão fortemente acopladas apkgs
.- Se precisarmos de dependências customizadas, temos que modificar
pkgs
ou 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.nix
se torna complexo, é difícil determinar de quais Derivations depkgs
ele 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 args
primeiro 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
,fetchurl
e, possivelmente, alguns parâmetros customizados.
- Após esta etapa, você tem uma função, tipicamente com parâmetros como
- Em seguida,
pkgs.callPackage fn args
mesclaargs
com o attribute setpkgs
. Se houver conflitos, os parâmetros emargs
irão sobrescrever aqueles empkgs
. - Então,
pkgs.callPackage fn args
extrai os parâmetros da funçãofn
do 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
pkgs
podem ser injetadas automaticamente.
Portanto, é sempre recomendado usar pkgs.callPackage
para definir Derivations.