Next: , Previous: , Up: 编程接口   [Contents][Index]


8.3 Defining Package Variants

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 软件包变换选项。). This section describes how to define package variants in code. This can be useful in “manifests” (see --manifest) and in your own package collection (see 创建一个频道), among others!

As discussed earlier, packages are first-class objects in the Scheme language. The (guix packages) module provides the package construct to define new package objects (see 软件包引用). The easiest way to define a package variant is using the inherit keyword together with package. This allows you to inherit from a package definition while overriding the fields you want.

For example, given the hello variable, which contains a definition for the current version of GNU Hello, here’s how you would define a variant for version 2.2 (released in 2006, it’s vintage!):

(use-modules (gnu packages base))    ;for '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-source package transformation option does. 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.

You can just as well define variants with a different set of dependencies than the original package. For example, the default gdb package depends on guile, but since that is an optional dependency, you can define a variant that removes that dependency like so:

(use-modules (gnu packages gdb)    ;for 'gdb'
             (srfi srfi-1))        ;for 'alist-delete'

(define gdb-sans-guile
  (package
    (inherit gdb)
    (inputs (alist-delete "guile"
                          (package-inputs gdb)))))

The alist-delete call above removes the tuple from the inputs field that has "guile" as its first element (see SRFI-1 Association Lists in GNU Guile Reference Manual).

In some cases, you may find it useful to write functions (“procedures”, in Scheme parlance) that return a package based on some parameters. For example, consider the luasocket library for the Lua programming language. We want to create luasocket packages for major versions of Lua. One way to do that is to define a procedure that takes a Lua package and returns a luasocket package that depends on it:

(define (make-lua-socket name lua)
  ;; Return a luasocket package built with LUA.
  (package
    (name name)
    (version "3.0")
    ;; several fields omitted
    (inputs
     `(("lua" ,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))

Here we have defined packages lua5.1-socket and lua5.2-socket by calling make-lua-socket with different arguments. See Procedures in GNU Guile Reference Manual, for more info on procedures. Having top-level public definitions for these two packages means that they can be referred to from the command line (see 软件包模块).

These are pretty simple package variants. As a convenience, the (guix transformations) module provides a high-level interface that directly maps to the more sophisticated package transformation options (see 软件包变换选项。):

Scheme Procedure: options->transformation opts

Return a procedure that, when passed an object to build (package, derivation, etc.), applies the transformations specified by opts and returns the resulting objects. opts must be a list of symbol/string pairs such as:

((with-branch . "guile-gcrypt=master")
 (without-tests . "libgcrypt"))

Each symbol names a transformation and the corresponding string is an argument to that transformation.

For instance, a manifest equivalent to this command:

guix build guix \
  --with-branch=guile-gcrypt=master \
  --with-debug-info=zlib

... would look like this:

(use-modules (guix transformations))

(define transform
  ;; The package transformation procedure.
  (options->transformation
   '((with-branch . "guile-gcrypt=master")
     (with-debug-info . "zlib"))))

(packages->manifest
 (list (transform (specification->package "guix"))))

The options->transformation procedure is convenient, but it’s perhaps also not as flexible as you may like. How is it implemented? The astute reader probably noticed that most package transformation options go beyond the superficial changes shown in the first examples of this section: they involve input rewriting, whereby the dependency graph of a package is rewritten by replacing specific inputs by others.

Dependency graph rewriting, for the purposes of swapping packages in the graph, is what the package-input-rewriting procedure in (guix packages) implements.

Scheme Procedure: package-input-rewriting replacements [rewrite-name] [#:deep? #t] Return a procedure that, when passed a

package, replaces its direct and indirect dependencies, including implicit inputs when deep? is true, according to replacements. replacements is a list of package pairs; the first element of each pair is the package to replace, and the second one is the replacement.

Optionally, rewrite-name is a one-argument procedure that takes the name of a package and returns its new name after rewrite.

Consider this example:

(define libressl-instead-of-openssl
  ;; This is a procedure to replace OPENSSL by LIBRESSL,
  ;; recursively.
  (package-input-rewriting `((,openssl . ,libressl))))

(define git-with-libressl
  (libressl-instead-of-openssl git))

Here we first define a rewriting procedure that replaces openssl with libressl. Then we use it to define a variant of the git package that uses libressl instead of openssl. This is exactly what the --with-input command-line option does (see --with-input).

The following variant of package-input-rewriting can match packages to be replaced by name rather than by identity.

Scheme Procedure: package-input-rewriting/spec replacements [#:deep? #t]

Return a procedure that, given a package, applies the given replacements to all the package graph, including implicit inputs unless deep? is false. 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.

The example above could be rewritten this way:

(define libressl-instead-of-openssl
  ;; Replace all the packages called "openssl" with LibreSSL.
  (package-input-rewriting/spec `(("openssl" . ,(const libressl)))))

The key difference here is that, this time, packages are matched by spec and not by identity. In other words, any package in the graph that is called openssl will be replaced.

A more generic procedure to rewrite a package dependency graph is package-mapping: it supports arbitrary changes to nodes in the graph.

Scheme Procedure: package-mapping proc [cut?] [#:deep? #f]

Return a procedure that, given a package, applies proc to all the packages depended on and returns the resulting package. The procedure stops recursion when cut? returns true for a given package. When deep? is true, proc is applied to implicit inputs as well.


Next: , Previous: , Up: 编程接口   [Contents][Index]