summary refs log tree commit diff
path: root/doc
diff options
context:
space:
mode:
authorMarius Bakke <marius@gnu.org>2020-10-19 00:17:48 +0200
committerMarius Bakke <marius@gnu.org>2020-10-19 00:17:48 +0200
commit1a8f7a0f584e5dd6e8f1a379b92f689b71902295 (patch)
tree8586450fc3068b217e60a7e942fa4c7d89ad74e7 /doc
parent19d42e0e23a7f90ac2dcc1c279bd23a967ff0314 (diff)
parent2a4f3c1711fdb947e615b5a89e285421b3bf0925 (diff)
downloadguix-1a8f7a0f584e5dd6e8f1a379b92f689b71902295.tar.gz
Merge branch 'master' into staging
Diffstat (limited to 'doc')
-rw-r--r--doc/build.scm457
-rw-r--r--doc/guix-cookbook.texi130
-rw-r--r--doc/guix.texi404
3 files changed, 817 insertions, 174 deletions
diff --git a/doc/build.scm b/doc/build.scm
index 97f4ab6b83..dac62493f4 100644
--- a/doc/build.scm
+++ b/doc/build.scm
@@ -33,6 +33,7 @@
              (guix utils)
              (git)
              (gnu packages base)
+             (gnu packages compression)
              (gnu packages gawk)
              (gnu packages gettext)
              (gnu packages guile)
@@ -40,7 +41,10 @@
              (gnu packages iso-codes)
              (gnu packages texinfo)
              (gnu packages tex)
+             (ice-9 match)
+             (srfi srfi-1)
              (srfi srfi-19)
+             (srfi srfi-26)
              (srfi srfi-71))
 
 (define file-append*
@@ -204,9 +208,168 @@ content=\"width=device-width, initial-scale=1\" />"))
                (setenv "XFAIL_TESTS" "htmlprag.scm")
                #t))))))))
 
