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

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.

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

Macro: 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.

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

В некоторых случаях вам может быть полезно написать функции (“процедуры” 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 Параметры преобразования пакета):

Procedure: options->transformation opts

Возвращает процедуру, которая при передаче объекта для сборки (пакета, производной и т. д.), применяет преобразования, указанные в 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).

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.

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

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. Matching packages that have the hidden? property set are not replaced.

Приведенный выше пример можно переписать так:

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

Ключевое отличие здесь в том, что на этот раз пакеты сопоставляются по спецификации, а не по идентичности. Другими словами, любой пакет в графе, который называется openssl, будет заменен.

Более общая процедура для перезаписи графа зависимостей пакетов - это package-mapping: она поддерживает произвольные изменения узлов в графе.

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

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

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:


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