Próximo: , Anterior: , Acima: Tutorial sobre empacotamento   [Conteúdo][Índice]


2.1.3 Exemplo estendido

O exemplo “Hello World” acima é tão simples quanto parece. Os pacotes podem ser mais complexos do que isso e o Guix pode lidar com cenários mais avançados. Vejamos outro pacote mais sofisticado (ligeiramente modificado em relação à fonte):

(define-module (gnu packages version-control)
  #:use-module ((guix licenses) #:prefix license:)
  #:use-module (guix utils)
  #:use-module (guix packages)
  #:use-module (guix git-download)
  #:use-module (guix build-system cmake)
  #:use-module (gnu packages compression)
  #:use-module (gnu packages pkg-config)
  #:use-module (gnu packages python)
  #:use-module (gnu packages ssh)
  #:use-module (gnu packages tls)
  #:use-module (gnu packages web))

(define-public my-libgit2
  (let ((commit "e98d0a37c93574d2c6107bf7f31140b548c6a7bf")
        (revision "1"))
    (package
      (name "my-libgit2")
      (version (git-version "0.26.6" revision commit))
      (source (origin
                (method git-fetch)
                (uri (git-reference
                      (url "https://github.com/libgit2/libgit2/")
                      (commit commit)))
                (file-name (git-file-name name version))
                (sha256
                 (base32
                  "17pjvprmdrx4h6bb1hhc98w9qi6ki7yl57f090n9kbhswxqfs7s3"))
                (patches (search-patches "libgit2-mtime-0.patch"))
                (modules '((guix build utils)))
                ;; Remove bundled software.
                (snippet '(delete-file-recursively "deps"))))
      (build-system cmake-build-system)
      (outputs '("out" "debug"))
      (arguments
       `(#:tests? #true                         ; Run the test suite (this is the default)
         #:configure-flags '("-DUSE_SHA1DC=ON") ; SHA-1 collision detection
         #:phases
         (modify-phases %standard-phases
           (add-after 'unpack 'fix-hardcoded-paths
             (lambda _
               (substitute* "tests/repo/init.c"
                 (("#!/bin/sh") (string-append "#!" (which "sh"))))
               (substitute* "tests/clar/fs.h"
                 (("/bin/cp") (which "cp"))
                 (("/bin/rm") (which "rm")))))
           ;; Run checks more verbosely.
           (replace 'check
             (lambda* (#:key tests? #:allow-other-keys)
               (when tests?
                 (invoke "./libgit2_clar" "-v" "-Q"))))
           (add-after 'unpack 'make-files-writable-for-tests
             (lambda _ (for-each make-file-writable (find-files ".")))))))
      (inputs
       (list libssh2 http-parser python-wrapper))
      (native-inputs
       (list pkg-config))
      (propagated-inputs
       ;; These two libraries are in 'Requires.private' in libgit2.pc.
       (list openssl zlib))
      (home-page "https://libgit2.github.com/")
      (synopsis "Library providing Git core methods")
      (description
       "Libgit2 is a portable, pure C implementation of the Git core methods
provided as a re-entrant linkable library with a solid API, allowing you to
write native speed custom Git applications in any language with bindings.")
      ;; GPLv2 with linking exception
      (license license:gpl2))))

(Nos casos em que você deseja ajustar apenas alguns campos de uma definição de pacote, você deve confiar na herança em vez de copiar e colar tudo. Veja abaixo.)

Vamos discutir esses campos em profundidade.

2.1.3.1 Método git-fetch

Ao contrário do método url-fetch, git-fetch espera um git-reference que usa um repositório Git e um commit. O commit pode ser qualquer referência do Git, como tags, portanto, se version estiver marcado, ele poderá ser usado diretamente. Às vezes, a tag é prefixada com v; nesse caso, você usaria (commit (string-append "v" version)).

Para garantir que o código-fonte do repositório Git seja armazenado em um diretório com um nome descritivo, usamos (nome‐do- arquivo (nome-do-arquivo-git versão)).

O procedimento git-version pode ser usado para derivar a versão ao empacotar programas para um commit específico, seguindo as diretrizes do contribuidor Guix (veja Números de versão em GNU Guix Reference Manual).

Como obter o hash sha256 que está aí, você pergunta? Invocando guix hash em um checkout do commit desejado, da seguinte forma:

git clone https://github.com/libgit2/libgit2/
cd libgit2
git checkout v0.26.6
guix hash -rx .

guix hash -rx calcula um hash SHA256 em todo o diretório, excluindo o subdiretório .git (veja Invocando guix hash em GNU Guix Reference Manual).

No futuro, guix download será capaz de executar essas etapas para você, assim como faz para downloads regulares.

2.1.3.2 Snippets

Os trechos ("Snippets" ) são códigos de esquema citados (ou seja, não avaliados) que são um meio de corrigir a fonte. Eles são uma alternativa Guix-y aos arquivos .patch tradicionais. Por causa da citação, o código só é avaliado quando passado para o daemon Guix para construção. Pode haver quantos trechos forem necessários.

Os snippets podem precisar de módulos Guile adicionais que podem ser importados do campo modules.

2.1.3.3 Entradas

Existem 3 tipos de entradas diferentes. Resumidamente:

native-inputs

Necessário para construção, mas não para tempo de execução - instalar um pacote por meio de um substituto não instalará essas entradas.

entradas

Instalado na loja mas não no perfil, além de estar presente na hora da construção.

propagated-inputs

Instalado na loja e no perfil, além de estar presente na hora da construção.

Veja referência de pacote em Manual de referência GNU Guix para mais detalhes.

A distinção entre as diversas entradas é importante: se uma dependência puder ser tratada como uma input em vez de uma propagated input, isso deverá ser feito, caso contrário ela “poluirá” o perfil do usuário sem nenhuma boa razão.

Por exemplo, um usuário que instala um programa gráfico que depende de uma ferramenta de linha de comando pode estar interessado apenas na parte gráfica, portanto não há necessidade de forçar a ferramenta de linha de comando no perfil do usuário. A dependência é uma preocupação do pacote, não do usuário. Inputs tornam possível lidar com dependências sem incomodar o usuário, adicionando arquivos executáveis (ou bibliotecas) indesejados ao seu perfil.

O mesmo vale para native-inputs: depois que o programa é instalado, as dependências em tempo de construção podem ser coletadas como lixo com segurança. Também importa quando um substituto está disponível, caso em que apenas inputs e propagated inputs serão obtidos: os native inputs não são necessários para instalar um pacote de um substituto.

Nota: Você pode ver aqui e ali trechos onde as entradas do pacote são escritas de maneira bem diferente, assim:

;; The "old style" for inputs.
(inputs
 `(("libssh2" ,libssh2)
   ("http-parser" ,http-parser)
   ("python" ,python-wrapper)))

Este é o “estilo antigo”, onde cada entrada na lista recebe explicitamente um rótulo (uma string). Ainda é compatível, mas recomendamos usar o estilo acima. Veja referência de pacote em GNU Guix Reference Manual, para mais informações.

2.1.3.4 Saídas

Assim como um pacote pode ter múltiplas entradas, ele também pode produzir múltiplas saídas.

Cada saída corresponde a um diretório separado na loja.

O usuário pode escolher qual saída instalar; isso é útil para economizar espaço ou evitar poluir o perfil do usuário com executáveis ou bibliotecas indesejadas.

A separação de saída é opcional. Quando o campo outputs é omitido, a saída padrão e única (o pacote completo) é referida como "out".

Nomes de saída separados típicos incluem debug e doc.

É aconselhável separar as saídas apenas quando você mostrar que vale a pena: se o tamanho da saída for significativo (compare com guix size) ou caso o pacote seja modular.

2.1.3.5 Argumentos do sistema de compilação

O arguments é uma lista de valores de palavras-chave usadas para configurar o processo de construção.

O argumento mais simples #:tests? pode ser usado para desabilitar o conjunto de testes ao construir o pacote. Isso é útil principalmente quando o pacote não apresenta nenhum conjunto de testes. É altamente recomendável manter o conjunto de testes ativado, se houver.

Outro argumento comum é :make-flags, que especifica uma lista de sinalizadores a serem acrescentados ao executar o make, como faria na linha de comando. Por exemplo, os seguintes sinalizadores

#:make-flags (list (string-append "prefix=" (assoc-ref %outputs "out"))
                   "CC=gcc")

traduzir para

$ make CC=gcc prefix=/gnu/store/...-<out>

Isso define o compilador C para gcc e a variável prefix (o diretório de instalação no jargão Make) para (assoc-ref %outputs "out"), que é um estágio de construção global variável apontando para o diretório de destino na loja (algo como /gnu/store/...-my-libgit2-20180408).

Da mesma forma, é possível definir os sinalizadores de configuração:

#:configure-flags '("-DUSE_SHA1DC=ON")

A variável %build-inputs também é gerada no escopo. Ela é uma tabela de associações que mapeia os nomes de entrada para seus diretórios de armazenamento.

A palavra-chave phases lista as etapas sequenciais do sistema de compilação. Normalmente as fases incluem unpack, configure, build, install e check. Para saber mais sobre essas fases, você precisa definir a definição apropriada do sistema de compilação em ‘$GUIX_CHECKOUT/guix/build/gnu-build-system.scm’:

(define %standard-phases
  ;; Standard build phases, as a list of symbol/procedure pairs.
  (let-syntax ((phases (syntax-rules ()
                         ((_ p ...) `((p . ,p) ...)))))
    (phases set-SOURCE-DATE-EPOCH set-paths install-locale unpack
            bootstrap
            patch-usr-bin-file
            patch-source-shebangs configure patch-generated-file-shebangs
            build check install
            patch-shebangs strip
            validate-runpath
            validate-documentation-location
            delete-info-dir-file
            patch-dot-desktop-files
            install-license-files
            reset-gzip-timestamps
            compress-documentation)))

Ou do REPL:

(add-to-load-path "/path/to/guix/checkout")
,use (guix build gnu-build-system)
(map first %standard-phases)
 (set-SOURCE-DATE-EPOCH set-paths install-locale unpack bootstrap patch-usr-bin-file patch-source-shebangs configure patch-generated-file-shebangs build check install patch-shebangs strip validate-runpath validate-documentation-location delete-info-dir-file patch-dot-desktop-files install-license-files reset-gzip-timestamps compress-documentation)

Se quiser saber mais sobre o que acontece nessas fases, consulte os procedimentos associados.

Por exemplo, no momento em que este livro foi escrito, a definição de unpack para o sistema de compilação GNU era:

(define* (unpack #:key source #:allow-other-keys)
  "Unpack SOURCE in the working directory, and change directory within the
source.  When SOURCE is a directory, copy it in a sub-directory of the current
working directory."
  (if (file-is-directory? source)
      (begin
        (mkdir "source")
        (chdir "source")

        ;; Preserve timestamps (set to the Epoch) on the copied tree so that
        ;; things work deterministically.
        (copy-recursively source "."
                          #:keep-mtime? #true))
      (begin
        (if (string-suffix? ".zip" source)
            (invoke "unzip" source)
            (invoke "tar" "xvf" source))
        (chdir (first-subdirectory "."))))
  #true)

Observe a chamada chdir: ela altera o diretório de trabalho para onde a fonte foi descompactada. Assim, cada fase após o unpack usará a fonte como diretório de trabalho, e é por isso que podemos trabalhar diretamente nos arquivos de origem. Isto é, a menos que uma fase posterior mude o diretório de trabalho para outro.

Modificamos a lista de %standard-phases do sistema de construção com a macro modify-phases conforme a lista de modificações especificadas, que pode ter os seguintes formatos:

O procedure suporta os argumentos de palavra-chave inputs e outputs. Cada entrada (seja native, propagated ou não) e diretório de saída são referenciados por seus nomes nessas variáveis. Assim (assoc-ref outputs "out") é o diretório de armazenamento da saída principal do pacote. Um procedimento de fase pode ser assim:

(lambda* (#:key inputs outputs #:allow-other-keys)
  (let ((bash-directory (assoc-ref inputs "bash"))
        (output-directory (assoc-ref outputs "out"))
        (doc-directory (assoc-ref outputs "doc")))
    ;; ...
    #true))

O procedimento deve retornar #true em caso de sucesso. É frágil confiar no valor de retorno da última expressão usada para ajustar a fase porque não há garantia de que seria um #true. Daí o #true final para garantir que o valor correto seja retornado em caso de sucesso.

2.1.3.6 Preparação de código

O leitor astuto deve ter notado a sintaxe de quase aspas e vírgula no campo do argumento. Na verdade, o código de construção na declaração do pacote não deve ser avaliado no lado do cliente, mas apenas quando passado para o daemon Guix. Esse mecanismo de passagem de código entre dois processos em execução é chamado preparação de código.

2.1.3.7 Funções utilitárias

Ao personalizar phases, muitas vezes precisamos escrever código que imite as invocações equivalentes do sistema (make, mkdir, cp, etc.) comumente usado durante “ Instalações regulares no estilo Unix”.

Alguns como chmod são nativos do Guile. Veja manual de referência do Guile para obter uma lista completa.

Guix fornece funções auxiliares adicionais que são especialmente úteis no contexto de gerenciamento de pacotes.

Algumas dessas funções podem ser encontradas em ‘$GUIX_CHECKOUT/guix/guix/build/utils.scm’. A maioria delas reflete o comportamento dos comandos tradicionais do sistema Unix:

which

Como o comando do sistema ‘which’.

find-files

Semelhante ao comando do sistema ‘find’.

mkdir-p

Como ‘mkdir -p’, que cria todos os diretórios conforme necessário.

install-file

Semelhante a ‘install’ ao instalar um arquivo em um diretório (possivelmente inexistente). Guile tem copy-file que funciona como ‘cp’.

copy-recursively

Como ‘cp -r’.

delete-file-recursively

Como ‘rm -rf’.

invoke

Executar um executável. Deve ser usado em vez de system*.

with-directory-excursion

Execute o corpo em um diretório de trabalho diferente e restaure o diretório de trabalho anterior.

substitute*

Uma função do tipo “sed”.

Veja Build Utilities em GNU Guix Reference Manual, para obter mais informações sobre esses utilitários.

2.1.3.8 Prefixo do módulo

A licença em nosso último exemplo precisa de um prefixo: isso se deve à forma como o módulo license foi importado no pacote, como #:use-module ((guix Licenses) #:prefix License:). O mecanismo de importação do módulo Guile (veja Using Guile Modules em manual de referência do Guile) dá ao usuário controle total sobre o namespace: isso é necessário para evitar conflitos entre, digamos, a variável ‘zlib’ de ‘licenses.scm’ (um valor license) e a variável ‘zlib’ de ‘compression.scm’ (um valor package).


Próximo: Outros sistemas de construção, Anterior: Configuração, Acima: Tutorial sobre empacotamento   [Conteúdo][Índice]