build: Require Guile >= 2.0.7.
[jackhill/guix/guix.git] / guix / ui.scm
CommitLineData
233e7676 1;;; GNU Guix --- Functional package management for GNU
e9651e39 2;;; Copyright © 2012, 2013, 2014, 2015 Ludovic Courtès <ludo@gnu.org>
e49951eb 3;;; Copyright © 2013 Mark H Weaver <mhw@netris.org>
98eb8cbe 4;;; Copyright © 2013 Nikita Karetnikov <nikita@karetnikov.org>
c0c018f1 5;;; Copyright © 2014 Alex Kost <alezost@gmail.com>
e9c1e22f 6;;; Copyright © 2014 Deck Pickard <deck.r.pickard@gmail.com>
073c34d7 7;;;
233e7676 8;;; This file is part of GNU Guix.
073c34d7 9;;;
233e7676 10;;; GNU Guix is free software; you can redistribute it and/or modify it
073c34d7
LC
11;;; under the terms of the GNU General Public License as published by
12;;; the Free Software Foundation; either version 3 of the License, or (at
13;;; your option) any later version.
14;;;
233e7676 15;;; GNU Guix is distributed in the hope that it will be useful, but
073c34d7
LC
16;;; WITHOUT ANY WARRANTY; without even the implied warranty of
17;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;;; GNU General Public License for more details.
19;;;
20;;; You should have received a copy of the GNU General Public License
233e7676 21;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
073c34d7
LC
22
23(define-module (guix ui)
24 #:use-module (guix utils)
25 #:use-module (guix store)
cdd5d6f9 26 #:use-module (guix config)
073c34d7 27 #:use-module (guix packages)
5d7a8584 28 #:use-module (guix profiles)
9bb2b96a 29 #:use-module (guix derivations)
b7071bc5
LC
30 #:use-module (guix build-system)
31 #:use-module (guix serialization)
59758816 32 #:use-module ((guix build utils) #:select (mkdir-p))
299112d3
LC
33 #:use-module ((guix licenses) #:select (license? license-name))
34 #:use-module (srfi srfi-1)
35 #:use-module (srfi srfi-11)
2cd09108 36 #:use-module (srfi srfi-19)
073c34d7
LC
37 #:use-module (srfi srfi-26)
38 #:use-module (srfi srfi-34)
c1d52c71 39 #:use-module (srfi srfi-35)
a5975ced 40 #:use-module (srfi srfi-37)
e31ff8b8 41 #:autoload (ice-9 ftw) (scandir)
64fc89b6 42 #:use-module (ice-9 match)
9bb2b96a 43 #:use-module (ice-9 format)
2cd09108 44 #:use-module (ice-9 regex)
44fd6ef1 45 #:replace (symlink)
073c34d7
LC
46 #:export (_
47 N_
ee764179 48 P_
70e629f5 49 report-error
073c34d7 50 leave
1151f6ae 51 report-load-error
4ae7559f 52 warn-about-load-error
cdd5d6f9 53 show-version-and-exit
3441e164 54 show-bug-report-information
969e678e 55 string->number*
1d6243cf 56 size->number
9bb2b96a 57 show-what-to-build
5d7a8584 58 show-manifest-transaction
073c34d7 59 call-with-error-handling
64fc89b6 60 with-error-handling
ac5de156 61 read/eval
eb0880e7 62 read/eval-package-expression
299112d3 63 location->string
c61b026e 64 switch-symlinks
f651b477 65 config-directory
299112d3
LC
66 fill-paragraph
67 string->recutils
e49951eb 68 package->recutils
2876b989 69 package-specification->name+version+output
2cd09108
NK
70 string->generations
71 string->duration
a5975ced 72 args-fold*
b3f21389 73 parse-command-line
e49951eb 74 run-guix-command
a2011be5
LC
75 program-name
76 guix-warning-port
77 warning
e49951eb 78 guix-main))
073c34d7
LC
79
80;;; Commentary:
81;;;
82;;; User interface facilities for command-line tools.
83;;;
84;;; Code:
85
86(define %gettext-domain
ee764179 87 ;; Text domain for strings used in the tools.
073c34d7
LC
88 "guix")
89
ee764179
LC
90(define %package-text-domain
91 ;; Text domain for package synopses and descriptions.
92 "guix-packages")
93
073c34d7
LC
94(define _ (cut gettext <> %gettext-domain))
95(define N_ (cut ngettext <> <> <> %gettext-domain))
ee764179 96(define P_ (cut gettext <> %package-text-domain))
073c34d7 97
b2a886f6
LC
98(define-syntax-rule (define-diagnostic name prefix)
99 "Create a diagnostic macro (i.e., NAME), which will prepend PREFIX to all
100messages."
101 (define-syntax name
102 (lambda (x)
103 (define (augmented-format-string fmt)
104 (string-append "~:[~*~;guix ~a: ~]~a" (syntax->datum fmt)))
105
89d02b98
LC
106 (syntax-case x ()
107 ((name (underscore fmt) args (... ...))
108 (and (string? (syntax->datum #'fmt))
109 (free-identifier=? #'underscore #'_))
b2a886f6
LC
110 (with-syntax ((fmt* (augmented-format-string #'fmt))
111 (prefix (datum->syntax x prefix)))
112 #'(format (guix-warning-port) (gettext fmt*)
113 (program-name) (program-name) prefix
114 args (... ...))))
89d02b98 115 ((name (N-underscore singular plural n) args (... ...))
b2a886f6 116 (and (string? (syntax->datum #'singular))
89d02b98
LC
117 (string? (syntax->datum #'plural))
118 (free-identifier=? #'N-underscore #'N_))
b2a886f6
LC
119 (with-syntax ((s (augmented-format-string #'singular))
120 (p (augmented-format-string #'plural))
121 (prefix (datum->syntax x prefix)))
122 #'(format (guix-warning-port)
123 (ngettext s p n %gettext-domain)
124 (program-name) (program-name) prefix
125 args (... ...))))))))
126
127(define-diagnostic warning "warning: ") ; emit a warning
128
129(define-diagnostic report-error "error: ")
130(define-syntax-rule (leave args ...)
131 "Emit an error message and exit."
132 (begin
133 (report-error args ...)
134 (exit 1)))
135
1151f6ae
LC
136(define (report-load-error file args)
137 "Report the failure to load FILE, a user-provided Scheme file, and exit.
138ARGS is the list of arguments received by the 'throw' handler."
139 (match args
140 (('system-error . _)
141 (let ((err (system-error-errno args)))
142 (leave (_ "failed to load '~a': ~a~%") file (strerror err))))
143 (('syntax-error proc message properties form . rest)
144 (let ((loc (source-properties->location properties)))
145 (format (current-error-port) (_ "~a: error: ~a~%")
146 (location->string loc) message)
147 (exit 1)))
148 ((error args ...)
149 (report-error (_ "failed to load '~a':~%") file)
150 (apply display-error #f (current-error-port) args)
151 (exit 1))))
152
4ae7559f
LC
153(define (warn-about-load-error file args) ;FIXME: factorize with ↑
154 "Report the failure to load FILE, a user-provided Scheme file, without
155exiting. ARGS is the list of arguments received by the 'throw' handler."
156 (match args
157 (('system-error . _)
158 (let ((err (system-error-errno args)))
159 (warning (_ "failed to load '~a': ~a~%") file (strerror err))))
160 (('syntax-error proc message properties form . rest)
161 (let ((loc (source-properties->location properties)))
162 (format (current-error-port) (_ "~a: warning: ~a~%")
163 (location->string loc) message)))
164 ((error args ...)
165 (warning (_ "failed to load '~a':~%") file)
166 (apply display-error #f (current-error-port) args))))
167
b2a886f6
LC
168(define (install-locale)
169 "Install the current locale settings."
170 (catch 'system-error
171 (lambda _
172 (setlocale LC_ALL ""))
173 (lambda args
174 (warning (_ "failed to install locale: ~a~%")
175 (strerror (system-error-errno args))))))
176
e49951eb 177(define (initialize-guix)
633f045f 178 "Perform the usual initialization for stand-alone Guix commands."
e49951eb 179 (install-locale)
39e9f95d 180 (textdomain %gettext-domain)
e14c3929
LC
181
182 ;; Ignore SIGPIPE. If the daemon closes the connection, we prefer to be
183 ;; notified via an EPIPE later.
184 (sigaction SIGPIPE SIG_IGN)
185
e49951eb
MW
186 (setvbuf (current-output-port) _IOLBF)
187 (setvbuf (current-error-port) _IOLBF))
188
cdd5d6f9
LC
189(define* (show-version-and-exit #:optional (command (car (command-line))))
190 "Display version information for COMMAND and `(exit 0)'."
191 (simple-format #t "~a (~a) ~a~%"
192 command %guix-package-name %guix-version)
9250b304 193 (display (_ "Copyright (C) 2015 the Guix authors
64a967cc
LC
194License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
195This is free software: you are free to change and redistribute it.
196There is NO WARRANTY, to the extent permitted by law.
197"))
cdd5d6f9
LC
198 (exit 0))
199
3441e164
LC
200(define (show-bug-report-information)
201 (format #t (_ "
202Report bugs to: ~a.") %guix-bug-report-address)
203 (format #t (_ "
204~a home page: <~a>") %guix-package-name %guix-home-page-url)
205 (display (_ "
206General help using GNU software: <http://www.gnu.org/gethelp/>"))
207 (newline))
208
44fd6ef1
LC
209(define symlink
210 (let ((real-symlink (@ (guile) symlink)))
211 (lambda (target link)
212 "This is a 'symlink' replacement that provides proper error reporting."
213 (catch 'system-error
214 (lambda ()
215 (real-symlink target link))
216 (lambda (key proc fmt args errno)
217 ;; Augment the FMT and ARGS with information about LINK (this
218 ;; information is missing as of Guile 2.0.11, making the exception
219 ;; uninformative.)
220 (apply throw key proc "~A: ~S"
221 (append args (list link))
222 errno))))))
223
969e678e
LC
224(define (string->number* str)
225 "Like `string->number', but error out with an error message on failure."
226 (or (string->number str)
227 (leave (_ "~a: invalid number~%") str)))
228
1d6243cf
LC
229(define (size->number str)
230 "Convert STR, a storage measurement representation such as \"1024\" or
231\"1MiB\", to a number of bytes. Raise an error if STR could not be
232interpreted."
233 (define unit-pos
234 (string-rindex str char-set:digit))
235
236 (define unit
237 (and unit-pos (substring str (+ 1 unit-pos))))
238
239 (let* ((numstr (if unit-pos
240 (substring str 0 (+ 1 unit-pos))
241 str))
242 (num (string->number numstr)))
243 (unless num
244 (leave (_ "invalid number: ~a~%") numstr))
245
246 ((compose inexact->exact round)
247 (* num
248 (match unit
4a44d7bb
LC
249 ((or "KiB" "K" "k") (expt 2 10))
250 ((or "MiB" "M") (expt 2 20))
251 ((or "GiB" "G") (expt 2 30))
252 ((or "TiB" "T") (expt 2 40))
253 ((or "PiB" "P") (expt 2 50))
254 ((or "EiB" "E") (expt 2 60))
255 ((or "ZiB" "Z") (expt 2 70))
256 ((or "YiB" "Y") (expt 2 80))
257 ("kB" (expt 10 3))
1d6243cf
LC
258 ("MB" (expt 10 6))
259 ("GB" (expt 10 9))
260 ("TB" (expt 10 12))
4a44d7bb
LC
261 ("PB" (expt 10 15))
262 ("EB" (expt 10 18))
263 ("ZB" (expt 10 21))
264 ("YB" (expt 10 24))
1d6243cf
LC
265 ("" 1)
266 (_
267 (leave (_ "unknown unit: ~a~%") unit)))))))
268
073c34d7
LC
269(define (call-with-error-handling thunk)
270 "Call THUNK within a user-friendly error handler."
271 (guard (c ((package-input-error? c)
272 (let* ((package (package-error-package c))
273 (input (package-error-invalid-input c))
274 (location (package-location package))
275 (file (location-file location))
276 (line (location-line location))
277 (column (location-column location)))
98eb8cbe 278 (leave (_ "~a:~a:~a: package `~a' has an invalid input: ~s~%")
073c34d7
LC
279 file line column
280 (package-full-name package) input)))
9b222abe
LC
281 ((package-cross-build-system-error? c)
282 (let* ((package (package-error-package c))
283 (loc (package-location package))
284 (system (package-build-system package)))
285 (leave (_ "~a: ~a: build system `~a' does not support cross builds~%")
286 (location->string loc)
287 (package-full-name package)
288 (build-system-name system))))
c0c018f1
AK
289 ((profile-not-found-error? c)
290 (leave (_ "profile '~a' does not exist~%")
291 (profile-error-profile c)))
292 ((missing-generation-error? c)
293 (leave (_ "generation ~a of profile '~a' does not exist~%")
294 (missing-generation-error-generation c)
295 (profile-error-profile c)))
b7071bc5
LC
296 ((nar-error? c)
297 (let ((file (nar-error-file c))
298 (port (nar-error-port c)))
299 (if file
300 (leave (_ "corrupt input while restoring '~a' from ~s~%")
301 file (or (port-filename port) port))
302 (leave (_ "corrupt input while restoring archive from ~s~%")
303 (or (port-filename port) port)))))
ef86c39f 304 ((nix-connection-error? c)
98eb8cbe 305 (leave (_ "failed to connect to `~a': ~a~%")
ef86c39f
LC
306 (nix-connection-error-file c)
307 (strerror (nix-connection-error-code c))))
073c34d7
LC
308 ((nix-protocol-error? c)
309 ;; FIXME: Server-provided error messages aren't i18n'd.
98eb8cbe 310 (leave (_ "build failed: ~a~%")
c1d52c71 311 (nix-protocol-error-message c)))
f304c9c2
LC
312 ((derivation-missing-output-error? c)
313 (leave (_ "reference to invalid output '~a' of derivation '~a'~%")
314 (derivation-missing-output c)
315 (derivation-file-name (derivation-error-derivation c))))
c1d52c71
LC
316 ((message-condition? c)
317 ;; Normally '&message' error conditions have an i18n'd message.
12703d08
LC
318 (leave (_ "~a~%")
319 (gettext (condition-message c) %gettext-domain))))
e14c3929
LC
320 ;; Catch EPIPE and the likes.
321 (catch 'system-error
322 thunk
bde8c0e6
LC
323 (lambda (key proc format-string format-args . rest)
324 (leave (_ "~a: ~a~%") proc
325 (apply format #f format-string format-args))))))
073c34d7 326
56b82106
LC
327(define %guix-user-module
328 ;; Module in which user expressions are evaluated.
07254feb
LC
329 ;; Compute lazily to avoid circularity with (guix gexp).
330 (delay
331 (let ((module (make-module)))
332 (beautify-user-module! module)
333 ;; Use (guix gexp) so that one can use #~ & co.
334 (module-use! module (resolve-interface '(guix gexp)))
335 module)))
56b82106 336
ac5de156
LC
337(define (read/eval str)
338 "Read and evaluate STR, raising an error if something goes wrong."
eb0880e7
LC
339 (let ((exp (catch #t
340 (lambda ()
341 (call-with-input-string str read))
342 (lambda args
343 (leave (_ "failed to read expression ~s: ~s~%")
344 str args)))))
ac5de156
LC
345 (catch #t
346 (lambda ()
07254feb 347 (eval exp (force %guix-user-module)))
ac5de156
LC
348 (lambda args
349 (leave (_ "failed to evaluate expression `~a': ~s~%")
350 exp args)))))
351
352(define (read/eval-package-expression str)
353 "Read and evaluate STR and return the package it refers to, or exit an
354error."
355 (match (read/eval str)
356 ((? package? p) p)
357 (_
358 (leave (_ "expression ~s does not evaluate to a package~%")
359 str))))
eb0880e7 360
dd36b51b
LC
361(define* (show-what-to-build store drv
362 #:key dry-run? (use-substitutes? #t))
9bb2b96a 363 "Show what will or would (depending on DRY-RUN?) be built in realizing the
4d60610a 364derivations listed in DRV. Return #t if there's something to build, #f
dd36b51b
LC
365otherwise. When USE-SUBSTITUTES?, check and report what is prerequisites are
366available for download."
e9651e39
LC
367 (define substitutable?
368 ;; Call 'substitutation-oracle' upfront so we don't end up launching the
369 ;; substituter many times. This makes a big difference, especially when
370 ;; DRV is a long list as is the case with 'guix environment'.
371 (if use-substitutes?
372 (substitution-oracle store drv)
373 (const #f)))
374
52ddf2ae 375 (define (built-or-substitutable? drv)
f304c9c2
LC
376 (or (null? (derivation-outputs drv))
377 (let ((out (derivation->output-path drv))) ;XXX: assume "out" exists
52ddf2ae 378 (or (valid-path? store out)
e9651e39 379 (substitutable? out)))))
52ddf2ae 380
dd36b51b 381 (let*-values (((build download)
59688fc4
LC
382 (fold2 (lambda (drv build download)
383 (let-values (((b d)
384 (derivation-prerequisites-to-build
385 store drv
e9651e39 386 #:substitutable? substitutable?)))
59688fc4
LC
387 (values (append b build)
388 (append d download))))
dd36b51b
LC
389 '() '()
390 drv))
391 ((build) ; add the DRV themselves
392 (delete-duplicates
59688fc4 393 (append (map derivation-file-name
52ddf2ae 394 (remove built-or-substitutable? drv))
dd36b51b
LC
395 (map derivation-input-path build))))
396 ((download) ; add the references of DOWNLOAD
1a8b7834
LC
397 (if use-substitutes?
398 (delete-duplicates
399 (append download
400 (remove (cut valid-path? store <>)
401 (append-map
402 substitutable-references
403 (substitutable-path-info store
404 download)))))
405 download)))
00554b2a 406 ;; TODO: Show the installed size of DOWNLOAD.
9bb2b96a 407 (if dry-run?
dd36b51b
LC
408 (begin
409 (format (current-error-port)
83e61a73
LC
410 (N_ "~:[The following derivation would be built:~%~{ ~a~%~}~;~]"
411 "~:[The following derivations would be built:~%~{ ~a~%~}~;~]"
dd36b51b
LC
412 (length build))
413 (null? build) build)
414 (format (current-error-port)
83e61a73
LC
415 (N_ "~:[The following file would be downloaded:~%~{ ~a~%~}~;~]"
416 "~:[The following files would be downloaded:~%~{ ~a~%~}~;~]"
dd36b51b
LC
417 (length download))
418 (null? download) download))
419 (begin
420 (format (current-error-port)
83e61a73
LC
421 (N_ "~:[The following derivation will be built:~%~{ ~a~%~}~;~]"
422 "~:[The following derivations will be built:~%~{ ~a~%~}~;~]"
dd36b51b
LC
423 (length build))
424 (null? build) build)
425 (format (current-error-port)
83e61a73
LC
426 (N_ "~:[The following file will be downloaded:~%~{ ~a~%~}~;~]"
427 "~:[The following files will be downloaded:~%~{ ~a~%~}~;~]"
dd36b51b
LC
428 (length download))
429 (null? download) download)))
430 (pair? build)))
9bb2b96a 431
5d7a8584
AK
432(define (right-arrow port)
433 "Return either a string containing the 'RIGHT ARROW' character, or an ASCII
434replacement if PORT is not Unicode-capable."
435 (with-fluids ((%default-port-encoding (port-encoding port)))
436 (let ((arrow "→"))
437 (catch 'encoding-error
438 (lambda ()
439 (call-with-output-string
440 (lambda (port)
441 (set-port-conversion-strategy! port 'error)
442 (display arrow port))))
443 (lambda (key . args)
444 "->")))))
445
446(define* (show-manifest-transaction store manifest transaction
447 #:key dry-run?)
448 "Display what will/would be installed/removed from MANIFEST by TRANSACTION."
449 (define (package-strings name version output item)
450 (map (lambda (name version output item)
451 (format #f " ~a~:[:~a~;~*~]\t~a\t~a"
452 name
453 (equal? output "out") output version
454 (if (package? item)
455 (package-output store item output)
456 item)))
457 name version output item))
458
459 (define → ;an arrow that can be represented on stderr
460 (right-arrow (current-error-port)))
461
462 (define (upgrade-string name old-version new-version output item)
463 (format #f " ~a~:[:~a~;~*~]\t~a ~a ~a\t~a"
464 name (equal? output "out") output
465 old-version → new-version
466 (if (package? item)
467 (package-output store item output)
468 item)))
469
46b23e1a 470 (let-values (((remove install upgrade downgrade)
5d7a8584
AK
471 (manifest-transaction-effects manifest transaction)))
472 (match remove
473 ((($ <manifest-entry> name version output item) ..1)
474 (let ((len (length name))
475 (remove (package-strings name version output item)))
476 (if dry-run?
477 (format (current-error-port)
478 (N_ "The following package would be removed:~%~{~a~%~}~%"
479 "The following packages would be removed:~%~{~a~%~}~%"
480 len)
481 remove)
482 (format (current-error-port)
483 (N_ "The following package will be removed:~%~{~a~%~}~%"
484 "The following packages will be removed:~%~{~a~%~}~%"
485 len)
486 remove))))
487 (_ #f))
46b23e1a
LC
488 (match downgrade
489 (((($ <manifest-entry> name old-version)
490 . ($ <manifest-entry> _ new-version output item)) ..1)
491 (let ((len (length name))
492 (downgrade (map upgrade-string
493 name old-version new-version output item)))
494 (if dry-run?
495 (format (current-error-port)
496 (N_ "The following package would be downgraded:~%~{~a~%~}~%"
497 "The following packages would be downgraded:~%~{~a~%~}~%"
498 len)
499 downgrade)
500 (format (current-error-port)
501 (N_ "The following package will be downgraded:~%~{~a~%~}~%"
502 "The following packages will be downgraded:~%~{~a~%~}~%"
503 len)
504 downgrade))))
505 (_ #f))
5d7a8584
AK
506 (match upgrade
507 (((($ <manifest-entry> name old-version)
508 . ($ <manifest-entry> _ new-version output item)) ..1)
509 (let ((len (length name))
510 (upgrade (map upgrade-string
511 name old-version new-version output item)))
512 (if dry-run?
513 (format (current-error-port)
514 (N_ "The following package would be upgraded:~%~{~a~%~}~%"
515 "The following packages would be upgraded:~%~{~a~%~}~%"
516 len)
517 upgrade)
518 (format (current-error-port)
519 (N_ "The following package will be upgraded:~%~{~a~%~}~%"
520 "The following packages will be upgraded:~%~{~a~%~}~%"
521 len)
522 upgrade))))
523 (_ #f))
524 (match install
525 ((($ <manifest-entry> name version output item _) ..1)
526 (let ((len (length name))
527 (install (package-strings name version output item)))
528 (if dry-run?
529 (format (current-error-port)
530 (N_ "The following package would be installed:~%~{~a~%~}~%"
531 "The following packages would be installed:~%~{~a~%~}~%"
532 len)
533 install)
534 (format (current-error-port)
535 (N_ "The following package will be installed:~%~{~a~%~}~%"
536 "The following packages will be installed:~%~{~a~%~}~%"
537 len)
538 install))))
539 (_ #f))))
540
073c34d7
LC
541(define-syntax with-error-handling
542 (syntax-rules ()
543 "Run BODY within a user-friendly error condition handler."
544 ((_ body ...)
545 (call-with-error-handling
546 (lambda ()
547 body ...)))))
548
64fc89b6
LC
549(define (location->string loc)
550 "Return a human-friendly, GNU-standard representation of LOC."
551 (match loc
552 (#f (_ "<unknown location>"))
553 (($ <location> file line column)
554 (format #f "~a:~a:~a" file line column))))
555
c61b026e
LC
556(define (switch-symlinks link target)
557 "Atomically switch LINK, a symbolic link, to point to TARGET. Works
558both when LINK already exists and when it does not."
559 (let ((pivot (string-append link ".new")))
560 (symlink target pivot)
561 (rename-file pivot link)))
562
f651b477
LC
563(define (config-directory)
564 "Return the name of the configuration directory, after making sure that it
565exists. Honor the XDG specs,
566<http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>."
567 (let ((dir (and=> (or (getenv "XDG_CONFIG_HOME")
568 (and=> (getenv "HOME")
569 (cut string-append <> "/.config")))
570 (cut string-append <> "/guix"))))
571 (catch 'system-error
572 (lambda ()
59758816 573 (mkdir-p dir)
f651b477
LC
574 dir)
575 (lambda args
59758816
LC
576 (let ((err (system-error-errno args)))
577 ;; ERR is necessarily different from EEXIST.
578 (leave (_ "failed to create configuration directory `~a': ~a~%")
579 dir (strerror err)))))))
f651b477 580
299112d3
LC
581(define* (fill-paragraph str width #:optional (column 0))
582 "Fill STR such that each line contains at most WIDTH characters, assuming
583that the first character is at COLUMN.
584
585When STR contains a single line break surrounded by other characters, it is
586converted to a space; sequences of more than one line break are preserved."
587 (define (maybe-break chr result)
588 (match result
589 ((column newlines chars)
590 (case chr
591 ((#\newline)
592 `(,column ,(+ 1 newlines) ,chars))
593 (else
3a09e1d2
CS
594 (let* ((spaces (if (and (pair? chars) (eqv? (car chars) #\.)) 2 1))
595 (chars (case newlines
596 ((0) chars)
597 ((1)
598 (append (make-list spaces #\space) chars))
599 (else
600 (append (make-list newlines #\newline) chars))))
601 (column (case newlines
602 ((0) column)
603 ((1) (+ spaces column))
604 (else 0))))
299112d3
LC
605 (let ((chars (cons chr chars))
606 (column (+ 1 column)))
607 (if (> column width)
608 (let*-values (((before after)
609 (break (cut eqv? #\space <>) chars))
610 ((len)
611 (length before)))
612 (if (<= len width)
613 `(,len
614 0
615 ,(if (null? after)
616 before
3a09e1d2
CS
617 (append before
618 (cons #\newline
619 (drop-while (cut eqv? #\space <>)
620 after)))))
299112d3
LC
621 `(,column 0 ,chars))) ; unbreakable
622 `(,column 0 ,chars)))))))))
623
624 (match (string-fold maybe-break
625 `(,column 0 ())
626 str)
627 ((_ _ chars)
628 (list->string (reverse chars)))))
629
2876b989
LC
630\f
631;;;
632;;; Packages.
633;;;
634
299112d3
LC
635(define (string->recutils str)
636 "Return a version of STR where newlines have been replaced by newlines
637followed by \"+ \", which makes for a valid multi-line field value in the
638`recutils' syntax."
639 (list->string
640 (string-fold-right (lambda (chr result)
641 (if (eqv? chr #\newline)
642 (cons* chr #\+ #\space result)
643 (cons chr result)))
644 '()
645 str)))
646
647(define* (package->recutils p port
648 #:optional (width (or (and=> (getenv "WIDTH")
649 string->number)
650 80)))
651 "Write to PORT a `recutils' record of package P, arranging to fit within
652WIDTH columns."
653 (define (description->recutils str)
ee764179 654 (let ((str (P_ str)))
299112d3
LC
655 (string->recutils
656 (fill-paragraph str width
657 (string-length "description: ")))))
658
20ffce82
LC
659 (define (dependencies->recutils packages)
660 (let ((list (string-join (map package-full-name
661 (sort packages package<?)) " ")))
662 (string->recutils
663 (fill-paragraph list width
664 (string-length "dependencies: ")))))
665
9c0fc279
CR
666 (define (package<? p1 p2)
667 (string<? (package-full-name p1) (package-full-name p2)))
668
299112d3
LC
669 ;; Note: Don't i18n field names so that people can post-process it.
670 (format port "name: ~a~%" (package-name p))
671 (format port "version: ~a~%" (package-version p))
5763ad92
LC
672 (format port "systems: ~a~%"
673 (string-join (package-transitive-supported-systems p)))
9c0fc279
CR
674 (format port "dependencies: ~a~%"
675 (match (package-direct-inputs p)
5e6feee6
EB
676 (((labels inputs . _) ...)
677 (dependencies->recutils (filter package? inputs)))))
299112d3
LC
678 (format port "location: ~a~%"
679 (or (and=> (package-location p) location->string)
680 (_ "unknown")))
8fa3e6b3
LC
681
682 ;; Note: Starting from version 1.6 or recutils, hyphens are not allowed in
683 ;; field identifiers.
684 (format port "homepage: ~a~%" (package-home-page p))
685
299112d3
LC
686 (format port "license: ~a~%"
687 (match (package-license p)
688 (((? license? licenses) ...)
689 (string-join (map license-name licenses)
690 ", "))
691 ((? license? license)
692 (license-name license))
693 (x
694 (_ "unknown"))))
695 (format port "synopsis: ~a~%"
696 (string-map (match-lambda
697 (#\newline #\space)
698 (chr chr))
ee764179 699 (or (and=> (package-synopsis p) P_)
299112d3
LC
700 "")))
701 (format port "description: ~a~%"
702 (and=> (package-description p) description->recutils))
703 (newline port))
704
2cd09108
NK
705(define (string->generations str)
706 "Return the list of generations matching a pattern in STR. This function
707accepts the following patterns: \"1\", \"1,2,3\", \"1..9\", \"1..\", \"..9\"."
708 (define (maybe-integer)
709 (let ((x (string->number str)))
710 (and (integer? x)
711 x)))
712
713 (define (maybe-comma-separated-integers)
714 (let ((lst (delete-duplicates
715 (map string->number
716 (string-split str #\,)))))
717 (and (every integer? lst)
718 lst)))
719
720 (cond ((maybe-integer)
721 =>
722 list)
723 ((maybe-comma-separated-integers)
724 =>
725 identity)
726 ((string-match "^([0-9]+)\\.\\.([0-9]+)$" str)
727 =>
728 (lambda (match)
729 (let ((s (string->number (match:substring match 1)))
730 (e (string->number (match:substring match 2))))
731 (and (every integer? (list s e))
732 (<= s e)
733 (iota (1+ (- e s)) s)))))
734 ((string-match "^([0-9]+)\\.\\.$" str)
735 =>
736 (lambda (match)
737 (let ((s (string->number (match:substring match 1))))
738 (and (integer? s)
739 `(>= ,s)))))
740 ((string-match "^\\.\\.([0-9]+)$" str)
741 =>
742 (lambda (match)
743 (let ((e (string->number (match:substring match 1))))
744 (and (integer? e)
745 `(<= ,e)))))
746 (else #f)))
747
748(define (string->duration str)
749 "Return the duration matching a pattern in STR. This function accepts the
750following patterns: \"1d\", \"1w\", \"1m\"."
751 (define (hours->duration hours match)
752 (make-time time-duration 0
753 (* 3600 hours (string->number (match:substring match 1)))))
754
755 (cond ((string-match "^([0-9]+)d$" str)
756 =>
757 (lambda (match)
758 (hours->duration 24 match)))
759 ((string-match "^([0-9]+)w$" str)
760 =>
761 (lambda (match)
762 (hours->duration (* 24 7) match)))
763 ((string-match "^([0-9]+)m$" str)
764 =>
765 (lambda (match)
766 (hours->duration (* 24 30) match)))
767 (else #f)))
768
2876b989
LC
769(define* (package-specification->name+version+output spec
770 #:optional (output "out"))
771 "Parse package specification SPEC and return three value: the specified
772package name, version number (or #f), and output name (or OUTPUT). SPEC may
773optionally contain a version number and an output name, as in these examples:
774
775 guile
776 guile-2.0.9
777 guile:debug
778 guile-2.0.9:debug
779"
780 (let*-values (((name sub-drv)
781 (match (string-rindex spec #\:)
782 (#f (values spec output))
783 (colon (values (substring spec 0 colon)
784 (substring spec (+ 1 colon))))))
785 ((name version)
786 (package-name->name+version name)))
787 (values name version sub-drv)))
788
789\f
790;;;
791;;; Command-line option processing.
792;;;
793
a5975ced
LC
794(define (args-fold* options unrecognized-option-proc operand-proc . seeds)
795 "A wrapper on top of `args-fold' that does proper user-facing error
796reporting."
797 (catch 'misc-error
798 (lambda ()
799 (apply args-fold options unrecognized-option-proc
800 operand-proc seeds))
801 (lambda (key proc msg args . rest)
802 ;; XXX: MSG is not i18n'd.
803 (leave (_ "invalid argument: ~a~%")
804 (apply format #f msg args)))))
805
16eb115e
DP
806(define (environment-build-options)
807 "Return additional build options passed as environment variables."
808 (arguments-from-environment-variable "GUIX_BUILD_OPTIONS"))
809
b3f21389
LC
810(define %default-argument-handler
811 ;; The default handler for non-option command-line arguments.
812 (lambda (arg result)
813 (alist-cons 'argument arg result)))
814
815(define* (parse-command-line args options seeds
816 #:key
817 (argument-handler %default-argument-handler))
818 "Parse the command-line arguments ARGS as well as arguments passed via the
819'GUIX_BUILD_OPTIONS' environment variable according to OPTIONS (a list of
820SRFI-37 options) and return the result, seeded by SEEDS.
821Command-line options take precedence those passed via 'GUIX_BUILD_OPTIONS'.
822
823ARGUMENT-HANDLER is called for non-option arguments, like the 'operand-proc'
824parameter of 'args-fold'."
cf6ce3e6 825 (define (parse-options-from args seeds)
b3f21389
LC
826 ;; Actual parsing takes place here.
827 (apply args-fold* args options
828 (lambda (opt name arg . rest)
829 (leave (_ "~A: unrecognized option~%") name))
830 argument-handler
831 seeds))
832
cf6ce3e6
LC
833 (call-with-values
834 (lambda ()
835 (parse-options-from (environment-build-options) seeds))
836 (lambda seeds
837 ;; ARGS take precedence over what the environment variable specifies.
838 (parse-options-from args seeds))))
b3f21389 839
e49951eb 840(define (show-guix-usage)
e49951eb 841 (format (current-error-port)
25c93676
LC
842 (_ "Try `guix --help' for more information.~%"))
843 (exit 1))
e49951eb 844
e31ff8b8
LC
845(define (command-files)
846 "Return the list of source files that define Guix sub-commands."
847 (define directory
848 (and=> (search-path %load-path "guix.scm")
849 (compose (cut string-append <> "/guix/scripts")
850 dirname)))
851
2b8cf44f
LC
852 (define dot-scm?
853 (cut string-suffix? ".scm" <>))
854
855 ;; In Guile 2.0.5 `scandir' would return "." and ".." regardless even though
856 ;; they don't match `dot-scm?'. Work around it by doing additional
857 ;; filtering.
e31ff8b8 858 (if directory
2b8cf44f 859 (filter dot-scm? (scandir directory dot-scm?))
e31ff8b8
LC
860 '()))
861
862(define (commands)
863 "Return the list of Guix command names."
864 (map (compose (cut string-drop-right <> 4)
865 basename)
866 (command-files)))
867
868(define (show-guix-help)
59f734f3 869 (define (internal? command)
2c74fde0 870 (member command '("substitute" "authenticate" "offload")))
59f734f3 871
e31ff8b8
LC
872 (format #t (_ "Usage: guix COMMAND ARGS...
873Run COMMAND with ARGS.\n"))
874 (newline)
875 (format #t (_ "COMMAND must be one of the sub-commands listed below:\n"))
876 (newline)
877 ;; TODO: Display a synopsis of each command.
59f734f3
LC
878 (format #t "~{ ~a~%~}" (sort (remove internal? (commands))
879 string<?))
e31ff8b8
LC
880 (show-bug-report-information))
881
a2011be5
LC
882(define program-name
883 ;; Name of the command-line program currently executing, or #f.
884 (make-parameter #f))
885
ec5d0a85
LC
886(define (run-guix-command command . args)
887 "Run COMMAND with the given ARGS. Report an error when COMMAND is not
888found."
889 (define module
890 (catch 'misc-error
891 (lambda ()
892 (resolve-interface `(guix scripts ,command)))
893 (lambda -
25c93676
LC
894 (format (current-error-port)
895 (_ "guix: ~a: command not found~%") command)
896 (show-guix-usage))))
ec5d0a85
LC
897
898 (let ((command-main (module-ref module
899 (symbol-append 'guix- command))))
900 (parameterize ((program-name command))
901 (apply command-main args))))
902
a2011be5
LC
903(define guix-warning-port
904 (make-parameter (current-warning-port)))
905
e49951eb
MW
906(define (guix-main arg0 . args)
907 (initialize-guix)
908 (let ()
909 (define (option? str) (string-prefix? "-" str))
910 (match args
25c93676
LC
911 (()
912 (format (current-error-port)
913 (_ "guix: missing command name~%"))
914 (show-guix-usage))
e12b3eb9 915 ((or ("-h") ("--help"))
25c93676
LC
916 (show-guix-help))
917 (("--version")
918 (show-version-and-exit "guix"))
919 (((? option? o) args ...)
920 (format (current-error-port)
921 (_ "guix: unrecognized option '~a'~%") o)
922 (show-guix-usage))
849eebbb
LC
923 (("help" args ...)
924 (show-guix-help))
e49951eb 925 ((command args ...)
ec5d0a85
LC
926 (apply run-guix-command
927 (string->symbol command)
928 args)))))
e49951eb 929
073c34d7 930;;; ui.scm ends here