Next: Invocación de guix repl
, Previous: La mónada del almacén, Up: Interfaz programática [Contents][Index]
Por tanto tenemos “derivaciones”, que representan una secuencia de
acciones de construcción a realizar para producir un elemento en el almacén
(see Derivaciones). Estas acciones de construcción se llevan a cabo
cuando se solicita al daemon construir realmente la derivación; se ejecutan
por el daemon en un contenedor (see Invocación de guix-daemon
).
No debería ser ninguna sorpresa que nos guste escribir estas acciones de
construcción en Scheme. Cuando lo hacemos, terminamos con dos estratos
de código Scheme25: el “código anfitrión”—código que define
paquetes, habla al daemon, etc.—y el “código de construcción”—código
que realmente realiza las acciones de construcción, como la creación de
directorios, la invocación de make
, etcétera (see Fases de construcción).
Para describir una derivación y sus acciones de construcción, típicamente se
necesita embeber código de construcción dentro del código anfitrión. Se
resume en la manipulación de código de construcción como datos, y la
homoiconicidad de Scheme—el código tiene representación directa como
datos—es útil para ello. Pero necesitamos más que el mecanismo normal de
quasiquote
en Scheme para construir expresiones de construcción.
El módulo (guix gexp)
implementa las expresiones-G, una forma
de expresiones-S adaptada para expresiones de construcción. Las
expresiones-G, o gexps, consiste esencialmente en tres formas
sintácticas: gexp
, ungexp
y ungexp-splicing
(o
simplemente: #~
, #$
y #$@
), que son comparables a
quasiquote
, unquote
y unquote-splicing
, respectivamente
(see quasiquote
in GNU Guile Reference
Manual). No obstante, hay importantes diferencias:
Este mecanismo no se limita a objetos de paquete ni derivación: pueden
definirse compiladores capaces de “bajar el nivel” de otros objetos
de alto nivel a derivaciones o archivos en el almacén, de modo que esos
objetos puedan introducirse también en expresiones-G. Por ejemplo, un tipo
útil de objetos de alto nivel que pueden insertarse en una expresión-G son
los “objetos tipo-archivo”, los cuales facilitan la adición de archivos al
almacén y su referencia en derivaciones y demás (vea local-file
y
plain-file
más adelante).
Para ilustrar la idea, aquí está un ejemplo de expresión-G:
(define exp-construccion
#~(begin
(mkdir #$output)
(chdir #$output)
(symlink (string-append #$coreutils "/bin/ls")
"enumera-archivos")))
Esta expresión-G puede pasarse a gexp->derivation
; obtenemos una
derivación que construye un directorio que contiene exactamente un enlace
simbólico a /gnu/store/…-coreutils-8.22/bin/ls:
(gexp->derivation "la-cosa" exp-construccion)
Como se puede esperar, la cadena "/gnu/store/…-coreutils-8.22"
se sustituye por la referencia al paquete coreutils en el código de
construcción real, y coreutils se marca automáticamente como una
entrada a la derivación. Del mismo modo, #$output
(equivalente a
(ungexp output)
) se reemplaza por una cadena que contiene el nombre
del directorio de la salida de la derivación.
En un contexto de compilación cruzada, es útil distinguir entre referencias
a construcciones nativas del paquete—que pueden ejecutarse en el
sistema anfitrión—de referencias de compilaciones cruzadas de un
paquete. Para dicho fin, #+
tiene el mismo papel que #$
, pero
es una referencia a una construcción nativa del paquete:
(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")
En el ejemplo previo, se usa la construcción nativa de coreutils, de
modo que ln
pueda realmente ejecutarse en el anfitrión; pero se
hace referencia a la construcción de compilación cruzada de emacs.
Otra característica de las expresiones-G son los módulos importados: a
veces deseará ser capaz de usar determinados módulos Guile del “entorno
anfitrión” en la expresión-G, de modo que esos módulos deban ser importados
en el “entorno de construcción”. La forma with-imported-modules
le
permite expresarlo:
(let ((build (with-imported-modules '((guix build utils))
#~(begin
(use-modules (guix build utils))
(mkdir-p (string-append #$output "/bin"))))))
(gexp->derivation "directorio-vacio"
#~(begin
#$build
(display "éxito!\n")
#t)))
En este ejemplo, el módulo (guix build utils)
se incorpora
automáticamente dentro del entorno de construcción aislado de nuestra
expresión-G, de modo que (use-modules (guix build utils))
funciona
como se espera.
De manera habitual deseará que la clausura del módulo se importe—es
decir, el módulo en sí y todos los módulos de los que depende—en vez del
módulo únicamente; si no se hace, cualquier intento de uso del módulo
fallará porque faltan módulos dependientes. El procedimiento
source-module-closure
computa la clausura de un módulo mirando en las
cabeceras de sus archivos de fuentes, lo que es útil en este caso:
(use-modules (guix modules)) ;para '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)) …)))
De la misma manera, a veces deseará importar no únicamente módulos puros de
Scheme, pero también “extensiones” como enlaces Guile a bibliotecas C u
otros paquetes “completos”. Si, digamos, necesitase el paquete
guile-json
disponible en el lado de construcción, esta sería la forma
de hacerlo:
(use-modules (gnu packages guile)) ;para 'guile-json' (with-extensions (list guile-json) (gexp->derivation "algo-con-json" #~(begin (use-modules (json)) …)))
La forma sintáctica para construir expresiones-G se resume a continuación.
Devuelve una expresión-G que contiene exp. exp puede contener una o más de las siguientes formas:
#$obj
(ungexp obj)
Introduce una referencia a obj. obj puede tener uno de los tipos
permitidos, por ejemplo un paquete o derivación, en cuyo caso la forma
ungexp
se substituye por el nombre de archivo de su salida—por
ejemplo, "/gnu/store/…-coreutils-8.22
.
Si obj es una lista, se recorre y las referencias a objetos permitidos se substituyen de manera similar.
Si obj es otra expresión-G, su contenido se inserta y sus dependencias se añaden a aquellas de la expresión-G que la contiene.
Si obj es otro tipo de objeto, se inserta tal cual es.
#$obj:salida
(ungexp obj salida)
Como la forma previa, pero referenciando explícitamente la salida de obj—esto es útil cuando obj produce múltiples salidas (see Paquetes con múltiples salidas).
#+obj
#+obj:salida
(ungexp-native obj)
(ungexp-native obj salida)
Igual que ungexp
, pero produce una referencia a la construcción
nativa de obj cuando se usa en un contexto de compilación
cruzada.
#$output[:salida]
(ungexp output [salida])
Inserta una referencia a la salida de la derivación salida, o a la salida principal cuando salida se omite.
Esto únicamente tiene sentido para expresiones-G pasadas a
gexp->derivation
.
#$@lst
(ungexp-splicing lst)
Lo mismo que la forma previa, pero expande el contenido de la lista lst como parte de la lista que la contiene.
#+@lst
(ungexp-native-splicing lst)
Lo mismo que la forma previa, pero hace referencia a las construcciones nativas de los objetos listados en lst.
Las expresiones-G creadas por gexp
o #~
son objetos del tipo
gexp?
en tiempo de ejecución (véase a continuación).
Marca las expresiones-G definidas en el cuerpo… como si requiriesen módulos en su entorno de ejecución.
Cada elemento en módulos puede ser el nombre de un módulo, como
(guix build utils)
, o puede ser el nombre de un módulo, seguido de
una flecha, seguido de un objeto tipo-archivo:
`((guix build utils) (guix gcrypt) ((guix config) => ,(scheme-file "config.scm" #~(define-module …))))
En el ejemplo previo, los dos primeros módulos se toman de la ruta de búsqueda, y el último se crea desde el objeto tipo-archivo proporcionado.
Esta forma tiene ámbito léxico: tiene efecto en las expresiones-G definidas en cuerpo…, pero no en aquellas definidas, digamos, en procedimientos llamados por cuerpo….
Marca que las expresiones definidas en cuerpo… requieren
extensiones en su entorno de construcción y
ejecución. extensiones es típicamente una lista de objetos de paquetes
como los que se definen en el módulo (gnu packages guile)
.
De manera concreta, los paquetes listados en extensiones se añaden a la ruta de carga mientras se compilan los módulos importados en cuerpo…; también se añaden a la ruta de carga en la expresión-G devuelta por cuerpo….
Devuelve #t
si obj es una expresión-G.
Las expresiones-G están destinadas a escribirse en disco, tanto en código que construye alguna derivación, como en archivos planos en el almacén. Los procedimientos monádicos siguientes le permiten hacerlo (see La mónada del almacén, para más información sobre mónadas).
%load-path
] [#:effective-version "2.2"] [#:references-graphs #f] [#:allowed-references #f] [#:disallowed-references #f] [#:leaked-env-vars #f] [#:script-name (string-append nombre "-builder")] [#:deprecation-warnings #f] [#:local-build? #f] [#:substitutable? #t] [#:properties '()] [#:guile-for-build #f]Devuelve una derivación nombre que ejecuta exp (una expresión-G) con guile-for-build (una derivación) en el sistema system; exp se almacena en un archivo llamado script-name. Cuando target tiene valor verdadero, se usa como tripleta de compilación cruzada para paquetes a los que haga referencia exp.
modules está obsoleto en favor de with-imported-modules
. Su
significado es hacer que los módulos modules estén disponibles en el
contexto de evaluación de exp; modules es una lista de nombres
de módulos Guile buscados en module-path para ser copiados al almacén,
compilados y disponibles en la ruta de carga durante la ejecución de
exp—por ejemplo, ((guix build utils) (gui build
gnu-build-system))
.
effective-version determina la cadena usada cuando se añaden las
extensiones de exp (vea with-extensions
) a la ruta de
búsqueda—por ejemplo, "2.2"
.
graft? determina si los paquetes a los que exp hace referencia deben ser injertados cuando sea posible.
Cuando references-graphs es verdadero, debe ser una lista de tuplas de una de las formas siguientes:
(nombre-archivo paquete) (nombre-archivo paquete salida) (nombre-archivo derivación) (nombre-archivo derivación salida) (nombre-archivo elemento-almacén)
El lado derecho de cada elemento de references-graphs se convierte automáticamente en una entrada del proceso de construcción de exp. En el entorno de construcción, cada nombre-archivo contiene el grafo de referencias del elemento correspondiente, en un formato de texto simple.
allowed-references debe ser o bien #f
o una lista de nombres y
paquetes de salida. En el último caso, la lista denota elementos del almacén
a los que el resultado puede hacer referencia. Cualquier referencia a otro
elemento del almacén produce un error de construcción. De igual manera con
disallowed-references, que enumera elementos a los que las salidas no
deben hacer referencia.
deprecation-warnings determina si mostrar avisos de obsolescencia
durante la compilación de los módulos. Puede ser #f
, #t
o
'detailed
.
El resto de parámetros funcionan como en derivation
(see Derivaciones).
Los procedimientos local-file
, plain-file
,
computed-file
, program-file
y scheme-file
a
continuación devuelven objetos tipo-archivo. Esto es, cuando se
expanden en una expresión-G, estos objetos dirigen a un archivo en el
almacén. Considere esta expresión-G:
#~(system* #$(file-append glibc "/sbin/nscd") "-f" #$(local-file "/tmp/mi-nscd.conf"))
El efecto aquí es el “internamiento” de /tmp/mi-nscd.conf mediante
su copia al almacén. Una vez expandida, por ejemplo vía
gexp->derivation
, la expresión-G hace referencia a la copia bajo
/gnu/store; por tanto, la modificación o el borrado del archivo en
/tmp no tiene ningún efecto en lo que la expresión-G
hace. plain-file
puede usarse de manera similar; se diferencia en que
el contenido del archivo se proporciona directamente como una cadena.
Devuelve un objeto que representa el archivo local archivo a añadir al almacén; este objeto puede usarse en una expresión-G. Si archivo es un nombre de archivo relativo, se busca de forma relativa al archivo fuente donde esta forma aparece; si archivo no es una cadena literal, se buscará de manera relativa al directorio de trabajo durante la ejecución. archivo se añadirá al almacén bajo nombre—de manera predeterminada el nombre de archivo sin los directorios.
Cuando recursive? es verdadero, los contenidos del archivo se añaden recursivamente; si archivo designa un archivo plano y recursive? es verdadero, sus contenidos se añaden, y sus bits de permisos se mantienen.
Cuando recursive? es verdadero, llama a (select?
archivo stat)
por cada entrada del directorio, donde
archivo es el nombre absoluto de archivo de la entrada y stat es
el resultado de lstat
; excluyendo las entradas para las cuales
select? no devuelve verdadero.
Esta es la contraparte declarativa del procedimiento monádico
interned-file
(see interned-file
).
Devuelve un objeto que representa un archivo de texto llamado nombre con el contenido proporcionado (una cadena o un vector de bytes) para ser añadido al almacén.
Esta es la contraparte declarativa de text-file
.
Devuelve un objeto que representa el elemento del almacén nombre, un
archivo o un directorio computado por gexp. Cuando local-build?
tiene valor verdadero (el caso predeterminado), la derivación se construye
de manera local. options es una lista de parámetros adicionales
proporcionados a gexp->derivation
.
Esta es la contraparte declarativa de gexp->derivation
.
(%current-system)] [#:target #f] Devuelve un guión ejecutable nombre que ejecuta exp usando guile, con los módulos importados por exp en su ruta de búsqueda. Busca los módulos de exp en module-path.
El ejemplo siguiente construye un guión que simplemente invoca la orden
ls
:
(use-modules (guix gexp) (gnu packages base)) (gexp->script "enumera-archivos" #~(execl #$(file-append coreutils "/bin/ls") "ls"))
Cuando se ejecuta a través del almacén (see run-with-store
), obtenemos una derivación que produce un archivo
ejecutable /gnu/store/…-enumera-archivos más o menos así:
#!/gnu/store/…-guile-2.0.11/bin/guile -ds !# (execl "/gnu/store/…-coreutils-8.22"/bin/ls" "ls")
Devuelve un objeto que representa el elemento ejecutable del almacén nombre que ejecuta gexp. guile es el paquete Guile usado para ejecutar el guión. Los módulos importados por gexp se buscan en module-path.
Esta es la contraparte declarativa de gexp->script
.
Devuelve una derivación que construye un archivo nombre que contiene exp. Cuando splice? es verdadero, se considera que exp es una lista de expresiones que deben ser expandidas en el archivo resultante.
Cuando set-load-path es verdadero, emite código en el archivo
resultante para establecer %load-path
y %load-compiled-path
de
manera que respeten los módulos importados por exp. Busca los módulos
de exp en module-path.
El archivo resultante hace referencia a todas las dependencias de exp o a un subconjunto de ellas.
Devuelve un objeto que representa el archivo Scheme nombre que contiene exp.
Esta es la contraparte declarativa de gexp->file
.
Devuelve como un valor monádico una derivación que construye un archivo de texto que contiene todo texto. texto puede ser una lista de, además de cadenas, objetos de cualquier tipo que pueda ser usado en expresiones-G: paquetes, derivaciones, archivos locales, objetos, etc. El archivo del almacén resultante hace referencia a todos ellos.
Esta variante debe preferirse sobre text-file
cuando el archivo a
crear haga referencia a elementos del almacén. Esto es el caso típico cuando
se construye un archivo de configuración que embebe nombres de archivos del
almacén, como este:
(define (perfil.sh)
;; Devuelve el nombre de un guión shell en el almacén
;; que establece la variable de entorno 'PATH'
(text-file* "perfil.sh"
"export PATH=" coreutils "/bin:"
grep "/bin:" sed "/bin\n"))
En este ejemplo, el archivo /gnu/store/…-perfil.sh resultante hará referencia a coreutils, grep y sed, por tanto evitando que se recolecten como basura durante su tiempo de vida.
Devuelve un objeto que representa el archivo del almacén nombre que contiene texto. texto es una secuencia de cadenas y objetos tipo-archivo, como en:
(mixed-text-file "perfil"
"export PATH=" coreutils "/bin:" grep "/bin")
Esta es la contraparte declarativa de text-file*
.
Devuelve un <computed-file>
que construye un directorio que contiene
todos los archivos. Cada elemento en archivos debe ser una lista
de dos elementos donde el primer elemento es el nombre de archivo usado en
el nuevo directorio y el segundo elemento es una expresión-G que denota el
archivo de destino. Aquí está un ejemplo:
(file-union "etc"
`(("hosts" ,(plain-file "hosts"
"127.0.0.1 localhost"))
("bashrc" ,(plain-file "bashrc"
"alias ls='ls --color=auto'"))))
Esto emite un directorio etc
que contiene estos dos archivos.
Devuelve un directorio que es la unión de cosas, donde cosas es una lista de objetos tipo-archivo que denotan directorios. Por ejemplo:
(directory-union "guile+emacs" (list guile emacs))
emite un directorio que es la unión de los paquetes guile
y
emacs
.
Devuelve un objeto tipo-archivo que se expande a la concatenación de obj y sufijo, donde obj es un objeto que se puede bajar de nivel y cada sufijo es una cadena.
Como un ejemplo, considere esta expresión-G:
(gexp->script "ejecuta-uname"
#~(system* #$(file-append coreutils
"/bin/uname")))
El mismo efecto podría conseguirse con:
(gexp->script "ejecuta-uname"
#~(system* (string-append #$coreutils
"/bin/uname")))
Hay una diferencia no obstante: en el caso de file-append
, el guión
resultante contiene una ruta absoluta de archivo como una cadena, mientras
que en el segundo caso, el guión resultante contiene una expresión
(string-append …)
para construir el nombre de archivo en
tiempo de ejecución.
Asocia sistema al sistema objetivo actual—por ejemplo,
"x86_64-linux"
—dentro de cuerpo.
En el segundo caso, asocia también objetivo al objetivo actual de
compilación cruzada—una tripleta de GNU como
"arm-linux-gnueabihf"
—o #f
si no se trata de una compilación
cruzada.
let-system
es útil en el caso ocasional en el que el objeto
introducido en la expresión-G depende del sistema objetivo, como en este
ejemplo:
#~(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 "¡ni idea!")))) "-net" "user" #$image)
Este macro es similar a la forma parameterize
para parámetros
asociados de forma dinámica (see Parameters in GNU Guile Reference
Manual). La principal diferencia es que se hace efectivo cuando el objeto
tipo-archivo devuelto por exp se baja de nivel a una derivación o un
elemento del almacén.
Un uso típico de with-parameters
es para forzar el sistema efectivo
de cierto objeto:
(with-parameters ((%current-system "i686-linux"))
coreutils)
El ejemplo previo devuelve un objeto que corresponde a la construcción en
i686 de Coreutils, independientemente del valor actual de
%current-system
.
Por supuesto, además de expresiones-G embebidas en código “anfitrión”, hay
también módulos que contienen herramientas de construcción. Para clarificar
que están destinados para su uso en el estrato de construcción, estos
módulos se mantienen en el espacio de nombres (guix build …)
.
Internamente, los objetos de alto nivel se bajan de nivel, usando su
compilador, a derivaciones o elementos del almacén. Por ejemplo, bajar de
nivel un paquete emite una derivación, y bajar de nivel un plain-file
emite un elemento del almacén. Esto se consigue usando el procedimiento
monádico lower-object
.
Devuelve como un valor en %store-monad
la derivación o elemento del
almacén que corresponde a obj en sistema, compilando de manera
cruzada para target si target es verdadero. obj debe ser
un objeto que tiene asociado un compilador de expresiones-G, como por
ejemplo un objeto del tipo <package>
.
Sometimes, it may be useful to convert a G-exp into a S-exp. For example,
some linters (see Invocación de guix lint
) peek into the build phases of a
package to detect potential problems. This conversion can be achieved with
this procedure. However, some information can be lost in the process. More
specifically, lowerable objects will be silently replaced with some
arbitrary object – currently the list (*approximate*)
, but this may
change.
El término estrato en este contexto se debe a Manuel Serrano et al. en el contexto de su trabajo en Hop. Oleg Kiselyov, quien ha escrito profundos ensayos sobre el tema, se refiere a este tipo de generación de código como separación en etapas o staging.
Next: Invocación de guix repl
, Previous: La mónada del almacén, Up: Interfaz programática [Contents][Index]