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


8.3 Definición de variantes de paquetes

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.

Macro: modify-inputs inputs clauses

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):

Procedimiento: options->transformation opciones

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).

Procedimiento: package-input-rewriting reemplazos [nombre-reescrito] [#:deep? #t]

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.

Procedimiento: package-input-rewriting/spec reemplazos [#:deep? #t]

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.

Procedimiento: package-mapping proc [cortar?] [#:deep? #f]

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:


Next: Writing Manifests, Previous: Definición de paquetes, Up: Interfaz programática   [Contents][Index]