Next: Writing Manifests, Previous: Definición de paquetes, Up: Interfaz programática [Contents][Index]
One of the nice things with Guix is that, given a package definition, you can easily derive variants of that package—for a different upstream version, with different dependencies, different compilation options, and so on. Some of these custom packages can be defined straight from the command line (see Opciones de transformación de paquetes). This section describes how to define package variants in code. This can be useful in “manifests” (see Writing Manifests) and in your own package collection (see Creación de un canal), among others!
Como se ha mostrado previamente, los paquetes son objetos de primera clase
del lenguage Scheme. El módulo (guix packages)
proporciona la forma
sintáctica package
para definir nuevos objetos de paquetes
(see Referencia de package
). La forma más fácil de definir una variante de
un paquete es usar la palabra clave inherit
junto a
package
. Esto le permite heredar de una definición de paquete y
modificar únicamente los campos que desee.
Por ejemplo, a partir de la variable hello
, que contiene la
definición de la versión actual de GNU Hello, podría definir de esta
forma una variante para la versión 2.2 (publicada 2006, ¡con solera!):
(use-modules (gnu packages base)) ;para '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"))))))
The example above corresponds to what the --with-version or
--with-source package transformations option do. Essentially
hello-2.2
preserves all the fields of hello
, except
version
and source
, which it overrides. Note that the
original hello
variable is still there, in the (gnu packages
base)
module, unchanged. When you define a custom package like this, you
are really adding a new package definition; the original one remains
available.
De igual manera puede definir variantes con un conjunto de dependencias
distinto al del paquete original. Por ejemplo, el paquete gdb
predeterminado depende de guile
pero, puesto que es una dependencia
opcional, podría definir una variante que elimina dicha dependencia de este
modo:
(use-modules (gnu packages gdb)) ;for 'gdb' (define gdb-sans-guile (package (inherit gdb) (inputs (modify-inputs (package-inputs gdb) (delete "guile")))))
The modify-inputs
form above removes the "guile"
package from
the inputs
field of gdb
. The modify-inputs
macro is a
helper that can prove useful anytime you want to remove, add, or replace
package inputs.
Modify the given package inputs, as returned by package-inputs
& co.,
according to the given clauses. Each clause must have one of the following
forms:
(delete name…)
Delete from the inputs packages with the given names (strings).
(prepend package…)
Add packages to the front of the input list.
(append package…)
Add packages to the end of the input list.
(replace name replacement)
Replace the package called name with replacement.
The example below removes the GMP and ACL inputs of Coreutils and adds libcap to the front of the input list:
(modify-inputs (package-inputs coreutils)
(delete "gmp" "acl")
(prepend libcap))
The example below replaces the guile
package from the inputs of
guile-redis
with guile-2.2
:
(modify-inputs (package-inputs guile-redis)
(replace "guile" guile-2.2))
The last type of clause is append
, to add inputs at the back of the
list.
En ciertos casos encontrará útil escribir funciones («procedimientos» en el
vocabulario de Scheme) que devuelven un paquete en base a ciertos
parámetros. Por ejemplo, considere la biblioteca luasocket
para el
lenguaje de programación Lua. Se desea crear paquetes de luasocket
para las versiones mayores de Lua. Una forma de hacerlo es definir un
procedimiento que recibe un paquete Lua y devuelve un paquete
luasocket
que depende de él:
(define (make-lua-socket name lua) ;; Return a luasocket package built with LUA. (package (name name) (version "3.0") ;; several fields omitted (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))
En este ejemplo se han definido los paquetes lua5.1-socket
y
lua5.2-socket
llamando a crea-lua-socket
con distintos
parámetros. See Procedures in GNU Guile Reference Manual para más
información sobre procedimientos. El hecho de disponer de definiciones
públicas de nivel superior de estos dos paquetes permite que se les haga
referencia desde la línea de órdenes (see Módulos de paquetes).
Estas son variantes muy simples. Para facilitar esta tarea, el módulo
(guix transformations)
proporciona una interfaz de alto nivel que se
corresponde directamente con las opciones de transformación de paquetes más
sofisticadas (see Opciones de transformación de paquetes):
Devuelve un procedimiento que, cuando se le proporciona un objeto que construir (paquete, derivación, etc.), aplica las transformaciones especificadas en opciones y devuelve los objetos resultantes. opciones debe ser una lista de pares símbolo/cadena como los siguientes:
((with-branch . "guile-gcrypt=master")
(without-tests . "libgcrypt"))
Cada símbolo nombra una transformación y la cadena correspondiente es el parámetro de dicha transformación.
Por ejemplo, un manifiesto equivalente a esta orden:
guix build guix \ --with-branch=guile-gcrypt=master \ --with-debug-info=zlib
... sería algo parecido a esto:
(use-modules (guix transformations)) (define transforma ;; El procedimiento de transformación del paquete. (options->transformation '((with-branch . "guile-gcrypt=master") (with-debug-info . "zlib")))) (packages->manifest (list (transforma (specification->package "guix"))))
El procedimiento options->transformation
es conveniente, pero quizá
no es tan flexible como pudiese desear. ¿Cómo se ha implementado? Es posible
que ya se haya percatado de que la mayoría de las opciones de transformación
de paquetes van más allá de los cambios superficiales mostrados en los
primeros ejemplos de esta sección: implican reescritura de entradas,
lo que significa que el grafo de dependencias de un paquete se reescribe
sustituyendo entradas específicas por otras.
La reescritura del grafo de dependencias, con el propósito de reemplazar
paquetes del grafo, es implementada por el procedimiento
package-input-rewriting
en (guix packages)
.
Devuelve un procedimiento que, cuando se le pasa un paquete, reemplaza sus dependencias directas e indirectas, incluyendo sus entradas implícitas cuando deep? es verdadero, de acuerdo a reemplazos. reemplazos es una lista de pares de paquetes; el primer elemento de cada par es el paquete a reemplazar, el segundo es el reemplazo.
Opcionalmente, nombre-reescrito es un procedimiento de un parámetro que toma el nombre del paquete y devuelve su nuevo nombre tras la reescritura.
Considere este ejemplo:
(define libressl-en-vez-de-openssl ;; Esto es un procedimiento para reemplazar OPENSSL ;; por LIBRESSL, recursivamente. (package-input-rewriting `((,openssl . ,libressl)))) (define git-con-libressl (libressl-en-vez-de-openssl git))
Aquí primero definimos un procedimiento de reescritura que substituye openssl por libressl. Una vez hecho esto, lo usamos para definir una variante del paquete git que usa libressl en vez de openssl. Esto es exactamente lo que hace la opción de línea de órdenes --with-input (see --with-input).
La siguiente variante de package-input-rewriting
puede encontrar
paquetes a reemplazar por su nombre en vez de por su identidad.
Devuelve un procedimiento que, proporcionado un paquete, realiza los reemplazos proporcionados sobre todo el grafo del paquete, incluyendo las entradas implícitas a menos que deep? sea falso.
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.
El ejemplo previo podría ser reescrito de esta forma:
(define libressl-en-vez-de-openssl
;; Reemplaza todos los paquetes llamados "openssl" con LibreSSL.
(package-input-rewriting/spec `(("openssl" . ,(const libressl)))))
La diferencia principal en este caso es que, esta vez, los paquetes se
buscan por su especificación y no por su identidad. En otras palabras,
cualquier paquete en el grafo que se llame openssl
será reemplazado.
Un procedimiento más genérico para reescribir el grafo de dependencias de un
paquete es package-mapping
: acepta cambios arbitrarios sobre nodos
del grafo.
Devuelve un procedimiento que, dado un paquete, aplica proc a todos los paquetes de los que depende y devuelve el paquete resultante. El procedimiento para la recursión cuando cortar? devuelve verdadero para un paquete dado. Cuando deep? tiene valor verdadero, proc se aplica también a las entradas implícitas.
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:
- You can inspect the package interactively at the REPL, for instance to view its inputs, the code of its build phases, or its configure flags (see Using Guix Interactively).
- When rewriting dependencies,
guix graph
can often help visualize the changes that are made (see Invocación deguix graph
).
Next: Writing Manifests, Previous: Definición de paquetes, Up: Interfaz programática [Contents][Index]