Próximo: Escrevendo manifestos, Anterior: Definindo pacotes, Acima: Interface de programação [Conteúdo][Índice]
Uma das coisas boas com o Guix é que, dada uma definição de pacote, você pode facilmente derivar variantes desse pacote—para uma versão upstream diferente, com dependências diferentes, opções de compilação diferentes e assim por diante. Alguns desses pacotes personalizados podem ser definidos diretamente da linha de comando (veja Opções de transformação de pacote). Esta seção descreve como definir variantes de pacotes no código. Isso pode ser útil em “manifestos” (veja Escrevendo manifestos) e em sua própria coleção de pacotes (veja Criando um canal), entre outros!
Conforme discutido anteriormente, os pacotes são objetos de primeira classe
na linguagem Scheme. O módulo (guix packages)
fornece a construção
package
para definir novos objetos de pacote (veja Referência do package
). A maneira mais fácil de definir uma variante de pacote é usando
a palavra-chave inherit
junto com package
. Isso permite que
você herde de uma definição de pacote enquanto substitui os campos que
deseja.
Por exemplo, dada a variável hello
, que contém uma definição para a
versão atual do GNU Hello, veja como você definiria uma variante para a
versão 2.2 (lançada em 2006, é vintage!):
(use-modules (gnu packages base)) ;para 'hello' (define hello-2.2 (package (inherit hello) (version "2.2") (source (origin (method url-fetch) (uri (string-append "mirror://gnu/hello/hello-" version ".tar.gz")) (sha256 (base32 "0lappv4slgb5spyqbh6yl5r013zv72yqg2pcl30mginf3wdqd8k9"))))))
O exemplo acima corresponde ao que as opções de transformação de pacote
--with-version ou --with-source fazem. Essencialmente,
hello-2.2
preserva todos os campos de hello
, exceto
version
e source
, que ele substitui. Observe que a variável
hello
original ainda está lá, no módulo (gnu packages base)
,
inalterada. Quando você define um pacote personalizado como este, você está
realmente adicionando uma nova definição de pacote; a original
permanece disponível.
Você pode também definir variantes com um conjunto diferente de dependências
do que o pacote original. Por exemplo, o pacote padrão gdb
depende de
guile
, mas como essa é uma dependência opcional, você pode definir
uma variante que remova essa dependência assim:
(use-modules (gnu packages gdb)) ;para 'gdb' (define gdb-sans-guile (package (inherit gdb) (inputs (modify-inputs (package-inputs gdb) (delete "guile")))))
O formulário modify-inputs
acima remove o pacote "guile"
do
campo inputs
de gdb
. A macro modify-inputs
é um
auxiliar que pode ser útil sempre que você quiser remover, adicionar ou
substituir entradas de pacote.
Modifique as entradas do pacote fornecido, conforme retornado por
package-inputs
& co., de acordo com as cláusulas fornecidas. Cada
cláusula deve ter uma das seguintes formas:
(delete nome…)
Exclua dos pacotes de entrada os nomes (strings) fornecidos.
(prepend pacote…)
Adicione pacotes à frente da lista de entrada.
(append pacote…)
Adicione pacotes ao final da lista de entrada.
(replace nome substituição)
Substitua o pacote chamado nome por substituição.
O exemplo abaixo remove as entradas GMP e ACL do Coreutils e adiciona libcap à frente da lista de entradas:
(modify-inputs (package-inputs coreutils)
(delete "gmp" "acl")
(prepend libcap))
O exemplo abaixo substitui o pacote guile
das entradas de
guile-redis
por guile-2.2
:
(modify-inputs (package-inputs guile-redis)
(replace "guile" guile-2.2))
The last type of clause is append
, to add inputs at the back of the
list.
Em alguns casos, você pode achar útil escrever funções (“procedimentos”,
no jargão do Scheme) que retornam um pacote com base em alguns
parâmetros. Por exemplo, considere a biblioteca luasocket
para a
linguagem de programação Lua. Queremos criar pacotes luasocket
para
as principais versões do Lua. Uma maneira de fazer isso é definir um
procedimento que pega um pacote Lua e retorna um pacote luasocket
que
depende dele:
(define (make-lua-socket name lua) ;; Return a luasocket package built with LUA. (package (name name) (version "3.0") ;; several fields omitted (inputs (list lua)) (synopsis "Socket library for Lua"))) (define-public lua5.1-socket (make-lua-socket "lua5.1-socket" lua-5.1)) (define-public lua5.2-socket (make-lua-socket "lua5.2-socket" lua-5.2))
Aqui definimos os pacotes lua5.1-socket
e lua5.2-socket
chamando make-lua-socket
com argumentos
diferentes. Veja Procedures em GNU Guile Reference Manual, para
mais informações sobre procedimentos. Ter definições públicas de nível
superior para esses dois pacotes significa que eles podem ser referenciados
a partir da linha de comando (veja Módulos de pacote).
Essas são variantes de pacote bem simples. Como uma conveniência, o módulo
(guix transformations)
fornece uma interface de alto nível que mapeia
diretamente para as opções de transformação de pacote mais sofisticadas
(veja Opções de transformação de pacote):
Retorna um procedimento que, quando passado um objeto para construção (pacote, derivação, etc.), aplica as transformações especificadas por opções e retorna os objetos resultantes. opções deve ser uma lista de pares símbolo/string como:
((with-branch . "guile-gcrypt=master")
(without-tests . "libgcrypt"))
Cada símbolo nomeia uma transformação e a string correspondente é um argumento para essa transformação.
Por exemplo, um manifesto equivalente a este comando:
guix build guix \ --with-branch=guile-gcrypt=master \ --with-debug-info=zlib
... ficaria assim:
(use-modules (guix transformations)) (define transform ;; The package transformation procedure. (options->transformation '((with-branch . "guile-gcrypt=master") (with-debug-info . "zlib")))) (packages->manifest (list (transform (specification->package "guix"))))
O procedimento options->transformation
é conveniente, mas talvez
também não seja tão flexível quanto você gostaria. Como ele é implementado?
O leitor astuto provavelmente notou que a maioria das opções de
transformação de pacotes vai além das mudanças superficiais mostradas nos
primeiros exemplos desta seção: elas envolvem reescrita de entrada,
por meio da qual o grafo de dependência de um pacote é reescrito pela
substituição de entradas específicas por outras.
A reescrita do grafo de dependência, para fins de troca de pacotes no grafo,
é o que o procedimento package-input-rewriting
em (guix
packages)
implementa.
package, replaces its direct and indirect dependencies, including implicit inputs when deep? is true, according to replacements. replacements is a list of package pairs; the first element of each pair is the package to replace, and the second one is the replacement.
When recursive? is true, apply replacements to the right-hand sides of replacements as well, recursively.
Opcionalmente, nome-da-reescrita é um procedimento de um argumento que recebe o nome de um pacote e retorna seu novo nome após a reescrita.
Considere este exemplo:
(define libressl-instead-of-openssl ;; This is a procedure to replace OPENSSL by LIBRESSL, ;; recursively. (package-input-rewriting `((,openssl . ,libressl)))) (define git-with-libressl (libressl-instead-of-openssl git))
Aqui, primeiro definimos um procedimento de reescrita que substitui openssl por libressl. Então, o usamos para definir uma variante do pacote git que usa libressl em vez de openssl. É exatamente isso que a opção de linha de comando --with-input faz (veja --with-input).
A seguinte variante de package-input-rewriting
pode corresponder a
pacotes a serem substituídos por nome em vez de identidade.
package, applies the given replacements to all the package graph, including implicit inputs unless deep? is false.
replacements is a list of spec/procedures pair; each spec is a package
specification such as "gcc"
or "guile@2"
, and each procedure
takes a matching package and returns a replacement for that package.
Matching packages that have the hidden?
property set are not replaced
unless replace-hidden? is set to true.
O exemplo acima poderia ser reescrito desta forma:
(define libressl-instead-of-openssl
;; Substitua todos os pacotes chamados "openssl" por LibreSSL.
(package-input-rewriting/spec `(("openssl" . ,(const libressl)))))
A principal diferença aqui é que, dessa vez, os pacotes são correspondidos
por spec e não por identidade. Em outras palavras, qualquer pacote no grafo
que seja chamado openssl
será substituído.
Um procedimento mais genérico para reescrever um grafo de dependência de
pacote é package-mapping
: ele suporta alterações arbitrárias em nós
no grafo.
Retorna um procedimento que, dado um pacote, aplica proc a todos os pacotes dependentes e retorna o pacote resultante. O procedimento para a recursão quando cortar? retorna true para um determinado pacote. Quando deep? é true, proc é aplicado a entradas implícitas também.
Dicas: Entender como uma variante realmente se parece pode ser difícil quando se começa a combinar as ferramentas mostradas acima. Há várias maneiras de inspecionar um pacote antes de tentar construí-lo que podem ser úteis:
- Você pode inspecionar o pacote interativamente no REPL, por exemplo, para visualizar suas entradas, o código de suas fases de construção ou seus sinalizadores de configuração (veja Usando Guix interativamente).
- Ao reescrever dependências,
guix graph
geralmente pode ajudar a visualizar as alterações feitas (veja Invocandoguix graph
).
Próximo: Escrevendo manifestos, Anterior: Definindo pacotes, Acima: Interface de programação [Conteúdo][Índice]