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 书写清单) and in your own package collection (see 创建一个频道), among others!
As discussed earlier, packages are first-class objects in the Scheme
(guix packages) module provides the
construct to define new package objects (see 软件包引用). The
easiest way to define a package variant is using the
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
source, which it
overrides. Note that the original
hello variable is still there, in
(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
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' (define gdb-sans-guile (package (inherit gdb) (inputs (modify-inputs (package-inputs gdb) (delete "guile")))))
modify-inputs form above removes the
"guile" package from
inputs field of
modify-inputs macro is a
helper that can prove useful anytime you want to remove, add, or replace
Modify the given package inputs, as returned by
package-inputs & co.,
according to the given clauses. Each clause must have one of the following
Delete from the inputs packages with the given names (strings).
Add packages to the front of the input list.
Add packages to the end of the input list.
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
(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
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 (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))
Here we have defined packages
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
transformations) module provides a high-level interface that directly maps
to the more sophisticated package transformation options (see 软件包转换选项):
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"))))
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
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.
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
"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.
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.