Próximo: Outros sistemas de construção, Anterior: Configuração, Acima: Tutorial sobre empacotamento [Conteúdo][Índice]
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.
git-fetch
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.
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
.
Existem 3 tipos de entradas diferentes. Resumidamente:
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.
Instalado na loja mas não no perfil, além de estar presente na hora da construção.
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.
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.
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:
(add-before phase new-phase procedure)
: Run
procedure named new-phase before phase.
(add-after phase new-phase procedure)
: O mesmo, mas
depois.
(replace phase procedure)
.
(delete phase)
.
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.
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.
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.
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]