Next: , Previous: , Up: Interfaz programática   [Contents][Index]


8.7 Utilidades de construcción

En cuanto empiece a escribir definiciones de paquete no-triviales (see Definición de paquetes) u otras acciones de construcción (see Expresiones-G), es probable que empiece a buscar funciones auxiliares parecidas a las habituales en el intérprete de ordenes—creación de directorios, borrado y copia recursiva de archivos, manipulación de fases de construcción, etcétera. El módulo (guix build utils) proporciona dichos procedimientos auxiliares.

La mayoría de sistemas de construcción cargan (guix build utils) (see Sistemas de construcción). Por tanto, cuando construya fases de construcción personalizadas para sus definiciones de paquetes, habitualmente puede asumir que dichos procedimientos ya han sido incorporados al ámbito de ejecución.

Cuando escriba G-expressions, puede importar (guix build utils) en el “lado de la construcción” mediante el uso with-imported-modules e importandolos al ámbito actual con la forma sintáctica use-modules (see Using Guile Modules in GNU Guile Reference Manual):

(with-imported-modules '((guix build utils))  ; Se importa.
  (computed-file "empty-tree"
                 #~(begin
                     ;; Se añade al ámbito actual.
                     (use-modules (guix build utils))

                     ;; Se usa su procedimiento 'mkdir-p'.
                     (mkdir-p (string-append #$output "/a/b/c")))))

El resto de esta sección es la referencia de la mayoría de las procedimientos de utilidad proporcionados por (guix build utils).

8.7.1 Tratamiento de nombres de archivo del almacén

Esta sección documenta procedimientos para el manejo de nombres de archivo del almacén.

Procedimiento: %store-directory

Devuelve el nombre del directorio del almacén.

Procedimiento: store-file-name? archivo

Devuelve verdadero si archivo está en el almacén.

Procedimiento: strip-store-file-name archivo

Elimina /gnu/store y el hash de archivo, un nombre de archivo del almacén. El resultado es habitualmente una cadena "paquete-versión".

Procedimiento: package-name>name+version nombre

Cuando se proporciona nombre, un nombre de paquete como "foo-0.9.1b", devuelve dos valores: "foo" y "0.9.1b". Cuando la perte de la versión no está disponible, se devuelve nombre y #f. Se considera que el primer guión seguido de un dígito introduce la parte de la versión.

8.7.2 Tipos de archivo

Los siguientes procedimientos tratan con archivos y tipos de archivos.

Procedimiento: directory-exists? dir

Devuelve #t si dir existe y es un directorio.

Procedimiento: executable-file archivo

Devuelve #t si archivo existe y es ejecutable.

Devuelve #t si archivo es enlace simbólico (“symlink”).

Procedimiento: elf-file? archivo
Procedimiento: ar-file? archivo
Procedimiento: gzip-file? archivo

Devuelve #t si archivo es, respectivamente, un archivo ELF, un archivador ar (como una biblioteca estática .a), o un archivo comprimido con gzip.

Procedimiento: reset-gzip-timestamp archivo [#:keep-mtime? #t]

Si archivo es un archivo gzip, reinicia su marca de tiempo embebida (como con gzip --no-name) y devuelve un valor verdadero. En otro caso devuelve #f. Cuando keep-mtime? es verdadero, se preserva el tiempo de modificación del archivo.

8.7.3 Manipulación de archivos

Los siguientes procedimientos y macros sirven de ayuda en la creación, modificación y borrado de archivos. Proporcionan funcionalidades comparables con las herrambientas comunes del intérprete de órdenes como mkdir -p, cp -r, rm -r y sed. Sirven de complemento a la interfaz de sistema de archivos de Guile, la cual es extensa pero de bajo nivel (see POSIX in GNU Guile Reference Manual).

Macro: with-directory-excursion directorio cuerpo …

Ejecuta cuerpo con directorio como el directorio actual del proceso.

Esecialmente este macro cambia el directorio actual a directorio antes de evaluar cuerpo, usando chdir (see Processes in GNU Guile Reference Manual). Se vuelve al directorio inicial en cuanto se abandone el ámbito dinámico de cuerpo, ya sea a través de su finalización normal o de una salida no-local como pueda ser una excepción.

Procedimiento: mkdir-p dir

Crea el directorio dir y todos sus predecesores.

Procedimiento: install-file file directorio

Crea directorio si no existe y copia archivo allí con el mismo nombre.

Procedimiento: make-file-writable archivo

Activa el permiso de escritura en archivo para su propietaria.

Procedimiento: copy-recursively fuente destino [#:log (current-output-port)] [#:follow-symlinks? #f]  [#:copy-file

copy-file] [#:keep-mtime? #f] [#:keep-permissions? #t]  [#:select? (const #t)] Copy source directory to destination. Follow symlinks if follow-symlinks? is true; otherwise, just preserve them. Call copy-file to copy regular files. Call select?, taking two arguments, file and stat, for each entry in source, where file is the entry’s absolute file name and stat is the result of lstat (or stat if follow-symlinks? is true); exclude entries for which select? does not return true. 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.

Procedimiento: delete-file-recursively dir [#:follow-mounts? #f]

Borra dir recursivamente, como rm -rf, sin seguir los enlaces simbólicos. No atraviesa puntos de montaje tampoco, a no ser que follow-mounts? sea verdadero. Informa de los errores, pero no devuelve error por ellos.

Macro: substitute* archivo ((expreg var-encontrada…) cuerpo…) …

Sustituye expreg en archivo con la cadena que devuelve cuerpo. La evaluación de cuerpo se realiza con cada var-encontrada asociada con la subexpresión posicional correspondiente de la expresión regular. Por ejemplo:

(substitute* archivo
  (("hola")
   "buenos días\n")
  (("algo([a-z]+)otro(.*)$" todo letras fin)
   (string-append "cosa" letras fin)))

En este ejemplo, cada ver que una línea de archivo contiene hola, esto se sustituye por buenos días. Cada vez que una línea del archivo corresponde con la segunda expresión regular, todo se asocia con la cadena encontrada al completo, letras toma el valor de la primera sub-expresión, y fin se asocia con la última.

Cuando una de las var-encontrada es _, no se asocia ninguna variable con la correspondiente subcadena.

También puede proporcionarse una lista como archivo, en cuyo caso los nombres de archivo que contenga serán los que se sometan a las sustituciones.

Be careful about using $ to match the end of a line; by itself it won’t match the terminating newline of a line. For example, to match a whole line ending with a backslash, one needs a regex like "(.*)\\\\\n$".

8.7.4 Búsqueda de archivos

Esta sección documenta procedimientos de búsqueda y filtrado de archivos.

Procedimiento: file-name-predicate expreg

Devuelve un predicado que devuelve un valor verdadero cuando el nombre del archivo proporcionado sin la parte del directorio corresponde con expreg.

Procedimiento: find-files dir [pred] [#:stat lstat] [#:directories? #f] [#:fail-on-error? #f]

Devuelve una lista ordenada lexicográficamente de los archivos que se encuentran en dir para los cuales pred devuelve verdadero. Se le proporcionan dos parámetros a pred: la ruta absoluta del archivo y su búfer de stat; el predicado predeteminado siempre devuelve verdadero. pred también puede ser una expresión regular, en cuyo caso es equivalente a escribir (file-name-predicate pred). stat se usa para obtener información del archivo; el uso de lstat significa que no se siguen los enlaces simbólicos. Si directories? es verdadero se incluyen también los directorios. Si fail-on-error? es verdadero, se emite una excepción en caso de error.

Aquí se pueden encontrar algunos ejemplos en los que se asume que el directorio actual es la raíz del arbol de fuentes de Guix:

;; Enumera todos los archivos regulares en el directorio actual.
(find-files ".")
 ("./.dir-locals.el" "./.gitignore" )

;; Enumera todos los archivos .scm en gnu/services.
(find-files "gnu/services" "\\.scm$")
 ("gnu/services/admin.scm" "gnu/services/audio.scm" )

;; Enumera los archivos ar en el directorio actual.
(find-files "." (lambda (file stat) (ar-file? file)))
 ("./libformat.a" "./libstore.a" )
Procedimiento: which programa

Devuelve el nombre de archivo completo para programa tal y como se encuentra en $PATH, o #f si no se ha encontrado programa.

Procedure: search-input-file inputs name
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 (see Fases de construcción).

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* (see system* in GNU Guile Reference Manual).

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.

Procedure: invoke-error? c

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

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

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

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
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 Fases de construcción

(guix build utils) también contiene herramientas para la manipulación de las fases de construcción usadas por los sistemas de construcción (see Sistemas de construcción). Las fases de construcción se representan como listas asociativas o “alists” (see Association Lists in GNU Guile Reference Manual) donde cada clave es un símbolo que nombra a la fase, y el valor asociado es un procedimiento (see Fases de construcción).

Tanto el propio Guile como el módulo (srfi srfi-1) proporcionan herramientas para la manipulación de listas asociativas. El módulo (guix build utils) complementa estas con herramientas pensadas para las fases de construcción.

Macro: modify-phases fases cláusula…

Modifica fases de manera secuencial com cada indique cada cláusula, que puede tener una de las siguentes formas:

(delete nombre-fase)
(replace nombre-fase nueva-fase)
(add-before nombre-fase nombre-nueva-fase nueva-fase)
(add-after nombre-fase nombre-nueva-fase nueva-fase)

Donde cada nombre-fase y nombre-nueva-fase es una expresión que evalúa aun símbolo, y nueva-fase es una expresión que evalúa a un procedimiento.

El siguiente ejemplo se ha tomado de la definición del paquete grep. Añade una fase que se ejecuta tras la fase install, llamada fix-egrep-and-fgrep. Dicha fase es un procedimiento (lambda* genera procedimientos anónimos) que toma un parámetro de palabra clave #:outputs e ignora el resto (see Optional Arguments in GNU Guile Reference Manual para obtener más información sobre lambda* y los parámetros opcionales y de palabras clave). La fase usa substitute* para modificar los guiones egrep y fgrep instalados para que hagan referencia a grep con su ruta de archivo absoluta:

(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")))))))

En el siguiente ejemplo se modifican las fases de dos maneras: se elimina la fase estándar configure, posiblemente porque el paquete no tiene un guión configure ni nada similar, y la fase install predeterminada se sustituye por una que copia manualmente el archivo ejecutable que se debe instalar:

(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 (see Search Paths). 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.

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.

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.


Next: Search Paths, Previous: Fases de construcción, Up: Interfaz programática   [Contents][Index]