gnu: wisp: update to 1.0.2.
[jackhill/guix/guix.git] / doc / build.scm
CommitLineData
ccadafdc
LC
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2019 Ludovic Courtès <ludo@gnu.org>
3;;;
4;;; This file is part of GNU Guix.
5;;;
6;;; GNU Guix is free software; you can redistribute it and/or modify it
7;;; under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 3 of the License, or (at
9;;; your option) any later version.
10;;;
11;;; GNU Guix is distributed in the hope that it will be useful, but
12;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19
20;; This file contains machinery to build HTML and PDF copies of the manual
21;; that can be readily published on the web site. To do that, run:
22;;
23;; guix build -f build.scm
24;;
25;; The result is a directory hierarchy that can be used as the manual/
26;; sub-directory of the web site.
27
28(use-modules (guix)
29 (guix gexp)
30 (guix git)
31 (guix git-download)
32 (git)
33 (gnu packages base)
34 (gnu packages gawk)
35 (gnu packages gettext)
36 (gnu packages guile)
37 (gnu packages texinfo)
38 (gnu packages tex)
39 (srfi srfi-19)
40 (srfi srfi-71))
41
42(define file-append*
43 (@@ (guix self) file-append*))
44
45(define translated-texi-manuals
46 (@@ (guix self) translate-texi-manuals))
47
48(define info-manual
49 (@@ (guix self) info-manual))
50
51(define %languages
52 '("de" "en" "es" "fr" "ru" "zh_CN"))
53
54(define (texinfo-manual-images source)
55 "Return a directory containing all the images used by the user manual, taken
56from SOURCE, the root of the source tree."
57 (define graphviz
58 (module-ref (resolve-interface '(gnu packages graphviz))
59 'graphviz))
60
61 (define images
62 (file-append* source "doc/images"))
63
64 (define build
65 (with-imported-modules '((guix build utils))
66 #~(begin
67 (use-modules (guix build utils)
68 (srfi srfi-26))
69
70 (define (dot->image dot-file format)
71 (invoke #+(file-append graphviz "/bin/dot")
72 "-T" format "-Gratio=.9" "-Gnodesep=.005"
73 "-Granksep=.00005" "-Nfontsize=9"
74 "-Nheight=.1" "-Nwidth=.1"
75 "-o" (string-append #$output "/"
76 (basename dot-file ".dot")
77 "." format)
78 dot-file))
79
80 ;; Build graphs.
81 (mkdir-p #$output)
82 (for-each (lambda (dot-file)
83 (for-each (cut dot->image dot-file <>)
84 '("png" "pdf")))
85 (find-files #$images "\\.dot$"))
86
87 ;; Copy other PNGs.
88 (for-each (lambda (png-file)
89 (install-file png-file #$output))
90 (find-files #$images "\\.png$")))))
91
92 (computed-file "texinfo-manual-images" build))
93
94(define* (texinfo-manual-source source #:key
95 (version "0.0")
96 (languages %languages)
97 (date 1))
98 "Gather all the source files of the Texinfo manuals from SOURCE--.texi file
99as well as images, OS examples, and translations."
100 (define documentation
101 (file-append* source "doc"))
102
103 (define examples
104 (file-append* source "gnu/system/examples"))
105
106 (define build
107 (with-imported-modules '((guix build utils))
108 #~(begin
109 (use-modules (guix build utils)
110 (srfi srfi-19))
111
112 (define (make-version-texi language)
113 ;; Create the 'version.texi' file for LANGUAGE.
114 (let ((file (if (string=? language "en")
115 "version.texi"
116 (string-append "version-" language ".texi"))))
117 (call-with-output-file (string-append #$output "/" file)
118 (lambda (port)
119 (let* ((version #$version)
120 (time (make-time time-utc 0 #$date))
121 (date (time-utc->date time)))
122 (format port "
123@set UPDATED ~a
124@set UPDATED-MONTH ~a
125@set EDITION ~a
126@set VERSION ~a\n"
127 (date->string date "~e ~B ~Y")
128 (date->string date "~B ~Y")
129 version version))))))
130
131 (install-file #$(file-append* documentation "/htmlxref.cnf")
132 #$output)
133
134 (for-each (lambda (texi)
135 (install-file texi #$output))
136 (append (find-files #$documentation "\\.(texi|scm)$")
137 (find-files #$(translated-texi-manuals source)
138 "\\.texi$")))
139
140 ;; Create 'version.texi'.
141 (for-each make-version-texi '#$languages)
142
143 ;; Copy configuration templates that the manual includes.
144 (for-each (lambda (template)
145 (copy-file template
146 (string-append
147 #$output "/os-config-"
148 (basename template ".tmpl")
149 ".texi")))
150 (find-files #$examples "\\.tmpl$"))
151
152 (symlink #$(texinfo-manual-images source)
153 (string-append #$output "/images")))))
154
155 (computed-file "texinfo-manual-source" build))
156
157(define %web-site-url
158 ;; URL of the web site home page.
159 (or (getenv "GUIX_WEB_SITE_URL")
160 "/software/guix/"))
161
162(define %makeinfo-html-options
163 ;; Options passed to 'makeinfo --html'.
164 '("--css-ref=https://www.gnu.org/software/gnulib/manual.css"))
165
166(define* (html-manual source #:key (languages %languages)
167 (version "0.0")
168 (manual "guix")
169 (date 1)
170 (options %makeinfo-html-options))
171 "Return the HTML manuals built from SOURCE for all LANGUAGES, with the given
172makeinfo OPTIONS."
173 (define manual-source
174 (texinfo-manual-source source
175 #:version version
176 #:languages languages
177 #:date date))
178
179 (define build
180 (with-imported-modules '((guix build utils))
181 #~(begin
182 (use-modules (guix build utils)
183 (ice-9 match))
184
185 (define (normalize language)
186 ;; Normalize LANGUAGE. For instance, "zh_CN" become "zh-cn".
187 (string-map (match-lambda
188 (#\_ #\-)
189 (chr chr))
190 (string-downcase language)))
191
192 ;; Install a UTF-8 locale so that 'makeinfo' is at ease.
193 (setenv "GUIX_LOCPATH"
194 #+(file-append glibc-utf8-locales "/lib/locale"))
195 (setenv "LC_ALL" "en_US.utf8")
196
197 (setvbuf (current-output-port) 'line)
198 (setvbuf (current-error-port) 'line)
199
200 (for-each (lambda (language)
201 (let ((opts `("--html"
202 "-c" ,(string-append "TOP_NODE_UP_URL=/manual/"
203 language)
204 #$@options
205 ,(if (string=? language "en")
206 (string-append #$manual-source "/"
207 #$manual ".texi")
208 (string-append #$manual-source "/"
209 #$manual "." language ".texi")))))
210 (format #t "building HTML manual for language '~a'...~%"
211 language)
212 (mkdir-p (string-append #$output "/"
213 (normalize language)))
214 (setenv "LANGUAGE" language)
215 (apply invoke #$(file-append texinfo "/bin/makeinfo")
216 "-o" (string-append #$output "/"
217 (normalize language)
218 "/html_node")
219 opts)
220 (apply invoke #$(file-append texinfo "/bin/makeinfo")
221 "--no-split"
222 "-o"
223 (string-append #$output "/"
224 (normalize language)
225 "/" #$manual
226 (if (string=? language "en")
227 ""
228 (string-append "." language))
229 ".html")
230 opts)))
231 '#$languages))))
232
233 (computed-file (string-append manual "-html-manual") build))
234
235(define* (pdf-manual source #:key (languages %languages)
236 (version "0.0")
237 (manual "guix")
238 (date 1)
239 (options '()))
240 "Return the HTML manuals built from SOURCE for all LANGUAGES, with the given
241makeinfo OPTIONS."
242 (define manual-source
243 (texinfo-manual-source source
244 #:version version
245 #:languages languages
246 #:date date))
247
248 ;; FIXME: This union works, except for the table of contents of non-English
249 ;; manuals, which contains escape sequences like "^^ca^^fe" instead of
250 ;; accented letters.
251 ;;
252 ;; (define texlive
253 ;; (texlive-union (list texlive-tex-texinfo
254 ;; texlive-generic-epsf
255 ;; texlive-fonts-ec)))
256
257 (define build
258 (with-imported-modules '((guix build utils))
259 #~(begin
260 (use-modules (guix build utils)
261 (srfi srfi-34)
262 (ice-9 match))
263
264 (define (normalize language) ;XXX: deduplicate
265 ;; Normalize LANGUAGE. For instance, "zh_CN" becomes "zh-cn".
266 (string-map (match-lambda
267 (#\_ #\-)
268 (chr chr))
269 (string-downcase language)))
270
271 ;; Install a UTF-8 locale so that 'makeinfo' is at ease.
272 (setenv "GUIX_LOCPATH"
273 #+(file-append glibc-utf8-locales "/lib/locale"))
274 (setenv "LC_ALL" "en_US.utf8")
275 (setenv "PATH"
276 (string-append #+(file-append texlive "/bin") ":"
277 #+(file-append texinfo "/bin") ":"
278
279 ;; Below are command-line tools needed by
280 ;; 'texi2dvi' and friends.
281 #+(file-append sed "/bin") ":"
282 #+(file-append grep "/bin") ":"
283 #+(file-append coreutils "/bin") ":"
284 #+(file-append gawk "/bin") ":"
285 #+(file-append tar "/bin") ":"
286 #+(file-append diffutils "/bin")))
287
288 (setvbuf (current-output-port) 'line)
289 (setvbuf (current-error-port) 'line)
290
291 (setenv "HOME" (getcwd)) ;for kpathsea/mktextfm
292
293 ;; 'SOURCE_DATE_EPOCH' is honored by pdftex.
294 (setenv "SOURCE_DATE_EPOCH" "1")
295
296 (for-each (lambda (language)
297 (let ((opts `("--pdf"
298 "-I" "."
299 #$@options
300 ,(if (string=? language "en")
301 (string-append #$manual-source "/"
302 #$manual ".texi")
303 (string-append #$manual-source "/"
304 #$manual "." language ".texi")))))
305 (format #t "building PDF manual for language '~a'...~%"
306 language)
307 (mkdir-p (string-append #$output "/"
308 (normalize language)))
309 (setenv "LANGUAGE" language)
310
311
312 ;; FIXME: Unfortunately building PDFs for non-Latin
313 ;; alphabets doesn't work:
314 ;; <https://lists.gnu.org/archive/html/help-texinfo/2012-01/msg00014.html>.
315 (guard (c ((invoke-error? c)
316 (format (current-error-port)
317 "~%~%Failed to produce \
318PDF for language '~a'!~%~%"
319 language)))
320 (apply invoke #$(file-append texinfo "/bin/makeinfo")
321 "--pdf" "-o"
322 (string-append #$output "/"
323 (normalize language)
324 "/" #$manual
325 (if (string=? language "en")
326 ""
327 (string-append "."
328 language))
329 ".pdf")
330 opts))))
331 '#$languages))))
332
333 (computed-file (string-append manual "-pdf-manual") build))
334
335(define (guix-manual-text-domain source languages)
336 "Return the PO files for LANGUAGES of the 'guix-manual' text domain taken
337from SOURCE."
338 (define po-directory
339 (file-append* source "/po/doc"))
340
341 (define build
342 (with-imported-modules '((guix build utils))
343 #~(begin
344 (use-modules (guix build utils))
345
346 (mkdir-p #$output)
347 (for-each (lambda (language)
348 (define directory
349 (string-append #$output "/" language
350 "/LC_MESSAGES"))
351
352 (mkdir-p directory)
353 (invoke #+(file-append gnu-gettext "/bin/msgfmt")
354 "-c" "-o"
355 (string-append directory "/guix-manual.mo")
356 (string-append #$po-directory "/guix-manual."
357 language ".po")))
358 '#$(delete "en" languages)))))
359
360 (computed-file "guix-manual-po" build))
361
362(define* (html-manual-indexes source
363 #:key (languages %languages)
364 (version "0.0")
365 (manual "guix")
366 (date 1))
367 (define build
368 (with-imported-modules '((guix build utils))
369 #~(begin
370 (use-modules (guix build utils)
371 (ice-9 match)
372 (ice-9 popen)
373 (sxml simple)
374 (srfi srfi-19))
375
376 (define (normalize language) ;XXX: deduplicate
377 ;; Normalize LANGUAGE. For instance, "zh_CN" become "zh-cn".
378 (string-map (match-lambda
379 (#\_ #\-)
380 (chr chr))
381 (string-downcase language)))
382
383 (define-syntax-rule (with-language language exp ...)
384 (let ((lang (getenv "LANGUAGE")))
385 (dynamic-wind
386 (lambda ()
387 (setenv "LANGUAGE" language)
388 (setlocale LC_MESSAGES))
389 (lambda () exp ...)
390 (lambda ()
391 (if lang
392 (setenv "LANGUAGE" lang)
393 (unsetenv "LANGUAGE"))
394 (setlocale LC_MESSAGES)))))
395
396 ;; (put 'with-language 'scheme-indent-function 1)
397 (define* (translate str language
398 #:key (domain "guix-manual"))
399 (define exp
400 `(begin
401 (bindtextdomain "guix-manual"
402 #+(guix-manual-text-domain
403 source
404 languages))
405 (write (gettext ,str "guix-manual"))))
406
407 (with-language language
408 ;; Since the 'gettext' function caches msgid translations,
409 ;; regardless of $LANGUAGE, we have to spawn a new process each
410 ;; time we want to translate to a different language. Bah!
411 (let* ((pipe (open-pipe* OPEN_READ
412 #+(file-append guile-2.2
413 "/bin/guile")
414 "-c" (object->string exp)))
415 (str (read pipe)))
416 (close-pipe pipe)
417 str)))
418
419 (define (seconds->string seconds language)
420 (let* ((time (make-time time-utc 0 seconds))
421 (date (time-utc->date time)))
422 (with-language language (date->string date "~e ~B ~Y"))))
423
424 (define (guix-url path)
425 (string-append #$%web-site-url path))
426
427 (define (sxml-index language)
428 (define title
429 (translate "GNU Guix Reference Manual" language))
430
431 ;; FIXME: Avoid duplicating styling info from guix-artwork.git.
432 `(html (@ (lang ,language))
433 (head
434 (title ,(string-append title " — GNU Guix"))
435 (meta (@ (charset "UTF-8")))
436 (meta (@ (name "viewport") (content "width=device-width, initial-scale=1.0")))
437 ;; Menu prefetch.
438 (link (@ (rel "prefetch") (href ,(guix-url "menu/index.html"))))
439 ;; Base CSS.
440 (link (@ (rel "stylesheet") (href ,(guix-url "static/base/css/elements.css"))))
441 (link (@ (rel "stylesheet") (href ,(guix-url "static/base/css/common.css"))))
442 (link (@ (rel "stylesheet") (href ,(guix-url "static/base/css/messages.css"))))
443 (link (@ (rel "stylesheet") (href ,(guix-url "static/base/css/navbar.css"))))
444 (link (@ (rel "stylesheet") (href ,(guix-url "static/base/css/breadcrumbs.css"))))
445 (link (@ (rel "stylesheet") (href ,(guix-url "static/base/css/buttons.css"))))
446 (link (@ (rel "stylesheet") (href ,(guix-url "static/base/css/footer.css"))))
447
448 (link (@ (rel "stylesheet") (href ,(guix-url "static/base/css/page.css"))))
449 (link (@ (rel "stylesheet") (href ,(guix-url "static/base/css/post.css")))))
450 (body
451 (header (@ (class "navbar"))
452 (h1 (a (@ (class "branding")
453 (href #$%web-site-url)))
454 (span (@ (class "a11y-offset"))
455 "Guix"))
456 (nav (@ (class "menu"))))
457 (nav (@ (class "breadcrumbs"))
458 (a (@ (class "crumb")
459 (href #$%web-site-url))
460 "Home"))
461 (main
462 (article
463 (@ (class "page centered-block limit-width"))
464 (h2 ,title)
465 (p (@ (class "post-metadata centered-text"))
466 #$version " — "
467 ,(seconds->string #$date language))
468
469 (div
470 (ul
471 (li (a (@ (href "html_node"))
472 "HTML, with one page per node"))
473 (li (a (@ (href
474 ,(string-append
475 #$manual
476 (if (string=? language
477 "en")
478 ""
479 (string-append "."
480 language))
481 ".html")))
482 "HTML, entirely on one page"))
483 ,@(if (member language '("ru" "zh_CN"))
484 '()
485 `((li (a (@ (href ,(string-append
486 #$manual
487 (if (string=? language "en")
488 ""
489 (string-append "."
490 language))
491 ".pdf"))))
492 "PDF")))))))
493 (footer))))
494
495 (define (write-index language file)
496 (call-with-output-file file
497 (lambda (port)
498 (display "<!DOCTYPE html>\n" port)
499 (sxml->xml (sxml-index language) port))))
500
501 (setenv "GUIX_LOCPATH"
502 #+(file-append glibc-utf8-locales "/lib/locale"))
503 (setenv "LC_ALL" "en_US.utf8")
504 (setlocale LC_ALL "en_US.utf8")
505
506 (bindtextdomain "guix-manual"
507 #+(guix-manual-text-domain source languages))
508
509 (for-each (lambda (language)
510 (define directory
511 (string-append #$output "/"
512 (normalize language)))
513
514 (mkdir-p directory)
515 (write-index language
516 (string-append directory
517 "/index.html")))
518 '#$languages))))
519
520 (computed-file "html-indexes" build))
521
522(define* (pdf+html-manual source
523 #:key (languages %languages)
524 (version "0.0")
525 (date (time-second (current-time time-utc)))
526 (manual "guix"))
527 "Return the union of the HTML and PDF manuals, as well as the indexes."
528 (directory-union (string-append manual "-manual")
529 (map (lambda (proc)
530 (proc source
531 #:date date
532 #:languages languages
533 #:version version
534 #:manual manual))
535 (list html-manual-indexes
536 html-manual pdf-manual))
537 #:copy? #t))
538
539(define (latest-commit+date directory)
540 "Return two values: the last commit ID (a hex string) for DIRECTORY, and its
541commit date (an integer)."
542 (let* ((repository (repository-open directory))
543 (head (repository-head repository))
544 (oid (reference-target head))
545 (commit (commit-lookup repository oid)))
546 ;; TODO: Use (git describe) when it's widely available.
547 (values (oid->string oid) (commit-time commit))))
548
549\f
550(let* ((root (canonicalize-path
551 (string-append (current-source-directory) "/..")))
552 (commit date (latest-commit+date root)))
553 (format (current-error-port)
554 "building manual from work tree around commit ~a, ~a~%"
555 commit
556 (let* ((time (make-time time-utc 0 date))
557 (date (time-utc->date time)))
558 (date->string date "~e ~B ~Y")))
559 (pdf+html-manual (local-file root "guix" #:recursive? #t
560 #:select? (git-predicate root))
561 #:version (or (getenv "GUIX_MANUAL_VERSION")
562 (string-take commit 7))
563 #:date date))