As soon as you start writing non-trivial package definitions
(see 定义软件包) or other build actions (see G-表达式),
you will likely start looking for helpers for “shell-like”
actions—creating directories, copying and deleting files recursively,
manipulating build phases, and so on. The
(guix build utils) module
provides such utility procedures.
Most build systems load
(guix build utils) (see 构建系统).
Thus, when writing custom build phases for your package definitions, you can
usually assume those procedures are in scope.
When writing G-expressions, you can import
(guix build utils) on the
“build side” using
with-imported-modules and then put it in scope
use-modules form (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")))))
The remainder of this section is the reference for most of the utility
procedures provided by
(guix build utils).
This section documents procedures that deal with store file names.
Return the directory name of the store.
Return true if file is in the store.
Strip the /gnu/store and hash from file, a store file name.
The result is typically a
Given name, a package name like
"foo-0.9.1b", return two
"0.9.1b". When the version part is
unavailable, name and
#f are returned. The first hyphen
followed by a digit is considered to introduce the version part.
The procedures below deal with files and file types.
#t if dir exists and is a directory.
#t if file exists and is executable.
#t if file is a symbolic link (aka. a “symlink”).
#t if file is, respectively, an ELF file, an
archive (such as a .a static library), or a gzip file.
If file is a gzip file, reset its embedded timestamp (as with
gzip --no-name) and return true. Otherwise return
When keep-mtime? is true, preserve file’s modification time.
The following procedures and macros help create, modify, and delete files.
They provide functionality comparable to common shell utilities such as
rm -r, and
They complement Guile’s extensive, but low-level, file system interface
(see POSIX in GNU Guile Reference Manual).
Run body with directory as the process’s current directory.
Essentially, this macro changes the current directory to directory
before evaluating body, using
chdir (see Processes in GNU Guile Reference Manual). It changes back to the initial directory when
the dynamic extent of body is left, be it via normal procedure
return or via a non-local exit such as an exception.
Create directory dir and all its ancestors.
Create directory if it does not exist and copy file in there under the same name.
Make file writable for its owner.
Copy source directory to destination. Follow symlinks if follow-symlinks? is true; otherwise, just preserve them. When keep-mtime? is true, keep the modification time of the files in source on those of destination. Write verbose output to the log port.
without following symlinks. Don’t follow mount points either, unless follow-mounts? is true. Report but ignore errors.
regexp in 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" letter end)))
Here, anytime a line of file contains
hello, it is replaced by
good morning. Anytime a line of file matches the second
all is bound to the complete match,
letters is bound
to the first sub-expression, and
end is bound to the last one.
When one of the match-var is
_, no variable is bound to the
corresponding match substring.
Alternatively, file may be a list of file names, in which case they are all subject to the substitutions.
Be careful about using
$ to match the end of a line; by itself it
won’t match the terminating newline of a line.
This section documents procedures to search and filter files.
Return a predicate that returns true when passed a file name whose base name matches regexp.
lexicographically sorted list of files under dir for which pred
returns true. pred is passed two arguments: the absolute file name,
and its stat buffer; the default predicate always returns true. pred
can also be a regular expression, in which case it is equivalent to
(file-name-predicate pred). stat is used to obtain file
lstat means that symlinks are not followed. If
directories? is true, then directories will also be included. If
fail-on-error? is true, raise an exception upon error.
Here are a few examples where we assume that the current directory is the root of the Guix source tree:
;; 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" …)
Return the complete file name for program as found in
#f if program could not be found.
(guix build utils) also contains tools to manipulate build phases
as used by build systems (see 构建系统). Build phases are
represented as association lists or “alists” (see Association Lists in GNU Guile Reference Manual) where each key is a symbol naming the
phase and the associated value is a procedure (see 构建系统).
Guile core and the
(srfi srfi-1) module both provide tools to
manipulate alists. The
(guix build utils) module complements those
with tools written with build phases in mind.
Modify phases sequentially as per each clause, which may have one of the following forms:
(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)
Where every phase-name above is an expression evaluating to a symbol, and new-phase an expression evaluating to a procedure.
The example below is taken from the definition of the
It adds a phase to run after the
install phase, called
fix-egrep-and-fgrep. That phase is a procedure (
for anonymous procedures) that takes a
#:outputs keyword argument and
ignores extra keyword arguments (see Optional Arguments in GNU
Guile Reference Manual, for more on
lambda* and optional and keyword
arguments.) The phase uses
substitute* to modify the installed
egrep and fgrep scripts so that they refer to
its absolute file name:
(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"))) #t))))
In the example below, phases are modified in two ways: the standard
configure phase is deleted, presumably because the package does not
have a configure script or anything similar, and the default
install phase is replaced by one that manually copies the executable
files to be installed:
(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) #t))))