Next: , Previous: , Up: Программный интерфейс   [Contents][Index]


9.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 Writing Manifests) and in your own package collection (see Создание канала), among others!

Как обсуждалось ранее, пакеты—это объекты первого класса на языке Scheme. Модуль (guix packages) предоставляет конструкцию package для определения новых объектов пакета (see package Ссылка). Самый простой способ определить вариант пакета—использовать ключевое слово inherit вместе с package. Это позволяет вам наследовать от определения пакета, переопределяя нужные поля.

Например, учитывая переменную hello, которая содержит определение для текущей версии GNU Hello, вот как вы могли бы определить вариант для версии 2.2 (выпущенной в 2006 году, это винтаж!):

(use-modules (gnu packages guile))  ;for 'guile-json'

(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"))))))

Приведенный выше пример соответствует тому, что делает опция преобразования пакета --with-source. По сути, hello-2.2 сохраняет все поля hello, кроме version и source, которые переопределяется. Обратите внимание, что исходная переменная hello все еще присутствует в модуле (gnu packages base) без изменений. Когда вы определяете собственный пакет таким образом, вы на самом деле добавляете новое определение пакета; оригинал остается доступным.

Вы также можете определить варианты с другим набором зависимостей, чем исходный пакет. Например, пакет gdb по умолчанию зависит от guile, но поскольку это необязательная зависимость, вы можете определить вариант, который удаляет эту зависимость следующим образом:

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

Scheme Syntax: 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.

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.

В некоторых случаях вам может быть полезно написать функции (“процедуры” Scheme), которые возвращают пакет на основе некоторых параметров. Например, рассмотрим библиотеку luasocket для языка программирования Lua. Мы хотим создать пакеты luasocket для основных версий Lua. Один из способов сделать это—определить процедуру, которая принимает пакет Lua и возвращает зависящий от него пакет luasocket:

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

Здесь мы определили пакеты lua5.1-socket и lua5.2-socket, вызвав make-lua-socket с разными аргументами. См. See Procedures in GNU Guile Reference Manual, для получения дополнительной информации о процедурах. Наличие общедоступных определений верхнего уровня для этих двух пакетов означает, что на них можно ссылаться из командной строки (see Пакетные модули).

Это довольно простые варианты пакета. Для удобства модуль (guix transformations) предоставляет высокоуровневый интерфейс, который напрямую сопоставляется с более сложными параметрами преобразования пакетов (see Параметры преобразования пакета):

Процедура Scheme: open-inferior directory Возвращает процедуру, которая при передаче объекта для сборки (пакета,

производной и т. д.), применяет преобразования, указанные в opts, и возвращает результирующие объекты. opts должен быть списком пар символ/строка, например:

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

Каждый символ именует преобразование, а соответствующая строка (string) является аргументом этого преобразования.

Например, команда:

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

Вывод должен быть таким:

(use-package-modules guile emacs)

(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 удобна, но, возможно, не так гибка, как вам хотелось бы. Как это реализовано? Проницательный читатель, вероятно, заметил, что большинство вариантов преобразования пакетов выходят за рамки поверхностных изменений, показанных в первых примерах этого раздела: они включают перезапись входных данных, в результате чего граф зависимостей пакета переписывается путем замены определенных входных данных другими.

Перезапись графа зависимостей для замены пакетов в графе реализуется процедурой package-input-rewriting в (guix packages).

Процедура Scheme: package-input-rewriting replacements [rewrite-name] [#:deep? #t] Возвращает процедуру, которая при передаче

пакета заменяет его прямые и косвенные зависимости, включая неявные входы, когда 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 может сопоставлять пакеты, подлежащие замене, по имени, а не по идентификатору.

Процедура Scheme: inferior-package-inputs package

Возвращает процедуру, которая для данного пакета применяет заданные 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: она поддерживает произвольные изменения узлов в графе.

Процедура Scheme: inferior-package-location package

Вернуть процедуру, которая для данного пакета применяет proc ко всем зависимым пакетам и возвращает полученный пакет. Процедура останавливает рекурсию, когда cut? возвращает истину для данного пакета. Когда deep? истинно, proc также применяется к неявным входным данным.


Next: Writing Manifests, Previous: Описание пакетов, Up: Программный интерфейс   [Contents][Index]