Next: Build Utilities, Previous: 构建系统, Up: 编程接口 [Contents][Index]
Almost all package build systems implement a notion build phases: a
sequence of actions that the build system executes, when you build the
package, leading to the installed byproducts in the store. A notable
exception is the “bare-bones” trivial-build-system
(see 构建系统).
As discussed in the previous section, those build systems provide a standard
list of phases. For gnu-build-system
, the main build phases are the
following:
set-paths
Define search path environment variables for all the input packages,
including PATH
(see Search Paths).
unpack
Unpack the source tarball, and change the current directory to the extracted source tree. If the source is actually a directory, copy it to the build tree, and enter that directory.
patch-source-shebangs
Patch shebangs encountered in source files so they refer to the right store
file names. For instance, this changes #!/bin/sh
to
#!/gnu/store/…-bash-4.3/bin/sh
.
configure
Run the configure script with a number of default options, such as
--prefix=/gnu/store/…, as well as the options specified by
the #:configure-flags
argument.
build
Run make
with the list of flags specified with #:make-flags
.
If the #:parallel-build?
argument is true (the default), build with
make -j
.
check
Run make check
, or some other target specified with
#:test-target
, unless #:tests? #f
is passed. If the
#:parallel-tests?
argument is true (the default), run make
check -j
.
install
Run make install
with the flags listed in #:make-flags
.
patch-shebangs
Patch shebangs on the installed executable files.
strip
Strip debugging symbols from ELF files (unless #:strip-binaries?
is
false), copying them to the debug
output when available
(see 安装调试文件).
validate-runpath
Validate the RUNPATH
of ELF binaries, unless
#:validate-runpath?
is false (see 构建系统).
This validation step consists in making sure that all the shared libraries
needed by an ELF binary, which are listed as DT_NEEDED
entries in its
PT_DYNAMIC
segment, appear in the DT_RUNPATH
entry of that
binary. In other words, it ensures that running or using those binaries
will not result in a “file not found” error at run time. See -rpath in The GNU Linker, for more information on
RUNPATH
.
Other build systems have similar phases, with some variations. For example,
cmake-build-system
has same-named phases but its configure
phases runs cmake
instead of ./configure
. Others, such as
python-build-system
, have a wholly different list of standard
phases. All this code runs on the build side: it is evaluated when
you actually build the package, in a dedicated build process spawned by the
build daemon (see 调用guix-daemon
).
Build phases are represented as association lists or “alists” (see Association Lists in GNU Guile Reference Manual) where each key is a symbol for the name of the phase and the associated value is a procedure that accepts an arbitrary number of arguments. By convention, those procedures receive information about the build in the form of keyword parameters, which they can use or ignore.
For example, here is how (guix build gnu-build-system)
defines
%standard-phases
, the variable holding its alist of build
phases21:
;; The build phases of 'gnu-build-system'. (define* (unpack #:key source #:allow-other-keys) ;; Extract the source tarball. (invoke "tar" "xvf" source)) (define* (configure #:key outputs #:allow-other-keys) ;; Run the 'configure' script. Install to output "out". (let ((out (assoc-ref outputs "out"))) (invoke "./configure" (string-append "--prefix=" out)))) (define* (build #:allow-other-keys) ;; Compile. (invoke "make")) (define* (check #:key (test-target "check") (tests? #true) #:allow-other-keys) ;; Run the test suite. (if tests? (invoke "make" test-target) (display "test suite not run\n"))) (define* (install #:allow-other-keys) ;; Install files to the prefix 'configure' specified. (invoke "make" "install")) (define %standard-phases ;; The list of standard phases (quite a few are omitted ;; for brevity). Each element is a symbol/procedure pair. (list (cons 'unpack unpack) (cons 'configure configure) (cons 'build build) (cons 'check check) (cons 'install install)))
This shows how %standard-phases
is defined as a list of
symbol/procedure pairs (see Pairs in GNU Guile Reference
Manual). The first pair associates the unpack
procedure with the
unpack
symbol—a name; the second pair defines the configure
phase similarly, and so on. When building a package that uses
gnu-build-system
with its default list of phases, those phases are
executed sequentially. You can see the name of each phase started and
completed in the build log of packages that you build.
Let’s now look at the procedures themselves. Each one is defined with
define*
: #:key
lists keyword parameters the procedure accepts,
possibly with a default value, and #:allow-other-keys
specifies that
other keyword parameters are ignored (see Optional Arguments in GNU Guile Reference Manual).
The unpack
procedure honors the source
parameter, which the
build system uses to pass the file name of the source tarball (or version
control checkout), and it ignores other parameters. The configure
phase only cares about the outputs
parameter, an alist mapping
package output names to their store file name (see 有多个输出的软件包). It extracts the file name of for out
, the default output,
and passes it to ./configure
as the installation prefix, meaning
that make install
will eventually copy all the files in that
directory (see configuration and makefile conventions in GNU Coding Standards). build
and install
ignore
all their arguments. check
honors the test-target
argument,
which specifies the name of the Makefile target to run tests; it prints a
message and skips tests when tests?
is false.
The list of phases used for a particular package can be changed with the
#:phases
parameter of the build system. Changing the set of build
phases boils down to building a new alist of phases based on the
%standard-phases
alist described above. This can be done with
standard alist procedures such as alist-delete
(see SRFI-1
Association Lists in GNU Guile Reference Manual); however, it is
more convenient to do so with modify-phases
(see modify-phases
).
Here is an example of a package definition that removes the configure
phase of %standard-phases
and inserts a new phase before the
build
phase, called set-prefix-in-makefile
:
(define-public example
(package
(name "example")
;; other fields omitted
(build-system gnu-build-system)
(arguments
(list
#:phases
#~(modify-phases %standard-phases
(delete 'configure)
(add-before 'build 'set-prefix-in-makefile
(lambda* (#:key inputs #:allow-other-keys)
;; Modify the makefile so that its
;; 'PREFIX' variable points to #$output and
;; 'XMLLINT' points to the correct path.
(substitute* "Makefile"
(("PREFIX =.*")
(string-append "PREFIX = " #$output "\n"))
(("XMLLINT =.*")
(string-append "XMLLINT = "
(search-input-file inputs "/bin/xmllint")
"\n"))))))))))
The new phase that is inserted is written as an anonymous procedure,
introduced with lambda*
; it looks for the xmllint executable
under a /bin directory among the package’s inputs (see package
Reference). It also honors the outputs
parameter we have seen
before. See Build Utilities, for more about the helpers used by this
phase, and for more examples of modify-phases
.
Tip: You can inspect the code associated with a package’s
#:phases
argument interactively, at the REPL (see 交互式使用 Guix).
Keep in mind that build phases are code evaluated at the time the package is
actually built. This explains why the whole modify-phases
expression
above is quoted (it comes after the #~
or hash-tilde): it is
staged for later execution. See G-表达式, for an explanation
of code staging and the code strata involved.
We present a simplified view of those build phases, but do
take a look at (guix build gnu-build-system)
to see all the
details!
Next: Build Utilities, Previous: 构建系统, Up: 编程接口 [Contents][Index]