Suivant: , Précédent: , Monter: Interface de programmation   [Table des matières][Index]


8.3 Définition de variantes de paquets

L’une des choses intéressantes avec Guix est qu’à partir d’une définition, on peut facilement dériver des variantes du paquet — pour une version en amont différente, avec des dépendances différentes, des options de compilation différentes, etc. Certains de ces paquets personnalisés peuvent être définis directement par la ligne de commande (voir Options de transformation de paquets). Cette section décrit comment définir des variantes de paquets avec du code. Cela est utile dans les « manifestes » (voir Écrire un manifeste) et dans votre propre collection de paquets (voir Écrire de nouveaux de canaux), entre autres choses !

Comme nous en avons parlé plus tôt, les paquets sont des objets de première classe dans le langage Scheme. Le module (guix packages) fournit la construction package pour définir de nouveaux objets de paquets (voir Référence de package). La manière la plus simple de définir la variante d’un paquet est d’utiliser le mot-clé inherit avec package. Cela vous permet d’hériter la définition d’un paquet tout en redéfinissant les champs que vous voulez.

Par exemple, étant donné la variable hello, qui contient la définition d’une version récente de GNU Hello, voici comment définir une variante pour la version 2.2 (publiée en 2006, c’est vintage !) :

(use-modules (gnu packages base))    ;pour '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"))))))

L’exemple ci-dessus correspond à ce que font les options de transformation des paquets --with-version et --with-source. En substance, hello-2.2 préserve tous les champs de hello, sauf version et source, qu’il remplace. Remarquez que la variable hello originale est toujours là, dans le module (gnu packages base), et n’est pas modifiée. Lorsque vous définissez un paquet personnalisé comme ceci, vous ajoutez en fait une nouvelle définition de paquet ; la version originale est toujours disponible.

Vous pouvez aussi bien définir des variantes avec un ensemble de dépendances différent du paquet d’origine. Par exemple, le paquet gdb par défaut dépend de guile, mais comme c’est une dépendance facultative, vous pouvez définir une variante qu supprime cette dépendance de cette manière :

(use-modules (gnu packages gdb))   ; pour « gdb »

(define gdb-sans-guile
  (package
    (inherit gdb)
    (inputs (modify-inputs (package-inputs gdb)
              (delete "guile")))))

La forme modify-inputs ci-dessus supprime le paquet "guile" du champ inputs de gdb. La macro modify-inputs est une macro auxiliaire qui peut être utile si vous voulez supprimer, ajouter et remplacer des paquets en entrée.

Macro :modify-inputs inputs clauses

Modifie les entrées de paquet spécifiées, telles que renvoyées par package-inputs & co., suivant les clauses données. Chaque clause doit avoir l’une des formes suivantes :

(delete nom…)

Supprime des entrées les paquets avec les noms donnés (chaines de caractères).

(prepend paquet…)

Ajoute les paquets au début de la liste des entrées.

(append paquet…)

Ajoute les paquets à la fin de la liste des entrées.

(replace name replacement)

Replace the package called name with replacement.

L’exemple suivant supprime les entrées GMP et ACL de Coreutils et ajoute libcap au début de la liste des entrées :

(modify-inputs (package-inputs coreutils)
  (delete "gmp" "acl")
  (prepend libcap))

L’exemple suivant remplace le paquet guile des entrées de guile-redis avec guile-2.2 :

(modify-inputs (package-inputs guile-redis)
  (replace "guile" guile-2.2))

Le dernier type de clause est append, pour ajouter des entrées à la fin de la liste.

Dans certains cas, vous pouvez trouver cela utile d’écrire des fonctions (« procédures », pour Scheme) qui renvoie un paquet en fonction de certains paramètres. Par exemple, considérez la bibliothèque luasocket pour le langage de programmation lua. Nous voulons créer des paquets luasocket pour les versions majeures de Lua. Une manière de faire est de définir une procédure qui prend un paquet Lua et renvoie un paquet luasocket qui en dépend :

