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

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

the same value as the hello variable of Figure 4, except for the version and source fields. Under the hood, again, this expands to a single make-struct call with struct-ref calls for fields whose value is reused.

The inherit 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 static-package procedure takes a <package> instance, and returns a variant of that package that is statically linked. It operates by just adding the relevant configure 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 guix package command automatically checks whether a newer version is available upstream from ftp.gnu.org, 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, <package> 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 input 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 package-derivation procedure (shortly introduced) sets up a current-system dynamically-scoped parameter, which allows field values to know what the target system is.

Finally, both <package> and <origin> records have an associated "compiler" that turns them into a derivation. origin-derivation takes an <origin> instance and returns a derivation that downloads it, according to its method field. Likewise, package-derivation takes a package and returns a derivation that builds it, according to its build-system and associated arguments (more on that in Section 3.4). As we have seen on Figure 4, the inputs field lists dependencies of a package, which are themselves <package> objects; the package-derivation procedure recursively applies to those inputs, such that their derivation is computed and passed as the inputs argument of the lower-level build-expression->derivation.

Guix essentially implements deep embedding of DSLs, where the semantics of the packaging DSL is interpreted by a dedicated compiler.[1] 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 build-system field, as shown on Figure 4, must be a build-system 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 package-derivation (or package-cross-derivation, 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.

(define* (gnu-build #:key (phases %standard-phases) 
                    #:allow-other-keys 
                    #:rest args) 
  ;; Run all the PHASES in order, passing them ARGS. 
  ;; Return true on success. 
  (every (match-lambda
          ((name . proc) 
           (format #t "starting phase '~a'~ %" name) 
           (let ((result (apply proc args))) 
             (format #t "phase '~a' done~ %" name)
             result)))
         phases))
Figure 7
Entry point of the builder side code of gnu-build-system.


The gnu-build-system 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 #!/-bin/sh by #!/nix/store/…-bash-4.2/bin/sh; this is required to allow scripts to work with our unusual file system layout;
  3. run ./configure --prefix=/nix/store/…, followed by make and make check
  1. P. Hudak. Building domain-specific embedded languages. In ACM Computing Surveys, 28(4es) , New York, NY, USA, December 1996.