Next: Writing Manifests, Previous: Defining Packages, Up: Programming Interface [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 Package Transformation Options). 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 Creating a Channel), 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 package
Reference).
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-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.
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' (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.
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 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 Package Modules).
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 Package Transformation Options):
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.
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.
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. Matching packages that have the hidden?
property
set are not replaced.
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.
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 Invokingguix graph
).
Next: Writing Manifests, Previous: Defining Packages, Up: Programming Interface [Contents][Index]