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