+(define (normalize-language-code language)        ;XXX: deduplicate
+  ;; Normalize LANGUAGE.  For instance, "zh_CN" becomes "zh-cn".
+  (string-map (match-lambda
+                (#\_ #\-)
+                (chr chr))
+              (string-downcase language)))
+
+(define* (html-manual-identifier-index manual base-url
+                                       #:key
+                                       (name "html-manual-identifier-index"))
+  "Return an index of all the identifiers that appear in MANUAL, a
+makeinfo-generated manual.  The index is a file that contains an alist; each
+key is an identifier and the associated value is the URL reference pointing to
+that identifier.  The URL is constructed by concatenating BASE-URL to the
+actual file name."
+  (define build
+    (with-extensions (list guile-lib/htmlprag-fixed)
+      (with-imported-modules '((guix build utils))
+        #~(begin
+            (use-modules (guix build utils)
+                         (htmlprag)
+                         (srfi srfi-1)
+                         (srfi srfi-26)
+                         (ice-9 ftw)
+                         (ice-9 match)
+                         (ice-9 threads)
+                         (ice-9 pretty-print))
+
+            (define file-url
+              (let ((prefix (string-append #$manual "/")))
+                (lambda (file)
+                  ;; Return the URL for FILE.
+                  (let ((file (string-drop file (string-length prefix)))
+                        (base #$base-url))
+                    (if (string-null? base)
+                        file
+                        (string-append base "/" file))))))
+
+            (define (underscore-decode str)
+              ;; Decode STR, an "underscore-encoded" string as produced by
+              ;; makeinfo for indexes, such as "_0025base_002dservices" for
+              ;; "%base-services".
+              (let loop ((str str)
+                         (result '()))
+                (match (string-index str #\_)
+                  (#f
+                   (string-concatenate-reverse (cons str result)))
+                  (index
+                   (let ((char (string->number
+                                (substring str (+ index 1) (+ index 5))
+                                16)))
+                     (loop (string-drop str (+ index 5))
+                           (append (list (string (integer->char char))
+                                         (string-take str index))
+                                   result)))))))
+
+            (define (anchor-id->key id)
+              ;; Convert ID, an anchor ID such as
+              ;; "index-pam_002dlimits_002dservice" to the corresponding key,
+              ;; "pam-limits-service" in this example.  Drop the suffix of
+              ;; duplicate anchor IDs like "operating_002dsystem-1".
+              (let ((id (if (any (cut string-suffix? <> id)
+                                 '("-1" "-2" "-3" "-4" "-5"))
+                            (string-drop-right id 2)
+                            id)))
+                (underscore-decode
+                 (string-drop id (string-length "index-")))))
+
+            (define* (collect-anchors file #:optional (anchors '()))
+              ;; Collect the anchors that appear in FILE, a makeinfo-generated
+              ;; file.  Grab those from <dt> tags, which corresponds to
+              ;; Texinfo @deftp, @defvr, etc.  Return ANCHORS augmented with
+              ;; more name/reference pairs.
+              (define string-or-entity?
+                (match-lambda
+                  ((? string?) #t)
+                  (('*ENTITY* _ ...) #t)
+                  (_ #f)))
+
+              (define (worthy-entry? lst)
+                ;; Attempt to match:
+                ;;   Scheme Variable: <strong>x</strong>
+                ;; but not:
+                ;;   <code>cups-configuration</code> parameter: …
+                (let loop ((lst lst))
+                  (match lst
+                    (((? string-or-entity?) rest ...)
+                     (loop rest))
+                    ((('strong _ ...) _ ...)
+                     #t)
+                    (_ #f))))
+
+              (let ((shtml (call-with-input-file file html->shtml)))
+                (let loop ((shtml shtml)
+                           (anchors anchors))
+                  (match shtml
+                    (('dt ('@ ('id id)) rest ...)
+                     (if (and (string-prefix? "index-" id)
+                              (worthy-entry? rest))
+                         (alist-cons (anchor-id->key id)
+                                     (string-append (file-url file)
+                                                    "#" id)
+                                     anchors)
+                         anchors))
+                    ((tag ('@ _ ...) body ...)
+                     (fold loop anchors body))
+                    ((tag body ...)
+                     (fold loop anchors body))
+                    (_ anchors)))))
+
+            (define (html-files directory)
+              ;; Return the list of HTML files under DIRECTORY.
+              (map (cut string-append directory "/" <>)
+                   (scandir #$manual (lambda (file)
+                                       (string-suffix? ".html" file)))))
+
+            (define anchors
+              (sort (concatenate
+                     (n-par-map (parallel-job-count)
+                                (cut collect-anchors <>)
+                                (html-files #$manual)))
+                    (match-lambda*
+                      (((key1 . url1) (key2 . url2))
+                       (if (string=? key1 key2)
+                           (string<? url1 url2)
+                           (string<? key1 key2))))))
+
+            (call-with-output-file #$output
+              (lambda (port)
+                (display ";; Identifier index for the manual.\n\n"
+                         port)
+                (pretty-print anchors port)))))))
+
+  (computed-file name build))
+
+(define* (html-identifier-indexes manual directory-suffix
+                                  #:key (languages %languages)
+                                  (manual-name %manual)
+                                  (base-url (const "")))
+  (map (lambda (language)
+         (let ((language (normalize-language-code language)))
+           (list language
+                 (html-manual-identifier-index
+                  (file-append manual "/" language directory-suffix)
+                  (base-url language)
+                  #:name (string-append manual-name "-html-index-"
+                                        language)))))
+       languages))
+
 (define* (syntax-highlighted-html input
                                   #:key
                                   (name "highlighted-syntax")
+                                  (languages %languages)
+                                  (mono-node-indexes
+                                   (html-identifier-indexes input ""
+                                                            #:languages
+                                                            languages))
+                                  (split-node-indexes
+                                   (html-identifier-indexes input
+                                                            "/html_node"
+                                                            #:languages
+                                                            languages))
                                   (syntax-css-url
                                    "/static/base/css/code.css"))
   "Return a derivation called NAME that processes all the HTML files in INPUT
@@ -341,78 +504,6 @@ its <pre class=\"lisp\"> blocks (as produced by 'makeinfo --html')."
                   ((? string? str)
                    str))))
 
-            (define (underscore-decode str)
-              ;; Decode STR, an "underscore-encoded" string as produced by
-              ;; makeinfo for indexes, such as "_0025base_002dservices" for
-              ;; "%base-services".
-              (let loop ((str str)
-                         (result '()))
-                (match (string-index str #\_)
-                  (#f
-                   (string-concatenate-reverse (cons str result)))
-                  (index
-                   (let ((char (string->number
-                                (substring str (+ index 1) (+ index 5))
-                                16)))
-                     (loop (string-drop str (+ index 5))
-                           (append (list (string (integer->char char))
-                                         (string-take str index))
-                                   result)))))))
-
-            (define (anchor-id->key id)
-              ;; Convert ID, an anchor ID such as
-              ;; "index-pam_002dlimits_002dservice" to the corresponding key,
-              ;; "pam-limits-service" in this example.  Drop the suffix of
-              ;; duplicate anchor IDs like "operating_002dsystem-1".
-              (let ((id (if (any (cut string-suffix? <> id)
-                                 '("-1" "-2" "-3" "-4" "-5"))
-                            (string-drop-right id 2)
-                            id)))
-                (underscore-decode
-                 (string-drop id (string-length "index-")))))
-
-            (define* (collect-anchors file #:optional (vhash vlist-null))
-              ;; Collect the anchors that appear in FILE, a makeinfo-generated
-              ;; file.  Grab those from <dt> tags, which corresponds to
-              ;; Texinfo @deftp, @defvr, etc.  Return VHASH augmented with
-              ;; more name/reference pairs.
-              (define string-or-entity?
-                (match-lambda
-                  ((? string?) #t)
-                  (('*ENTITY* _ ...) #t)
-                  (_ #f)))
-
-              (define (worthy-entry? lst)
-                ;; Attempt to match:
-                ;;   Scheme Variable: <strong>x</strong>
-                ;; but not:
-                ;;   <code>cups-configuration</code> parameter: …
-                (let loop ((lst lst))
-                  (match lst
-                    (((? string-or-entity?) rest ...)
-                     (loop rest))
-                    ((('strong _ ...) _ ...)
-                     #t)
-                    (_ #f))))
-
-              (let ((shtml (call-with-input-file file html->shtml)))
-                (let loop ((shtml shtml)
-                           (vhash vhash))
-                  (match shtml
-                    (('dt ('@ ('id id)) rest ...)
-                     (if (and (string-prefix? "index-" id)
-                              (worthy-entry? rest))
-                         (vhash-cons (anchor-id->key id)
-                                     (string-append (basename file)
-                                                    "#" id)
-                                     vhash)
-                         vhash))
-                    ((tag ('@ _ ...) body ...)
-                     (fold loop vhash body))
-                    ((tag body ...)
-                     (fold loop vhash body))
-                    (_ vhash)))))
-
             (define (process-html file anchors)
               ;; Parse FILE and perform syntax highlighting for its Scheme
               ;; snippets.  Install the result to #$output.
@@ -444,38 +535,59 @@ its <pre class=\"lisp\"> blocks (as produced by 'makeinfo --html')."
             (define (html? file stat)
               (string-suffix? ".html" file))
 
+            (define language+node-anchors
+              (match-lambda
+                ((language files ...)
+                 (cons language
+                       (fold (lambda (file vhash)
+                               (let ((alist (call-with-input-file file read)))
+                                 ;; Use 'fold-right' so that the first entry
+                                 ;; wins (e.g., "car" from "Pairs" rather than
+                                 ;; from "rnrs base" in the Guile manual).
+                                 (fold-right (match-lambda*
+                                               (((key . value) vhash)
+                                                (vhash-cons key value vhash)))
+                                             vhash
+                                             alist)))
+                             vlist-null
+                             files)))))
+
+            (define mono-node-anchors
+              ;; List of language/vhash pairs, where each vhash maps an
+              ;; identifier to the corresponding URL in a single-page manual.
+              (map language+node-anchors '#$mono-node-indexes))
+
+            (define multi-node-anchors
+              ;; Likewise for split-node manuals.
+              (map language+node-anchors '#$split-node-indexes))
+
             ;; Install a UTF-8 locale so we can process UTF-8 files.
             (setenv "GUIX_LOCPATH"
                     #+(file-append glibc-utf8-locales "/lib/locale"))
             (setlocale LC_ALL "en_US.utf8")
 
             ;; First process the mono-node 'guix.html' files.
-            (n-par-for-each (parallel-job-count)
-                            (lambda (mono)
-                              (let ((anchors (collect-anchors mono)))
-                                (process-html mono anchors)))
-                            (find-files
-                             #$input
-                             "^guix(-cookbook|)(\\.[a-zA-Z_-]+)?\\.html$"))
-
-            ;; Next process the multi-node HTML files in two phases: (1)
-            ;; collect the list of anchors, and (2) perform
-            ;; syntax-highlighting.
-            (let* ((multi   (find-files #$input "^html_node$"
-                                        #:directories? #t))
-                   (anchors (n-par-map (parallel-job-count)
-                                       (lambda (multi)
-                                         (cons multi
-                                               (fold collect-anchors vlist-null
-                                                     (find-files multi html?))))
-                                       multi)))
-              (n-par-for-each (parallel-job-count)
-                              (lambda (file)
-                                (let ((anchors (assoc-ref anchors (dirname file))))
-                                  (process-html file anchors)))
-                              (append-map (lambda (multi)
-                                            (find-files multi html?))
-                                          multi)))
+            (for-each (match-lambda
+                        ((language . anchors)
+                         (let ((files (find-files
+                                       (string-append #$input "/" language)
+                                       "^guix(-cookbook|)(\\.[a-zA-Z_-]+)?\\.html$")))
+                           (n-par-for-each (parallel-job-count)
+                                           (cut process-html <> anchors)
+                                           files))))
+                      mono-node-anchors)
+
+            ;; Process the multi-node HTML files.
+            (for-each (match-lambda
+                        ((language . anchors)
+                         (let ((files (find-files
+                                       (string-append #$input "/" language
+                                                      "/html_node")
+                                       "\\.html$")))
+                           (n-par-for-each (parallel-job-count)
+                                           (cut process-html <> anchors)
+                                           files))))
+                      multi-node-anchors)
 
             ;; Last, copy non-HTML files as is.
             (for-each copy-as-is
@@ -486,6 +598,8 @@ its <pre class=\"lisp\"> blocks (as produced by 'makeinfo --html')."
 (define* (html-manual source #:key (languages %languages)
                       (version "0.0")
                       (manual %manual)
+                      (mono-node-indexes (map list languages))
+                      (split-node-indexes (map list languages))
                       (date 1)
                       (options %makeinfo-html-options))
   "Return the HTML manuals built from SOURCE for all LANGUAGES, with the given
@@ -574,6 +688,8 @@ makeinfo OPTIONS."
   (let* ((name   (string-append manual "-html-manual"))
          (manual (computed-file name build)))
     (syntax-highlighted-html manual
+                             #:mono-node-indexes mono-node-indexes
+                             #:split-node-indexes split-node-indexes
                              #:name (string-append name "-highlighted"))))
 
 (define* (pdf-manual source #:key (languages %languages)
@@ -920,6 +1036,8 @@ languages:\n"
                           #:key (languages %languages)
                           (version "0.0")
                           (date (time-second (current-time time-utc)))
+                          (mono-node-indexes (map list %languages))
+                          (split-node-indexes (map list %languages))
                           (manual %manual))
   "Return the union of the HTML and PDF manuals, as well as the indexes."
   (directory-union (string-append manual "-manual")
@@ -930,7 +1048,12 @@ languages:\n"
                                 #:version version
                                 #:manual manual))
                         (list html-manual-indexes
-                              html-manual pdf-manual))
+                              (lambda (source . args)
+                                (apply html-manual source
+                                       #:mono-node-indexes mono-node-indexes
+                                       #:split-node-indexes split-node-indexes
+                                       args))
+                              pdf-manual))
                    #:copy? #t))
 
 (define (latest-commit+date directory)
@@ -944,17 +1067,143 @@ commit date (an integer)."
     (values (oid->string oid) (commit-time commit))))
 
 
+;;;
+;;; Guile manual.
+;;;
+
+(define guile-manual
+  ;; The Guile manual as HTML, including both the mono-node "guile.html" and
+  ;; the split-node "html_node" directory.
+  (let ((guile guile-3.0-latest))
+    (computed-file (string-append "guile-manual-" (package-version guile))
+                   (with-imported-modules '((guix build utils))
+                     #~(begin
+                         (use-modules (guix build utils)
+                                      (ice-9 match))
+
+                         (setenv "PATH"
+                                 (string-append #+tar "/bin:"
+                                                #+xz "/bin:"
+                                                #+texinfo "/bin"))
+                         (invoke "tar" "xf" #$(package-source guile))
+                         (mkdir-p (string-append #$output "/en/html_node"))
+
+                         (let* ((texi (find-files "." "^guile\\.texi$"))
+                                (documentation (match texi
+                                                 ((file) (dirname file)))))
+                           (with-directory-excursion documentation
+                             (invoke "makeinfo" "--html" "--no-split"
+                                     "-o" (string-append #$output
+                                                         "/en/guile.html")
+                                     "guile.texi")
+                             (invoke "makeinfo" "--html" "-o" "split"
+                                     "guile.texi")
+                             (copy-recursively
+                              "split"
+                              (string-append #$output "/en/html_node")))))))))
+
+(define %guile-manual-base-url
+  "https://www.gnu.org/software/guile/manual")
+
+(define (for-all-languages index)
+  (map (lambda (language)
+         (list language index))
+       %languages))
+
+(define guile-mono-node-indexes
+  ;; The Guile manual is only available in English so use the same index in
+  ;; all languages.
+  (for-all-languages
+   (html-manual-identifier-index (file-append guile-manual "/en")
+                                 %guile-manual-base-url
+                                 #:name "guile-html-index-en")))
+
+(define guile-split-node-indexes
+  (for-all-languages
+   (html-manual-identifier-index (file-append guile-manual "/en/html_node")
+                                 (string-append %guile-manual-base-url
+                                                "/html_node")
+                                 #:name "guile-html-index-en")))
+
+(define (merge-index-alists alist1 alist2)
+  "Merge ALIST1 and ALIST2, both of which are list of tuples like:
+
+  (LANGUAGE INDEX1 INDEX2 ...)
+
+where LANGUAGE is a string like \"en\" and INDEX1 etc. are indexes as returned
+by 'html-identifier-indexes'."
+  (let ((languages (delete-duplicates
+                    (append (match alist1
+                              (((languages . _) ...)
+                               languages))
+                            (match alist2
+                              (((languages . _) ...)
+                               languages))))))
+    (map (lambda (language)
+           (cons language
+                 (append (or (assoc-ref alist1 language) '())
+                         (or (assoc-ref alist2 language) '()))))
+         languages)))
+
+
 (let* ((root (canonicalize-path
               (string-append (current-source-directory) "/..")))
-       (commit date (latest-commit+date root)))
+       (commit date (latest-commit+date root))
+       (version (or (getenv "GUIX_MANUAL_VERSION")
+                    (string-take commit 7)))
+       (select? (let ((vcs? (git-predicate root)))
+                  (lambda (file stat)
+                    (and (vcs? file stat)
+                         ;; Filter out this file.
+                         (not (string=? (basename file) "build.scm"))))))
+       (source (local-file root "guix" #:recursive? #t
+                           #:select? select?)))
+
+  (define guix-manual
+    (html-manual source
+                 #:manual "guix"
+                 #:version version
+                 #:date date))
+
+  (define guix-mono-node-indexes
+    ;; Alist of indexes for GUIX-MANUAL, where each key is a language code and
+    ;; each value is a file-like object containing the identifier index.
+    (html-identifier-indexes guix-manual ""
+                             #:manual-name "guix"
+                             #:base-url (if (string=? %manual "guix")
+                                            (const "")
+                                            (cut string-append "/manual/" <>))
+                             #:languages %languages))
+
+  (define guix-split-node-indexes
+    ;; Likewise for the split-node variant of GUIX-MANUAL.
+    (html-identifier-indexes guix-manual "/html_node"
+                             #:manual-name "guix"
+                             #:base-url (if (string=? %manual "guix")
+                                            (const "")
+                                            (cut string-append "/manual/" <>
+                                                 "/html_node"))
+                             #:languages %languages))
+
+  (define mono-node-indexes
+    (merge-index-alists guix-mono-node-indexes guile-mono-node-indexes))
+
+  (define split-node-indexes
+    (merge-index-alists guix-split-node-indexes guile-split-node-indexes))
+
   (format (current-error-port)
           "building manual from work tree around commit ~a, ~a~%"
           commit
           (let* ((time (make-time time-utc 0 date))
                  (date (time-utc->date time)))
             (date->string date "~e ~B ~Y")))
-  (pdf+html-manual (local-file root "guix" #:recursive? #t
-                               #:select? (git-predicate root))
-                   #:version (or (getenv "GUIX_MANUAL_VERSION")
-                                 (string-take commit 7))
+
+  (pdf+html-manual source
+                   ;; Always use the identifier indexes of GUIX-MANUAL and
+                   ;; GUILE-MANUAL.  Both "guix" and "guix-cookbook" can
+                   ;; contain links to definitions that appear in either of
+                   ;; these two manuals.
+                   #:mono-node-indexes mono-node-indexes
+                   #:split-node-indexes split-node-indexes
+                   #:version version
                    #:date date))
diff --git a/doc/guix-cookbook.texi b/doc/guix-cookbook.texi
index a783c0ae4c..581b8c3595 100644
--- a/doc/guix-cookbook.texi
+++ b/doc/guix-cookbook.texi
@@ -128,8 +128,9 @@ REPL.
 @item
 Scheme syntax boils down to a tree of expressions (or @emph{s-expression} in
 Lisp lingo).  An expression can be a literal such as numbers and strings, or a
-compound which is a parenthesized list of compounds and literals.  @code{#t}
-and @code{#f} stand for the Booleans ``true'' and ``false'', respectively.
+compound which is a parenthesized list of compounds and literals.  @code{#true}
+and @code{#false} (abbreviated @code{#t} and @code{#f}) stand for the
+Booleans ``true'' and ``false'', respectively.
 
 Examples of valid expressions:
 
@@ -249,8 +250,10 @@ definitions.
 @end lisp
 
 @item
-The keyword syntax is @code{#:}; it is used to create unique identifiers.
-@pxref{Keywords,,, guile, GNU Guile Reference Manual}.
+@dfn{Keywords} are typically used to identify the named parameters of a
+procedure.  They are prefixed by @code{#:} (hash, colon) followed by
+alphanumeric characters: @code{#:like-this}.
+@xref{Keywords,,, guile, GNU Guile Reference Manual}.
 
 @item
 The percentage @code{%} is typically used for read-only global variables in
@@ -791,11 +794,11 @@ another, more sophisticated package (slightly modified from the source):
                 (snippet '(begin
                             ;; Remove bundled software.
                             (delete-file-recursively "deps")
-                            #t))))
+                            #true))))
       (build-system cmake-build-system)
       (outputs '("out" "debug"))
       (arguments
-       `(#:tests? #t                            ; Run the test suite (this is the default)
+       `(#:tests? #true                         ; Run the test suite (this is the default)
          #:configure-flags '("-DUSE_SHA1DC=ON") ; SHA-1 collision detection
          #:phases
          (modify-phases %standard-phases
@@ -806,12 +809,12 @@ another, more sophisticated package (slightly modified from the source):
                (substitute* "tests/clar/fs.h"
                  (("/bin/cp") (which "cp"))
                  (("/bin/rm") (which "rm")))
-               #t))
+               #true))
            ;; Run checks more verbosely.
            (replace 'check
              (lambda _ (invoke "./libgit2_clar" "-v" "-Q")))
            (add-after 'unpack 'make-files-writable-for-tests
-               (lambda _ (for-each make-file-writable (find-files "." ".*")))))))
+             (lambda _ (for-each make-file-writable (find-files "." ".*")))))))
       (inputs
        `(("libssh2" ,libssh2)
          ("http-parser" ,http-parser)
@@ -1029,7 +1032,7 @@ If you want to know more about what happens during those phases, consult the
 associated procedures.
 
 For instance, as of this writing the definition of @code{unpack} for the GNU build
-system is
+system is:
 
 @lisp
 (define* (unpack #:key source #:allow-other-keys)
@@ -1044,13 +1047,13 @@ working directory."
         ;; Preserve timestamps (set to the Epoch) on the copied tree so that
         ;; things work deterministically.
         (copy-recursively source "."
-                          #:keep-mtime? #t))
+                          #:keep-mtime? #true))
       (begin
         (if (string-suffix? ".zip" source)
             (invoke "unzip" source)
             (invoke "tar" "xvf" source))
         (chdir (first-subdirectory "."))))
-  #t)
+  #true)
 @end lisp
 
 Note the @code{chdir} call: it changes the working directory to where the source was
@@ -1066,16 +1069,16 @@ the following forms:
 
 @itemize
 @item
-@code{(add-before PHASE NEW-PHASE PROCEDURE)}: Run @code{PROCEDURE} named @code{NEW-PHASE} before @code{PHASE}.
+@code{(add-before @var{phase} @var{new-phase} @var{procedure})}: Run @var{procedure} named @var{new-phase} before @var{phase}.
 @item
-@code{(add-after PHASE NEW-PHASE PROCEDURE)}: Same, but afterwards.
+@code{(add-after @var{phase} @var{new-phase} @var{procedure})}: Same, but afterwards.
 @item
-@code{(replace PHASE PROCEDURE)}.
+@code{(replace @var{phase} @var{procedure})}.
 @item
-@code{(delete PHASE)}.
+@code{(delete @var{phase})}.
 @end itemize
 
-The @code{PROCEDURE} supports the keyword arguments @code{inputs} and @code{outputs}.  Each
+The @var{procedure} supports the keyword arguments @code{inputs} and @code{outputs}.  Each
 input (whether @emph{native}, @emph{propagated} or not) and output directory is referenced
 by their name in those variables.  Thus @code{(assoc-ref outputs "out")} is the store
 directory of the main output of the package.  A phase procedure may look like
@@ -1083,16 +1086,16 @@ this:
 
 @lisp
 (lambda* (#:key inputs outputs #:allow-other-keys)
-  (let (((bash-directory (assoc-ref inputs "bash"))
-         (output-directory (assoc-ref outputs "out"))
-         (doc-directory (assoc-ref outputs "doc"))
-  ; ...
-  #t)
+  (let ((bash-directory (assoc-ref inputs "bash"))
+        (output-directory (assoc-ref outputs "out"))
+        (doc-directory (assoc-ref outputs "doc")))
+    ;; ...
+    #true))
 @end lisp
 
-The procedure must return @code{#t} on success.  It's brittle to rely on the return
+The procedure must return @code{#true} on success.  It's brittle to rely on the return
 value of the last expression used to tweak the phase because there is no
-guarantee it would be a @code{#t}.  Hence the trailing @code{#t} to ensure the right value
+guarantee it would be a @code{#true}.  Hence the trailing @code{#true} to ensure the right value
 is returned on success.
 
 @subsubsection Code staging
@@ -1118,7 +1121,7 @@ Some of those functions can be found in
 @samp{$GUIX_CHECKOUT/guix/guix/build/utils.scm}.  Most of them mirror the behaviour
 of the traditional Unix system commands:
 
-@table @asis
+@table @code
 @item which
 Like the @samp{which} system command.
 @item find-files
@@ -1142,6 +1145,9 @@ then restore the previous working directory.
 A ``@command{sed}-like'' function.
 @end table
 
+@xref{Build Utilities,,, guix, GNU Guix Reference Manual}, for more
+information on these utilities.
+
 @subsubsection Module prefix
 
 The license in our last example needs a prefix: this is because of how the
@@ -1352,6 +1358,7 @@ reference.
 * Running Guix on a Linode Server:: Running Guix on a Linode Server
 * Setting up a bind mount:: Setting up a bind mount in the file-systems definition.
 * Getting substitutes from Tor:: Configuring Guix daemon to get substitutes through Tor.
+* Setting up NGINX with Lua:: Configuring NGINX web-server to load Lua modules.
 @end menu
 
 @node Customizing the Kernel
@@ -1384,8 +1391,8 @@ creates a package.
                            #:key
                            ;; A function that takes an arch and a variant.
                            ;; See kernel-config for an example.
-                           (extra-version #f)
-                           (configuration-file #f)
+                           (extra-version #false)
+                           (configuration-file #false)
                            (defconfig "defconfig")
                            (extra-options %default-extra-linux-options)
                            (patches (list %boot-logo-patch)))
@@ -1428,7 +1435,7 @@ the @code{make-linux-libre} package definition:
       (begin
         (copy-file config ".config")
         (chmod ".config" #o666))
-      (invoke "make" ,defconfig))
+      (invoke "make" ,defconfig)))
 @end lisp
 
 Below is a sample kernel package.  The @code{linux-libre} package is nothing
@@ -1459,7 +1466,7 @@ it:
 @lisp
 (define %default-extra-linux-options
   `(;; https://lists.gnu.org/archive/html/guix-devel/2014-04/msg00039.html
-   ("CONFIG_DEVPTS_MULTIPLE_INSTANCES" . #t)
+   ("CONFIG_DEVPTS_MULTIPLE_INSTANCES" . #true)
    ;; Modules required for initrd:
    ("CONFIG_NET_9P" . m)
    ("CONFIG_NET_9P_VIRTIO" . m)
@@ -1476,9 +1483,9 @@ it:
   (string-join (map (match-lambda
                       ((option . 'm)
                        (string-append option "=m"))
-                      ((option . #t)
+                      ((option . #true)
                        (string-append option "=y"))
-                      ((option . #f)
+                      ((option . #false)
                        (string-append option "=n")))
                     options)
                "\n"))
@@ -1494,7 +1501,7 @@ And in the custom configure script from the `make-linux-libre` package:
   (display extra-configuration port)
   (close-port port))
 
-(invoke "make" "oldconfig"))))
+(invoke "make" "oldconfig")
 @end lisp
 
 So by not providing a configuration-file the @file{.config} starts blank, and
@@ -1865,7 +1872,7 @@ is below. Save the resulting file as @file{guix-config.scm}.
                (bootloader
                 (bootloader
                  (inherit grub-bootloader)
-                 (installer #~(const #t))))))
+                 (installer #~(const #true))))))
   (file-systems (cons (file-system
                         (device "/dev/sda")
                         (mount-point "/")
@@ -1897,7 +1904,7 @@ is below. Save the resulting file as @file{guix-config.scm}.
              (service openssh-service-type
                       (openssh-configuration
                        (openssh openssh-sans-x)
-                       (password-authentication? #f)
+                       (password-authentication? #false)
                        (authorized-keys
                         `(("janedoe" ,(local-file "janedoe_rsa.pub"))
                           ("root" ,(local-file "janedoe_rsa.pub"))))))
@@ -2113,6 +2120,63 @@ sudo herd set-http-proxy guix-daemon http://localhost:9250
 guix build --substitute-urls=https://bp7o7ckwlewr4slm.onion …
 @end example
 
+@node Setting up NGINX with Lua
+@section Setting up NGINX with Lua
+@cindex nginx, lua, openresty, resty
+
+NGINX could be extended with Lua scripts.
+
+Guix provides NGINX service with ability to load Lua module and specific
+Lua packages, and reply to requests by evaluating Lua scripts.
+
+The following example demonstrates system definition with configuration
+to evaluate @file{index.lua} Lua script on HTTP request to
+@uref{http://localhost/hello} endpoint:
+
+@example
+local shell = require "resty.shell"
+
+local stdin = ""
+local timeout = 1000  -- ms
+local max_size = 4096  -- byte
+
+local ok, stdout, stderr, reason, status =
+   shell.run([[/run/current-system/profile/bin/ls /tmp]], stdin, timeout, max_size)
+
+ngx.say(stdout)
+@end example
+
+@lisp
+(use-modules (gnu))
+(use-service-modules #;… web)
+(use-package-modules #;… lua)
+(operating-system
+  ;; …
+  (services
+   ;; …
+   (service nginx-service-type
+            (nginx-configuration
+             (modules
+              (list
+               (file-append nginx-lua-module "/etc/nginx/modules/ngx_http_lua_module.so")))
+             (lua-package-path (list lua-resty-core
+                                     lua-resty-lrucache
+                                     lua-resty-signal
+                                     lua-tablepool
+                                     lua-resty-shell))
+             (lua-package-cpath (list lua-resty-signal))
+             (server-blocks
+              (list (nginx-server-configuration
+                     (server-name '("localhost"))
+                     (listen '("80"))
+                     (root "/etc")
+                     (locations (list
+                                 (nginx-location-configuration
+                                  (uri "/hello")
+                                  (body (list #~(format #f "content_by_lua_file ~s;"
+                                                        #$(local-file "index.lua"))))))))))))))
+@end lisp
+
 @c *********************************************************************
 @node Advanced package management
 @chapter Advanced package management
diff --git a/doc/guix.texi b/doc/guix.texi
index 0be2c7303d..e275463eca 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -252,6 +252,7 @@ Programming Interface
 * Package Modules::             Packages from the programmer's viewpoint.
 * Defining Packages::           Defining new packages.
 * Build Systems::               Specifying how packages are built.
+* Build Utilities::             Helpers for your package definitions and more.
 * The Store::                   Manipulating the package store.
 * Derivations::                 Low-level interface to package derivations.
 * The Store Monad::             Purely functional interface to the store.
@@ -477,10 +478,10 @@ Packages are currently available on the following platforms:
 @table @code
 
 @item x86_64-linux
-Intel/AMD @code{x86_64} architecture, Linux-Libre kernel;
+Intel/AMD @code{x86_64} architecture, Linux-Libre kernel.
 
 @item i686-linux
-Intel 32-bit architecture (IA32), Linux-Libre kernel;
+Intel 32-bit architecture (IA32), Linux-Libre kernel.
 
 @item armhf-linux
 ARMv7-A architecture with hard float, Thumb-2 and NEON,
@@ -490,6 +491,16 @@ and Linux-Libre kernel.
 @item aarch64-linux
 little-endian 64-bit ARMv8-A processors, Linux-Libre kernel.
 
+@item i586-gnu
+@uref{https://hurd.gnu.org, GNU/Hurd} on the Intel 32-bit architecture
+(IA32).
+
+This configuration is experimental and under development.  The easiest
+way for you to give it a try is by setting up an instance of
+@code{hurd-vm-service-type} on your GNU/Linux machine
+(@pxref{transparent-emulation-qemu, @code{hurd-vm-service-type}}).
+@xref{Contributing}, on how to help!
+
 @item mips64el-linux (deprecated)
 little-endian 64-bit MIPS processors, specifically the Loongson series,
 n32 ABI, and Linux-Libre kernel.  This configuration is no longer fully
@@ -1077,8 +1088,9 @@ is requested, for instance via @code{guix build}, the daemon attempts to
 offload it to one of the machines that satisfy the constraints of the
 derivation, in particular its system types---e.g., @code{x86_64-linux}.
 A single machine can have multiple system types, either because its
-architecture natively supports it, via emulation (@pxref{Transparent
-Emulation with QEMU}), or both.  Missing prerequisites for the build are
+architecture natively supports it, via emulation
+(@pxref{transparent-emulation-qemu, Transparent Emulation with QEMU}),
+or both.  Missing prerequisites for the build are
 copied over SSH to the target machine, which then proceeds with the
 build; upon success the output(s) of the build are copied back to the
 initial machine.  The offload facility comes with a basic scheduler that
@@ -5824,7 +5836,7 @@ direct syscalls are not intercepted either, leading to erratic behavior.
 @vindex GUIX_EXECUTION_ENGINE
 When running a wrapped program, you can explicitly request one of the
 execution engines listed above by setting the
-@code{GUIX_EXECUTION_ENGINE} environment variable accordingly.
+@env{GUIX_EXECUTION_ENGINE} environment variable accordingly.
 @end quotation
 
 @cindex entry point, for Docker images
@@ -6074,6 +6086,7 @@ package definitions.
 * Package Modules::             Packages from the programmer's viewpoint.
 * Defining Packages::           Defining new packages.
 * Build Systems::               Specifying how packages are built.
+* Build Utilities::             Helpers for your package definitions and more.
 * The Store::                   Manipulating the package store.
 * Derivations::                 Low-level interface to package derivations.
 * The Store Monad::             Purely functional interface to the store.
@@ -6230,6 +6243,10 @@ represents the familiar GNU Build System, where packages may be
 configured, built, and installed with the usual @code{./configure &&
 make && make check && make install} command sequence.
 
+When you start packaging non-trivial software, you may need tools to
+manipulate those build phases, manipulate files, and so on.  @xref{Build
+Utilities}, for more on this.
+
 @item
 The @code{arguments} field specifies options for the build system
 (@pxref{Build Systems}).  Here it is interpreted by
@@ -6805,7 +6822,8 @@ The list of phases used for a particular package can be changed with the
 @end example
 
 means that all the phases described above will be used, except the
-@code{configure} phase.
+@code{configure} phase.  @xref{Build Utilities}, for more info on
+@code{modify-phases} and build phases in general.
 
 In addition, this build system ensures that the ``standard'' environment
 for GNU packages is available.  This includes tools such as GCC, libc,
@@ -6940,8 +6958,8 @@ In its @code{configure} phase, this build system will make any source inputs
 specified in the @code{#:cargo-inputs} and @code{#:cargo-development-inputs}
 parameters available to cargo.  It will also remove an included
 @code{Cargo.lock} file to be recreated by @code{cargo} during the
-@code{build} phase.  The @code{install} phase installs any crate the binaries
-if they are defined by the crate.
+@code{build} phase.  The @code{install} phase installs the binaries
+defined by the crate.
 @end defvr
 
 
@@ -7187,7 +7205,7 @@ implements the build procedure used by @uref{https://julialang.org/,
 julia} packages, which essentially is similar to running @samp{julia -e
 'using Pkg; Pkg.add(package)'} in an environment where
 @env{JULIA_LOAD_PATH} contains the paths to all Julia package inputs.
-Tests are run not run.
+Tests are run with @code{Pkg.test}.
 
 Julia packages require the source @code{file-name} to be the real name of the
 package, correctly capitalized.
@@ -7632,6 +7650,294 @@ with @code{build-expression->derivation} (@pxref{Derivations,
 @code{build-expression->derivation}}).
 @end defvr
 
+@node Build Utilities
+@section Build Utilities
+
+As soon as you start writing non-trivial package definitions
+(@pxref{Defining Packages}) or other build actions
+(@pxref{G-Expressions}), you will likely start looking for helpers for
+``shell-like'' actions---creating directories, copying and deleting
+files recursively, manipulating build phases, and so on.  The
+@code{(guix build utils)} module provides such utility procedures.
+
+Most build systems load @code{(guix build utils)} (@pxref{Build
+Systems}).  Thus, when writing custom build phases for your package
+definitions, you can usually assume those procedures are in scope.
+
+When writing G-expressions, you can import @code{(guix build utils)} on
+the ``build side'' using @code{with-imported-modules} and then put it in
+scope with the @code{use-modules} form (@pxref{Using Guile Modules,,,
+guile, GNU Guile Reference Manual}):
+
+@lisp
+(with-imported-modules '((guix build utils))  ;import it
+  (computed-file "empty-tree"
+                 #~(begin
+                     ;; Put it in scope.
+                     (use-modules (guix build utils))
+
+                     ;; Happily use its 'mkdir-p' procedure.
+                     (mkdir-p (string-append #$output "/a/b/c")))))
+@end lisp
+
+The remainder of this section is the reference for most of the utility
+procedures provided by @code{(guix build utils)}.
+
+@c TODO Document what's missing.
+
+@subsection Dealing with Store File Names
+
+This section documents procedures that deal with store file names.
+
+@deffn {Scheme Procedure} %store-directory
+Return the directory name of the store.
+@end deffn
+
+@deffn {Scheme Procedure} store-file-name? @var{file}
+Return true if @var{file} is in the store.
+@end deffn
+
+@deffn {Scheme Procedure} strip-store-file-name @var{file}
+Strip the @file{/gnu/store} and hash from @var{file}, a store file name.
+The result is typically a @code{"@var{package}-@var{version}"} string.
+@end deffn
+
+@deffn {Scheme Procedure} package-name->name+version @var{name}
+Given @var{name}, a package name like @code{"foo-0.9.1b"}, return two
+values: @code{"foo"} and @code{"0.9.1b"}.  When the version part is
+unavailable, @var{name} and @code{#f} are returned.  The first hyphen
+followed by a digit is considered to introduce the version part.
+@end deffn
+
+@subsection File Types
+
+The procedures below deal with files and file types.
+
+@deffn {Scheme Procedure} directory-exists? @var{dir}
+Return @code{#t} if @var{dir} exists and is a directory.
+@end deffn
+
+@deffn {Scheme Procedure} executable-file? @var{file}
+Return @code{#t} if @var{file} exists and is executable.
+@end deffn
+
+@deffn {Scheme Procedure} symbolic-link? @var{file}
+Return @code{#t} if @var{file} is a symbolic link (aka. a ``symlink'').
+@end deffn
+
+@deffn {Scheme Procedure} elf-file? @var{file}
+@deffnx {Scheme Procedure} ar-file? @var{file}
+@deffnx {Scheme Procedure} gzip-file? @var{file}
+Return @code{#t} if @var{file} is, respectively, an ELF file, an
+@code{ar} archive (such as a @file{.a} static library), or a gzip file.
+@end deffn
+
+@deffn {Scheme Procedure} reset-gzip-timestamp @var{file} [#:keep-mtime? #t]
+If @var{file} is a gzip file, reset its embedded timestamp (as with
+@command{gzip --no-name}) and return true.  Otherwise return @code{#f}.
+When @var{keep-mtime?} is true, preserve @var{file}'s modification time.
+@end deffn
+
+@subsection File Manipulation
+
+The following procedures and macros help create, modify, and delete
+files.  They provide functionality comparable to common shell utilities
+such as @command{mkdir -p}, @command{cp -r}, @command{rm -r}, and
+@command{sed}.  They complement Guile's extensive, but low-level, file
+system interface (@pxref{POSIX,,, guile, GNU Guile Reference Manual}).
+
+@deffn {Scheme Syntax} with-directory-excursion @var{directory} @var{body}@dots{}
+Run @var{body} with @var{directory} as the process's current directory.
+
+Essentially, this macro changes the current directory to @var{directory}
+before evaluating @var{body}, using @code{chdir} (@pxref{Processes,,,
+guile, GNU Guile Reference Manual}).  It changes back to the initial
+directory when the dynamic extent of @var{body} is left, be it @i{via}
+normal procedure return or @i{via} a non-local exit such as an
+exception.
+@end deffn
+
+@deffn {Scheme Procedure} mkdir-p @var{dir}
+Create directory @var{dir} and all its ancestors.
+@end deffn
+
+@deffn {Scheme Procedure} install-file @var{file} @var{directory}
+Create @var{directory} if it does not exist and copy @var{file} in there
+under the same name.
+@end deffn
+
+@deffn {Scheme Procedure} make-file-writable @var{file}
+Make @var{file} writable for its owner.
+@end deffn
+
+@deffn {Scheme Procedure} copy-recursively @var{source} @var{destination} @
+  [#:log (current-output-port)] [#:follow-symlinks? #f] [#:keep-mtime? #f]
+Copy @var{source} directory to @var{destination}.  Follow symlinks if
+@var{follow-symlinks?}  is true; otherwise, just preserve them.  When
+@var{keep-mtime?} is true, keep the modification time of the files in
+@var{source} on those of @var{destination}.  Write verbose output to the
+@var{log} port.
+@end deffn
+
+@deffn {Scheme Procedure} delete-file-recursively @var{dir} @
+  [#:follow-mounts? #f]
+Delete @var{dir} recursively, like @command{rm -rf}, without following
+symlinks.  Don't follow mount points either, unless @var{follow-mounts?}
+is true.  Report but ignore errors.
+@end deffn
+
+@deffn {Scheme Syntax} substitute* @var{file} @
+  ((@var{regexp} @var{match-var}@dots{}) @var{body}@dots{}) @dots{}
+Substitute @var{regexp} in @var{file} by the string returned by
+@var{body}.  @var{body} is evaluated with each @var{match-var} bound to
+the corresponding positional regexp sub-expression.  For example:
+
+@lisp
+(substitute* file
+  (("hello")
+   "good morning\n")
+  (("foo([a-z]+)bar(.*)$" all letters end)
+   (string-append "baz" letter end)))
+@end lisp
+
+Here, anytime a line of @var{file} contains @code{hello}, it is replaced
+by @code{good morning}.  Anytime a line of @var{file} matches the second
+regexp, @code{all} is bound to the complete match, @code{letters} is bound
+to the first sub-expression, and @code{end} is bound to the last one.
+
+When one of the @var{match-var} is @code{_}, no variable is bound to the
+corresponding match substring.
+
+Alternatively, @var{file} may be a list of file names, in which case
+they are all subject to the substitutions.
+
+Be careful about using @code{$} to match the end of a line; by itself it
+won't match the terminating newline of a line.
+@end deffn
+
+@subsection File Search
+
+@cindex file, searching
+This section documents procedures to search and filter files.
+
+@deffn {Scheme Procedure} file-name-predicate @var{regexp}
+Return a predicate that returns true when passed a file name whose base
+name matches @var{regexp}.
+@end deffn
+
+@deffn {Scheme Procedure} find-files @var{dir} [@var{pred}] @
+  [#:stat lstat] [#:directories? #f] [#:fail-on-error? #f]
+Return the lexicographically sorted list of files under @var{dir} for
+which @var{pred} returns true.  @var{pred} is passed two arguments: the
+absolute file name, and its stat buffer; the default predicate always
+returns true.  @var{pred} can also be a regular expression, in which
+case it is equivalent to @code{(file-name-predicate @var{pred})}.
+@var{stat} is used to obtain file information; using @code{lstat} means
+that symlinks are not followed.  If @var{directories?} is true, then
+directories will also be included.  If @var{fail-on-error?} is true,
+raise an exception upon error.
+@end deffn
+
+Here are a few examples where we assume that the current directory is
+the root of the Guix source tree:
+
+@lisp
+;; List all the regular files in the current directory.
+(find-files ".")
+@result{} ("./.dir-locals.el" "./.gitignore" @dots{})
+
+;; List all the .scm files under gnu/services.
+(find-files "gnu/services" "\\.scm$")
+@result{} ("gnu/services/admin.scm" "gnu/services/audio.scm" @dots{})
+
+;; List ar files in the current directory.
+(find-files "." (lambda (file stat) (ar-file? file)))
+@result{} ("./libformat.a" "./libstore.a" @dots{})
+@end lisp
+
+@deffn {Scheme Procedure} which @var{program}
+Return the complete file name for @var{program} as found in
+@code{$PATH}, or @code{#f} if @var{program} could not be found.
+@end deffn
+
+@subsection Build Phases
+
+@cindex build phases
+The @code{(guix build utils)} also contains tools to manipulate
+@dfn{build phases} as found in @code{gnu-build-system} and in fact most
+build systems (@pxref{Build Systems}).  Build phases are represented as
+association lists or ``alists'' (@pxref{Association Lists,,, guile, GNU
+Guile Reference Manual}) where each key is a symbol for the name of the
+phase, and the associated value is a procedure that accepts an arbitrary
+number of arguments.
+
+Guile core and the @code{(srfi srfi-1)} module both provide tools to
+manipulate alists.  The @code{(guix build utils)} module complements
+those with tools written with build phases in mind.
+
+@cindex build phases, modifying
+@deffn {Scheme Syntax} modify-phases @var{phases} @var{clause}@dots{}
+Modify @var{phases} sequentially as per each @var{clause}, which may
+have one of the following forms:
+
+@lisp
+(delete @var{old-phase-name})
+(replace @var{old-phase-name} @var{new-phase})
+(add-before @var{old-phase-name} @var{new-phase-name} @var{new-phase})
+(add-after @var{old-phase-name} @var{new-phase-name} @var{new-phase})
+@end lisp
+
+Where every @var{phase-name} above is an expression evaluating to a
+symbol, and @var{new-phase} an expression evaluating to a procedure.
+@end deffn
+
+The example below is taken from the definition of the @code{grep}
+package.  It adds a phase to run after the @code{install} phase, called
+@code{fix-egrep-and-fgrep}.  That phase is a procedure (@code{lambda*}
+is for anonymous procedures) that takes a @code{#:outputs} keyword
+argument and ignores extra keyword arguments (@pxref{Optional
+Arguments,,, guile, GNU Guile Reference Manual}, for more on
+@code{lambda*} and optional and keyword arguments.)  The phase uses
+@code{substitute*} to modify the installed @file{egrep} and @file{fgrep}
+scripts so that they refer to @code{grep} by its absolute file name:
+
+@lisp
+(modify-phases %standard-phases
+  (add-after 'install 'fix-egrep-and-fgrep
+    ;; Patch 'egrep' and 'fgrep' to execute 'grep' via its
+    ;; absolute file name instead of searching for it in $PATH.
+    (lambda* (#:key outputs #:allow-other-keys)
+      (let* ((out (assoc-ref outputs "out"))
+             (bin (string-append out "/bin")))
+        (substitute* (list (string-append bin "/egrep")
+                           (string-append bin "/fgrep"))
+          (("^exec grep")
+           (string-append "exec " bin "/grep")))
+        #t))))
+@end lisp
+
+In the example below, phases are modified in two ways: the standard
+@code{configure} phase is deleted, presumably because the package does
+not have a @file{configure} script or anything similar, and the default
+@code{install} phase is replaced by one that manually copies the
+executable files to be installed:
+
+@lisp
+(modify-phases %standard-phases
+  (delete 'configure)      ;no 'configure' script
+  (replace 'install
+    (lambda* (#:key outputs #:allow-other-keys)
+      ;; The package's Makefile doesn't provide an "install"
+      ;; rule so do it by ourselves.
+      (let ((bin (string-append (assoc-ref outputs "out")
+                                "/bin")))
+        (install-file "footswitch" bin)
+        (install-file "scythe" bin)
+        #t))))
+@end lisp
+
+@c TODO: Add more examples.
+
 @node The Store
 @section The Store
 
@@ -10091,7 +10397,7 @@ package expressions for all those packages that are not yet in Guix.
 
 When @option{--archive=bioconductor} is added, metadata is imported from
 @uref{https://www.bioconductor.org/, Bioconductor}, a repository of R
-packages for for the analysis and comprehension of high-throughput
+packages for the analysis and comprehension of high-throughput
 genomic data in bioinformatics.
 
 Information is extracted from the @file{DESCRIPTION} file contained in the
@@ -17838,10 +18144,10 @@ List of settings to set in @file{daemon.conf}, formatted just like
 @var{client-conf}.
 
 @item @var{script-file} (default: @code{(file-append pulseaudio "/etc/pulse/default.pa")})
-Script file to use as as @file{default.pa}.
+Script file to use as @file{default.pa}.
 
 @item @var{system-script-file} (default: @code{(file-append pulseaudio "/etc/pulse/system.pa")})
-Script file to use as as @file{system.pa}.
+Script file to use as @file{system.pa}.
 @end table
 @end deftp
 
@@ -21970,7 +22276,29 @@ names of loadable modules, as in this example:
 (modules
  (list
   (file-append nginx-accept-language-module "\
-/etc/nginx/modules/ngx_http_accept_language_module.so")))
+/etc/nginx/modules/ngx_http_accept_language_module.so")
+  (file-append nginx-lua-module "\
+/etc/nginx/modules/ngx_http_lua_module.so")))
+@end lisp
+
+@item @code{lua-package-path} (default: @code{'()})
+List of nginx lua packages to load.  This should be a list of package
+names of loadable lua modules, as in this example:
+
+@lisp
+(lua-package-path (list lua-resty-core
+                        lua-resty-lrucache
+                        lua-resty-signal
+                        lua-tablepool
+                        lua-resty-shell))
+@end lisp
+
+@item @code{lua-package-cpath} (default: @code{'()})
+List of nginx lua C packages to load.  This should be a list of package
+names of loadable lua C modules, as in this example:
+
+@lisp
+(lua-package-cpath (list lua-resty-signal))
 @end lisp
 
 @item @code{global-directives} (default: @code{'((events . ()))})
@@ -22973,7 +23301,7 @@ This type has the following parameters:
 
 @table @asis
 @item @code{id} (default: @code{""})
-An identifier for ether configuration fields to refer to this key. IDs must be
+An identifier for other configuration fields to refer to this key. IDs must be
 unique and must not be empty.
 
 @item @code{address} (default: @code{'()})
@@ -24964,7 +25292,7 @@ mixer, the @code{null} mixer (allows setting the volume, but with no
 effect; this can be used as a trick to implement an external mixer
 External Mixer) or no mixer (@code{none}).
 
-@item @code{extra-options} (default: @code{'()"})
+@item @code{extra-options} (default: @code{'()})
 An association list of option symbols to string values to be appended to
 the audio output configuration.
 
@@ -24989,13 +25317,14 @@ an HTTP audio streaming output.
 
 
 @node Virtualization Services
-@subsection Virtualization services
+@subsection Virtualization Services
 
 The @code{(gnu services virtualization)} module provides services for
 the libvirt and virtlog daemons, as well as other virtualization-related
 services.
 
 @subsubheading Libvirt daemon
+
 @code{libvirtd} is the server side daemon component of the libvirt
 virtualization management system. This daemon runs on host servers
 and performs required management tasks for virtualized guests.
@@ -25022,7 +25351,7 @@ Libvirt package.
 
 @deftypevr {@code{libvirt-configuration} parameter} boolean listen-tls?
 Flag listening for secure TLS connections on the public TCP/IP port.
-must set @code{listen} for this to have any effect.
+You must set @code{listen} for this to have any effect.
 
 It is necessary to setup a CA and issue server certificates before using
 this capability.
@@ -25032,28 +25361,28 @@ Defaults to @samp{#t}.
 @end deftypevr
 
 @deftypevr {@code{libvirt-configuration} parameter} boolean listen-tcp?
-Listen for unencrypted TCP connections on the public TCP/IP port.  must
+Listen for unencrypted TCP connections on the public TCP/IP port.  You must
 set @code{listen} for this to have any effect.
 
 Using the TCP socket requires SASL authentication by default.  Only SASL
 mechanisms which support data encryption are allowed.  This is
-DIGEST_MD5 and GSSAPI (Kerberos5)
+DIGEST_MD5 and GSSAPI (Kerberos5).
 
 Defaults to @samp{#f}.
 
 @end deftypevr
 
 @deftypevr {@code{libvirt-configuration} parameter} string tls-port
-Port for accepting secure TLS connections This can be a port number, or
-service name
+Port for accepting secure TLS connections.   This can be a port number,
+or service name.
 
 Defaults to @samp{"16514"}.
 
 @end deftypevr
 
 @deftypevr {@code{libvirt-configuration} parameter} string tcp-port
-Port for accepting insecure TCP connections This can be a port number,
-or service name
+Port for accepting insecure TCP connections.  This can be a port number,
+or service name.
 
 Defaults to @samp{"16509"}.
 
@@ -25365,7 +25694,7 @@ Defaults to @samp{3}.
 Logging filters.
 
 A filter allows to select a different logging level for a given category
-of logs The format for a filter is one of:
+of logs.  The format for a filter is one of:
 
 @itemize @bullet
 @item
@@ -25696,7 +26025,8 @@ Maximum number of backup files to keep.
 Defaults to @samp{3}
 
 @end deftypevr
-@node Transparent Emulation with QEMU
+
+@anchor{transparent-emulation-qemu}
 @subsubheading Transparent Emulation with QEMU
 
 @cindex emulation
@@ -25895,7 +26225,7 @@ By default, it produces
 with forwarded ports:
 
 @example
-@var{ssh-port}: @code{(+ 11004 (* 1000 @var{ID}))}
+@var{secrets-port}: @code{(+ 11004 (* 1000 @var{ID}))}
 @var{ssh-port}: @code{(+ 10022 (* 1000 @var{ID}))}
 @var{vnc-port}: @code{(+ 15900 (* 1000 @var{ID}))}
 @end example
@@ -26540,7 +26870,7 @@ When true, the daemon performs additional logging for debugging purposes.
 @defvr {Scheme Variable} ganeti-watcher-service-type
 @command{ganeti-watcher} is a script designed to run periodically and ensure
 the health of a cluster.  It will automatically restart instances that have
-stopped without Ganetis consent, and repairs DRBD links in case a node has
+stopped without Ganeti's consent, and repairs DRBD links in case a node has
 rebooted.  It also archives old cluster jobs and restarts Ganeti daemons
 that are not running.  If the cluster parameter @code{ensure_node_health}
 is set, the watcher will also shutdown instances and DRBD devices if the
@@ -27977,7 +28307,7 @@ allocation plan in the database.
 
 @item @code{hooks} (default: @var{'()})
 An association list of hooks.  These provide a way to execute arbitrary
-code upon certian events, like a build result being processed.
+code upon certain events, like a build result being processed.
 
 @item @code{guile} (default: @code{guile-3.0-latest})
 The Guile package with which to run the Guix Build Coordinator.
@@ -28254,22 +28584,22 @@ This is the data type representing the configuration for the zram-device
 service.
 
 @table @asis
-@item @code{size} (default @var{"1G"})
+@item @code{size} (default @code{"1G"})
 This is the amount of space you wish to provide for the zram device.  It
 accepts a string and can be a number of bytes or use a suffix, eg.:
-@var{"512M"} or @var{1024000}.
-@item @code{compression-algorithm} (default @var{'lzo})
+@code{"512M"} or @code{1024000}.
+@item @code{compression-algorithm} (default @code{'lzo})
 This is the compression algorithm you wish to use.  It is difficult to
 list all the possible compression options, but common ones supported by
-Guix's Linux Libre Kernel include @var{'lzo}, @var{'lz4} and @var{'zstd}.
-@item @code{memory-limit} (default @var{0})
+Guix's Linux Libre Kernel include @code{'lzo}, @code{'lz4} and @code{'zstd}.
+@item @code{memory-limit} (default @code{0})
 This is the maximum amount of memory which the zram device can use.
 Setting it to '0' disables the limit.  While it is generally expected
 that compression will be 2:1, it is possible that uncompressable data
 can be written to swap and this is a method to limit how much memory can
 be used.  It accepts a string and can be a number of bytes or use a
-suffix, eg.: @var{"2G"}.
-@item @code{priority} (default @var{-1})
+suffix, eg.: @code{"2G"}.
+@item @code{priority} (default @code{-1})
 This is the priority of the swap device created from the zram device.
 @code{swapon} accepts values between -1 and 32767, with higher values
 indicating higher priority.  Higher priority swap will generally be used
@@ -28682,7 +29012,7 @@ The @code{(gnu services science)} module provides the following service.
 @defvr {Scheme Variable} rshiny-service-type
 
 This is a type of service which is used to run a webapp created with
-@code{r-shiny}.  This service sets the @code{R_LIBS_USER} environment
+@code{r-shiny}.  This service sets the @env{R_LIBS_USER} environment
 variable and runs the provided script to call @code{runApp}.
 
 @deftp {Data Type} rshiny-configuration
@@ -29477,7 +29807,7 @@ Data type representing the configuration of the GRUB theme.
 
 @table @asis
 @item @code{gfxmode} (default: @code{'("auto")})
-The GRUB @code{gfxmode} to set (a list of screen resolution strings, see
+The GRUB @code{gfxmode} to set (a list of screen resolution strings,
 @pxref{gfxmode,,, grub, GNU GRUB manual}).
 @end table
 @end deftp