domingo, 11 de março de 2018

"Gerenciando Pacotes Nix Privados Fora da Árvore Nixpkgs" [sander van der burg]

Gerenciando Pacotes Nix Privados Fora da Árvore Nixpkgs

Gerenciando Pacotes Nix Privados Fora da Árvore Nixpkgs

Em um par de antigos posts, eu expliquei os conceitos básicos do gerenciador de pacotes Nix, bem como expliquei como escrever "receitas de construção" (mais conhecidas como expressões Nix) para ele.

Apesar de as expressões Nix parecerem pouco convencionais, a ideia básica por detrás de especificar pacotes no mundo Nix é simples: você define uma função que descreve como construir um pacote a partir do código-fonte e suas dependências, e invoca a função com as variantes desejadas das dependências como parâmetros para construí-la. No Nixpkgs, um coleção de mais de 2500 pacotes (maioria livres e de código aberto) que podem ser implementados com Nix, todos os pacotes são basicamente especificados desta forma.

Porém, pode ainda haver algumas questões práticas. Em alguns casos, você pode apenas querer experimentar Nix ou tem um pacote de software privado sem intenções de distribuição. Em tais casos, você tipicamente quer salvá-los fora da árvore Nixpkgs.

Apesar de o manual Nix descrever como as coisas são empacotadas no Nixpkgs, ele não descreve (com clareza) como definir e compor pacotes enquanto os mantém separados do Nixpkgs.

Dado que isto não é oficialmente documentado em lugar algum e eu estou recebendo (um monte de) questões sobre isso de iniciantes, eu decidi escrever algo sobre isso.

1 Especificando um Simples Pacote Privado

Em situações que eu quero testar rapidamente um pacote simples, eu tipicamente escrevo uma expressão Nix que se parece com isso:

with import <nixpkgs> {};

stdenv.mkDerivation {
  name = "mc-4.8.12";

  src = fetchurl {
    url = http://www.midnight-commander.org/downloads/mc-4.8.12.tar.bz2;
    sha256 = "15lkwcis0labshq9k8c2fqdwv8az2c87qpdqwp5p31s8gb1gqm0h";
  };

  buildInputs = [ pkgconfig perl glib gpm slang zip unzip file gettext
      xlibs.libX11 xlibs.libICE e2fsprogs ];

  meta = {
    description = "File Manager and User Shell for the GNU Project";
    homepage = http://www.midnight-commander.org;
    license = "GPLv2+";
    maintainers = [ stdenv.lib.maintainers.sander ];
  };
}

A expressão acima monta o Midnight Commander, uma das minhas utilidades UNIX favoritas (em particular o editor que o acompanha :-) ).

Na expressão Nix acima, não existe distinção entre a definição e a invocação da função. Em vez disso, eu invoco diretamente stdenv.mkDerivation {} para construir o Midnight Commander a partir dos fontes e de suas dependências. Eu obtenho as dependências do Nixpkgs importando o conjunto de atributos de composição dentro do escopo léxico da expressão mediante import <nixpkgs> {};.

Eu posso colocar o arquivo acima (de nome mc.nix) em uma pasta fora da árvore Nixpkgs, e montá-lo como se segue:

$ nix-build mc.nix
/nix/store/svm98wmbf01dswlfcvvxfqqzckbhp5n5-mc-4.8.12

Ou instalar no meu perfil executando:

$ nix-env -f mc.nix -i mc

As dependências (que são providas por Nixpkgs) podem ser encontradas graças à variável de ambiente NIX_PATH que contém uma configuração para o nixpkgs. No NixOS, esta variável de ambiente já tem valor atribuído. Em outras distribuições Linux ou instalações não-NixOS, esta variável deve ser manualmente configurada para conter a localização do Nixpkgs. Um exemplo poderia ser:

$ export NIX_PATH=nixpkgs=/home/sander/nixpkgs

A configuração acima especifica que uma cópia do Nixpkgs reside no meu diretório pessoal.

2 Mantendo Uma Coleção de Pacotes Privados

Também pode acontecer que você queira empacotar algumas dependências do pacote privado enquanto os mantêm fora da árvore ou simplesmente quer manter uma coleção de pacotes privados. Em tais casos, eu basicamente defino cada pacote como uma função, o que não é diferente da forma que é feita em Nixpkgs e descrita no manual Nix:

{ stdenv, fetchurl, pkgconfig, glib, gpm, file, e2fsprogs
, libX11, libICE, perl, zip, unzip, gettext, slang
}:

stdenv.mkDerivation rec {
  name = "mc-4.8.12";

  src = fetchurl {
    url = http://www.midnight-commander.org/downloads/mc-4.8.12.tar.bz2;
    sha256 = "15lkwcis0labshq9k8c2fqdwv8az2c87qpdqwp5p31s8gb1gqm0h";
  };

  buildInputs = [ pkgconfig perl glib gpm slang zip unzip file gettext
      libX11 libICE e2fsprogs ];

  meta = {
    description = "File Manager and User Shell for the GNU Project";
    homepage = http://www.midnight-commander.org;
    license = "GPLv2+";
    maintainers = [ stdenv.lib.maintainers.sander ];
  };
}

Porém, para compor o pacote (i.e. chamar a função com os argumentos que são usados como dependências), eu tenho que criar uma composição privada de expressões em vez de adaptar pkgs/top-level/all-packages.nix em Nixpkgs.

Uma composição privada de expressões pode ser definida assim:

{ system ? builtins.currentSystem }:

