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


8.7 Utilitaires de construction

Dès que vous commencerez à écrire des définitions de paquets non triviales (voir Définition des paquets) ou d’autres actions de compilation (voir G-Expressions), vous commencerez probablement à chercher des fonctions auxiliaires pour les actions « de type shell » — créer des répertoires, copier et supprimer des fichiers de manière récursive, manipuler les phases de compilation, etc. Le module (guix build utils) fournit de telles procédures utilitaires.

La plupart des systèmes de construction chargent (guix build utils) (voir Systèmes de construction). Ainsi, lorsque vous écrivez des phases de construction personnalisées pour vos définitions de paquets, vous pouvez généralement supposer que ces procédures sont disponibles.

Lorsque vous écrivez des G-expressions, vous pouvez importer (guix build utils) « côté construction » en utilisant with-imported-modules et ensuite le rendre disponible avec la forme use-modules (voir Using Guile Modules dans GNU Guile Reference Manual) :

(with-imported-modules '((guix build utils))  ;on l'importe
  (computed-file "empty-tree"
                 #~(begin
                     ;; On le rend disponible.
                     (use-modules (guix build utils))

                     ;; On utilise sa procédure « mkdir-p » sans souci.
                     (mkdir-p (string-append #$output "/a/b/c")))))

Le reste de cette section est la référence pour la plupart des procédures auxiliaires fournies par (guix build utils).

8.7.1 Traitement des noms de fichiers du dépôt

Cette section documente les procédures de traitement des noms de fichier du dépôt.

Procédure Scheme : %store-directory

Renvoie le nom de répertoire du dépôt.

Procédure Scheme : store-file-name? fichier

Renvoie vrai si fichier est dans le dépôt.

Procédure Scheme : strip-store-file-name fichier

Enlevez le /gnu/store et le hash de fichier, un nom de fichier du dépôt. Le résultat est généralement une chaîne "paquet-version".

Procédure Scheme : package-name->name+version nom

Si l’on passe nom, un nom de paquet comme "toto-0.9.1b", on obtient deux valeurs : "toto" et "0.9.1b". Lorsque la partie version est indisponible, nom et #f sont renvoyés. Le premier trait d’union suivi d’un chiffre est considéré comme introduisant la partie version.

8.7.2 Types de fichier

Les procédures ci-dessous portent sur les fichiers et les types de fichiers.

Procédure Scheme : directory-exists? dir

Renvoie #t si dir existe et est un répertoire.

Procédure Scheme : executable-file? fichier

Renvoie #t si fichier existe et est exécutable.

Renvoie #t si fichier est un lien symbolique (un « symlink »).

Procédure Scheme : elf-file? fichier
Procédure Scheme : ar-file? fichier
Procédure Scheme : gzip-file? fichier

Renvoie #t si fichier est, respectivement, un fichier ELF, une archive ar (telle qu’une bibliothèque statique .a), ou un fichier gzip.

Procédure Scheme : reset-gzip-timestamp fichier [#:keep-mtime? #t]

Si fichier est un fichier gzip, réinitialise son horodatage intégré (comme avec gzip --no-name) et renvoie vrai. Sinon, renvoie #f. Lorsque keep-mtime? est vrai, conserve la date de modification de fichier.

8.7.3 Manipulation de fichiers

Les procédures et macros suivantes permettent de créer, modifier et supprimer des fichiers. Elles offrent des fonctionnalités comparables à celles des utilitaires shell courants tels que mkdir -p, cp -r, rm -r et sed. Elles complètent l’interface de système de fichiers étendue, mais de bas niveau, de Guile (voir POSIX dans GNU Guile Reference Manual).

Syntaxe Scheme : with-directory-excursion répertoire corps

Exécute corps avec répertoire comme répertoire actuel du processus.

En gros, cette macro change le répertoire actuel en répertoire avant d’évaluer corps, en utilisant chdir. (voir Processus dans Manuel de référence de GNU Guile). Elle revient au répertoire initial à la sortie de la portée dynamique de corps, qu’il s’agisse d’un retour de procédure normal ou d’une sortie non locale telle qu’une exception.

Procédure Scheme : mkdir-p dir

Crée le répertoire dir et tous ses ancètres.

Procédure Scheme : install-file fichier répertoire

Crée répertoire s’il n’existe pas et y copie fichier sous le même nom.

Procédure Scheme : make-file-writable fichier

Rend fichier inscriptible pour son propriétaire.

Procédure Scheme : copy-recursively source destination [#:log (current-output-port)] [#:follow-symlinks? #f]  [#:copy-file

copy-file] [#:keep-mtime? #f] [#:keep-permissions? #t] Copy source directory to destination. Follow symlinks if follow-symlinks? is true; otherwise, just preserve them. Call copy-file to copy regular files. When keep-mtime? is true, keep the modification time of the files in source on those of destination. When keep-permissions? is true, preserve file permissions. Write verbose output to the log port.

Procédure Scheme : delete-file-recursively dir [#:follow-mounts? #f]

Efface récursivement dir, comme rm -rf, sans les liens symboliques suivants. Ne suit pas non plus les points de montage, à moins que follow-mounts? ne soit vrai. Rapporte mais ignore les erreurs.

Syntaxe Scheme : substitute* fichier ((regexp match-var…) body…) …

Remplace regexp dans fichier par la chaîne renvoyée par body. body est évalué avec chaque match-var lié à la sous-expression de regexp positionnelle correspondante. Par exemple :

(substitute* file
  (("hello")
   "good morning\n")
  (("toto([a-z]+)titi(.*)$" all letters end)
   (string-append "tata" letters end)))

Ici, chaque fois qu’une ligne de fichier contient hello, elle est remplacée par good morning. Chaque fois qu’une ligne de fichier correspond à la deuxième regexp, all est lié à la correspondance complète, letters est lié à la première sous-expression, et end est lié à la dernière.

Lorsque l’une des match-var est _, aucune variable n’est liée à la sous-chaîne de correspondance associée.

Autrement, fichier peut être une liste de noms de fichier, auquel cas ils sont tous sujets aux substitutions.

Faites attention à l’utilisation du $ pour faire correspondre la fin d’une ligne ; à lui seul, il ne correspondra pas à la fin d’une nouvelle ligne.

8.7.4 Recherche de fichiers

Cette section documente les procédures pour chercher et filtrer des fichiers.

Procédure Scheme : file-name-predicate regexp

Renvoie un prédicat qui devient vrai lorsqu’on lui passe un nom de fichier dont le nom de base correspond à regexp.

Procédure Scheme : find-files dir [pred] [#:stat lstat] [#:directories ? #f] [#:fail-on-error ? #f]

Renvoie la liste des fichiers triés lexicographiquement sous dir pour lesquels pred renvoie « vrai ». Deux arguments sont passés à pred : le nom absolu du fichier et son tampon stat ; le prédicat par défaut renvoie toujours « vrai ». pred peut également être une expression régulière, auquel cas elle est équivalente à (file-name-predicate pred). stat est utilisé pour obtenir des informations sur les fichiers ; l’utilisation de lstat signifie que les liens symboliques ne sont pas suivis. Si directories? est vrai, alors les répertoires seront également inclus. Si fail-on-error? est vrai, il lance une exception en cas d’erreur.

Voici quelques exemples où nous supposons que le répertoire actuel est la racine de l’arborescence des sources de Guix :

;; Liste tous les fichiers normaux dans le répertoire actuel.
(find-files ".")
 ("./.dir-locals.el" "./.gitignore" )

;; Liste tous les fichiers .scm sous gnu/services.
(find-files "gnu/services" "\\.scm$")
 ("gnu/services/admin.scm" "gnu/services/audio.scm" )

;; Liste les fichiers ar dans le répertoire actuel.
(find-files "." (lambda (file stat) (ar-file? file)))
 ("./libformat.a" "./libstore.a" )
Procédure Scheme : which programme

Renvoie le nom complet du fichier pour programme tel qu’il se trouve dans $PATH, ou #f si programme n’a pas pu être trouvé.

Scheme Procedure : search-input-file inputs name
Scheme Procedure : search-input-directory inputs name

Return the complete file name for name as found in inputs; search-input-file searches for a regular file and search-input-directory searches for a directory. If name could not be found, an exception is raised.

Here, inputs must be an association list like inputs and native-inputs as available to build phases (voir Phases de construction).

Here is a (simplified) example of how search-input-file is used in a build phase of the wireguard-tools package:

(add-after 'install 'wrap-wg-quick
  (lambda* (#:key inputs outputs #:allow-other-keys)
    (let ((coreutils (string-append (assoc-ref inputs "coreutils")
                                    "/bin")))
      (wrap-program (search-input-file outputs "bin/wg-quick")
        #:sh (search-input-file inputs "bin/bash")
        `("PATH" ":" prefix ,(list coreutils))))))

8.7.5 Program Invocation

You’ll find handy procedures to spawn processes in this module, essentially convenient wrappers around Guile’s system* (voir system* dans GNU Guile Reference Manual).

Scheme Procedure : invoke program args

Invoke program with the given args. Raise an &invoke-error exception if the exit code is non-zero; otherwise return #t.

The advantage compared to system* is that you do not need to check the return value. This reduces boilerplate in shell-script-like snippets for instance in package build phases.

Scheme Procedure : invoke-error? c

Return true if c is an &invoke-error condition.

Scheme Procedure : invoke-error-program c
Scheme Procedure : invoke-error-arguments c
Scheme Procedure : invoke-error-exit-status c
Scheme Procedure : invoke-error-term-signal c
Scheme Procedure : invoke-error-stop-signal c

Access specific fields of c, an &invoke-error condition.

Scheme Procedure : report-invoke-error c [port]

Report to port (by default the current error port) about c, an &invoke-error condition, in a human-friendly way.

Typical usage would look like this:

(use-modules (srfi srfi-34) ;for 'guard'
             (guix build utils))

(guard (c ((invoke-error? c)
           (report-invoke-error c)))
  (invoke "date" "--imaginary-option"))

-| command "date" "--imaginary-option" failed with status 1
Scheme Procedure : invoke/quiet program args

Invoke program with args and capture program’s standard output and standard error. If program succeeds, print nothing and return the unspecified value; otherwise, raise a &message error condition that includes the status code and the output of program.

Here’s an example:

(use-modules (srfi srfi-34) ;for 'guard'
             (srfi srfi-35) ;for 'message-condition?'
             (guix build utils))

(guard (c ((message-condition? c)
           (display (condition-message c))))
  (invoke/quiet "date")  ;all is fine
  (invoke/quiet "date" "--imaginary-option"))

-| 'date --imaginary-option' exited with status 1; output follows:

    date: unrecognized option '--imaginary-option'
    Try 'date --help' for more information.

8.7.6 Phases de construction

Le (guix build utils) contient également des outils permettant de manipuler les phases de construction telles qu’elles sont utilisées par les systèmes de construction (voir Systèmes de construction). Les phases de construction sont représentées sous forme de listes d’associations ou « alists » (voir Association Lists dans Manuel de référence de GNU Guile) où chaque clé est un symbole désignant la phase et où la valeur associée est une procédure (voir Phases de construction).

Le noyau Guile et le module (srfi srfi-1) fournissent tous deux des outils pour manipuler les alignements. Le module (guix build utils) complète ces outils avec des outils écrits en tenant compte des phases de construction.

Syntaxe Scheme : modify-phases phases clause

Modifie phases séquentiellement selon chaque clause, qui peut avoir l’une des formes suivantes :

(delete old-phase-name)
(replace old-phase-name new-phase)
(add-before old-phase-name new-phase-name new-phase)
(add-after old-phase-name new-phase-name new-phase)

Où chaque phase-name ci-dessus est une expression s’évaluant en un symbole, et new-phase une expression évaluant à une procédure.

L’exemple ci-dessous est tiré de la définition du paquet grep. Il ajoute une phase à exécuter après la phase install, appelée fix-egrep-and-fgrep. Cette phase est une procédure (lambda* pour les procédures anonymes) qui prend un paramètre nommé #:outputs et ignore les paramètres nommés supplémentaires (voir Optional Arguments dans GNU Guile Reference Manual, pour en savoir plus sur lambda* et les arguments optionnels et nommés). La phase utilise substitute* pour modifier les scripts egrep et fgrep installés afin qu’ils se réfèrent à grep par son nom de fichier absolu :

(modify-phases %standard-phases
  (add-after 'install 'fix-egrep-and-fgrep
    ;; Patch 'egrep' and 'fgrep' to execute 'grep' via its
    ;; absolute file name instead of searching for it in $PATH.
    (lambda* (#:key outputs #:allow-other-keys)
      (let* ((out (assoc-ref outputs "out"))
             (bin (string-append out "/bin")))
        (substitute* (list (string-append bin "/egrep")
                           (string-append bin "/fgrep"))
          (("^exec grep")
           (string-append "exec " bin "/grep")))))))

Dans l’exemple ci-dessous, les phases sont modifiées de deux façons : la phase standard configure est supprimée, vraisemblablement parce que le paquet n’a pas de script configure ou quelque chose de similaire, et la phase par défaut install est remplacée par une phase qui copie manuellement les fichiers exécutables à installer :

(modify-phases %standard-phases
  (delete 'configure)      ;no 'configure' script
  (replace 'install
    (lambda* (#:key outputs #:allow-other-keys)
      ;; The package's Makefile doesn't provide an "install"
      ;; rule so do it by ourselves.
      (let ((bin (string-append (assoc-ref outputs "out")
                                "/bin")))
        (install-file "footswitch" bin)
        (install-file "scythe" bin)))))

8.7.7 Wrappers

It is not unusual for a command to require certain environment variables to be set for proper functioning, typically search paths (voir Chemins de recherche). Failing to do that, the command might fail to find files or other commands it relies on, or it might pick the “wrong” ones—depending on the environment in which it runs. Examples include:

For a package writer, the goal is to make sure commands always work the same rather than depend on some external settings. One way to achieve that is to wrap commands in a thin script that sets those environment variables, thereby ensuring that those run-time dependencies are always found. The wrapper would be used to set PATH, GUILE_LOAD_PATH, or QT_PLUGIN_PATH in the examples above.

To ease that task, the (guix build utils) module provides a couple of helpers to wrap commands.

Scheme Procedure : wrap-program program [#:sh sh] [#:rest variables] Make a wrapper for program.

variables should look like this:

'(variable delimiter position list-of-directories)

where delimiter is optional. : will be used if delimiter is not given.

For example, this call:

(wrap-program "foo"
              '("PATH" ":" = ("/gnu/.../bar/bin"))
              '("CERT_PATH" suffix ("/gnu/.../baz/certs"
                                    "/qux/certs")))

will copy foo to .foo-real and create the file foo with the following contents:

#!location/of/bin/bash
export PATH="/gnu/.../bar/bin"
export CERT_PATH="$CERT_PATH${CERT_PATH:+:}/gnu/.../baz/certs:/qux/certs"
exec -a $0 location/of/.foo-real "$@"

If program has previously been wrapped by wrap-program, the wrapper is extended with definitions for variables. If it is not, sh will be used as the interpreter.

Scheme Procedure : wrap-script program [#:guile guile] [#:rest variables] Wrap the script program

such that variables are set first. The format of variables is the same as in the wrap-program procedure. This procedure differs from wrap-program in that it does not create a separate shell script. Instead, program is modified directly by prepending a Guile script, which is interpreted as a comment in the script’s language.

Special encoding comments as supported by Python are recreated on the second line.

Note that this procedure can only be used once per file as Guile scripts are not supported.


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