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

 the same value as the  variable of Figure 4, except for the   and   fields. Under the hood, again, this expands to a single  call with   calls for fields whose value is reused. The  feature supports a very useful idiom. It allows new package variants to be created programmatically, concisely, and in a purely functional way. It is notably used to bootstrap the software distribution, where bootstrap variants of packages such as GCC or the GNU libc are built with different inputs and configuration flags than the final versions. Users can similarly define customized variants of the packages found in the distribution. This feature also allows high-level transformations to be implemented as pure functions. For instance, the  procedure takes a   instance, and returns a variant of that package that is statically linked. It operates by just adding the relevant  flags, and recursively applying itself to the package’s inputs. Another application is the on-line auto-updater: when installing a GNU package defined in the distribution, the  command automatically checks whether a newer version is available upstream from , and offers the option to substitute the package’s source with a fresh download of the new upstream version—all at run time. This kind of feature is hardly accessible to an external DSL implementation. Among other things, this feature requires networking primitives (for the FTP client), which are typically unavailable in an external DSL such as the Nix language. The feature could be implemented in a language other than the DSL—for instance, Nix can export its abstract syntax tree as XML to external programs. However, this approach is often inefficient, due to the format conversion, and lossy: the exported representation may be either be too distant from the source code, or too distant from the preferred abstraction level. The author’s own experience writing an off-line auto-updater for Nix revealed other specific issues; for instance, the Nix language is lazily evaluated, but to make use of its XML output, one has to force strict evaluation, which in turn may generate more data than needed. In Guix,  instances have the expected level of abstraction, and they are readily accessible as first-class Scheme objects.

Sometimes it is desirable for the value of a field to depend on the system type targeted. For instance, for bootstrapping purposes, MIT/GNU Scheme’s build system depends on pre-compiled binaries, which are architecture-dependent; its  field must be able to select the right binaries depending on thearchitecture. To allow field values to refer to the target system type, we resort to thunked fields, as shown on line 13 of Figure 5. These fields have their value automatically wrapped in a thunk (a zero-argument procedure); when accessing them with the associated accessor, the thunk is transparently invoked. Thus, the values of thunked fields are computed lazily; more to the point, they can refer to dynamic state in place at their invocation point. In particular, the  procedure (shortly introduced) sets up a current-system dynamically-scoped parameter, which allows field values to know what the target system is.

Finally, both  and   records have an associated "compiler" that turns them into a derivation. takes an  instance and returns a derivation that downloads it, according to its   field. Likewise,  takes a package and returns a derivation that builds it, according to its   and associated   (more on that in Section 3.4). As we have seen on Figure 4, the  field lists dependencies of a package, which are themselves   objects; the   procedure recursively applies to those inputs, such that their derivation is computed and passed as the inputs argument of the lower-level. Guix essentially implements deep embedding of DSLs, where the semantics of the packaging DSL is interpreted by a dedicated compiler. Of course the DSLs defined here are simple, but they illustrate how Scheme’s primitive mechanisms, in particular macros, make it easy to implement such DSLs without requiring any special support from the Scheme implementation.

Build Programs
The value of the  field, as shown on Figure 4, must be a   object, which is essentially a wrapper around two procedure: one procedure to do a native build, and one to do a cross-build. When the aforementioned  (or , when cross-building) is called, it invokes the build system’s build procedure, passing it a connection to the build daemon, the system type, derivation name, and inputs. It is the build system’s responsibility to return a derivation that actually builds the software.


 * Figure 7: Entry point of the builder side code of.

The  object (line 10 of Figure 4) provides procedures to build and cross-build software that uses the GNU build system or similar. In a nutshell, it runs the following phases by default:


 * 1) unpack the source tarball, and change the current directory to the resulting directory;
 * 2) patch shebangs on installed files—e.g., replace  by  ; this is required to allow scripts to work with our unusual file system layout;
 * 3) run …, followed by   and