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

 Again, its build result is a single file reading, and its build is performed in an environment where the only visible file is a copy of   under.

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  or   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, which works similarly to  , 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.

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  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 : it is run by a Guile process launched by the build daemon, in a. 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.

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

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 ( …) 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  procedure has an optional   keyword parameter, allowing additional modules to be imported into the expression’s environment.

When  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  script that generates a makefile supporting standardized targets such as   and. It is a direct mapping of the abstract notion of a software package and should be rather self-descriptive.

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

The  field specifies arguments to be passed to the build system. Here, unsurprisingly, specifies flags for the   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  global variable introduced in the build stratum by , as seen before. That variable is bound to an association list that maps input names, like " ", to their actual directory name on disk, like.

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