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


8.6 Build Utilities

Как только вы начнете писать нетривиальные определения пакетов (see Описание пакетов) или другие действия сборки (see G-Expressions), вы, скорее всего, начнете искать помощников для действий, подобных оболочке—создание каталогов, рекурсивное копирование и удаление файлов, управление этапами сборки и т.д. Модуль (guix build utils) предоставляет такие служебные процедуры.

Большинство систем сборки загружают (guix build utils) (see Системы сборки). Таким образом, при написании настраиваемых фаз сборки для определений пакетов вы обычно можете предположить, что эти процедуры входят в область действия.

При написании G-выражений вы можете импортировать (guix build utils) на “стороне сборки”, используя with-import-modules, а затем поместить его в область видимости с помощью формы use-modules (see Using Guile Modules in GNU Guile Reference Manual):

(with-imported-modules '((guix build utils))  ;import it
  (computed-file "empty-tree"
                 #~(begin
                     ;; Put it in scope.
                     (use-modules (guix build utils))

                     ;; Happily use its 'mkdir-p' procedure.
                     (mkdir-p (string-append #$output "/a/b/c")))))

Оставшаяся часть этого раздела является справочником по большинству служебных процедур, предоставляемых (guix build utils).

8.6.1 Работа с именами файлов в store

В этом разделе описаны процедуры, относящиеся к именам файлов в store.

Процедура Scheme: sane-service-type

Проверить целостность склада.

Scheme Procedure: file-union name files

Возвращает true, если объект obj — это пакет ранней версии.

Scheme Procedure: file-union name files

Удалиnm /gnu/store и хэш из file, имени файла в store. Результатом обычно является строка "package-version".

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

Учитывая name, имя пакета, такое как "foo-0.9.1b", возвращает два значения: "foo" и "0.9.1b". Если часть версии недоступна, возвращаются name и #f. Считается, что первый дефис, за которым следует цифра, обозначает часть версии.

8.6.2 Файловые системы

Процедуры, приведённые ниже, обеспечивают работу и управление ранними версиями пакетов.

Scheme Procedure: directory-union name things

Вернуть #t, если dir существует и является каталогом.

Scheme Procedure: file-union name files

Вернуть #t, если file существует и исполняемый файл.

Scheme Procedure: file-union name files

Вернуть #t, если file является символической ссылкой (также известной как “символическая ссылка”).

Scheme Procedure: file-union name files
Scheme Procedure: file-union name files
Scheme Procedure: file-union name files

Вернуть #t, если file является, соответственно, файлом ELF, архивом ar (например, статической библиотекой .a) или файлом gzip.

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

Если file является файлом gzip, сбросить его timestamp (как в случае gzip --no-name) и вернуть истину. В противном случае вернуть #f. Когда keep-mtime? истинна, сохранить время модификации file.

8.6.3 Управление файлами

Следующие процедуры и макросы помогают создавать, изменять и удалять файлы. Они обеспечивают функциональность, сопоставимую с такими обычными утилитами оболочки, как mkdir -p, cp -r, rm -r и sed. Они дополняют обширный, но низкоуровневый интерфейс файловой системы Guile (see POSIX in GNU Guile Reference Manual).

Scheme Syntax: let-system system body

Запустить body с directory в качестве текущего каталога процесса.

По сути, этот макрос изменяет текущий каталог на directory перед вычислением body, используя chdir (see Processes in GNU Guile Reference Manual). Она возвращается в исходный каталог, когда остается динамический extent body, будь то через возврат нормальной процедуры или через нелокальный выход, такой как исключение.

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

Создать каталог dir и всех его предков.

Процедура Scheme: open-inferior directory Создать каталог, если он не существует, и скопировать туда file

под тем же именем.

Scheme Procedure: file-union name files

Сделать file доступным для записи его владельцу.

Scheme Procedure: lookup-inferior-packages inferior name [#:log (current-output-port)] [#:follow-symlinks? #f]  [#:copy-file

copy-file] [#:keep-mtime? #f] [#:keep-permissions? #t] Copy source directory to destination. Follow symlinks if follow-symlinks? is true; otherwise, just preserve them. Call copy-file to copy regular files. When keep-mtime? is true, keep the modification time of the files in source on those of destination. When keep-permissions? is true, preserve file permissions. Write verbose output to the log port.

Процедура Scheme: open-inferior directory [#:follow-mounts? #f] dir рекурсивно, как rm -rf, без

использования символических ссылок. Также не следовать точкам монтирования, если follow-mounts? не истинна. Сообщать об ошибках, но игнорировать их.

Scheme Syntax: let-system system body

((regexp match-var…) body…) … Заменить regexp в file строкой, возвращаемой body. body вычисляется с каждой привязкой match-var к соответствующему подвыражению позиционного регулярного выражения. Например:

(substitute* file
  (("hello")
   "good morning\n")
  (("foo([a-z]+)bar(.*)$" all letters end)
   (string-append "baz" letters end)))

Здесь, когда строка file содержит hello, она заменяется на good morning. Каждый раз, когда строка file соответствует второму регулярному выражению, all привязывается к полному совпадению, letters привязано к первому подвыражению, а end привязано к последнему.

Когда одно из match-var - _, никакая переменная не связана с соответствующей подстрокой соответствия.

В качестве альтернативы file может быть списком имен файлов, и в этом случае все они могут быть заменены.

В качестве альтернативы file может быть списком имен файлов, и в этом случае все они могут быть заменены.

8.6.4 Файловые системы

В этом разделе описаны процедуры поиска и фильтрации файлов.

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

Вернуть предикат, который возвращает истину при передаче имени файла, базовое имя которого совпадает с regexp.

Процедура Scheme: lookup-inferior-packages inferior name [#:stat lstat] [#:directories? #f] [#:fail-on-error? #f] Возвращает

лексикографически отсортированный список файлов в dir, для которых pred возвращает истину. pred передается два аргумента: абсолютное имя файла и его буфер статистики; предикат по умолчанию всегда возвращает истину. pred также может быть регулярным выражением, в этом случае оно эквивалентно (file-name-predicate pred). stat используется для получения информации о файле; использование lstat означает, что символические ссылки не соблюдаются. Если directories? истина, то каталоги также будут включены. Если fail-on-error? истина, генерировать исключение при ошибке.

Вот несколько примеров, в которых мы предполагаем, что текущий каталог является корнем дерева исходников Guix:

;; List all the regular files in the current directory.
(find-files ".")
⇒ ("./.dir-locals.el" "./.gitignore" …)

;; List all the .scm files under gnu/services.
(find-files "gnu/services" "\\.scm$")
⇒ ("gnu/services/admin.scm" "gnu/services/audio.scm" …)

;; List ar files in the current directory.
(find-files "." (lambda (file stat) (ar-file? file)))
⇒ ("./libformat.a" "./libstore.a" …)
Процедура Scheme: inferior-packages inferior

Вернуть полное имя файла для program, как в $PATH, или #f, если program не найдена.

Scheme Procedure: search-input-file inputs name
Scheme Procedure: search-input-directory inputs name

Return the complete file name for name as found in inputs; search-input-file searches for a regular file and search-input-directory searches for a directory. If name could not be found, an exception is raised.

Here, inputs must be an association list like inputs and native-inputs as available to build phases (see Фазы сборки).

Here is a (simplified) example of how search-input-file is used in a build phase of the wireguard-tools package:

(add-after 'install 'wrap-wg-quick
  (lambda* (#:key inputs outputs #:allow-other-keys)
    (let ((coreutils (string-append (assoc-ref inputs "coreutils")
                                    "/bin")))
      (wrap-program (search-input-file outputs "bin/wg-quick")
        #:sh (search-input-file inputs "bin/bash")
        `("PATH" ":" prefix ,(list coreutils))))))

8.6.5 Program Invocation

You’ll find handy procedures to spawn processes in this module, essentially convenient wrappers around Guile’s system* (see system* in GNU Guile Reference Manual).

Scheme Procedure: invoke program args

Invoke program with the given args. Raise an &invoke-error exception if the exit code is non-zero; otherwise return #t.

The advantage compared to system* is that you do not need to check the return value. This reduces boilerplate in shell-script-like snippets for instance in package build phases.

Scheme Procedure: invoke-error? c

Return true if c is an &invoke-error condition.

Scheme Procedure: invoke-error-program c
Scheme Procedure: invoke-error-arguments c
Scheme Procedure: invoke-error-exit-status c
Scheme Procedure: invoke-error-term-signal c
Scheme Procedure: invoke-error-stop-signal c

Access specific fields of c, an &invoke-error condition.

Scheme Procedure: report-invoke-error c [port]

Report to port (by default the current error port) about c, an &invoke-error condition, in a human-friendly way.

Typical usage would look like this:

(use-modules (srfi srfi-34) ;for 'guard'
             (guix build utils))

(guard (c ((invoke-error? c)
           (report-invoke-error c)))
  (invoke "date" "--imaginary-option"))

-| command "date" "--imaginary-option" failed with status 1
Scheme Procedure: invoke/quiet program args

Invoke program with args and capture program’s standard output and standard error. If program succeeds, print nothing and return the unspecified value; otherwise, raise a &message error condition that includes the status code and the output of program.

Here’s an example:

(use-modules (srfi srfi-34) ;for 'guard'
             (srfi srfi-35) ;for 'message-condition?'
             (guix build utils))

(guard (c ((message-condition? c)
           (display (condition-message c))))
  (invoke/quiet "date")  ;all is fine
  (invoke/quiet "date" "--imaginary-option"))

-| 'date --imaginary-option' exited with status 1; output follows:

    date: unrecognized option '--imaginary-option'
    Try 'date --help' for more information.

8.6.6 Фазы сборки

(guix build utils) также содержит инструменты для управления фазами сборки, которые используются системами сборки (see Системы сборки). Фазы сборки представлены в виде ассоциативных списков или “alists” (see Association Lists in GNU Guile Reference Manual), где каждый ключ представляет собой символ, обозначающий фазу, а связанное значение представляет собой процедуру (see Фазы сборки).

Ядро Guile и модуль (srfi srfi-1) предоставляют инструменты для управления списками. Модуль (guix build utils) дополняет их инструментами, написанными с учетом фаз сборки.

Scheme Syntax: let-system system body

Изменить phases последовательно в соответствии с каждым clause, которое может иметь одну из следующих форм:

(delete old-phase-name)
(replace old-phase-name new-phase)
(add-before old-phase-name new-phase-name new-phase)
(add-after old-phase-name new-phase-name new-phase)

Где каждая phase-name выше - это выражение, преобразующееся в символ, а new-phase - выражение, преобразующееся в процедуру.

Пример ниже взят из определения пакета grep. Он добавляет фазу для запуска после фазы install, которая называется fix-egrep-and-fgrep. Эта фаза представляет собой процедуру (lambda* обозначает анонимную процедуру), которая принимает аргумент ключевого слова #:output и игнорирует дополнительные аргументы ключевого слова (see Optional Arguments in GNU Guile Reference Manual, for more on lambda* and optional and keyword arguments.) В фазе используется substitute* для изменения установленных сценариев egrep и fgrep, чтобы они ссылались на grep по абсолютному имени файла:

(modify-phases %standard-phases
  (add-after 'install 'fix-egrep-and-fgrep
    ;; Patch 'egrep' and 'fgrep' to execute 'grep' via its
    ;; absolute file name instead of searching for it in $PATH.
    (lambda* (#:key outputs #:allow-other-keys)
      (let* ((out (assoc-ref outputs "out"))
             (bin (string-append out "/bin")))
        (substitute* (list (string-append bin "/egrep")
                           (string-append bin "/fgrep"))
          (("^exec grep")
           (string-append "exec " bin "/grep")))))))

В приведенном ниже примере фазы изменяются двумя способами: стандартная фаза configure удаляется, предположительно потому, что в пакете нет сценария configure или чего-то подобного, и фаза install по умолчанию заменяется файлом, который вручную копирует устанавливаемые исполняемые файлы:

(modify-phases %standard-phases
  (delete 'configure)      ;no 'configure' script
  (replace 'install
    (lambda* (#:key outputs #:allow-other-keys)
      ;; The package's Makefile doesn't provide an "install"
      ;; rule so do it by ourselves.
      (let ((bin (string-append (assoc-ref outputs "out")
                                "/bin")))
        (install-file "footswitch" bin)
        (install-file "scythe" bin)))))

8.6.7 Wrappers

It is not unusual for a command to require certain environment variables to be set for proper functioning, typically search paths (see Search Paths). Failing to do that, the command might fail to find files or other commands it relies on, or it might pick the “wrong” ones—depending on the environment in which it runs. Examples include:

For a package writer, the goal is to make sure commands always work the same rather than depend on some external settings. One way to achieve that is to wrap commands in a thin script that sets those environment variables, thereby ensuring that those run-time dependencies are always found. The wrapper would be used to set PATH, GUILE_LOAD_PATH, or QT_PLUGIN_PATH in the examples above.

To ease that task, the (guix build utils) module provides a couple of helpers to wrap commands.

Scheme Procedure: wrap-program program [#:sh sh] [#:rest variables] Make a wrapper for program.

variables should look like this:

'(variable delimiter position list-of-directories)

where delimiter is optional. : will be used if delimiter is not given.

For example, this call:

(wrap-program "foo"
              '("PATH" ":" = ("/gnu/.../bar/bin"))
              '("CERT_PATH" suffix ("/gnu/.../baz/certs"
                                    "/qux/certs")))

will copy foo to .foo-real and create the file foo with the following contents:

#!location/of/bin/bash
export PATH="/gnu/.../bar/bin"
export CERT_PATH="$CERT_PATH${CERT_PATH:+:}/gnu/.../baz/certs:/qux/certs"
exec -a $0 location/of/.foo-real "$@"

If program has previously been wrapped by wrap-program, the wrapper is extended with definitions for variables. If it is not, sh will be used as the interpreter.

Scheme Procedure: wrap-script program [#:guile guile] [#:rest variables] Wrap the script program

such that variables are set first. The format of variables is the same as in the wrap-program procedure. This procedure differs from wrap-program in that it does not create a separate shell script. Instead, program is modified directly by prepending a Guile script, which is interpreted as a comment in the script’s language.

Special encoding comments as supported by Python are recreated on the second line.

Note that this procedure can only be used once per file as Guile scripts are not supported.


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