let
  pkgs = import <nixpkgs> { inherit system; };
in
rec {
  pkgconfig = import ./pkgs/pkgconfig {
    inherit (pkgs) stdenv fetchurl automake;
  };

  gpm = import ./pkgs/gpm {
    inherit (pkgs) stdenv fetchurl flex bison ncurses;
  };

  mc = import ./pkgs/mc {
    # Use custom pkgconfig and gpm packages as dependencies
    inherit pkgconfig gpm;
    # The remaining dependencies come from Nixpkgs
    inherit (pkgs) stdenv fetchurl glib file perl;
    inherit (pkgs) zip unzip gettext slang e2fsprogs;
    inherit (pkgs.xlibs) libX11 libICE;
  };
}

O arquivo acima (denominado custom-packages.nix) invoca a expressão do Midnight Commander (definindo uma função) com seus parâmetros requisitados.

Duas de suas dependências também são compostas na mesma expressão, a saber: pkgconfig e gpm que também estão salvas fora da árvore Nixpkgs. As dependências restantes do Midnight Commander são fornecidas pelo Nixpkgs.

Para tornar o exemplo acima completo, a estrutura de diretórios do conjunto de expressões Nix deve ser semnelhante a isso:

pkgs/
  pkgconfig/
    default.nix
    requires-private.patch 
    setup-hook.sh
  gpm/
    default.nix
-  mc/
    default.nix
custom-packages.nix

As expressões para gpm e pkgconfig podem ser copiadas do Nixpkgs, executando ($nixpkgs deve ser substituído pelo caminho do Nixpkgs em seu sistema):

cp -a $nixpkgs/pkgs/pkgs/development/tools/misc/pkgconfig pkgs
cp -a $nixpkgs/pkgs/servers/gpm pkgs

Usando a composição Nix acima (custom-packages.nix), as outras expressões às quais ela se refere, e executando a seguinte linha de comando:

$ nix-build custom-packages.nix -A mc
/nix/store/svm98wmbf01dswlfcvvxfqqzckbhp5n5-mc-4.8.12

eu posso montar meu próprio pacote usando nossa composição privada. Além disso, também posso instalar em meu perfil Nix executando:

$ nix-env -f custom-packages.nix -iA mc

Como a composição também é uma função tomando o sistema como parâmetro (o que por padrão é a mesma arquitetura de sistema do hospedeiro), eu posso também construir o Midnight Commander para uma arquitetura distinta, como o sistema Intel Linux 32 bits:

$ nix-build custom-packages.nix -A mc --argstr system i686-linux

3 Simplificando a Composição de Expressões Privadas

A composição privada mostrada antes passa todos os argumentos requeridos pela função para cada definição de pacote, o que basicamente requer que se escrevam os argumentos duas vezes: primeiro para defini-los e depois para fornecê-los.

Em 95% dos casos, os parâmetros de função são tipicamente pacotes definidos no mesmo conjunto de atributos da composição com os mesmos nomes de atributo como parâmetros de função.

No Nixpkgs existe uma utilidade de nome callPackage {} que simplifica as coisas consideravelmente – ele automaticamente passa todos os requerimentos para a função tomando os atributos com o mesmo nome da composição. Então não existe mais a necessidade de escrever inherit gpm ....

Nós também podemos definir nossa própria função privada callPackage {} que faz isso para nossa composição privada:

{ system ? builtins.currentSystem }:

let
  pkgs = import <nixpkgs> { inherit system; };

  callPackage = pkgs.lib.callPackageWith (pkgs // pkgs.xlibs // self);

  self = {
    pkgconfig = callPackage ./pkgs/pkgconfig { };

    gpm = callPackage ./pkgs/gpm { };

    mc = callPackage ./pkgs/mc { };
  };
in
self

A expressão acima é uma versão simplificada da nossa antiga expressão (denominada custom-packages.nix) que usa callPackage {} a fim de automaticamente passar todas as dependências requeridas para as funções que constroem o pacote.

A própria callPackage é composta da função pkgs.lib.callPackageWith. O primeiro parâmetro (pkgs // pkgs.xlibs // self) define os auto-argumentos. Neste caso particular, eu especifiquei que os argumentos de função automáticos vêm de self (nossa composição privada) primeiro, daí do sub-atributo xlibs de Nixpkgs, e daí da composição principal do Nixpkgs.

Com a expressão acima, nós obtemos exatamente a mesma coisa que na anterior, mas com menos linhas de código. Nós também podemos construir o Midnight Commander exatamente da mesma maneira que antes:

$ nix-build custom-packages.nix -A mc
/nix/store/svm98wmbf01dswlfcvvxfqqzckbhp5n5-mc-4.8.12

4 Conclusão

Neste post eu descrevi como eu tipicamente mantenho um pacote ou uma coleção de pacotes fora da árvore Nixpkgs. Mais informação sobre como empacotar as coisas em Nix pode ser encontrada nos manuais do Nix e do Nixpkgs.


5 META

Table 1: META
Título Original Managing private Nix packages outside the Nixpkgs tree
Autor Sander van der Burg
Link Original http://sandervanderburg.blogspot.com.br/2014/07/managing-private-nix-packages-outside.html
Link Arquivado http://archive.is/MDoZv

Created: 2018-03-24 sáb 08:25

Validate

Nenhum comentário:

Postar um comentário

Entendendo o Combinador Y [hishamhm]

Entendendo, Finalmente, o Combinador Y - Uma Abordagem de uma Perspectiva Amigável a Programadores Entendendo, Finalmente, o...