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

domingo, 4 de março de 2018

Controle Seus Pacotes Com Perfis Criados Em Nix! [matej cotman]

Controle Seus Pacotes Com Perfis Criados Em Nix!

Controle Seus Pacotes Com Perfis Criados Em Nix!

Nix tem muitas vantagens sobre a maioria dos gerenciadores de pacotes. Hoje, eu vou escrever sobre uma delas.

Nix é um gerenciador de pacotes para o NixOS, mas você pode instalá-lo em outros sistemas operacionais. Um problema nos sistemas normais é que você está / deveria estar nervoso quando instala pacotes, porque você simplesmente não sabe o que e exatamente aonde eles serão instalados. Tente removê-los, e aí então…

Bem, existe uma solução - Nix - que funciona assim: quando você instala um pacote, ele vai diretamente para algum diretório de nome comprido com um hash em seu nome, no /nix/store/... e é linkado no perfil que está disponível para um usuário específico ou para todos os usuários, você pode criar ambientes com estes perfis.

Existem três formas de se instalar um pacote com o Nix:

  • No ambiente de sistema, dentro do sistema global em /etc/nixos/configuration.nix (disponível somente para usuários de NixOS);
  • No ambiente de usuário com o comando nix-env –install; e
  • No ambiente customizado

1 Ambiente de Sistema

Exemplo (declare dentro de /etc/nixos/configuration.nix):

environment.systemPackages = [ pkgs.perl pkgs.emacs pkgs.thunderbird pkgs.firefox ];

Não há muito a se dizer; os pacotes serão baixados do cache ou compilados se não existirem binários disponíveis.

Lembre-se: quando se instala, digamos, uma aplicação perl, em um ambiente, você não verá o executável perl propriamente dito, você terá que especificá-lo separadamente.

2 Ambiente de Usuário

Exemplo (como usuário normal, sem sudo, é claro):

$ nix-env -iA pkgs.emacs24

Este pacote será instalado em algum lugar como /nix/store/qv39sv3981kk5h4p260y879wilfc3c26-emacs-24.3; vejamos o que tem lá dentro (em dois níveis de profundidade):

/nix/store/qv39sv3981kk5h4p260y879wilfc3c26-emacs-24.3
|-- bin/
|   |-- ctags*
|   |-- ebrowse*
|   |-- emacs -> emacs-24.3*
|   |-- emacs-24.3*
|   |-- emacsclient*
|   |-- etags*
|   `-- grep-changelog*
|-- libexec/
|   `-- emacs/
|-- share/
|   |-- applications/
|   |-- emacs/
|   |-- icons/
|   |-- info/
|   `-- man/
`-- var/
    `-- games/

Você pode ver que tudo deste pacote está em um só lugar. Dependências também são lidadas de uma maneira similar e são reutilizadas para outros pacotes, claro.

Bem, como é que um usuário pode ver/usar o pacote correto? A resposta é simples: cada usuário tem seu próprio perfil, o qual é um diretório, e links são criados dentro dele.

Se algum usuário quer o emacs23 e não o emacs24, isto não é um problema, as dependências serão reutilizadas e novos links no outro perfil de usuário serão criados.

3 Ambientes Customizados

Aqui é que começa a diversão!

Se você quer o emacs23 lado a lado com o emacs24, você precisa criar um ambiente customizado. Neste exemplo eu criarei dois ambientes customizados, um para cada pacote, mas você pode ter um emacs no ambiente de usuário e outro no customizado.

Edite/crie um novo arquivo ~/.nixpkgs/config.nix:

{
  packageOverrides = pkgs:
  rec {
    homeEnv = pkgs.buildEnv {
      name = "homeEnv";
      paths = [ pkgs.emacs24 pkgs.bsdgames ];
    };
    workEnv = pkgs.buildEnv {
      name = "workEnv";
      paths = [ pkgs.emacs23 pkgs.perl ];
    };
  };
}

Então você precisa construir estes dois perfis:

$ nix-env -p /nix/var/nix/profiles/per-user/<USERNAME>/workEnv -i workEnv
$ nix-env -p /nix/var/nix/profiles/per-user/<USERNAME>/homeEnv -i homeEnv

Eu escolhi criar perfis dentro da pasta /nix/var/nix/profiles/per-user/<USERNAME>/, mas você pode usar ~/profiles/ ou o que bem entender. A flag -i é a --install que vimos antes e estamos desta vez instalando perfis de nome homeEnv e workEnv. Vamos ver o que tem dentro de homeEnv (desta vez em um nível de profundidade):

/nix/store/rg4zh4s2gaw1b3dm2kgicis8admsaanx-homeEnv
|-- bin/
|-- libexec -> /nix/store/qv39sv3981kk5h4p260y879wilfc3c26-emacs-24.3/libexec/
|-- share/
`-- var -> /nix/store/qv39sv3981kk5h4p260y879wilfc3c26-emacs-24.3/var/

Você pode ver que o perfil é na realidade criado dentro de /nix/store e se algum outro usuário escolhe criar os mesmos pacotes para o seu perfil, apenas um link será criado - o perfil dele será o mesmo que o nosso.

Falta mais uma coisinha… Crie mais dois arquivos, um para o ambiente homeEnv

export PATH=/nix/var/nix/profiles/per-user/<USERNAME>/homeEnv/bin
$@

e outro para o workEnv

export PATH=/nix/var/nix/profiles/per-user/<USERNAME>/workEnv/bin
$@

Você precisará invocar estes scripts via source para montar estas variáveis de ambiente para o perfil ou torná-los executáveis (também torne-os disponíveis na variável de ambiente PATH) e execute comandos como argumentos de linha de comando (por isso o $@).

Isto foi brincadeirra de criança. Ambientes são úteis em desenvolvimento, você pode ter um perfil para desenvolvimento Perl e outro para Python, um em cada perfil é claro, aplicações só verão umas às outras, isso também depende de variáveis de ambiente da shell, para meu perfil Python 2.7 o arquivo se parece com isso:

#!/usr/bin/env bash

NIX_USER_PROFILE_DIR=/nix/var/nix/profiles/per-user/matej
nixprofile=$NIX_USER_PROFILE_DIR/py27

export PATH="$nixprofile/bin"
export LD_LIBRARY_PATH="$nixprofile/lib"
export NIX_LDFLAGS="-L$nixprofile/lib -L$nixprofile/lib/pkgconfig"
export NIX_CFLAGS_COMPILE="-I$nixprofile/include -I$nixprofile/include/sasl"
export PKG_CONFIG_PATH="$nixprofile/lib/pkgconfig"
export PYTHONPATH="$nixprofile/lib/python2.7/site-packages"
export PS1="py27 $PS1"

export INCLUDE="$nixprofile/include:$INCLUDE"
export LIB="$nixprofile/lib:$LIB"
export C_INCLUDE_PATH="$nixprofile/include:$C_INCLUDE_PATH"
export LD_RUN_PATH="$nixprofile/lib:$LD_RUN_PATH"
export LIBRARY_PATH="$nixprofile/lib:$LIBRARY_PATH"
export CFLAGS=$NIX_CFLAGS_COMPILE
export LDFLAGS=$NIX_LDFLAGS

"$@"

Isto conclui este post.


4 META

Table 1: META
Título Original Control your packages with Nix created profiles!
Autor Matej Cotman
Link Original http://blog.matejc.com/blogs/myblog/control-your-packages-with-nix-environments
Link Arquivado http://archive.is/OZ3CG

Created: 2018-03-04 dom 13:41

Validate

Entendendo o Combinador Y [hishamhm]

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