Page:Ludovic Courtès - Functional Package Management with Guix.djvu/7

 #run  and patch shebangs in installed files.

Of course, that is all implemented in Scheme, via. Supporting code is available as a build-side module that  automatically adds as an input to its build scripts. The default build programs just call the procedure of that module that runs the above phases.

The  module contains the implementation of the above phases; it is imported on the builder side. The phases are modeled as follows: each phase is a procedure accepting several keyword arguments, and ignoring any keyword arguments it does not recognize. For instance the  procedure is in charge of running the package’s   script; that procedure honors the   keyword parameter seen on Figure 4. Similarly, the,  , and   procedures run the   command, and all honor the   keyword parameter.

All the procedures implementing the standard phases of the GNU build system are listed in the  builder-side variable, in the form of a list of phase name-/procedure pairs. The entry point of the builder-side code of  is shown on Figure 7. It calls all the phase procedures in order, by default those listed in the  association list, passing them all the arguments it got; its return value is true when every procedure’s return value is true.

The  field, shown on Figure 4, allows users to pass keyword arguments to the builder-side code. In addition to the  argument shown on the figure, users may use the   argument to specify a different set of phases. The value of the  must be a list of phase name/procedure pairs, as discussed above. This allows users to arbitrarily extend or modify the behavior of the build system. Figure 8 shows a variant of the definition in Figure 4 that adds a custom build phase. The  procedure is used to add a pair with   as its first item and the   as its second item right after the pair in   whose first item is  ; in other words, it reuses the standard build phases, but with an additional   phase right after the configure phase. The whole  expression is evaluated on the builder side.
 * Figure 8: Package specification with custom build phases.

This approach was inspired by that of NixOS, which uses Bash for its build scripts. Even with "advanced" Bash features such as functions, arrays, and associative arrays, the phases mechanism in NixOS remains limited and fragile, often leading to string escaping issues and obscure error reports due to the use of. Again, using Scheme instead of Bash unsurprisingly allows for better code structuring, and improves flexibility.

Other build systems are provided. For instance, the standard build procedure for Perl packages is slightly different: mainly, the configuration phase consists in running, and test suites are run with   instead of make. To accommodate that, Guix provides. Its companion build-side module essentially calls out to that of, only with appropriate   and   phases. This mechanism is similarly used for other build systems such as CMake and Python’s build system.


 * Figure 9: The substitute* macro for sed-like substitutions.

Build programs often need to traverse file trees, modify files according to a given pattern, etc. One example is the "patch shebang" phase mentioned above: all the source files must be traversed, and those starting with  are candidate to patching. This kind of task is usually associated with "shell programming"—as is the case with the build scripts found in NixOS, which are written in Bash, and resort to sed, find, etc. In Guix, a build-side Scheme module provides the necessary tools, built on top of Guile’s operating system interface. For instance,  returns a list of files whose names matches a given pattern;   performs the   adjustment described above; , of the shell   and   commands; etc.

An interesting example is the  macro, which does sed-style substitution on files. Figure 9 illustrates its use to patch a series of files returned by. There are two clauses, each with a pattern in the form of a POSIX regular expression; each clause’s body returns a string, which is the substitution for any matching line in the given files. In the first clause’s body,  is bound to the submatch corresponding to   in the regexp; in the second clause,   is bound to the whole match for that regexp. This snippet is nearly as concise than equivalent shell code using