doc: Publish the Slovak translation of the cookbook.
[jackhill/guix/guix.git] / doc / build.scm
CommitLineData
ccadafdc 1;;; GNU Guix --- Functional package management for GNU
560fd990 2;;; Copyright © 2019-2022 Ludovic Courtès <ludo@gnu.org>
7c65fc37 3;;; Copyright © 2020 Björn Höfling <bjoern.hoefling@bjoernhoefling.de>
ccadafdc
LC
4;;;
5;;; This file is part of GNU Guix.
6;;;
7;;; GNU Guix is free software; you can redistribute it and/or modify it
8;;; under the terms of the GNU General Public License as published by
9;;; the Free Software Foundation; either version 3 of the License, or (at
10;;; your option) any later version.
11;;;
12;;; GNU Guix is distributed in the hope that it will be useful, but
13;;; WITHOUT ANY WARRANTY; without even the implied warranty of
14;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;;; GNU General Public License for more details.
16;;;
17;;; You should have received a copy of the GNU General Public License
18;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
19
20
21;; This file contains machinery to build HTML and PDF copies of the manual
22;; that can be readily published on the web site. To do that, run:
23;;
24;; guix build -f build.scm
25;;
26;; The result is a directory hierarchy that can be used as the manual/
27;; sub-directory of the web site.
28
29(use-modules (guix)
30 (guix gexp)
31 (guix git)
32 (guix git-download)
7854bbeb 33 (guix utils)
ccadafdc
LC
34 (git)
35 (gnu packages base)
97ce30cc 36 (gnu packages compression)
ccadafdc
LC
37 (gnu packages gawk)
38 (gnu packages gettext)
39 (gnu packages guile)
f8c143a7 40 (gnu packages guile-xyz)
e591541d 41 (gnu packages iso-codes)
ccadafdc
LC
42 (gnu packages texinfo)
43 (gnu packages tex)
0f7d0743 44 (ice-9 match)
97ce30cc 45 (srfi srfi-1)
ccadafdc 46 (srfi srfi-19)
db1d4453 47 (srfi srfi-26)
ccadafdc
LC
48 (srfi srfi-71))
49
50(define file-append*
51 (@@ (guix self) file-append*))
52
53(define translated-texi-manuals
560fd990 54 (@@ (guix self) translate-texi-manuals))
ccadafdc
LC
55
56(define info-manual
57 (@@ (guix self) info-manual))
58
cacb5576
LC
59(define %manual
60 ;; The manual to build--i.e., the base name of a .texi file, such as "guix"
61 ;; or "guix-cookbook".
62 (or (getenv "GUIX_MANUAL")
63 "guix"))
64
ccadafdc 65(define %languages
fef2f08b 66 ;; The cookbook is not translated in the same languages as the manual
7c65fc37 67 (if (string=? %manual "guix-cookbook")
be84fb70 68 '("de" "en" "fr" "sk")
7c65fc37 69 '("de" "en" "es" "fr" "ru" "zh_CN")))
ccadafdc
LC
70
71(define (texinfo-manual-images source)
72 "Return a directory containing all the images used by the user manual, taken
73from SOURCE, the root of the source tree."
74 (define graphviz
75 (module-ref (resolve-interface '(gnu packages graphviz))
76 'graphviz))
77
78 (define images
79 (file-append* source "doc/images"))
80
81 (define build
82 (with-imported-modules '((guix build utils))
83 #~(begin
84 (use-modules (guix build utils)
85 (srfi srfi-26))
86
87 (define (dot->image dot-file format)
88 (invoke #+(file-append graphviz "/bin/dot")
89 "-T" format "-Gratio=.9" "-Gnodesep=.005"
90 "-Granksep=.00005" "-Nfontsize=9"
91 "-Nheight=.1" "-Nwidth=.1"
92 "-o" (string-append #$output "/"
93 (basename dot-file ".dot")
94 "." format)
95 dot-file))
96
97 ;; Build graphs.
98 (mkdir-p #$output)
99 (for-each (lambda (dot-file)
100 (for-each (cut dot->image dot-file <>)
101 '("png" "pdf")))
102 (find-files #$images "\\.dot$"))
103
104 ;; Copy other PNGs.
105 (for-each (lambda (png-file)
106 (install-file png-file #$output))
107 (find-files #$images "\\.png$")))))
108
109 (computed-file "texinfo-manual-images" build))
110
111(define* (texinfo-manual-source source #:key
112 (version "0.0")
113 (languages %languages)
114 (date 1))
115 "Gather all the source files of the Texinfo manuals from SOURCE--.texi file
116as well as images, OS examples, and translations."
117 (define documentation
118 (file-append* source "doc"))
119
120 (define examples
121 (file-append* source "gnu/system/examples"))
122
123 (define build
124 (with-imported-modules '((guix build utils))
125 #~(begin
126 (use-modules (guix build utils)
127 (srfi srfi-19))
128
129 (define (make-version-texi language)
130 ;; Create the 'version.texi' file for LANGUAGE.
131 (let ((file (if (string=? language "en")
132 "version.texi"
133 (string-append "version-" language ".texi"))))
134 (call-with-output-file (string-append #$output "/" file)
135 (lambda (port)
136 (let* ((version #$version)
137 (time (make-time time-utc 0 #$date))
138 (date (time-utc->date time)))
139 (format port "
140@set UPDATED ~a
141@set UPDATED-MONTH ~a
142@set EDITION ~a
143@set VERSION ~a\n"
144 (date->string date "~e ~B ~Y")
145 (date->string date "~B ~Y")
146 version version))))))
147
8c23d7a1 148 (install-file #$(file-append documentation "/htmlxref.cnf")
ccadafdc
LC
149 #$output)
150
151 (for-each (lambda (texi)
152 (install-file texi #$output))
cb26edc8 153 (append (find-files #$documentation "\\.(texi|scm|json)$")
ccadafdc
LC
154 (find-files #$(translated-texi-manuals source)
155 "\\.texi$")))
156
157 ;; Create 'version.texi'.
158 (for-each make-version-texi '#$languages)
159
160 ;; Copy configuration templates that the manual includes.
161 (for-each (lambda (template)
162 (copy-file template
163 (string-append
164 #$output "/os-config-"
165 (basename template ".tmpl")
166 ".texi")))
167 (find-files #$examples "\\.tmpl$"))
168
169 (symlink #$(texinfo-manual-images source)
170 (string-append #$output "/images")))))
171
172 (computed-file "texinfo-manual-source" build))
173
174(define %web-site-url
175 ;; URL of the web site home page.
176 (or (getenv "GUIX_WEB_SITE_URL")
177 "/software/guix/"))
178
179(define %makeinfo-html-options
180 ;; Options passed to 'makeinfo --html'.
2f000f2e
JL
181 '("--css-ref=https://www.gnu.org/software/gnulib/manual.css"
182 "-c" "EXTRA_HEAD=<meta name=\"viewport\" \
183content=\"width=device-width, initial-scale=1\" />"))
ccadafdc 184
0f7d0743
LC
185(define (normalize-language-code language) ;XXX: deduplicate
186 ;; Normalize LANGUAGE. For instance, "zh_CN" becomes "zh-cn".
187 (string-map (match-lambda
188 (#\_ #\-)
189 (chr chr))
190 (string-downcase language)))
191
192(define* (html-manual-identifier-index manual base-url
193 #:key
194 (name "html-manual-identifier-index"))
195 "Return an index of all the identifiers that appear in MANUAL, a
196makeinfo-generated manual. The index is a file that contains an alist; each
197key is an identifier and the associated value is the URL reference pointing to
198that identifier. The URL is constructed by concatenating BASE-URL to the
199actual file name."
200 (define build
25db3b2f 201 (with-extensions (list guile-lib)
0f7d0743
LC
202 (with-imported-modules '((guix build utils))
203 #~(begin
204 (use-modules (guix build utils)
205 (htmlprag)
206 (srfi srfi-1)
207 (srfi srfi-26)
208 (ice-9 ftw)
209 (ice-9 match)
210 (ice-9 threads)
211 (ice-9 pretty-print))
212
25db3b2f
MC
213 (%strict-tokenizer? #t)
214
0f7d0743
LC
215 (define file-url
216 (let ((prefix (string-append #$manual "/")))
217 (lambda (file)
218 ;; Return the URL for FILE.
219 (let ((file (string-drop file (string-length prefix)))
220 (base #$base-url))
221 (if (string-null? base)
222 file
223 (string-append base "/" file))))))
224
225 (define (underscore-decode str)
226 ;; Decode STR, an "underscore-encoded" string as produced by
227 ;; makeinfo for indexes, such as "_0025base_002dservices" for
228 ;; "%base-services".
229 (let loop ((str str)
230 (result '()))
231 (match (string-index str #\_)
232 (#f
233 (string-concatenate-reverse (cons str result)))
234 (index
235 (let ((char (string->number
236 (substring str (+ index 1) (+ index 5))
237 16)))
238 (loop (string-drop str (+ index 5))
239 (append (list (string (integer->char char))
240 (string-take str index))
241 result)))))))
242
243 (define (anchor-id->key id)
244 ;; Convert ID, an anchor ID such as
245 ;; "index-pam_002dlimits_002dservice" to the corresponding key,
246 ;; "pam-limits-service" in this example. Drop the suffix of
247 ;; duplicate anchor IDs like "operating_002dsystem-1".
248 (let ((id (if (any (cut string-suffix? <> id)
249 '("-1" "-2" "-3" "-4" "-5"))
250 (string-drop-right id 2)
251 id)))
252 (underscore-decode
253 (string-drop id (string-length "index-")))))
254
255 (define* (collect-anchors file #:optional (anchors '()))
256 ;; Collect the anchors that appear in FILE, a makeinfo-generated
257 ;; file. Grab those from <dt> tags, which corresponds to
258 ;; Texinfo @deftp, @defvr, etc. Return ANCHORS augmented with
259 ;; more name/reference pairs.
260 (define string-or-entity?
261 (match-lambda
262 ((? string?) #t)
263 (('*ENTITY* _ ...) #t)
264 (_ #f)))
265
266 (define (worthy-entry? lst)
267 ;; Attempt to match:
268 ;; Scheme Variable: <strong>x</strong>
269 ;; but not:
270 ;; <code>cups-configuration</code> parameter: …
271 (let loop ((lst lst))
272 (match lst
273 (((? string-or-entity?) rest ...)
274 (loop rest))
275 ((('strong _ ...) _ ...)
276 #t)
d66a4eac
LC
277 ((('span ('@ ('class "symbol-definition-category"))
278 (? string-or-entity?) ...) rest ...)
279 #t)
280 (x
281 #f))))
0f7d0743
LC
282
283 (let ((shtml (call-with-input-file file html->shtml)))
284 (let loop ((shtml shtml)
285 (anchors anchors))
286 (match shtml
d66a4eac 287 (('dt ('@ ('id id) _ ...) rest ...)
0f7d0743
LC
288 (if (and (string-prefix? "index-" id)
289 (worthy-entry? rest))
290 (alist-cons (anchor-id->key id)
291 (string-append (file-url file)
292 "#" id)
293 anchors)
294 anchors))
295 ((tag ('@ _ ...) body ...)
296 (fold loop anchors body))
297 ((tag body ...)
298 (fold loop anchors body))
299 (_ anchors)))))
300
301 (define (html-files directory)
302 ;; Return the list of HTML files under DIRECTORY.
303 (map (cut string-append directory "/" <>)
304 (scandir #$manual (lambda (file)
305 (string-suffix? ".html" file)))))
306
307 (define anchors
308 (sort (concatenate
309 (n-par-map (parallel-job-count)
310 (cut collect-anchors <>)
311 (html-files #$manual)))
312 (match-lambda*
313 (((key1 . url1) (key2 . url2))
314 (if (string=? key1 key2)
315 (string<? url1 url2)
316 (string<? key1 key2))))))
317
318 (call-with-output-file #$output
319 (lambda (port)
320 (display ";; Identifier index for the manual.\n\n"
321 port)
322 (pretty-print anchors port)))))))
323
324 (computed-file name build))
325
326(define* (html-identifier-indexes manual directory-suffix
327 #:key (languages %languages)
328 (manual-name %manual)
329 (base-url (const "")))
330 (map (lambda (language)
331 (let ((language (normalize-language-code language)))
332 (list language
333 (html-manual-identifier-index
334 (file-append manual "/" language directory-suffix)
335 (base-url language)
336 #:name (string-append manual-name "-html-index-"
337 language)))))
338 languages))
339
f8c143a7
LC
340(define* (syntax-highlighted-html input
341 #:key
342 (name "highlighted-syntax")
0f7d0743
LC
343 (languages %languages)
344 (mono-node-indexes
345 (html-identifier-indexes input ""
346 #:languages
347 languages))
348 (split-node-indexes
349 (html-identifier-indexes input
350 "/html_node"
351 #:languages
352 languages))
f8c143a7
LC
353 (syntax-css-url
354 "/static/base/css/code.css"))
355 "Return a derivation called NAME that processes all the HTML files in INPUT
356to (1) add them a link to SYNTAX-CSS-URL, and (2) highlight the syntax of all
357its <pre class=\"lisp\"> blocks (as produced by 'makeinfo --html')."
358 (define build
25db3b2f 359 (with-extensions (list guile-lib guile-syntax-highlight)
f8c143a7
LC
360 (with-imported-modules '((guix build utils))
361 #~(begin
362 (use-modules (htmlprag)
363 (syntax-highlight)
364 (syntax-highlight scheme)
365 (syntax-highlight lexers)
366 (guix build utils)
da9deba1 367 (srfi srfi-1)
deac7bf6 368 (srfi srfi-26)
f8c143a7 369 (ice-9 match)
da9deba1
LC
370 (ice-9 threads)
371 (ice-9 vlist))
f8c143a7 372
25db3b2f
MC
373 (%strict-tokenizer? #t)
374
012c93e9
LC
375 (define (pair-open/close lst)
376 ;; Pair 'open' and 'close' tags produced by 'highlights' and
377 ;; produce nested 'paren' tags instead.
378 (let loop ((lst lst)
379 (level 0)
380 (result '()))
381 (match lst
382 ((('open open) rest ...)
383 (call-with-values
384 (lambda ()
385 (loop rest (+ 1 level) '()))
386 (lambda (inner close rest)
387 (loop rest level
388 (cons `(paren ,level ,open ,inner ,close)
389 result)))))
390 ((('close str) rest ...)
391 (if (> level 0)
392 (values (reverse result) str rest)
393 (begin
394 (format (current-error-port)
395 "warning: extra closing paren; context:~% ~y~%"
396 (reverse result))
397 (loop rest 0 (cons `(close ,str) result)))))
398 ((item rest ...)
399 (loop rest level (cons item result)))
400 (()
401 (when (> level 0)
402 (format (current-error-port)
403 "warning: missing ~a closing parens; context:~% ~y%"
404 level (reverse result)))
405 (values (reverse result) "" '())))))
406
da9deba1 407 (define (highlights->sxml* highlights anchors)
012c93e9
LC
408 ;; Like 'highlights->sxml', but handle nested 'paren tags. This
409 ;; allows for paren matching highlights via appropriate CSS
da9deba1
LC
410 ;; "hover" properties. When a symbol is encountered, look it up
411 ;; in ANCHORS, a vhash, and emit the corresponding href, if any.
012c93e9
LC
412 (define (tag->class tag)
413 (string-append "syntax-" (symbol->string tag)))
414
415 (map (match-lambda
416 ((? string? str) str)
417 (('paren level open (body ...) close)
418 `(span (@ (class ,(string-append "syntax-paren"
419 (number->string level))))
420 ,open
421 (span (@ (class "syntax-symbol"))
da9deba1 422 ,@(highlights->sxml* body anchors))
012c93e9 423 ,close))
da9deba1
LC
424 (('symbol text)
425 ;; Check whether we can emit a hyperlink for TEXT.
426 (match (vhash-assoc text anchors)
427 (#f
428 `(span (@ (class ,(tag->class 'symbol))) ,text))
429 ((_ . target)
430 `(a (@ (class ,(tag->class 'symbol)) (href ,target))
431 ,text))))
012c93e9
LC
432 ((tag text)
433 `(span (@ (class ,(tag->class tag))) ,text)))
434 highlights))
435
f8c143a7
LC
436 (define entity->string
437 (match-lambda
438 ("rArr" "⇒")
295c6a7e 439 ("rarr" "→")
f8c143a7
LC
440 ("hellip" "…")
441 ("rsquo" "’")
c946e146 442 ("nbsp" " ")
f8c143a7
LC
443 (e (pk 'unknown-entity e) (primitive-exit 2))))
444
445 (define (concatenate-snippets pieces)
446 ;; Concatenate PIECES, which contains strings and entities,
447 ;; replacing entities with their corresponding string.
448 (let loop ((pieces pieces)
449 (strings '()))
450 (match pieces
451 (()
452 (string-concatenate-reverse strings))
453 (((? string? str) . rest)
454 (loop rest (cons str strings)))
455 ((('*ENTITY* "additional" entity) . rest)
456 (loop rest (cons (entity->string entity) strings)))
457 ((('span _ lst ...) . rest) ;for <span class="roman">
458 (loop (append lst rest) strings))
fe409700
LC
459 ((('var name) . rest) ;for @var{name} within @lisp
460 (loop rest (cons name strings))) ;XXX: losing formatting
f8c143a7
LC
461 (something
462 (pk 'unsupported-code-snippet something)
463 (primitive-exit 1)))))
464
d66a4eac
LC
465 (define (highlight-definition id category symbol args)
466 ;; Produce stylable HTML for the given definition (an @deftp,
467 ;; @deffn, or similar).
468 `(dt (@ (id ,id) (class "symbol-definition"))
469 (span (@ (class "symbol-definition-category"))
470 ,@category)
471 (span (@ (class "symbol-definition-prototype"))
472 ,symbol " " ,@args)))
473
474 (define (space? obj)
475 (and (string? obj)
476 (string-every char-set:whitespace obj)))
477
da9deba1 478 (define (syntax-highlight sxml anchors)
f8c143a7 479 ;; Recurse over SXML and syntax-highlight code snippets.
da9deba1
LC
480 (let loop ((sxml sxml))
481 (match sxml
482 (('*TOP* decl body ...)
483 `(*TOP* ,decl ,@(map loop body)))
484 (('head things ...)
485 `(head ,@things
486 (link (@ (rel "stylesheet")
487 (type "text/css")
488 (href #$syntax-css-url)))))
489 (('pre ('@ ('class "lisp")) code-snippet ...)
490 `(pre (@ (class "lisp"))
491 ,@(highlights->sxml*
492 (pair-open/close
493 (highlight lex-scheme
494 (concatenate-snippets code-snippet)))
495 anchors)))
d66a4eac
LC
496
497 ;; Replace the ugly <strong> used for @deffn etc., which
498 ;; translate to <dt>, with more stylable markup.
499 (('dt (@ ('id id)) category ... ('strong thing))
500 (highlight-definition id category thing '()))
501 (('dt (@ ('id id)) category ... ('strong thing)
502 (? space?) ('em args ...))
503 (highlight-definition id category thing args))
504
da9deba1
LC
505 ((tag ('@ attributes ...) body ...)
506 `(,tag (@ ,@attributes) ,@(map loop body)))
507 ((tag body ...)
508 `(,tag ,@(map loop body)))
509 ((? string? str)
510 str))))
511
da9deba1 512 (define (process-html file anchors)
f8c143a7
LC
513 ;; Parse FILE and perform syntax highlighting for its Scheme
514 ;; snippets. Install the result to #$output.
515 (format (current-error-port) "processing ~a...~%" file)
516 (let* ((shtml (call-with-input-file file html->shtml))
da9deba1 517 (highlighted (syntax-highlight shtml anchors))
f8c143a7
LC
518 (base (string-drop file (string-length #$input)))
519 (target (string-append #$output base)))
520 (mkdir-p (dirname target))
521 (call-with-output-file target
522 (lambda (port)
523 (write-shtml-as-html highlighted port)))))
524
525 (define (copy-as-is file)
526 ;; Copy FILE as is to #$output.
527 (let* ((base (string-drop file (string-length #$input)))
528 (target (string-append #$output base)))
529 (mkdir-p (dirname target))
530 (catch 'system-error
531 (lambda ()
532 (if (eq? 'symlink (stat:type (lstat file)))
533 (symlink (readlink file) target)
534 (link file target)))
535 (lambda args
536 (let ((errno (system-error-errno args)))
537 (pk 'error-link file target (strerror errno))
538 (primitive-exit 3))))))
539
da9deba1
LC
540 (define (html? file stat)
541 (string-suffix? ".html" file))
542
0f7d0743
LC
543 (define language+node-anchors
544 (match-lambda
545 ((language files ...)
546 (cons language
547 (fold (lambda (file vhash)
548 (let ((alist (call-with-input-file file read)))
549 ;; Use 'fold-right' so that the first entry
550 ;; wins (e.g., "car" from "Pairs" rather than
551 ;; from "rnrs base" in the Guile manual).
552 (fold-right (match-lambda*
553 (((key . value) vhash)
554 (vhash-cons key value vhash)))
555 vhash
556 alist)))
557 vlist-null
558 files)))))
559
560 (define mono-node-anchors
561 ;; List of language/vhash pairs, where each vhash maps an
562 ;; identifier to the corresponding URL in a single-page manual.
563 (map language+node-anchors '#$mono-node-indexes))
564
565 (define multi-node-anchors
566 ;; Likewise for split-node manuals.
567 (map language+node-anchors '#$split-node-indexes))
568
f8c143a7
LC
569 ;; Install a UTF-8 locale so we can process UTF-8 files.
570 (setenv "GUIX_LOCPATH"
571 #+(file-append glibc-utf8-locales "/lib/locale"))
572 (setlocale LC_ALL "en_US.utf8")
573
da9deba1 574 ;; First process the mono-node 'guix.html' files.
0f7d0743
LC
575 (for-each (match-lambda
576 ((language . anchors)
577 (let ((files (find-files
578 (string-append #$input "/" language)
579 "^guix(-cookbook|)(\\.[a-zA-Z_-]+)?\\.html$")))
580 (n-par-for-each (parallel-job-count)
581 (cut process-html <> anchors)
582 files))))
583 mono-node-anchors)
584
585 ;; Process the multi-node HTML files.
586 (for-each (match-lambda
587 ((language . anchors)
588 (let ((files (find-files
589 (string-append #$input "/" language
590 "/html_node")
591 "\\.html$")))
592 (n-par-for-each (parallel-job-count)
593 (cut process-html <> anchors)
594 files))))
595 multi-node-anchors)
da9deba1
LC
596
597 ;; Last, copy non-HTML files as is.
598 (for-each copy-as-is
599 (find-files #$input (negate html?)))))))
f8c143a7
LC
600
601 (computed-file name build))
602
7eb883b7
LC
603(define* (stylized-html source input
604 #:key
605 (languages %languages)
606 (manual %manual)
607 (manual-css-url "/static/base/css/manual.css"))
608 "Process all the HTML files in INPUT; add them MANUAL-CSS-URL as a <style>
609link, and add a menu to choose among LANGUAGES. Use the Guix PO files found
610in SOURCE."
611 (define build
612 (with-extensions (list guile-lib)
613 (with-imported-modules `((guix build utils)
614 ((localization)
615 => ,(localization-helper-module
616 source languages)))
617 #~(begin
618 (use-modules (htmlprag)
619 (localization)
620 (guix build utils)
621 (srfi srfi-1)
622 (ice-9 match)
623 (ice-9 threads))
624
625 (define* (menu-dropdown #:key (label "Item") (url "#") (items '()))
626 ;; Return an SHTML <li> element representing a dropdown for the
627 ;; navbar. LABEL is the text of the dropdown menu, and ITEMS is
628 ;; the list of items in this menu.
629 (define id "visible-dropdown")
630
631 `(li
632 (@ (class "navbar-menu-item dropdown dropdown-btn"))
633 (input (@ (class "navbar-menu-hidden-input")
634 (type "radio")
635 (name "dropdown")
636 (id ,id)))
637 (label (@ (for ,id)) ,label)
638 (label (@ (for "all-dropdowns-hidden")) ,label)
639 (div
640 (@ (class "navbar-submenu")
641 (id "navbar-submenu"))
642 (div (@ (class "navbar-submenu-triangle"))
643 " ")
644 (ul ,@items))))
645
646 (define (menu-item label url)
647 ;; Return an SHTML <li> element for a menu item with the given
648 ;; LABEL and URL.
649 `(li (a (@ (class "navbar-menu-item")
650 (href ,url))
651 ,label)))
652
fa580bf3
LC
653 (define* (navigation-bar menus #:key split-node?)
654 ;; Return the navigation bar showing all of MENUS.
655 `(header (@ (class "navbar"))
656 (h1 (a (@ (class "branding")
657 (href ,(if split-node? ".." "#")))))
658 (nav (@ (class "navbar-menu"))
659 (input (@ (class "navbar-menu-hidden-input")
660 (type "radio")
661 (name "dropdown")
662 (id "all-dropdowns-hidden")))
663 (ul ,@menus))
664
665 ;; This is the button that shows up on small screen in
666 ;; lieu of the drop-down button.
667 (a (@ (class "navbar-menu-btn")
668 (href ,(if split-node? "../.." ".."))))))
669
7eb883b7
LC
670 (define* (base-language-url code manual
671 #:key split-node?)
672 ;; Return the base URL of MANUAL for language CODE.
673 (if split-node?
a27e47f9
LC
674 (string-append "../../" (normalize code) "/html_node")
675 (string-append "../" (normalize code) "/" manual
7eb883b7
LC
676 (if (string=? code "en")
677 ""
678 (string-append "." code))
679 ".html")))
680
681 (define (language-menu-items file)
682 ;; Return the language menu items to be inserted in FILE.
683 (define split-node?
684 (string-contains file "/html_node/"))
685
686 (append
687 (map (lambda (code)
688 (menu-item (language-code->native-name code)
689 (base-language-url code #$manual
690 #:split-node?
691 split-node?)))
692 '#$%languages)
693 (list
694 (menu-item "⊕"
695 (if (string=? #$manual "guix-cookbook")
696 "https://translate.fedoraproject.org/projects/guix/documentation-cookbook/"
697 "https://translate.fedoraproject.org/projects/guix/documentation-manual/")))))
698
699 (define (stylized-html sxml file)
700 ;; Return SXML, which was read from FILE, with additional
701 ;; styling.
fa580bf3
LC
702 (define split-node?
703 (string-contains file "/html_node/"))
704
7eb883b7
LC
705 (let loop ((sxml sxml))
706 (match sxml
707 (('*TOP* decl body ...)
708 `(*TOP* ,decl ,@(map loop body)))
709 (('head elements ...)
710 ;; Add reference to our own manual CSS, which provides
711 ;; support for the language menu.
712 `(head ,@elements
713 (link (@ (rel "stylesheet")
714 (type "text/css")
715 (href #$manual-css-url)))))
716 (('body ('@ attributes ...) elements ...)
717 `(body (@ ,@attributes)
fa580bf3
LC
718 ,(navigation-bar
719 ;; TODO: Add "Contribute" menu, to report
720 ;; errors, etc.
721 (list (menu-dropdown
722 #:label
723 `(img (@ (alt "Language")
724 (src "/static/base/img/language-picker.svg")))
725 #:items
726 (language-menu-items file)))
727 #:split-node? split-node?)
7eb883b7
LC
728 ,@elements))
729 ((tag ('@ attributes ...) body ...)
730 `(,tag (@ ,@attributes) ,@(map loop body)))
731 ((tag body ...)
732 `(,tag ,@(map loop body)))
733 ((? string? str)
734 str))))
735
736 (define (process-html file)
737 ;; Parse FILE and add links to translations. Install the result
738 ;; to #$output.
739 (format (current-error-port) "processing ~a...~%" file)
740 (let* ((shtml (parameterize ((%strict-tokenizer? #t))
741 (call-with-input-file file html->shtml)))
742 (processed (stylized-html shtml file))
743 (base (string-drop file (string-length #$input)))
744 (target (string-append #$output base)))
745 (mkdir-p (dirname target))
746 (call-with-output-file target
747 (lambda (port)
748 (write-shtml-as-html processed port)))))
749
750 ;; Install a UTF-8 locale so we can process UTF-8 files.
751 (setenv "GUIX_LOCPATH"
752 #+(file-append glibc-utf8-locales "/lib/locale"))
753 (setlocale LC_ALL "en_US.utf8")
754 (setenv "LC_ALL" "en_US.utf8")
755 (setvbuf (current-error-port) 'line)
756
757 (n-par-for-each (parallel-job-count)
758 (lambda (file)
759 (if (string-suffix? ".html" file)
760 (process-html file)
761 ;; Copy FILE as is to #$output.
762 (let* ((base (string-drop file (string-length #$input)))
763 (target (string-append #$output base)))
764 (mkdir-p (dirname target))
765 (if (eq? 'symlink (stat:type (lstat file)))
766 (symlink (readlink file) target)
767 (copy-file file target)))))
768 (find-files #$input))))))
769
770 (computed-file "stylized-html-manual" build))
771
ccadafdc
LC
772(define* (html-manual source #:key (languages %languages)
773 (version "0.0")
cacb5576 774 (manual %manual)
db1d4453
LC
775 (mono-node-indexes (map list languages))
776 (split-node-indexes (map list languages))
ccadafdc
LC
777 (date 1)
778 (options %makeinfo-html-options))
779 "Return the HTML manuals built from SOURCE for all LANGUAGES, with the given
780makeinfo OPTIONS."
781 (define manual-source
782 (texinfo-manual-source source
783 #:version version
784 #:languages languages
785 #:date date))
786
e3e9c191
LC
787 (define images
788 (texinfo-manual-images source))
789
ccadafdc
LC
790 (define build
791 (with-imported-modules '((guix build utils))
792 #~(begin
793 (use-modules (guix build utils)
794 (ice-9 match))
795
796 (define (normalize language)
e591541d 797 ;; Normalize LANGUAGE. For instance, "zh_CN" becomes "zh-cn".
ccadafdc
LC
798 (string-map (match-lambda
799 (#\_ #\-)
800 (chr chr))
801 (string-downcase language)))
802
cacb5576
LC
803 (define (language->texi-file-name language)
804 (if (string=? language "en")
805 (string-append #$manual-source "/"
806 #$manual ".texi")
807 (string-append #$manual-source "/"
808 #$manual "." language ".texi")))
809
ccadafdc
LC
810 ;; Install a UTF-8 locale so that 'makeinfo' is at ease.
811 (setenv "GUIX_LOCPATH"
812 #+(file-append glibc-utf8-locales "/lib/locale"))
813 (setenv "LC_ALL" "en_US.utf8")
814
815 (setvbuf (current-output-port) 'line)
816 (setvbuf (current-error-port) 'line)
817
f9e0488c
LC
818 ;; 'makeinfo' looks for "htmlxref.cnf" in the current directory, so
819 ;; copy it right here.
820 (copy-file (string-append #$manual-source "/htmlxref.cnf")
821 "htmlxref.cnf")
822
ccadafdc 823 (for-each (lambda (language)
cacb5576
LC
824 (let* ((texi (language->texi-file-name language))
825 (opts `("--html"
826 "-c" ,(string-append "TOP_NODE_UP_URL=/manual/"
ccadafdc 827 language)
cacb5576
LC
828 #$@options
829 ,texi)))
ccadafdc
LC
830 (format #t "building HTML manual for language '~a'...~%"
831 language)
832 (mkdir-p (string-append #$output "/"
833 (normalize language)))
834 (setenv "LANGUAGE" language)
835 (apply invoke #$(file-append texinfo "/bin/makeinfo")
836 "-o" (string-append #$output "/"
837 (normalize language)
838 "/html_node")
839 opts)
840 (apply invoke #$(file-append texinfo "/bin/makeinfo")
841 "--no-split"
842 "-o"
843 (string-append #$output "/"
844 (normalize language)
845 "/" #$manual
846 (if (string=? language "en")
847 ""
848 (string-append "." language))
849 ".html")
e3e9c191
LC
850 opts)
851
852 ;; Make sure images are available.
853 (symlink #$images
854 (string-append #$output "/" (normalize language)
855 "/images"))
856 (symlink #$images
857 (string-append #$output "/" (normalize language)
858 "/html_node/images"))))
cacb5576
LC
859 (filter (compose file-exists? language->texi-file-name)
860 '#$languages)))))
ccadafdc 861
7eb883b7
LC
862 (let* ((name (string-append manual "-html-manual"))
863 (manual* (computed-file name build #:local-build? #f)))
864 (syntax-highlighted-html (stylized-html source manual*
865 #:languages languages
866 #:manual manual)
db1d4453
LC
867 #:mono-node-indexes mono-node-indexes
868 #:split-node-indexes split-node-indexes
f8c143a7 869 #:name (string-append name "-highlighted"))))
ccadafdc
LC
870
871(define* (pdf-manual source #:key (languages %languages)
872 (version "0.0")
cacb5576 873 (manual %manual)
ccadafdc
LC
874 (date 1)
875 (options '()))
876 "Return the HTML manuals built from SOURCE for all LANGUAGES, with the given
877makeinfo OPTIONS."
878 (define manual-source
879 (texinfo-manual-source source
880 #:version version
881 #:languages languages
882 #:date date))
883
884 ;; FIXME: This union works, except for the table of contents of non-English
885 ;; manuals, which contains escape sequences like "^^ca^^fe" instead of
886 ;; accented letters.
887 ;;
888 ;; (define texlive
82f5f6b1 889 ;; (texlive-updmap.cfg (list texlive-tex-texinfo
ccadafdc
LC
890 ;; texlive-generic-epsf
891 ;; texlive-fonts-ec)))
892
893 (define build
894 (with-imported-modules '((guix build utils))
895 #~(begin
896 (use-modules (guix build utils)
897 (srfi srfi-34)
898 (ice-9 match))
899
900 (define (normalize language) ;XXX: deduplicate
901 ;; Normalize LANGUAGE. For instance, "zh_CN" becomes "zh-cn".
902 (string-map (match-lambda
903 (#\_ #\-)
904 (chr chr))
905 (string-downcase language)))
906
907 ;; Install a UTF-8 locale so that 'makeinfo' is at ease.
908 (setenv "GUIX_LOCPATH"
909 #+(file-append glibc-utf8-locales "/lib/locale"))
910 (setenv "LC_ALL" "en_US.utf8")
911 (setenv "PATH"
912 (string-append #+(file-append texlive "/bin") ":"
913 #+(file-append texinfo "/bin") ":"
914
915 ;; Below are command-line tools needed by
916 ;; 'texi2dvi' and friends.
917 #+(file-append sed "/bin") ":"
918 #+(file-append grep "/bin") ":"
919 #+(file-append coreutils "/bin") ":"
920 #+(file-append gawk "/bin") ":"
921 #+(file-append tar "/bin") ":"
922 #+(file-append diffutils "/bin")))
923
924 (setvbuf (current-output-port) 'line)
925 (setvbuf (current-error-port) 'line)
926
927 (setenv "HOME" (getcwd)) ;for kpathsea/mktextfm
928
929 ;; 'SOURCE_DATE_EPOCH' is honored by pdftex.
930 (setenv "SOURCE_DATE_EPOCH" "1")
931
932 (for-each (lambda (language)
933 (let ((opts `("--pdf"
934 "-I" "."
935 #$@options
936 ,(if (string=? language "en")
937 (string-append #$manual-source "/"
938 #$manual ".texi")
939 (string-append #$manual-source "/"
940 #$manual "." language ".texi")))))
941 (format #t "building PDF manual for language '~a'...~%"
942 language)
943 (mkdir-p (string-append #$output "/"
944 (normalize language)))
945 (setenv "LANGUAGE" language)
946
947
948 ;; FIXME: Unfortunately building PDFs for non-Latin
949 ;; alphabets doesn't work:
950 ;; <https://lists.gnu.org/archive/html/help-texinfo/2012-01/msg00014.html>.
951 (guard (c ((invoke-error? c)
952 (format (current-error-port)
953 "~%~%Failed to produce \
954PDF for language '~a'!~%~%"
955 language)))
956 (apply invoke #$(file-append texinfo "/bin/makeinfo")
957 "--pdf" "-o"
958 (string-append #$output "/"
959 (normalize language)
960 "/" #$manual
961 (if (string=? language "en")
962 ""
963 (string-append "."
964 language))
965 ".pdf")
966 opts))))
967 '#$languages))))
968
45b251fd
LC
969 (computed-file (string-append manual "-pdf-manual") build
970 #:local-build? #f))
ccadafdc
LC
971
972(define (guix-manual-text-domain source languages)
973 "Return the PO files for LANGUAGES of the 'guix-manual' text domain taken
974from SOURCE."
975 (define po-directory
976 (file-append* source "/po/doc"))
977
978 (define build
979 (with-imported-modules '((guix build utils))
980 #~(begin
981 (use-modules (guix build utils))
982
983 (mkdir-p #$output)
984 (for-each (lambda (language)
985 (define directory
986 (string-append #$output "/" language
987 "/LC_MESSAGES"))
988
989 (mkdir-p directory)
990 (invoke #+(file-append gnu-gettext "/bin/msgfmt")
991 "-c" "-o"
992 (string-append directory "/guix-manual.mo")
993 (string-append #$po-directory "/guix-manual."
994 language ".po")))
995 '#$(delete "en" languages)))))
996
997 (computed-file "guix-manual-po" build))
998
62fc6fdb
LC
999(define* (localization-helper-module source
1000 #:optional (languages %languages))
1001 "Return a file-like object for use as the (localization) module. SOURCE
1002must be the Guix top-level source directory, from which PO files are taken."
1003 (define content
1004 (with-extensions (list guile-json-3)
1005 #~(begin
1006 (define-module (localization)
1007 #:use-module (json)
1008 #:use-module (srfi srfi-1)
1009 #:use-module (srfi srfi-19)
1010 #:use-module (ice-9 match)
1011 #:use-module (ice-9 popen)
1012 #:export (normalize
1013 with-language
1014 translate
1015 language-code->name
ee16e4e8 1016 language-code->native-name
62fc6fdb
LC
1017 seconds->string))
1018
1019 (define (normalize language) ;XXX: deduplicate
1020 ;; Normalize LANGUAGE. For instance, "zh_CN" becomes "zh-cn".
1021 (string-map (match-lambda
1022 (#\_ #\-)
1023 (chr chr))
1024 (string-downcase language)))
1025
1026 (define-syntax-rule (with-language language exp ...)
1027 (let ((lang (getenv "LANGUAGE")))
1028 (dynamic-wind
1029 (lambda ()
1030 (setenv "LANGUAGE" language)
1031 (setlocale LC_MESSAGES))
1032 (lambda () exp ...)
1033 (lambda ()
1034 (if lang
1035 (setenv "LANGUAGE" lang)
1036 (unsetenv "LANGUAGE"))
1037 (setlocale LC_MESSAGES)))))
1038
1039 ;; (put 'with-language 'scheme-indent-function 1)
1040 (define* (translate str language
1041 #:key (domain "guix-manual"))
1042 (define exp
1043 `(begin
1044 (bindtextdomain "guix-manual"
1045 #+(guix-manual-text-domain
1046 source
1047 languages))
1048 (bindtextdomain "iso_639-3" ;language names
1049 #+(file-append iso-codes
1050 "/share/locale"))
ee16e4e8 1051 (setenv "LANGUAGE" ,language)
62fc6fdb
LC
1052 (write (gettext ,str ,domain))))
1053
ee16e4e8
LC
1054 ;; Since the 'gettext' function caches msgid translations,
1055 ;; regardless of $LANGUAGE, we have to spawn a new process each
1056 ;; time we want to translate to a different language. Bah!
1057 (let* ((pipe (open-pipe* OPEN_READ
1058 #+(file-append guile-3.0
1059 "/bin/guile")
1060 "-c" (object->string exp)))
1061 (str (read pipe)))
1062 (close-pipe pipe)
1063 str))
62fc6fdb
LC
1064
1065 (define %iso639-languages
1066 (vector->list
1067 (assoc-ref (call-with-input-file
1068 #+(file-append iso-codes
1069 "/share/iso-codes/json/iso_639-3.json")
1070 json->scm)
1071 "639-3")))
1072
1073 (define (language-code->name code)
1074 "Return the full name of a language from its ISO-639-3 code."
1075 (let ((code (match (string-index code #\_)
1076 (#f code)
1077 (index (string-take code index)))))
1078 (any (lambda (language)
1079 (and (string=? (or (assoc-ref language "alpha_2")
1080 (assoc-ref language "alpha_3"))
1081 code)
1082 (assoc-ref language "name")))
1083 %iso639-languages)))
1084
ee16e4e8
LC
1085 (define (language-code->native-name code)
1086 "Return the name of language CODE in that language."
1087 (translate (language-code->name code) code
1088 #:domain "iso_639-3"))
1089
62fc6fdb
LC
1090 (define (seconds->string seconds language)
1091 (let* ((time (make-time time-utc 0 seconds))
1092 (date (time-utc->date time)))
1093 (with-language language (date->string date "~e ~B ~Y")))))))
1094
1095 (scheme-file "localization.scm" content))
1096
ccadafdc
LC
1097(define* (html-manual-indexes source
1098 #:key (languages %languages)
1099 (version "0.0")
cacb5576 1100 (manual %manual)
208cc522
LC
1101 (title (if (string=? "guix" manual)
1102 "GNU Guix Reference Manual"
1103 "GNU Guix Cookbook"))
ccadafdc
LC
1104 (date 1))
1105 (define build
62fc6fdb
LC
1106 (with-imported-modules `((guix build utils)
1107 ((localization)
1108 => ,(localization-helper-module
1109 source languages)))
1110 #~(begin
1111 (use-modules (guix build utils)
1112 (localization)
1113 (sxml simple)
1114 (srfi srfi-1))
1115
1116 (define (guix-url path)
1117 (string-append #$%web-site-url path))
1118
1119 (define (sxml-index language title body)
1120 ;; FIXME: Avoid duplicating styling info from guix-artwork.git.
1121 `(html (@ (lang ,language))
1122 (head
1123 (title ,(string-append title " — GNU Guix"))
1124 (meta (@ (charset "UTF-8")))
1125 (meta (@ (name "viewport") (content "width=device-width, initial-scale=1.0")))
1126 ;; Menu prefetch.
1127 (link (@ (rel "prefetch") (href ,(guix-url "menu/index.html"))))
1128 ;; Base CSS.
1129 (link (@ (rel "stylesheet") (href ,(guix-url "static/base/css/elements.css"))))
1130 (link (@ (rel "stylesheet") (href ,(guix-url "static/base/css/common.css"))))
1131 (link (@ (rel "stylesheet") (href ,(guix-url "static/base/css/messages.css"))))
1132 (link (@ (rel "stylesheet") (href ,(guix-url "static/base/css/navbar.css"))))
1133 (link (@ (rel "stylesheet") (href ,(guix-url "static/base/css/breadcrumbs.css"))))
1134 (link (@ (rel "stylesheet") (href ,(guix-url "static/base/css/buttons.css"))))
1135 (link (@ (rel "stylesheet") (href ,(guix-url "static/base/css/footer.css"))))
1136
1137 (link (@ (rel "stylesheet") (href ,(guix-url "static/base/css/page.css"))))
1138 (link (@ (rel "stylesheet") (href ,(guix-url "static/base/css/post.css")))))
1139 (body
1140 (header (@ (class "navbar"))
1141 (h1 (a (@ (class "branding")
1142 (href #$%web-site-url)))
1143 (span (@ (class "a11y-offset"))
1144 "Guix"))
1145 (nav (@ (class "menu"))))
1146 (nav (@ (class "breadcrumbs"))
1147 (a (@ (class "crumb")
1148 (href #$%web-site-url))
1149 "Home"))
1150 ,body
1151 (footer))))
1152
1153 (define (language-index language)
1154 (define title
1155 (translate #$title language))
1156
1157 (sxml-index
1158 language title
1159 `(main
1160 (article
1161 (@ (class "page centered-block limit-width"))
1162 (h2 ,title)
1163 (p (@ (class "post-metadata centered-text"))
1164 #$version " — "
1165 ,(seconds->string #$date language))
1166
1167 (div
1168 (ul
1169 (li (a (@ (href "html_node"))
1170 "HTML, with a separate page per node"))
1171 (li (a (@ (href
1172 ,(string-append
1173 #$manual
1174 (if (string=? language
1175 "en")
1176 ""
1177 (string-append "."
1178 language))
1179 ".html")))
1180 "HTML, entirely on one page"))
1181 ,@(if (member language '("ru" "zh_CN"))
1182 '()
1183 `((li (a (@ (href ,(string-append
1184 #$manual
1185 (if (string=? language "en")
1186 ""
1187 (string-append "."
1188 language))
1189 ".pdf"))))
1190 "PDF")))))))))
1191
1192 (define (top-level-index languages)
1193 (define title #$title)
1194 (sxml-index
1195 "en" title
1196 `(main
1197 (article
1198 (@ (class "page centered-block limit-width"))
1199 (h2 ,title)
1200 (div
1201 "This document is available in the following
e591541d 1202languages:\n"
62fc6fdb
LC
1203 (ul
1204 ,@(map (lambda (language)
1205 `(li (a (@ (href ,(normalize language)))
ee16e4e8 1206 ,(language-code->native-name language))))
62fc6fdb
LC
1207 languages)))))))
1208
1209 (define (write-html file sxml)
1210 (call-with-output-file file
1211 (lambda (port)
1212 (display "<!DOCTYPE html>\n" port)
1213 (sxml->xml sxml port))))
e591541d 1214
62fc6fdb
LC
1215 (setenv "GUIX_LOCPATH"
1216 #+(file-append glibc-utf8-locales "/lib/locale"))
1217 (setenv "LC_ALL" "en_US.utf8")
1218 (setlocale LC_ALL "en_US.utf8")
e591541d 1219
62fc6fdb
LC
1220 (for-each (lambda (language)
1221 (define directory
1222 (string-append #$output "/"
1223 (normalize language)))
e591541d 1224
62fc6fdb
LC
1225 (mkdir-p directory)
1226 (write-html (string-append directory "/index.html")
1227 (language-index language)))
1228 '#$languages)
e591541d 1229
62fc6fdb
LC
1230 (write-html (string-append #$output "/index.html")
1231 (top-level-index '#$languages)))))
ccadafdc
LC
1232
1233 (computed-file "html-indexes" build))
1234
1235(define* (pdf+html-manual source
1236 #:key (languages %languages)
1237 (version "0.0")
1238 (date (time-second (current-time time-utc)))
db1d4453
LC
1239 (mono-node-indexes (map list %languages))
1240 (split-node-indexes (map list %languages))
cacb5576 1241 (manual %manual))
ccadafdc
LC
1242 "Return the union of the HTML and PDF manuals, as well as the indexes."
1243 (directory-union (string-append manual "-manual")
1244 (map (lambda (proc)
1245 (proc source
1246 #:date date
1247 #:languages languages
1248 #:version version
1249 #:manual manual))
1250 (list html-manual-indexes
db1d4453
LC
1251 (lambda (source . args)
1252 (apply html-manual source
1253 #:mono-node-indexes mono-node-indexes
1254 #:split-node-indexes split-node-indexes
1255 args))
1256 pdf-manual))
ccadafdc
LC
1257 #:copy? #t))
1258
1259(define (latest-commit+date directory)
1260 "Return two values: the last commit ID (a hex string) for DIRECTORY, and its
1261commit date (an integer)."
1262 (let* ((repository (repository-open directory))
1263 (head (repository-head repository))
1264 (oid (reference-target head))
1265 (commit (commit-lookup repository oid)))
1266 ;; TODO: Use (git describe) when it's widely available.
1267 (values (oid->string oid) (commit-time commit))))
1268
1269\f
97ce30cc
LC
1270;;;
1271;;; Guile manual.
1272;;;
1273
1274(define guile-manual
1275 ;; The Guile manual as HTML, including both the mono-node "guile.html" and
1276 ;; the split-node "html_node" directory.
1277 (let ((guile guile-3.0-latest))
1278 (computed-file (string-append "guile-manual-" (package-version guile))
1279 (with-imported-modules '((guix build utils))
1280 #~(begin
1281 (use-modules (guix build utils)
1282 (ice-9 match))
1283
1284 (setenv "PATH"
1285 (string-append #+tar "/bin:"
1286 #+xz "/bin:"
1287 #+texinfo "/bin"))
1288 (invoke "tar" "xf" #$(package-source guile))
1289 (mkdir-p (string-append #$output "/en/html_node"))
1290
1291 (let* ((texi (find-files "." "^guile\\.texi$"))
1292 (documentation (match texi
1293 ((file) (dirname file)))))
1294 (with-directory-excursion documentation
1295 (invoke "makeinfo" "--html" "--no-split"
1296 "-o" (string-append #$output
1297 "/en/guile.html")
1298 "guile.texi")
1299 (invoke "makeinfo" "--html" "-o" "split"
1300 "guile.texi")
1301 (copy-recursively
1302 "split"
1303 (string-append #$output "/en/html_node")))))))))
1304
1305(define %guile-manual-base-url
1306 "https://www.gnu.org/software/guile/manual")
1307
1308(define (for-all-languages index)
1309 (map (lambda (language)
1310 (list language index))
1311 %languages))
1312
1313(define guile-mono-node-indexes
1314 ;; The Guile manual is only available in English so use the same index in
1315 ;; all languages.
1316 (for-all-languages
1317 (html-manual-identifier-index (file-append guile-manual "/en")
1318 %guile-manual-base-url
1319 #:name "guile-html-index-en")))
1320
1321(define guile-split-node-indexes
1322 (for-all-languages
1323 (html-manual-identifier-index (file-append guile-manual "/en/html_node")
1324 (string-append %guile-manual-base-url
1325 "/html_node")
1326 #:name "guile-html-index-en")))
1327
1328(define (merge-index-alists alist1 alist2)
1329 "Merge ALIST1 and ALIST2, both of which are list of tuples like:
1330
1331 (LANGUAGE INDEX1 INDEX2 ...)
1332
1333where LANGUAGE is a string like \"en\" and INDEX1 etc. are indexes as returned
1334by 'html-identifier-indexes'."
1335 (let ((languages (delete-duplicates
1336 (append (match alist1
1337 (((languages . _) ...)
1338 languages))
1339 (match alist2
1340 (((languages . _) ...)
1341 languages))))))
1342 (map (lambda (language)
1343 (cons language
1344 (append (or (assoc-ref alist1 language) '())
1345 (or (assoc-ref alist2 language) '()))))
1346 languages)))
1347
1348\f
ccadafdc
LC
1349(let* ((root (canonicalize-path
1350 (string-append (current-source-directory) "/..")))
3cd1a7ac 1351 (commit date (latest-commit+date root))
db1d4453
LC
1352 (version (or (getenv "GUIX_MANUAL_VERSION")
1353 (string-take commit 7)))
3cd1a7ac
LC
1354 (select? (let ((vcs? (git-predicate root)))
1355 (lambda (file stat)
1356 (and (vcs? file stat)
1357 ;; Filter out this file.
db1d4453
LC
1358 (not (string=? (basename file) "build.scm"))))))
1359 (source (local-file root "guix" #:recursive? #t
1360 #:select? select?)))
1361
1362 (define guix-manual
1363 (html-manual source
1364 #:manual "guix"
1365 #:version version
1366 #:date date))
1367
97ce30cc 1368 (define guix-mono-node-indexes
db1d4453
LC
1369 ;; Alist of indexes for GUIX-MANUAL, where each key is a language code and
1370 ;; each value is a file-like object containing the identifier index.
1371 (html-identifier-indexes guix-manual ""
97ce30cc 1372 #:manual-name "guix"
db1d4453
LC
1373 #:base-url (if (string=? %manual "guix")
1374 (const "")
8fe7c89f
LC
1375 (cut string-append
1376 "/manual/devel/" <>))
db1d4453
LC
1377 #:languages %languages))
1378
97ce30cc 1379 (define guix-split-node-indexes
db1d4453
LC
1380 ;; Likewise for the split-node variant of GUIX-MANUAL.
1381 (html-identifier-indexes guix-manual "/html_node"
97ce30cc 1382 #:manual-name "guix"
db1d4453
LC
1383 #:base-url (if (string=? %manual "guix")
1384 (const "")
8fe7c89f
LC
1385 (cut string-append
1386 "/manual/devel/" <>
1387 "/html_node"))
db1d4453
LC
1388 #:languages %languages))
1389
97ce30cc
LC
1390 (define mono-node-indexes
1391 (merge-index-alists guix-mono-node-indexes guile-mono-node-indexes))
1392
1393 (define split-node-indexes
1394 (merge-index-alists guix-split-node-indexes guile-split-node-indexes))
1395
ccadafdc
LC
1396 (format (current-error-port)
1397 "building manual from work tree around commit ~a, ~a~%"
1398 commit
1399 (let* ((time (make-time time-utc 0 date))
1400 (date (time-utc->date time)))
1401 (date->string date "~e ~B ~Y")))
db1d4453
LC
1402
1403 (pdf+html-manual source
97ce30cc
LC
1404 ;; Always use the identifier indexes of GUIX-MANUAL and
1405 ;; GUILE-MANUAL. Both "guix" and "guix-cookbook" can
1406 ;; contain links to definitions that appear in either of
1407 ;; these two manuals.
db1d4453
LC
1408 #:mono-node-indexes mono-node-indexes
1409 #:split-node-indexes split-node-indexes
1410 #:version version
ccadafdc 1411 #:date date))