gnu: Add ucd.
[jackhill/guix/guix.git] / gnu / installer.scm
CommitLineData
d0f3a672
MO
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2018 Mathieu Othacehe <m.othacehe@gmail.com>
3191b5f6 3;;; Copyright © 2019 Ludovic Courtès <ludo@gnu.org>
8fec416c 4;;; Copyright © 2019, 2020 Tobias Geerinckx-Rice <me@tobias.gr>
d0f3a672
MO
5;;;
6;;; This file is part of GNU Guix.
7;;;
8;;; GNU Guix is free software; you can redistribute it and/or modify it
9;;; under the terms of the GNU General Public License as published by
10;;; the Free Software Foundation; either version 3 of the License, or (at
11;;; your option) any later version.
12;;;
13;;; GNU Guix is distributed in the hope that it will be useful, but
14;;; WITHOUT ANY WARRANTY; without even the implied warranty of
15;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16;;; GNU General Public License for more details.
17;;;
18;;; You should have received a copy of the GNU General Public License
19;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
20
21(define-module (gnu installer)
69a934f2 22 #:use-module (guix discovery)
a49d633c
MO
23 #:use-module (guix packages)
24 #:use-module (guix gexp)
25 #:use-module (guix modules)
26 #:use-module (guix utils)
d0f3a672 27 #:use-module (guix ui)
a49d633c
MO
28 #:use-module ((guix self) #:select (make-config.scm))
29 #:use-module (gnu packages admin)
30 #:use-module (gnu packages base)
31 #:use-module (gnu packages bash)
32 #:use-module (gnu packages connman)
bf304dbc 33 #:use-module (gnu packages cryptsetup)
69a934f2 34 #:use-module (gnu packages disk)
8fec416c 35 #:use-module (gnu packages file-systems)
a49d633c 36 #:use-module (gnu packages guile)
0791437f 37 #:use-module (gnu packages guile-xyz)
a49d633c
MO
38 #:autoload (gnu packages gnupg) (guile-gcrypt)
39 #:use-module (gnu packages iso-codes)
40 #:use-module (gnu packages linux)
41 #:use-module (gnu packages ncurses)
42 #:use-module (gnu packages package-management)
43 #:use-module (gnu packages xorg)
76269f6b 44 #:use-module (gnu system locale)
a49d633c 45 #:use-module (ice-9 match)
d0f3a672 46 #:use-module (srfi srfi-1)
a49d633c 47 #:export (installer-program))
d0f3a672 48
50247be5
LC
49(define module-to-import?
50 ;; Return true for modules that should be imported. For (gnu system …) and
51 ;; (gnu packages …) modules, we simply add the whole 'guix' package via
52 ;; 'with-extensions' (to avoid having to rebuild it all), which is why these
53 ;; modules are excluded here.
a49d633c
MO
54 (match-lambda
55 (('guix 'config) #f)
50247be5
LC
56 (('gnu 'installer _ ...) #t)
57 (('gnu 'build _ ...) #t)
58 (('guix 'build _ ...) #t)
59 (_ #f)))
a49d633c
MO
60
61(define* (build-compiled-file name locale-builder)
62 "Return a file-like object that evalutes the gexp LOCALE-BUILDER and store
63its result in the scheme file NAME. The derivation will also build a compiled
64version of this file."
65 (define set-utf8-locale
66 #~(begin
67 (setenv "LOCPATH"
68 #$(file-append glibc-utf8-locales "/lib/locale/"
69 (version-major+minor
70 (package-version glibc-utf8-locales))))
71 (setlocale LC_ALL "en_US.utf8")))
72
73 (define builder
81c3dc32 74 (with-extensions (list guile-json-3)
a49d633c
MO
75 (with-imported-modules (source-module-closure
76 '((gnu installer locale)))
77 #~(begin
78 (use-modules (gnu installer locale))
79
80 ;; The locale files contain non-ASCII characters.
81 #$set-utf8-locale
82
83 (mkdir #$output)
84 (let ((locale-file
85 (string-append #$output "/" #$name ".scm"))
86 (locale-compiled-file
87 (string-append #$output "/" #$name ".go")))
88 (call-with-output-file locale-file
89 (lambda (port)
90 (write #$locale-builder port)))
91 (compile-file locale-file
92 #:output-file locale-compiled-file))))))
93 (computed-file name builder))
94
95(define apply-locale
96 ;; Install the specified locale.
c7dc6042
LC
97 (with-imported-modules (source-module-closure '((gnu services herd)))
98 #~(lambda (locale)
99 (false-if-exception
100 (setlocale LC_ALL locale))
101
102 ;; Restart the documentation viewer so it displays the manual in
103 ;; language that corresponds to LOCALE.
104 (with-error-to-port (%make-void-port "w")
105 (lambda ()
106 (stop-service 'term-tty2)
107 (start-service 'term-tty2 (list locale)))))))
a49d633c
MO
108
109(define* (compute-locale-step #:key
110 locales-name
111 iso639-languages-name
112 iso3166-territories-name)
113 "Return a gexp that run the locale-page of INSTALLER, and install the
114selected locale. The list of locales, languages and territories passed to
115locale-page are computed in derivations named respectively LOCALES-NAME,
116ISO639-LANGUAGES-NAME and ISO3166-TERRITORIES-NAME. Those lists are compiled,
117so that when the installer is run, all the lengthy operations have already
118been performed at build time."
119 (define (compiled-file-loader file name)
120 #~(load-compiled
121 (string-append #$file "/" #$name ".go")))
122
123 (let* ((supported-locales #~(supported-locales->locales
76269f6b 124 #+(glibc-supported-locales)))
a49d633c
MO
125 (iso-codes #~(string-append #$iso-codes "/share/iso-codes/json/"))
126 (iso639-3 #~(string-append #$iso-codes "iso_639-3.json"))
127 (iso639-5 #~(string-append #$iso-codes "iso_639-5.json"))
128 (iso3166 #~(string-append #$iso-codes "iso_3166-1.json"))
129 (locales-file (build-compiled-file
130 locales-name
131 #~`(quote ,#$supported-locales)))
132 (iso639-file (build-compiled-file
133 iso639-languages-name
134 #~`(quote ,(iso639->iso639-languages
135 #$supported-locales
136 #$iso639-3 #$iso639-5))))
137 (iso3166-file (build-compiled-file
138 iso3166-territories-name
139 #~`(quote ,(iso3166->iso3166-territories #$iso3166))))
140 (locales-loader (compiled-file-loader locales-file
141 locales-name))
142 (iso639-loader (compiled-file-loader iso639-file
143 iso639-languages-name))
144 (iso3166-loader (compiled-file-loader iso3166-file
145 iso3166-territories-name)))
146 #~(lambda (current-installer)
147 (let ((result
148 ((installer-locale-page current-installer)
149 #:supported-locales #$locales-loader
150 #:iso639-languages #$iso639-loader
151 #:iso3166-territories #$iso3166-loader)))
dc5f3275
MO
152 (#$apply-locale result)
153 result))))
a49d633c
MO
154
155(define apply-keymap
c088b2e4 156 ;; Apply the specified keymap. Use the default keyboard model.
a49d633c 157 #~(match-lambda
c088b2e4
MO
158 ((layout variant)
159 (kmscon-update-keymap (default-keyboard-model)
160 layout variant))))
a49d633c
MO
161
162(define* (compute-keymap-step)
163 "Return a gexp that runs the keymap-page of INSTALLER and install the
164selected keymap."
165 #~(lambda (current-installer)
166 (let ((result
167 (call-with-values
168 (lambda ()
169 (xkb-rules->models+layouts
170 (string-append #$xkeyboard-config
171 "/share/X11/xkb/rules/base.xml")))
172 (lambda (models layouts)
173 ((installer-keymap-page current-installer)
c088b2e4 174 layouts)))))
3191b5f6
LC
175 (#$apply-keymap result)
176 result)))
a49d633c
MO
177
178(define (installer-steps)
179 (let ((locale-step (compute-locale-step
180 #:locales-name "locales"
181 #:iso639-languages-name "iso639-languages"
182 #:iso3166-territories-name "iso3166-territories"))
183 (keymap-step (compute-keymap-step))
184 (timezone-data #~(string-append #$tzdata
185 "/share/zoneinfo/zone.tab")))
186 #~(lambda (current-installer)
187 (list
6efd8430
MO
188 ;; Ask the user to choose a locale among those supported by
189 ;; the glibc. Install the selected locale right away, so that
190 ;; the user may benefit from any available translation for the
191 ;; installer messages.
a49d633c
MO
192 (installer-step
193 (id 'locale)
dc5f3275 194 (description (G_ "Locale"))
a49d633c 195 (compute (lambda _
dc5f3275
MO
196 (#$locale-step current-installer)))
197 (configuration-formatter locale->configuration))
a49d633c 198
850ddf94
LC
199 ;; Welcome the user and ask them to choose between manual
200 ;; installation and graphical install.
201 (installer-step
202 (id 'welcome)
203 (compute (lambda _
204 ((installer-welcome-page current-installer)
205 #$(local-file "installer/aux-files/logo.txt")))))
206
a49d633c
MO
207 ;; Ask the user to select a timezone under glibc format.
208 (installer-step
209 (id 'timezone)
dc5f3275 210 (description (G_ "Timezone"))
a49d633c
MO
211 (compute (lambda _
212 ((installer-timezone-page current-installer)
dc5f3275
MO
213 #$timezone-data)))
214 (configuration-formatter posix-tz->configuration))
a49d633c
MO
215
216 ;; The installer runs in a kmscon virtual terminal where loadkeys
217 ;; won't work. kmscon uses libxkbcommon as a backend for keyboard
218 ;; input. It is possible to update kmscon current keymap by sending it
219 ;; a keyboard model, layout and variant, in a somehow similar way as
220 ;; what is done with setxkbmap utility.
221 ;;
222 ;; So ask for a keyboard model, layout and variant to update the
223 ;; current kmscon keymap.
224 (installer-step
225 (id 'keymap)
226 (description (G_ "Keyboard mapping selection"))
227 (compute (lambda _
3191b5f6
LC
228 (#$keymap-step current-installer)))
229 (configuration-formatter keyboard-layout->configuration))
a49d633c
MO
230
231 ;; Ask the user to input a hostname for the system.
232 (installer-step
233 (id 'hostname)
dc5f3275 234 (description (G_ "Hostname"))
a49d633c 235 (compute (lambda _
dc5f3275
MO
236 ((installer-hostname-page current-installer))))
237 (configuration-formatter hostname->configuration))
a49d633c
MO
238
239 ;; Provide an interface above connmanctl, so that the user can select
240 ;; a network susceptible to acces Internet.
241 (installer-step
242 (id 'network)
243 (description (G_ "Network selection"))
244 (compute (lambda _
245 ((installer-network-page current-installer)))))
246
247 ;; Prompt for users (name, group and home directory).
248 (installer-step
dc5f3275
MO
249 (id 'user)
250 (description (G_ "User creation"))
251 (compute (lambda _
252 ((installer-user-page current-installer))))
253 (configuration-formatter users->configuration))
254
b51bde71
MO
255 ;; Ask the user to choose one or many desktop environment(s).
256 (installer-step
257 (id 'services)
258 (description (G_ "Services"))
a49d633c 259 (compute (lambda _
b51bde71 260 ((installer-services-page current-installer))))
75988317 261 (configuration-formatter system-services->configuration))
dc5f3275 262
d4d27ddb
TGR
263 ;; Run a partitioning tool allowing the user to modify
264 ;; partition tables, partitions and their mount points.
265 ;; Do this last so the user has something to boot if any
266 ;; of the previous steps didn't go as expected.
267 (installer-step
268 (id 'partition)
269 (description (G_ "Partitioning"))
270 (compute (lambda _
271 ((installer-partition-page current-installer))))
272 (configuration-formatter user-partitions->configuration))
273
b51bde71 274 (installer-step
dc5f3275
MO
275 (id 'final)
276 (description (G_ "Configuration file"))
277 (compute
278 (lambda (result prev-steps)
279 ((installer-final-page current-installer)
b51bde71 280 result prev-steps))))))))
a49d633c
MO
281
282(define (installer-program)
283 "Return a file-like object that runs the given INSTALLER."
284 (define init-gettext
285 ;; Initialize gettext support, so that installer messages can be
286 ;; translated.
287 #~(begin
288 (bindtextdomain "guix" (string-append #$guix "/share/locale"))
289 (textdomain "guix")))
290
291 (define set-installer-path
292 ;; Add the specified binary to PATH for later use by the installer.
293 #~(let* ((inputs
69a934f2
MO
294 '#$(append (list bash ;start subshells
295 connman ;call connmanctl
bf304dbc 296 cryptsetup
69a934f2
MO
297 dosfstools ;mkfs.fat
298 e2fsprogs ;mkfs.ext4
8fec416c
TGR
299 btrfs-progs ;mkfs.btrfs
300 jfsutils ;jfs_mkfs
69a934f2
MO
301 kbd ;chvt
302 guix ;guix system init call
303 util-linux ;mkwap
304 shadow)
a49d633c
MO
305 (map canonical-package (list coreutils)))))
306 (with-output-to-port (%make-void-port "w")
307 (lambda ()
308 (set-path-environment-variable "PATH" '("bin" "sbin") inputs)))))
309
310 (define steps (installer-steps))
69a934f2
MO
311 (define modules
312 (scheme-modules*
313 (string-append (current-source-directory) "/..")
314 "gnu/installer"))
a49d633c
MO
315
316 (define installer-builder
50247be5
LC
317 ;; Note: Include GUIX as an extension to get all the (gnu system …), (gnu
318 ;; packages …), etc. modules.
69a934f2
MO
319 (with-extensions (list guile-gcrypt guile-newt
320 guile-parted guile-bytestructures
81c3dc32 321 guile-json-3 guile-git guix)
a49d633c 322 (with-imported-modules `(,@(source-module-closure
69a934f2 323 `(,@modules
98f03548 324 (gnu services herd)
a49d633c 325 (guix build utils))
50247be5 326 #:select? module-to-import?)
a49d633c
MO
327 ((guix config) => ,(make-config.scm)))
328 #~(begin
329 (use-modules (gnu installer record)
330 (gnu installer keymap)
331 (gnu installer steps)
dc5f3275 332 (gnu installer final)
b4658c25 333 (gnu installer hostname)
a49d633c 334 (gnu installer locale)
dc5f3275
MO
335 (gnu installer parted)
336 (gnu installer services)
337 (gnu installer timezone)
338 (gnu installer user)
a49d633c 339 (gnu installer newt)
3191b5f6
LC
340 ((gnu installer newt keymap)
341 #:select (keyboard-layout->configuration))
c7dc6042 342 (gnu services herd)
a49d633c
MO
343 (guix i18n)
344 (guix build utils)
3114786e
MO
345 ((system repl debug)
346 #:select (terminal-width))
a49d633c
MO
347 (ice-9 match))
348
a49d633c
MO
349 ;; Initialize gettext support so that installers can use
350 ;; (guix i18n) module.
351 #$init-gettext
352
353 ;; Add some binaries used by the installers to PATH.
354 #$set-installer-path
355
7837a572
LC
356 ;; Arrange for language and territory name translations to be
357 ;; available. We need them at run time, not just compile time,
358 ;; because some territories have several corresponding languages
359 ;; (e.g., "French" is always displayed as "français", but
360 ;; "Belgium" could be translated to Dutch, French, or German.)
361 (bindtextdomain "iso_639-3" ;languages
362 #+(file-append iso-codes "/share/locale"))
363 (bindtextdomain "iso_3166-1" ;territories
364 #+(file-append iso-codes "/share/locale"))
365
feaa83a3
LC
366 ;; Likewise for XKB keyboard layout names.
367 (bindtextdomain "xkeyboard-config"
368 #+(file-append xkeyboard-config "/share/locale"))
369
3114786e
MO
370 ;; Initialize 'terminal-width' in (system repl debug)
371 ;; to a large-enough value to make backtrace more
372 ;; verbose.
373 (terminal-width 200)
374
dc5f3275
MO
375 (let* ((current-installer newt-installer)
376 (steps (#$steps current-installer)))
a49d633c
MO
377 ((installer-init current-installer))
378
379 (catch #t
380 (lambda ()
98f03548
LC
381 (define results
382 (run-installer-steps
383 #:rewind-strategy 'menu
384 #:menu-proc (installer-menu-page current-installer)
385 #:steps steps))
386
387 (match (result-step results 'final)
388 ('success
389 ;; We did it! Let's reboot!
390 (sync)
391 (stop-service 'root))
392 (_ ;installation failed
393 ;; TODO: Honor the result of 'run-install-failed-page'.
394 #f)))
a49d633c
MO
395 (const #f)
396 (lambda (key . args)
133c401f
MO
397 (let ((error-file "/tmp/last-installer-error"))
398 (call-with-output-file error-file
399 (lambda (port)
400 (display-backtrace (make-stack #t) port)
401 (print-exception port
402 (stack-ref (make-stack #t) 1)
403 key args)))
404 ((installer-exit-error current-installer)
405 error-file key args))
dc5f3275
MO
406 (primitive-exit 1)))
407
408 ((installer-exit current-installer)))))))
d0f3a672 409
6b48825e
MO
410 (program-file
411 "installer"
412 #~(begin
413 ;; Set the default locale to install unicode support. For
414 ;; some reason, unicode support is not correctly installed
415 ;; when calling this in 'installer-builder'.
416 (setenv "LANG" "en_US.UTF-8")
6c849cdb
LC
417 (execl #$(program-file "installer-real" installer-builder)
418 "installer-real"))))