Suivant: Invoquer guix repl
, Précédent: La monade du dépôt, Monter: Interface de programmation [Table des matières][Index]
On a donc des « dérivations » qui représentent une séquence d’actions de
construction à effectuer pour produire un élément du dépôt
(voir Dérivations). Ces actions de construction sont effectuées
lorsqu’on demande au démon de construire effectivement les dérivations ;
elles sont lancées par le démon dans un conteneur (voir Invoquer guix-daemon
).
Ça ne devrait pas vous surprendre, mais nous aimons écrire ces actions de
construction en Scheme. Lorsqu’on fait ça, on fini avec deux strates
de code Scheme24 : le « code hôte » — le code qui définit
les paquets, parle au démon, etc. — et le « code côté construction » — le
code qui effectue effectivement les actions de construction, comme créer des
répertoires, invoquer make
, etc. (voir Phases de construction).
Pour décrire une dérivation et ses actions de construction, on a typiquement
besoin d’intégrer le code de construction dans le code hôte. Ça revient à
manipuler le code de construction comme de la donnée, et l’homoiconicité de
Scheme — le code a une représentation directe en tant que donnée — est très
utile pour cela. Mais on a besoin de plus que le mécanisme de
quasiquote
en Scheme pour construire des expressions de construction.
Le module (guix gexp)
implémente les G-expressions, une forme
de S-expression adaptée aux expressions de construction. Les G-expression,
ou gexps, consistent en gros en trois formes syntaxiques :
gexp
, ungexp
et ungexp-splicing
(ou plus simplement :
#~
, #$
et #$@
), qui sont comparable à
quasiquote
, unquote
et unquote-splicing
respectivement
(voir quasiquote
dans GNU Guile Reference
Manual). Cependant il y a des différences majeures :
Ce mécanisme n’est pas limité aux paquets et aux objets de dérivation : Des
compilateurs capables d’ « abaisser » d’autres objets de haut niveau
vers des dérivations ou des fichiers dans le dépôt peuvent être définis, de
sorte que ces objets peuvent également être insérés dans des gexps. Par
exemple, un type utile d’objets de haut niveau pouvant être insérés dans un
gexp est celui des « objets de type fichier », qui permettent d’ajouter
facilement des fichiers au dépôt et d’y faire référence dans des dérivations
et autres (voir local-file
et plain-file
ci-dessous).
Pour illustrer cette idée, voici un exemple de gexp :
(define build-exp
#~(begin
(mkdir #$output)
(chdir #$output)
(symlink (string-append #$coreutils "/bin/ls")
"list-files")))
Cette gexp peut être passée à gexp->derivation
; on obtient une
dérivation qui construit un répertoire contenant exactement un lien
symbolique à /gnu/store/…-coreutils-8.22/bin/ls :
(gexp->derivation "the-thing" build-exp)
Comme on pourrait s’y attendre, la chaîne
"/gnu/store/…-coreutils-8.22"
est substituée à la place de la
référence au paquet coreutils dans le code de construction final, et
coreutils est automatiquement devenu une entrée de la dérivation. De
même, #$output
(équivalent à (ungexp output)
) est remplacé par
une chaîne de caractères contenant le nom du répertoire de la sortie de la
dérivation.
Dans le contexte d’une compilation croisée, il est utile de distinguer entre
des références à la construction native d’un paquet — qui peut être
lancé par l’hôte — et des références à la construction croisée d’un paquet.
Pour cela, #+
joue le même rôle que #$
, mais référence une
construction native d’un paquet :
(gexp->derivation "vi"
#~(begin
(mkdir #$output)
(mkdir (string-append #$output "/bin"))
(system* (string-append #+coreutils "/bin/ln")
"-s"
(string-append #$emacs "/bin/emacs")
(string-append #$output "/bin/vi")))
#:target "aarch64-linux-gnu")
Dans l’exemple ci-dessus, la construction native de coreutils est
utilisée, pour que ln
puisse effectivement être lancé sur l’hôte ;
mais ensuite la construction croisée d’emacs est utilisée.
Une autre fonctionnalité, ce sont les modules importés : parfois vous
voudriez pouvoir utiliser certains modules Guile de « l’environnement hôte »
dans la gexp, donc ces modules devraient être importés dans «
l’environnement de construction ». La forme with-imported-modules
vous permet d’exprimer ça :
(let ((build (with-imported-modules '((guix build utils))
#~(begin
(use-modules (guix build utils))
(mkdir-p (string-append #$output "/bin"))))))
(gexp->derivation "empty-dir"
#~(begin
#$build
(display "success!\n")
#t)))
Dans cet exemple, le module (guix build utils)
est automatiquement
récupéré dans l’environnement de construction isolé de notre gexp, pour que
(use-modules (guix build utils))
fonctionne comme on s’y attendrait.
Typiquement, vous voudriez que la closure complète du module soit
importé — c.-à-d. le module lui-même et tous les modules dont il dépend —
plutôt que seulement le module ; sinon, une tentative de chargement du
module échouera à cause des modules dépendants manquants. La procédure
source-module-closure
calcule la closure d’un module en cherchant
dans ses en-têtes sources, ce qui est pratique dans ce cas :
(use-modules (guix modules)) ;pour 'source-module-closure' (with-imported-modules (source-module-closure '((guix build utils) (gnu build image))) (gexp->derivation "something-with-vms" #~(begin (use-modules (guix build utils) (gnu build image)) …)))
Dans la même idée, parfois vous pouvez souhaiter importer non seulement des
modules en Scheme pur, mais aussi des « extensions » comme des liaisons
Guile de bibliothèques C ou d’autres paquet « complets ». Disons que vous
voulez utiliser le paquet guile-json
du côté de la construction,
voici comme procéder :
(use-modules (gnu packages guile)) ;pour 'guile-json' (with-extensions (list guile-json) (gexp->derivation "something-with-json" #~(begin (use-modules (json)) …)))
La forme syntaxique pour construire des gexps est résumée ci-dessous.
Renvoie une G-expression contenant exp. exp peut contenir une ou plusieurs de ces formes :
#$obj
(ungexp obj)
Introduit une référence à obj. obj peut être d’un des types
supportés, par exemple un paquet ou une dérivation, auquel cas la forme
ungexp
est remplacée par le nom de fichier de sa sortie — p. ex.
"/gnu/store/…-coreutils-8.22
.
Si boj est une liste, elle est traversée et les références aux objets supportés sont substitués de manière similaire.
Si obj est une autre gexp, son contenu est inséré et ses dépendances sont ajoutées à celle de la gexp qui l’entoure.
Si obj est un autre type d’objet, il est inséré tel quel.
#$obj:output
(ungexp obj output)
Cette forme est similaire à la précédente, mais se réfère explicitement à la sortie output de l’objet obj — c’est utile lorsque obj produit plusieurs sorties (voir Des paquets avec plusieurs résultats).
#+obj
#+obj:output
(ungexp-native obj)
(ungexp-native obj output)
Comme ungexp
, mais produit une référence à la construction
native de obj lorsqu’elle est utilisée dans une compilation
croisée.
#$output[:output]
(ungexp output [output])
Insère une référence à la sortie output de la dérivation, ou à la sortie principale lorsque output est omis.
Cela ne fait du sens que pour les gexps passées à gexp->derivation
.
#$@lst
(ungexp-splicing lst)
Comme au dessus, mais recolle (splice) le contenu de lst dans la liste qui la contient.
#+@lst
(ungexp-native-splicing lst)
Comme au dessus, mais se réfère à la construction native des objets listés dans lst.
Les G-expressions créées par gexp
ou #~
sont des objets
d’exécution du type gexp?
(voir ci-dessous).
Marque les gexps définies dans body… comme requérant modules dans leur environnement d’exécution.
Chaque élément dans module peut être le nom d’un module, comme
(guix build utils)
ou le nom d’un module suivi d’une flèche, suivie
d’un objet simili-fichier :
`((guix build utils) (guix gcrypt) ((guix config) => ,(scheme-file "config.scm" #~(define-module …))))
Dans l’exemple au dessus, les deux premiers modules sont récupérés dans le chemin de recherche, et le dernier est créé à partir d’un objet simili-fichier.
Cette forme a une portée lexicale : elle a un effet sur les gexp directement définies dans body…, mais pas sur celles définies dans des procédures appelées par body….
Marque les gexps définies dans body… comme requérant
extensions dans leur environnement de construction et d’exécution.
extensions est typiquement une liste d’objets paquets comme définis
dans le module (gnu packages guile)
.
Concrètement, les paquets listés dans extensions sont ajoutés au chemin de chargement lors de la compilation des modules importés dans body… ; ils sont aussi ajoutés au chemin de chargement de la gexp renvoyée par body….
Renvoie #t
si obj est une G-expression.
Les G-expressions sont conçues pour être écrites sur le disque, soit en tant que code pour construire une dérivation, soit en tant que fichier normal dans le dépôt. Les procédures monadiques suivantes vous permettent de faire cela (voir La monade du dépôt, pour plus d’information sur les monads).
%load-path
] [#:effective-version "2. 2"] [#:references-graphs #f] [#:allowed-references #f] [#:disallowed-references #f] [#:leaked-env-vars #f] [#:script-name (string-append name "-builder")] [#:deprecation-warnings #f] [#:local-build ? #f] [#:substituable ? #t] [#:properties '()] [#:guile-for-build #f]Renvoie une dérivation name qui exécute exp (un gexp) avec guile-for-build (une dérivation) sur system ; exp est stocké dans un fichier appelé script-name. Lorsque target est vrai, il est utilisé comme le triplet cible de compilation croisée pour les paquets auxquels exp fait référence.
modules est devenu obsolète en faveur de
with-imported-modules
. Sa signification est de rendre modules
disponibles dans le contexte d’évaluation de exp ; modules est
une liste de noms de modules Guile qui sont cherchés dans module-path
pour les copier dans le dépôt, les compiler et les rendre disponibles dans
le chemin de chargement pendant l’exécution de exp — p. ex.
((guix build utils) (guix build gnu-build-system))
.
effective-version détermine la chaîne à utiliser lors d’ajout
d’extensions de exp (voir with-extensions
) au chemin de
recherche — p. ex. "2.2"
.
graft? détermine si les paquets référencés par exp devraient être greffés si possible.
Lorsque references-graphs est vrai, il doit s’agir d’une liste de tuples de la forme suivante :
(file-name package) (file-name package output) (file-name derivation) (file-name derivation output) (file-name store-item)
La partie droite des éléments de references-graphs est automatiquement transformée en une entrée du processus de construction exp. Dans l’environnement de construction, chaque file-name contient le graphe des références de l’élément correspondant, dans un format texte simple.
allowed-references doit soit être #f
, soit une liste de noms de
sorties ou de paquets. Dans ce dernier cas, la liste dénote les éléments du
dépôt auxquels le résultat a le droit de faire référence. Toute référence à
un autre élément du dépôt conduira à une erreur à la construction. Comme
pour disallowed-references, qui peut lister des éléments qui ne
doivent pas être référencés par les sorties.
deprecation-warnings détermine s’il faut afficher les avertissement
d’obsolescence à la compilation de modules. Il peut valoir #f
,
t
ou 'detailed
.
Les autres arguments sont les mêmes que pour derivation
(voir Dérivations).
Les procédures local-file
, plain-file
, computed-file
,
program-file
et scheme-file
ci-dessous renvoient des
objets simili-fichiers. C’est-à-dire, lorsqu’ils sont unquotés dans
une G-expression, ces objets donnent un fichier dans le dépôt. Considérez
cette G-expression :
#~(system* #$(file-append glibc "/sbin/nscd") "-f" #$(local-file "/tmp/my-nscd.conf"))
Ici, l’effet est « d’internaliser » /tmp/my-nscd.conf en le copiant
dans le dépôt. Une fois étendu, par exemple via gexp->derivation
, la
G-expression se réfère à cette copie dans /gnu/store ; ainsi,
modifier ou supprimer le fichier dans /tmp n’a aucun effet sur ce que
fait la G-expression. plain-file
peut être utilisé de la même
manière ; elle est seulement différente par le fait que le contenu du
fichier est passé directement par une chaîne de caractères.
Renvoie un objet représentant le fichier local file à ajouter au magasin ; cet objet peut être utilisé dans un gexp. Si file est une chaîne littérale désignant un nom de fichier relatif, il est recherché par rapport au fichier source où il apparaît ; si file n’est pas une chaîne littérale, il est recherché par rapport au répertoire de travail courant au moment de l’exécution. file sera ajouté au dépôt sous name–par défaut le nom de base de file.
Lorsque recursive? est vraie, le contenu de file est ajouté récursivement ; si file désigne un fichier simple et que recursive? est vrai, son contenu est ajouté et ses bits de permissions sont préservés.
Lorsque recursive? est vraie, appelle (select? file
stat)
pour chaque répertoire où file est le nom de fichier
absolu de l’entrée et stat est le résultat de lstat
; à
l’exception des entrées pour lesquelles select? ne renvoie pas vrai.
C’est la version déclarative de la procédure monadique interned-file
(voir interned-file
).
Renvoie un objet représentant un fichier texte nommé name avec pour contenu content (une chaîne de caractères ou un vecteur d’octets) à ajouter un dépôt.
C’est la version déclarative de text-file
.
Renvoie un objet représentant l’élément du dépôt name, un fichier ou
un répertoire calculé par gexp. Lorsque local-build? est vrai
(par défaut), la dérivation est construite localement. options est
une liste d’arguments supplémentaires à passer à gexp->derivation
.
C’est la version déclarative de gexp->derivation
.
Renvoie un script exécutable name qui exécute exp en utilisant guile, avec les modules importés de exp dans son chemin de recherche. Recherchez les modules de exp dans module-path.
L’exemple ci-dessous construit un script qui invoque simplement la commande
ls
:
(use-modules (guix gexp) (gnu packages base)) (gexp->script "list-files" #~(execl #$(file-append coreutils "/bin/ls") "ls"))
Lorsqu’elle est « lancée » à travers le dépôt (voir run-with-store
), on obtient une dérivation qui produit une fichier
exécutable /gnu/store/…-list-files qui ressemble à :
#!/gnu/store/…-guile-2.0.11/bin/guile -ds !# (execl "/gnu/store/…-coreutils-8.22"/bin/ls" "ls")
Renvoie un objet représentant un élément du dépôt name qui lance gexp. guile est le paquet Guile à utiliser pour exécuter le script. Les modules importés par gexp sont recherchés dans module-path.
C’est la version déclarative de gexp->script
.
Renvoie une dérivation qui construit un fichier name contenant exp. Lorsque splice? est vrai, exp est considéré comme une liste d’expressions qui seront splicée dans le fichier qui en résulte.
Lorsque set-load-path? est vrai, émet du code dans le fichier de
résultat pour initialiser %load-path
et %load-compiled-path
pour prendre en compte les modules importés de exp. Les modules de
exp sont trouvés dans module-path.
Le fichier qui en résulte retient les références à toutes les dépendances de exp ou un sous-ensemble.
Renvoie un objet représentant le fichier Scheme name qui contient exp.
C’est la version déclarative de gexp->file
.
Renvoie une valeur monadique qui construit un ficher texte contenant text. text peut lister, en plus de chaînes de caractères, des objet de n’importe quel type qui peut être utilisé dans une gexp : des paquets, des dérivations, des fichiers objet locaux, etc. Le fichier du dépôt qui en résulte en retient toutes les références.
Cette variante devrait être préférée à text-file
lorsque vous
souhaitez créer des fichiers qui référencent le dépôt. Cela est le cas
typiquement lorsque vous construisez un fichier de configuration qui
contient des noms de fichiers du dépôt, comme ceci :
(define (profile.sh)
;; Renvoie le nom d'un script shell dans le dépôt qui initialise
;; la variable d'environnement « PATH ».
(text-file* "profile.sh"
"export PATH=" coreutils "/bin:"
grep "/bin:" sed "/bin\n"))
Dans cet exemple, le fichier /gnu/store/…-profile.sh qui en résulte référence coreutils, grep et sed, ce qui les empêche d’être glanés tant que le script est accessible.
Renvoie un objet représentant le fichier du dépôt name contenant text. text est une séquence de chaînes de caractères et de fichiers simili-objets, comme dans :
(mixed-text-file "profile"
"export PATH=" coreutils "/bin:" grep "/bin")
C’est la version déclarative de text-file*
.
Renvoie un <computed-file>
qui construit un répertoire qui contient
tous les fichiers de files. Chaque élément de files doit être
une paire où le premier élément est le nom de fichier à utiliser dans le
nouveau répertoire et le second élément est une gexp dénotant le fichier
cible. Voici un exemple :
(file-union "etc"
`(("hosts" ,(plain-file "hosts"
"127.0.0.1 localhost"))
("bashrc" ,(plain-file "bashrc"
"alias ls='ls --color=auto'"))))
Cela crée un répertoire etc
contenant ces deux fichiers.
Renvoie un répertoire qui est l’union de things, où things est une liste d’objets simili-fichiers qui dénotent des répertoires. Par exemple :
(directory-union "guile+emacs" (list guile emacs))
crée un répertoire qui est l’union des paquets guile
et emacs
.
Renvoie un objet simili-fichier qui correspond à la concaténation de obj et suffix où obj est un objet abaissable et chaque suffix est une chaîne de caractères.
Par exemple, considérez cette gexp :
(gexp->script "run-uname"
#~(system* #$(file-append coreutils
"/bin/uname")))
On peut obtenir le même effet avec :
(gexp->script "run-uname"
#~(system* (string-append #$coreutils
"/bin/uname")))
Il y a une différence cependant : dans le cas file-append
, le script
qui en résulte contient le nom de fichier absolu comme une chaîne de
caractère alors que dans le deuxième cas, le script contient une expression
(string-append …)
pour construire le nom de fichier à
l’exécution.
Lier system au système actuellement visé—par exemple,
"x86_64-linux"
—dans body.
Dans le second cas, lier également target à la cible de compilation
croisée actuelle–un triplet GNU tel que "arm-linux-gnueabihf"
—ou
#f
si nous ne faisons pas de compilation croisée.
let-system
est utile occasionellement dans le cas où l’objet raccordé
au gexp dépend de la cible sur le système cible, comme dans cet exemple :
#~(system* #+(let-system system (cond ((string-prefix? "armhf-" system) (file-append qemu "/bin/qemu-system-arm")) ((string-prefix? "x86_64-" system) (file-append qemu "/bin/qemu-system-x86_64")) (else (error "dunno!")))) "-net" "user" #$image)
Cette macro est similaire à la forme parameterize
pour les
paramètres liés dynamiquement (voir Parameters dans GNU Guile
Reference Manual). La principale différence est qu’il prend effet lorsque
l’objet de type fichier renvoyé par exp est abaissé à un élément de
dérivation ou de stockage.
Une utilisation typique de with-parameters
consiste à forcer le
système en vigueur pour un objet donné :
(with-parameters ((%current-system "i686-linux"))
coreutils)
L’exemple ci-dessus renvoie un objet qui correspond à la version i686 de
Coreutils, quelle que soit la valeur actuelle de %current-système
.
Bien sûr, en plus de gexps inclues dans le code « hôte », certains modules
contiennent des outils de construction. Pour savoir facilement qu’ils sont
à utiliser dans la strate de construction, ces modules sont gardés dans
l’espace de nom (guix build …)
.
En interne, les objets de haut-niveau sont abaissés, avec leur
compilateur, soit en des dérivations, soit en des objets du dépôt. Par
exemple, abaisser un paquet crée une dérivation, et abaisser un
plain-file
crée un élément du dépôt. Cela est effectué par la
procédure monadique lower-object
.
Renvoie comme valeur dans %store-monad
la dérivation ou l’élément de
dépôt correspondant à obj pour system, en effectuant une
compilation croisée pour target si target est vrai. obj
doit être un objet auquel est associé un compilateur gexp, tel qu’un
<package>
.
Il peut parfois être utile de convertir une G-expression en
S-expression. Par exemple, certains outils d’analyse statique de style (dits
« linters », voir Invoquer guix lint
) vérifient les phases de compilation
des paquets pour détecter des problèmes potentiels. Cette conversion peut
être réalisée avec cette procédure. Toutefois, certaines informations
peuvent être perdues au cours de l’opération. Plus spécifiquement, les
objets abaissables seront remplacés silencieusement par un objet arbitraire
– pour l’instant la liste (*approximate*)
, mais cela pourrait
changer.
Le terme de strate dans ce contexte a été inventé par Manuel Serrano et ses collègues dans le contexte de leurs travaux sur Hop. Oleg Kiselyov, qui a écrit des essais perspicaces ainsi que du code sur le sujet, utilise le terme de « mise en scène » pour ce genre de génération de code.
Suivant: Invoquer guix repl
, Précédent: La monade du dépôt, Monter: Interface de programmation [Table des matières][Index]