Suivant: Autres systèmes de construction, Précédent: Configuration, Monter: Didacticiel d’empaquetage [Table des matières][Index]
L’exemple « Hello World » précédent est le plus simple possible. Les paquets peuvent devenir plus complexes que cela et Guix peut gérer des scénarios plus avancés. Voyons un autre paquet plus sophistiqué (légèrement modifié à partir des sources) :
(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))) ;; Suppression des logiciels embarqués. (snippet '(delete-file-recursively "deps")))) (build-system cmake-build-system) (outputs '("out" "debug")) (arguments `(#:tests? #true ; Lancer la suite de tests (c'est la valeur par défaut) #:configure-flags '("-DUSE_SHA1DC=ON") ; détection de collision SHA-1 #: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"))))) ;; Lancer les tests avec plus de verbosité. (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 ;; Ces deux bibliothèques sont dans « Requires.private », dans 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))))
(Dans les cas où vous voulez seulement changer quelques champs d’une définition de paquets, vous devriez utiliser l’héritage au lieu de tout copier-coller. Voir plus bas.)
Parlons maintenant de ces champs en détail.
git-fetch
git-fetch
Contrairement à la méthode url-fetch
, git-fetch
a besoin d’un
git-reference
qui prend un dépôt Git et un commit. Le commit peut
être n’importe quelle référence Git comme des tags, donc si la
version
a un tag associé, vous pouvez l’utiliser directement. Parfois
le tag est précédé de v
, auquel cas vous pouvez utiliser
(commit (string-append "v" version))
.
Pour vous assurer que le code source du dépôt Git est stocké dans un
répertoire avec un nom descriptif, utilisez (file-name (git-file-name
name version))
.
Vous pouvez utiliser la procédure git-version
pour calculer la
version quand vous empaquetez des programmes pour un commit spécifique, en
suivant le guide de contribution (voir Numéros de version dans le
manuel de référence de GNU Guix).
Comment obtenir le hash sha256
, vous demandez-vous ? En invoquant
guix hash
sur un clone du commit voulu, de cette manière :
git clone https://github.com/libgit2/libgit2/ cd libgit2 git checkout v0.26.6 guix hash -rx .
guix hash -rx
calcul un SHA256 sur le répertoire entier, en
excluant le sous-répertoire .git (voir Invoquer guix hash dans le manuel de référence de GNU Guix).
Dans le futur, guix download
sera sans doute capable de faire cela
pour vous, comme il le fait pour les téléchargements directs.
Les bouts de code (snippet) sont des fragments quotés (c.-à-d. non évalués) de code Scheme utilisés pour modifier les sources. C’est une alternative aux fichiers .patch traditionnels, plus proche de l’esprit de Guix. À cause de la quote, le code n’est évalué que lorsqu’il est passé au démon Guix pour la construction. Il peut y avoir autant de bout de code que nécessaire.
Les bouts de code on parfois besoin de modules Guile supplémentaires qui
peuvent être importés dans le champ modules
.
Il y a trois types d’entrées. En résumé :
Requis pour construire mais pas à l’exécution – installer un paquet avec un substitut n’installera pas ces entrées.
Installées dans le dépôt mais pas dans le profil, et présentes à la construction.
Installées dans le dépôt et dans le profil, et présentes à la construction.
Voir Référence de package dans le manuel de référence de GNU Guix pour plus de détails.
La différence entre les différents types d’entrées est importante : si une dépendance peut être utilisée comme entrée plutôt que comme entrée propagée, il faut faire ça, sinon elle « polluera » le profil utilisateur sans raison.
Par exemple, si vous installez un programme graphique qui dépend d’un outil en ligne de commande, vous êtes probablement intéressé uniquement par la partie graphique, donc inutile de forcer l’outil en ligne de commande à être présent dans le profil utilisateur. Les dépendances sont gérés par les paquets, pas par les utilisateurs et utilisatrices. Les entrées permettent de gérer les dépendances sans ennuyer les utilisateurs et utilisatrices en ajoutant des fichiers exécutables (ou bibliothèque) inutiles dans leur profil.
Pareil pour native-inputs : une fois le programme installé, les dépendances à la construction peuvent être supprimées sans problème par le ramasse-miettes. Lorsqu’un substitut est disponible, seuls les entrées et les entrées propagées sont récupérées : les entrées natives ne sont pas requises pour installer un paquet à partir d’un substitut.
Remarque : Vous trouverez ici et là des extraits où les entrées des paquets sont écrites assez différemment, comme ceci :
;; « L'ancien style » pour les entrées. (inputs `(("libssh2" ,libssh2) ("http-parser" ,http-parser) ("python" ,python-wrapper)))C’est « l’ancien style », où chaque entrée est une liste que donne une étiquette explicite (une chaine). C’est une méthode prise en charge mais nous vous recommandons plutôt d’utiliser le style présenté plus haut. Voir Référence de package dans le manuel de référence de GNU Guix, pour plus d’informations.
De la même manière qu’un paquet peut avoir plusieurs entrées, il peut aussi avoir plusieurs sorties.
Chaque sortie correspond à un répertoire différent dans le dépôt.
Vous pouvez choisir quelle sortie installer ; c’est utile pour préserver l’espace disque et éviter de polluer le profil utilisateur avec des exécutables et des bibliothèques inutiles.
La séparation des sorties est facultative. Lorsque le champ outputs
n’est pas spécifié, l’unique sortie par défaut (le paquet complet donc) est
"out"
.
Les sorties séparées sont en général debug
et doc
.
Vous devriez séparer les sorties seulement si vous pouvez montrer que c’est
utile : si la taille de la sortie est importante (vous pouvez comparer avec
guix size
) ou si le paquet est modulaire.
Le champ arguments
est une liste de mot-clés et de valeurs utilisés
pour configurer le processus de construction.
L’argument le plus simple est #:tests?
et on l’utilise pour
désactiver la suite de tests pendant la construction du paquet. C’est
surtout utile si le paquet n’a pas de suite de tests. Nous vous recommandons
fortement de laisser tourner la suite de tests s’il y en a une.
Un autre argument courant est #:make-flags
, qui spécifie une liste de
drapeaux à ajouter en lançant make, comme ce que vous feriez sur la ligne de
commande. Par exemple, les drapeaux suivants
#:make-flags (list (string-append "prefix=" (assoc-ref %outputs "out")) "CC=gcc")
se traduisent en
$ make CC=gcc prefix=/gnu/store/...-<out>
Cela indique que le compilateur C sera gcc
et la variable
prefix
(le répertoire d’installation pour Make) sera (assoc-ref
%outputs "out")
, qui est une variable globale côté construction qui pointe
vers le répertoire de destination dans le dépôt (quelque chose comme
/gnu/store/…-my-libgit2-20180408).
De manière identique, vous pouvez indiquer les drapeaux de configuration :
#:configure-flags '("-DUSE_SHA1DC=ON")
La variable %build-inputs
est aussi générée dans cette portée. C’est
une liste d’association qui fait correspondre les noms des entrées à leur
répertoire dans le dépôt.
Le mot-clé phases
liste la séquence d’étapes du système de
construction. Les phases usuelles sont unpack
, configure
,
build
, install
et check
. Pour en savoir plus, vous
devez trouver la bonne définition du système de construction dans
‘$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 depuis la 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)
Si vous voulez en apprendre plus sur ce qui arrive pendant ces phases, consultez les procédures associées.
Par exemple, au moment d’écrire ces lignes, la définition de unpack
dans le système de construction de GNU est :
(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)
Remarquez l’appel à chdir
: il change de répertoire courant vers la
source qui vient d’être décompressée. Ainsi toutes les phases suivantes
utiliseront le répertoire des sources comme répertoire de travail, ce qui
explique qu’on peut travailler directement sur les fichiers sources. Du
moins, tant qu’une phase suivante ne change pas le répertoire de travail.
Nous modifions la liste des %standard-phases
du système de
construction avec la macro modify-phases
qui indique la liste des
modifications, sous cette formes :
(add-before phase nouvelle-phase procédure)
: Lance
une procédure nommée nouvelle-phase avant phase.
(add-after phase nouvelle-phase procédure)
:
Pareil, mais après la phase.
(replace phase procédure)
.
(delete phase)
.
La procédure prend en charge les arguments inputs
et
outputs
sous forme de mot-clés. Les entrées (natives,
propagées et simples) et répertoires de sortie sont référencés par
leur nom dans ces variables. Ainsi (assoc-ref outputs "out")
est le
répertoire du dépôt de la sortie principale du paquet. Une procédure de
phase ressemble à cela :
(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))
La procédure doit renvoyer #true
si elle réussit. S’appuyer sur la
valeur de retour de la dernière expression n’est pas très solide parce qu’il
n’y a pas de garantie qu’elle sera #true
. Donc le #true
à la
fin permet de s’assurer que la bonne valeur est renvoyée si la phase
réussit.
Si vous avez été attentif, vous aurez remarqué la quasi-quote et la virgule dans le champ argument. En effet, le code de construction dans la déclaration du paquet ne doit pas être évalué côté client, mais seulement après avoir été passé au démon Guix. Ce mécanisme de passage de code entre deux processus s’appelle l’échelonnage de code.
Lorsque vous modifiez les phases
, vous aurez souvent besoin d’écrire
du code qui ressemble aux invocation équivalentes (make
,
mkdir
, cp
, etc) couramment utilisées durant une installatio
plus standard dans le monde Unix.
Certaines comme chmod
sont natives dans Guile. Voir Guile
reference manual pour une liste complète.
Guix fournit des fonctions utilitaires supplémentaires qui sont particulièrement utiles pour la gestion des paquets.
Certaines de ces fonctions se trouvent dans ‘$GUIX_CHECKOUT/guix/guix/build/utils.scm’. La plupart copient le comportement des commandes systèmes Unix traditionnelles :
which
Fonctionne comme la commande système ‘which’.
find-files
Fonctionne un peu comme la commande ‘find’.
mkdir-p
Fonctionne comme ‘mkdir -p’, qui crée tous les parents si besoin.
install-file
Fonctionne comme ‘install’ pour installer un fichier vers un répertoire
(éventuellement non existant). Guile a copy-file
qui fonctionne comme
‘cp’.
copy-recursively
Fonctionne comme ‘cp -r’.
delete-file-recursively
Fonctionne comme ‘rm -rf’.
invoke
Lance un exécutable. Vous devriez utiliser cela à la place de
system*
.
with-directory-excursion
Lance le corps dans un répertoire de travail différent, puis revient au répertoire de travail précédent.
substitute*
Une fonction similaire à sed
.
Voir Utilitaires de construction dans le manuel de référence de GNU Guix, pour plus d’informations sur ces utilitaires.
La licence dans notre dernier exemple a besoin d’un préfixe à cause de la
manière dont le module licenses
a été importé dans le paquet, avec
#:use-module ((guix licenses) #:prefix license:)
. Le mécanisme
d’import de module de Guile (voir Using Guile Modules dans Guile
reference manual) permet de contrôler complètement l’espace de nom. Cela
évite les conflits entre, disons, la variable ‘zlib’ de
‘licenses.scm’ (un licence) et la variable ‘zlib’ de
‘compression.scm’ (un paquet).
Suivant: Autres systèmes de construction, Précédent: Configuration, Monter: Didacticiel d’empaquetage [Table des matières][Index]