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

From Wikisource
Jump to navigation Jump to search
This page has been proofread, but needs to be validated.

Again, its build result is a single file reading hello, and its build is performed in an environment where the only visible file is a copy of static-bash under /nix/store.

3.3 Build Expressions

The Nix language heavily relies on string interpolation to allow users to insert references to build results, while hiding the underlying add-to-store or build-derivations operations that appear explicitly in Figure 2. Scheme has no support for string interpolation; adding it to the underlying Scheme implementation is certainly feasible, but it’s also unnatural.

The obvious strategy here is to instead leverage Scheme’s homoiconicity. This leads us to the definition of build-expression->derivation, which works similarly to derivation, except that it expects a build expression as an S-expression instead of a builder. Figure 3 shows the same derivation as before but rewritten to use this new interface.

 1: (let ((store (open-connection)) 
 2:       (builder ’(call-with-output-file %output 
 3:                   (lambda () 
 4:                     (display "hello"))))) 
 5:   (build-expression->derivation store 
 6:                                 "example-1.0" 
 7:                                 "x86_64-linux" 
 8:                                 builder ’())) 
 9:
10: =>
11: "/nix/store/zv3b3…-example-1.0.drv" 
12: #<derivation "example-1.0" …>

Figure 3: Build expression written in Scheme.

This time the builder on line 2 is purely a Scheme expression. That expression will be evaluated when the derivation is built, in the specified build environment with no inputs. The environment implicitly includes a copy of Guile, which is used to evaluate the builder expression. By default this is a stand-alone, statically-linked Guile, but users can also specify a derivation denoting a different Guile variant.

Remember that this expression is run by a separate Guile process than the one that calls build-expression->derivation: it is run by a Guile process launched by the build daemon, in a chroot. So, while there is a single language for both the "host" and the "build" side, there are really two strata of code, or tiers: the host-side, and the build-side code.[n 1]

Notice how the output file name is reified via the %output variable automatically added to builder’s scope. Input file names are similarly reified through the %build-inputs variable (not shown here). Both variables are non-hygienically introduced in the build expression by build-expression->derivation.

Sometimes the build expression needs to use functionality from other modules. For modules that come with Guile, the expression just needs to be augmented with the needed (use-modules …) clause. Conversely, external modules first need to be imported into the derivation’s build environment so the build expression can use them. To that end, the build-expression->derivation procedure has an optional #:modules keyword parameter, allowing additional modules to be imported into the expression’s environment.

When #:modules specifies a non-empty module list, an auxiliary derivation is created and added as an input to the initial derivation. That auxiliary derivation copies the module source and compiled files in the store. This mechanism allows build expressions to easily use helper modules, as described in Section 3.4.

3.3 Package Declarations

The interfaces described above remain fairly low-level. In particular, they explicitly manipulate the store, pass around the system type, and are very distant from the abstract notion of a software package that we want to focus on. To address this, Guix provides a high-level package definition interface. It is designed to be purely declarative in common cases, while allowing users to customize the underlying build process. That way, it should be intelligible and directly usable by packagers will little or no experience with Scheme. As an additional constraint, this extra layer should be efficient in space and time: package management tools need to be able to load and traverse a distribution consisting of thousands of packages.

Figure 4 shows the definition of the GNU Hello package, a typical GNU package written in C and using the GNU build system—i.e., a configure script that generates a makefile supporting standardized targets such as check and install. It is a direct mapping of the abstract notion of a software package and should be rather self-descriptive.

The inputs field specifies additional dependencies of the package. Here line 16 means that Hello has a dependency labeled "gawk" on GNU Awk, whose value is that of the gawk global variable; gawk is bound to a similar package declaration, omitted for conciseness.

The arguments field specifies arguments to be passed to the build system. Here #:configure-flags, unsurprisingly, specifies flags for the configure script. Its value is quoted because it will be evaluated in the build stratum—i.e., in the build process, when the derivation is built. It refers to the %build-inputs global variable introduced in the build stratum by build-expression->derivation, as seen before. That variable is bound to an association list that maps input names, like "gawk", to their actual directory name on disk, like /nix/store/…-gawk-4.0.2.

The code in Figure 4 demonstrates Guix’s use of embedded domain-specific languages (EDSLs). The package form, the origin form (line 5), and the base32 form (line 9) are expanded at macro-expansion time. The package and origin forms expand to a call to Guile’s make-struct primitive, which instantiates a record of the given type and with the given field values;[n 2] these macros look up the mapping of

  1. M. Serrano, G. Berry. Multitier Programming in Hop. In Queue, 10(7), New York, NY, USA, July 2012, pp. 10:10-10:22.

  1. The term "stratum" in this context was coined by Manuel Serrano et al. for their work on Hop where a similar situation arises.[1]
  2. The make-struct instantiates SRFI-9-style flat records,