Próximo: , Anterior: , Acima: Interface de programação   [Conteúdo][Índice]


8.3 Definindo variantes de pacote

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.

Macro: modify-inputs entradas cláusulas

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):

Procedimento: options->transformation opções

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.

Procedure: package-input-rewriting replacements [rewrite-name] [#:deep? #t] [#:recursive? #f] Return a procedure that, when passed a

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.

Procedure: package-input-rewriting/spec replacements [#:deep? #t] [#:replace-hidden? #t] Return a procedure that, given a

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.

Procedimento: package-mapping proc [cortar?] [#:deep? #f]

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:


Próximo: Escrevendo manifestos, Anterior: Definindo pacotes, Acima: Interface de programação   [Conteúdo][Índice]