(define (make-lua-socket name lua)
  ;; Renvoie un paquet luasocket construit avec LUA.
  (package
    (name name)
    (version "3.0")
    ;; plusieurs champs ont été omis
    (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))

ici nous avons défini les paquets lua5.1-socket et lua5.2-socket en appelant make-lua-socket avec différents arguments. Voir Procedures dans GNU Guile Reference Manual, pour plus d’informations sur les procédures. Avoir des définitions publiques au premier niveau pour ces deux paquets signifie qu’ils sont disponibles depuis la ligne de commande (voir Modules de paquets).

Ce sont des variantes simples de paquets. Par simplicité, le module (guix transformations) fournit une interface de haut niveau qui correspond directement aux options de transformations de paquets plus sophistiquées (voir Options de transformation de paquets) :

Procédure :options->transformation opts

Renvoie une procédure qui, lorsqu’on lui passe un objet à construire (un paquet, une dérivation, etc), applique les transformations spécifiées par opts et renvoie les objets qui en résultent. opts doit être une liste de pairs de symboles et de chaine comme dans :

((with-branch . "guile-gcrypt=master")
 (without-tests . "libgcrypt"))

Chaque nom de symbole nomme une transformation et la chaine correspondante est un argument pour cette transformation.

Par exemple, un manifeste équivalent à cette commande :

guix build guix \
  --with-branch=guile-gcrypt=master \
  --with-debug-info=zlib

... ressemblerait à ceci :

(use-modules (guix transformations))

(define transform
  ;; La procédure de transformation des paquets.
  (options->transformation
   '((with-branch . "guile-gcrypt=master")
     (with-debug-info . "zlib"))))

(packages->manifest
 (list (transform (specification->package "guix"))))

La procédure options->transformation est pratique, mais elle n’est peut-être pas aussi flexible que vous pourriez le souhaiter. Comment est-elle implémentée ? Le lecteur attentif aura sans doute remarqué que la plupart des options de transformation des paquets va plus loin que les changements superficiels montrés dans les premiers exemples de cette sections : ils font de la réécriture d’entrées, où le graphe de dépendance d’un paquet est réécrit en remplaçant des entrées spécifiques par d’autres.

La procédure package-input-rewriting de (guix packages) implémente la réécriture du graphe de dépendance, pour remplacer des paquets dans le graphe.

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.

De manière facultative, nom-réécrit est une procédure à un argument qui prend le nom d’un paquet et renvoie son nouveau nom après l’avoir réécrit.

Regardez cet exemple :

(define libressl-instead-of-openssl
  ;; Cette procédure remplace OPENSSL par LIBRESSL,
  ;; récursivement.
  (package-input-rewriting `((,openssl . ,libressl))))

(define git-with-libressl
  (libressl-instead-of-openssl git))

Ici nous définissons d’abord une procédure de réécriture qui remplace openssl par libressl. Ensuite nous l’utilisons pour définir une variante du paquet git qui utilise libressl plutôt que openssl. cela est exactement ce que l’option en ligne de commande --with-input fait (voir --with-input).

La variante suivante de package-input-rewriting peut repérer les paquets à remplacer par nom à la place de leur identité.

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.

L’exemple ci-dessus pourrait être réécrit de cette manière :

(define libressl-instead-of-openssl
  ;; Remplace tous les paquets nommés « openssl » par LibreSSL.
  (package-input-rewriting/spec `(("openssl" . ,(const libressl)))))

Le différence clef est que, cette fois-ci, les paquets correspondent à la spécification et non à l’identité. En d’autres termes, tout paquet dans le graphe qui est appelé openssl sera remplacé.

Une procédure plus générique pour réécrire un graphe de dépendance d’un paquet est package-mapping : elle supporte n’importe quel changement dans les nœuds du graphe.

Procédure :package-mapping proc [cut?] [#:deep? #f]

Renvoie une procédure qui, pour un paquet donné, applique proc à tous les paquets dont il dépend et renvoie le paquet résultant. La procédure arrête la récursion lorsque cut? renvoie true pour un paquet donné. Lorsque deep? est vrai, proc est également appliqué aux entrées implicites.

Tips : Understanding what a variant really looks like can be difficult as one starts combining the tools shown above. There are several ways to inspect a package before attempting to build it that can prove handy:


Suivant: Écrire un manifeste, Précédent: Définition des paquets, Monter: Interface de programmation   [Table des matières][Index]