Next: Системы сборки, Previous: Описание пакетов, Up: Программный интерфейс [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 Параметры преобразования пакета). 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 Параметры преобразования пакета):
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.
пакета заменяет его прямые и косвенные зависимости, включая неявные входы, когда deep? истинна, согласно replacements. replacements - это список пар пакетов; первый элемент каждой пары - это заменяемый пакет, а второй - заменяющий.
При необходимости, rewrite-name - это процедура с одним аргументом, которая принимает имя пакета и возвращает его новое имя после перезаписи.
Рассмотрим пример:
(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))
Здесь мы сначала определяем процедуру перезаписи, которая заменяет openssl на libressl. Затем мы используем это, чтобы определить вариант пакета git, который использует libressl вместо openssl. Это именно то, что делает параметр командной строки --with-input (see --with-input).
Следующий вариант package-input-rewriting
может сопоставлять пакеты,
подлежащие замене, по имени, а не по идентификатору.
Возвращает процедуру, которая для данного пакета применяет заданные
replacements ко всему графу пакета, включая неявные входные данные,
если deep? не ложно. replacements - это список пары
спецификация/процедуры; каждая спецификация - это спецификация пакета, такая
как "gcc"
или "guile@2"
, и каждая процедура берет
соответствующий пакет и возвращает замену для этого пакета.
Приведенный выше пример можно переписать так:
(define libressl-instead-of-openssl
;; Replace all the packages called "openssl" with LibreSSL.
(package-input-rewriting/spec `(("openssl" . ,(const libressl)))))
Ключевое отличие здесь в том, что на этот раз пакеты сопоставляются по
спецификации, а не по идентичности. Другими словами, любой пакет в графе,
который называется openssl
, будет заменен.
Более общая процедура для перезаписи графа зависимостей пакетов - это
package-mapping
: она поддерживает произвольные изменения узлов в
графе.
Вернуть процедуру, которая для данного пакета применяет proc ко всем зависимым пакетам и возвращает полученный пакет. Процедура останавливает рекурсию, когда cut? возвращает истину для данного пакета. Когда deep? истинно, proc также применяется к неявным входным данным.
Next: Системы сборки, Previous: Описание пакетов, Up: Программный интерфейс [Contents][Index]