Next: Search Paths, Previous: Фазы сборки, Up: Программный интерфейс [Contents][Index]
Как только вы начнете писать нетривиальные определения пакетов
(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)
.
В этом разделе описаны процедуры, относящиеся к именам файлов в store.
Проверить целостность склада.
Возвращает true, если объект obj — это пакет ранней версии.
Удалиnm /gnu/store и хэш из file, имени файла в store.
Результатом обычно является строка "package-version"
.
Учитывая name, имя пакета, такое как "foo-0.9.1b"
, возвращает
два значения: "foo"
и "0.9.1b"
. Если часть версии недоступна,
возвращаются name и #f
. Считается, что первый дефис, за
которым следует цифра, обозначает часть версии.
Процедуры, приведённые ниже, обеспечивают работу и управление ранними версиями пакетов.
Вернуть #t
, если dir существует и является каталогом.
Вернуть #t
, если file существует и исполняемый файл.
Вернуть #t
, если file является символической ссылкой (также
известной как “символическая ссылка”).
Вернуть #t
, если file является, соответственно, файлом ELF,
архивом ar
(например, статической библиотекой .a) или файлом
gzip.
Если file является файлом gzip, сбросить его timestamp (как в случае
gzip --no-name
) и вернуть истину. В противном случае вернуть
#f
. Когда keep-mtime? истинна, сохранить время модификации
file.
Следующие процедуры и макросы помогают создавать, изменять и удалять файлы.
Они обеспечивают функциональность, сопоставимую с такими обычными утилитами
оболочки, как mkdir -p
, cp -r
, rm -r
и
sed
. Они дополняют обширный, но низкоуровневый интерфейс файловой
системы Guile (see POSIX in GNU Guile Reference Manual).
Запустить body с directory в качестве текущего каталога процесса.
По сути, этот макрос изменяет текущий каталог на directory перед
вычислением body, используя chdir
(see Processes in GNU Guile Reference Manual). Она возвращается в исходный каталог, когда
остается динамический extent body, будь то через возврат нормальной
процедуры или через нелокальный выход, такой как исключение.
Создать каталог dir и всех его предков.
Создать каталог, если он не существует, и скопировать туда file под тем же именем.
Сделать file доступным для записи его владельцу.
copy-file] [#:keep-mtime? #f] [#:keep-permissions? #t] [#:select? (const
#t)] Copy source directory to destination. Follow symlinks if
follow-symlinks? is true; otherwise, just preserve them. Call
copy-file to copy regular files. Call select?, taking two
arguments, file and stat, for each entry in source, where
file is the entry’s absolute file name and stat is the result of
lstat
(or stat
if follow-symlinks? is true); exclude
entries for which select? does not return true. 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.
Delete dir recursively, like rm -rf
, without following
symlinks. Don’t follow mount points either, unless follow-mounts? is
true. Report but ignore errors.
file by the string returned by body. body is evaluated with each match-var bound to the corresponding positional regexp sub-expression. For example:
(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 может быть списком имен файлов, и в этом случае все они могут быть заменены.
Будьте осторожны с использованием $
для поиска конца строки; само по
себе он не будет соответствовать завершению новой строки в строке.
Например, для поиска целой строки, заканчивающейся обратной косой чертой,
нужен регекс типа "(.*)\\\\\n$"
.
В этом разделе описаны процедуры поиска и фильтрации файлов.
Вернуть предикат, который возвращает истину при передаче имени файла, базовое имя которого совпадает с regexp.
лексикографически отсортированный список файлов в 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" …)
Вернуть полное имя файла для program, как в $PATH
, или
#f
, если program не найдена.
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))))))
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).
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.
Return true if c is an &invoke-error
condition.
Access specific fields of c, an &invoke-error
condition.
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
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.
(guix build utils)
также содержит инструменты для управления фазами
сборки, которые используются системами сборки (see Системы сборки). Фазы
сборки представлены в виде ассоциативных списков или “alists”
(see Association Lists in GNU Guile Reference Manual), где каждый
ключ представляет собой символ, обозначающий фазу, а связанное значение
представляет собой процедуру (see Фазы сборки).
Ядро Guile и модуль (srfi srfi-1)
предоставляют инструменты для
управления списками. Модуль (guix build utils)
дополняет их
инструментами, написанными с учетом фаз сборки.
Изменить 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)))))
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:
PATH
;
GUILE_LOAD_PATH
and GUILE_LOAD_COMPILED_PATH
;
QT_PLUGIN_PATH
.
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.
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.
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: Search Paths, Previous: Фазы сборки, Up: Программный интерфейс [Contents][Index]