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