gnu: Add GCC 4.8.0 and Binutils 2.23.2.
[jackhill/guix/guix.git] / guix / ui.scm
CommitLineData
233e7676
LC
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2012, 2013 Ludovic Courtès <ludo@gnu.org>
e49951eb 3;;; Copyright © 2013 Mark H Weaver <mhw@netris.org>
073c34d7 4;;;
233e7676 5;;; This file is part of GNU Guix.
073c34d7 6;;;
233e7676 7;;; GNU Guix is free software; you can redistribute it and/or modify it
073c34d7
LC
8;;; under the terms of the GNU General Public License as published by
9;;; the Free Software Foundation; either version 3 of the License, or (at
10;;; your option) any later version.
11;;;
233e7676 12;;; GNU Guix is distributed in the hope that it will be useful, but
073c34d7
LC
13;;; WITHOUT ANY WARRANTY; without even the implied warranty of
14;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;;; GNU General Public License for more details.
16;;;
17;;; You should have received a copy of the GNU General Public License
233e7676 18;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
073c34d7
LC
19
20(define-module (guix ui)
21 #:use-module (guix utils)
22 #:use-module (guix store)
cdd5d6f9 23 #:use-module (guix config)
073c34d7 24 #:use-module (guix packages)
9bb2b96a 25 #:use-module (guix derivations)
299112d3
LC
26 #:use-module ((guix licenses) #:select (license? license-name))
27 #:use-module (srfi srfi-1)
28 #:use-module (srfi srfi-11)
073c34d7
LC
29 #:use-module (srfi srfi-26)
30 #:use-module (srfi srfi-34)
64fc89b6 31 #:use-module (ice-9 match)
9bb2b96a 32 #:use-module (ice-9 format)
073c34d7
LC
33 #:export (_
34 N_
35 leave
cdd5d6f9 36 show-version-and-exit
3441e164 37 show-bug-report-information
9bb2b96a 38 show-what-to-build
073c34d7 39 call-with-error-handling
64fc89b6 40 with-error-handling
eb0880e7 41 read/eval-package-expression
299112d3 42 location->string
c61b026e
LC
43 call-with-temporary-output-file
44 switch-symlinks
f651b477 45 config-directory
299112d3
LC
46 fill-paragraph
47 string->recutils
e49951eb
MW
48 package->recutils
49 run-guix-command
a2011be5
LC
50 program-name
51 guix-warning-port
52 warning
e49951eb 53 guix-main))
073c34d7
LC
54
55;;; Commentary:
56;;;
57;;; User interface facilities for command-line tools.
58;;;
59;;; Code:
60
61(define %gettext-domain
62 "guix")
63
64(define _ (cut gettext <> %gettext-domain))
65(define N_ (cut ngettext <> <> <> %gettext-domain))
66
473b03b3
LC
67(define (install-locale)
68 "Install the current locale settings."
69 (catch 'system-error
70 (lambda _
71 (setlocale LC_ALL ""))
72 (lambda args
73 (format (current-error-port)
74 (_ "warning: failed to install locale: ~a~%")
75 (strerror (system-error-errno args))))))
76
e49951eb 77(define (initialize-guix)
633f045f 78 "Perform the usual initialization for stand-alone Guix commands."
e49951eb
MW
79 (install-locale)
80 (textdomain "guix")
81 (setvbuf (current-output-port) _IOLBF)
82 (setvbuf (current-error-port) _IOLBF))
83
073c34d7
LC
84(define-syntax-rule (leave fmt args ...)
85 "Format FMT and ARGS to the error port and exit."
86 (begin
87 (format (current-error-port) fmt args ...)
88 (exit 1)))
89
cdd5d6f9
LC
90(define* (show-version-and-exit #:optional (command (car (command-line))))
91 "Display version information for COMMAND and `(exit 0)'."
92 (simple-format #t "~a (~a) ~a~%"
93 command %guix-package-name %guix-version)
94 (exit 0))
95
3441e164
LC
96(define (show-bug-report-information)
97 (format #t (_ "
98Report bugs to: ~a.") %guix-bug-report-address)
99 (format #t (_ "
100~a home page: <~a>") %guix-package-name %guix-home-page-url)
101 (display (_ "
102General help using GNU software: <http://www.gnu.org/gethelp/>"))
103 (newline))
104
073c34d7
LC
105(define (call-with-error-handling thunk)
106 "Call THUNK within a user-friendly error handler."
107 (guard (c ((package-input-error? c)
108 (let* ((package (package-error-package c))
109 (input (package-error-invalid-input c))
110 (location (package-location package))
111 (file (location-file location))
112 (line (location-line location))
113 (column (location-column location)))
114 (leave (_ "~a:~a:~a: error: package `~a' has an invalid input: ~s~%")
115 file line column
116 (package-full-name package) input)))
ef86c39f
LC
117 ((nix-connection-error? c)
118 (leave (_ "error: failed to connect to `~a': ~a~%")
119 (nix-connection-error-file c)
120 (strerror (nix-connection-error-code c))))
073c34d7
LC
121 ((nix-protocol-error? c)
122 ;; FIXME: Server-provided error messages aren't i18n'd.
123 (leave (_ "error: build failed: ~a~%")
124 (nix-protocol-error-message c))))
125 (thunk)))
126
eb0880e7
LC
127(define (read/eval-package-expression str)
128 "Read and evaluate STR and return the package it refers to, or exit an
129error."
130 (let ((exp (catch #t
131 (lambda ()
132 (call-with-input-string str read))
133 (lambda args
134 (leave (_ "failed to read expression ~s: ~s~%")
135 str args)))))
136 (let ((p (catch #t
137 (lambda ()
138 (eval exp the-scm-module))
139 (lambda args
140 (leave (_ "failed to evaluate expression `~a': ~s~%")
141 exp args)))))
142 (if (package? p)
143 p
144 (leave (_ "expression `~s' does not evaluate to a package~%")
145 exp)))))
146
dd36b51b
LC
147(define* (show-what-to-build store drv
148 #:key dry-run? (use-substitutes? #t))
9bb2b96a 149 "Show what will or would (depending on DRY-RUN?) be built in realizing the
4d60610a 150derivations listed in DRV. Return #t if there's something to build, #f
dd36b51b
LC
151otherwise. When USE-SUBSTITUTES?, check and report what is prerequisites are
152available for download."
153 (let*-values (((build download)
154 (fold2 (lambda (drv-path build download)
155 (let ((drv (call-with-input-file drv-path
156 read-derivation)))
157 (let-values (((b d)
158 (derivation-prerequisites-to-build
159 store drv
160 #:use-substitutes?
161 use-substitutes?)))
162 (values (append b build)
163 (append d download)))))
164 '() '()
165 drv))
166 ((build) ; add the DRV themselves
167 (delete-duplicates
168 (append (remove (compose (lambda (out)
169 (or (valid-path? store out)
170 (and use-substitutes?
171 (has-substitutes? store
172 out))))
173 derivation-path->output-path)
174 drv)
175 (map derivation-input-path build))))
176 ((download) ; add the references of DOWNLOAD
177 (delete-duplicates
178 (append download
179 (remove (cut valid-path? store <>)
180 (append-map
181 substitutable-references
182 (substitutable-path-info store download)))))))
9bb2b96a 183 (if dry-run?
dd36b51b
LC
184 (begin
185 (format (current-error-port)
186 (N_ "~:[the following derivation would be built:~%~{ ~a~%~}~;~]"
187 "~:[the following derivations would be built:~%~{ ~a~%~}~;~]"
188 (length build))
189 (null? build) build)
190 (format (current-error-port)
191 (N_ "~:[the following file would be downloaded:~%~{ ~a~%~}~;~]"
192 "~:[the following files would be downloaded:~%~{ ~a~%~}~;~]"
193 (length download))
194 (null? download) download))
195 (begin
196 (format (current-error-port)
197 (N_ "~:[the following derivation will be built:~%~{ ~a~%~}~;~]"
198 "~:[the following derivations will be built:~%~{ ~a~%~}~;~]"
199 (length build))
200 (null? build) build)
201 (format (current-error-port)
202 (N_ "~:[the following file will be downloaded:~%~{ ~a~%~}~;~]"
203 "~:[the following files will be downloaded:~%~{ ~a~%~}~;~]"
204 (length download))
205 (null? download) download)))
206 (pair? build)))
9bb2b96a 207
073c34d7
LC
208(define-syntax with-error-handling
209 (syntax-rules ()
210 "Run BODY within a user-friendly error condition handler."
211 ((_ body ...)
212 (call-with-error-handling
213 (lambda ()
214 body ...)))))
215
64fc89b6
LC
216(define (location->string loc)
217 "Return a human-friendly, GNU-standard representation of LOC."
218 (match loc
219 (#f (_ "<unknown location>"))
220 (($ <location> file line column)
221 (format #f "~a:~a:~a" file line column))))
222
c61b026e
LC
223(define (call-with-temporary-output-file proc)
224 "Call PROC with a name of a temporary file and open output port to that
225file; close the file and delete it when leaving the dynamic extent of this
226call."
227 (let* ((template (string-copy "guix-file.XXXXXX"))
228 (out (mkstemp! template)))
229 (dynamic-wind
230 (lambda ()
231 #t)
232 (lambda ()
233 (proc template out))
234 (lambda ()
235 (false-if-exception (close out))
236 (false-if-exception (delete-file template))))))
237
238(define (switch-symlinks link target)
239 "Atomically switch LINK, a symbolic link, to point to TARGET. Works
240both when LINK already exists and when it does not."
241 (let ((pivot (string-append link ".new")))
242 (symlink target pivot)
243 (rename-file pivot link)))
244
f651b477
LC
245(define (config-directory)
246 "Return the name of the configuration directory, after making sure that it
247exists. Honor the XDG specs,
248<http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>."
249 (let ((dir (and=> (or (getenv "XDG_CONFIG_HOME")
250 (and=> (getenv "HOME")
251 (cut string-append <> "/.config")))
252 (cut string-append <> "/guix"))))
253 (catch 'system-error
254 (lambda ()
255 (mkdir dir)
256 dir)
257 (lambda args
258 (match (system-error-errno args)
259 ((or EEXIST 0)
260 dir)
261 (err
262 (leave (_ "failed to create configuration directory `~a': ~a~%")
263 dir (strerror err))))))))
264
299112d3
LC
265(define* (fill-paragraph str width #:optional (column 0))
266 "Fill STR such that each line contains at most WIDTH characters, assuming
267that the first character is at COLUMN.
268
269When STR contains a single line break surrounded by other characters, it is
270converted to a space; sequences of more than one line break are preserved."
271 (define (maybe-break chr result)
272 (match result
273 ((column newlines chars)
274 (case chr
275 ((#\newline)
276 `(,column ,(+ 1 newlines) ,chars))
277 (else
278 (let ((chars (case newlines
279 ((0) chars)
280 ((1) (cons #\space chars))
281 (else
282 (append (make-list newlines #\newline) chars))))
283 (column (case newlines
284 ((0) column)
285 ((1) (+ 1 column))
286 (else 0))))
287 (let ((chars (cons chr chars))
288 (column (+ 1 column)))
289 (if (> column width)
290 (let*-values (((before after)
291 (break (cut eqv? #\space <>) chars))
292 ((len)
293 (length before)))
294 (if (<= len width)
295 `(,len
296 0
297 ,(if (null? after)
298 before
299 (append before (cons #\newline (cdr after)))))
300 `(,column 0 ,chars))) ; unbreakable
301 `(,column 0 ,chars)))))))))
302
303 (match (string-fold maybe-break
304 `(,column 0 ())
305 str)
306 ((_ _ chars)
307 (list->string (reverse chars)))))
308
309(define (string->recutils str)
310 "Return a version of STR where newlines have been replaced by newlines
311followed by \"+ \", which makes for a valid multi-line field value in the
312`recutils' syntax."
313 (list->string
314 (string-fold-right (lambda (chr result)
315 (if (eqv? chr #\newline)
316 (cons* chr #\+ #\space result)
317 (cons chr result)))
318 '()
319 str)))
320
321(define* (package->recutils p port
322 #:optional (width (or (and=> (getenv "WIDTH")
323 string->number)
324 80)))
325 "Write to PORT a `recutils' record of package P, arranging to fit within
326WIDTH columns."
327 (define (description->recutils str)
328 (let ((str (_ str)))
329 (string->recutils
330 (fill-paragraph str width
331 (string-length "description: ")))))
332
333 ;; Note: Don't i18n field names so that people can post-process it.
334 (format port "name: ~a~%" (package-name p))
335 (format port "version: ~a~%" (package-version p))
336 (format port "location: ~a~%"
337 (or (and=> (package-location p) location->string)
338 (_ "unknown")))
339 (format port "home-page: ~a~%" (package-home-page p))
340 (format port "license: ~a~%"
341 (match (package-license p)
342 (((? license? licenses) ...)
343 (string-join (map license-name licenses)
344 ", "))
345 ((? license? license)
346 (license-name license))
347 (x
348 (_ "unknown"))))
349 (format port "synopsis: ~a~%"
350 (string-map (match-lambda
351 (#\newline #\space)
352 (chr chr))
353 (or (and=> (package-synopsis p) _)
354 "")))
355 (format port "description: ~a~%"
356 (and=> (package-description p) description->recutils))
357 (newline port))
358
e49951eb
MW
359(define (show-guix-usage)
360 ;; TODO: Dynamically generate a summary of available commands.
361 (format (current-error-port)
362 (_ "Usage: guix COMMAND ARGS...~%")))
363
364(define (run-guix-command command . args)
365 ;; TODO: Gracefully report errors
366 (let* ((module (resolve-interface `(guix scripts ,command)))
367 (command-main (module-ref module
368 (symbol-append 'guix- command))))
369 (apply command-main args)))
370
a2011be5
LC
371(define program-name
372 ;; Name of the command-line program currently executing, or #f.
373 (make-parameter #f))
374
375(define guix-warning-port
376 (make-parameter (current-warning-port)))
377
378(define-syntax warning
379 (lambda (s)
380 "Emit a warming. The macro assumes that `_' is bound to `gettext'."
381 ;; All this just to preserve `-Wformat' warnings. Too much?
382
383 (define (augmented-format-string fmt)
801486fe 384 (string-append "~:[~*~;guix ~a: ~]~a" (syntax->datum fmt)))
a2011be5
LC
385
386 (define prefix
387 #'(_ "warning: "))
388
389 (syntax-case s (N_ _) ; these are literals, yeah...
390 ((warning (_ fmt) args ...)
391 (string? (syntax->datum #'fmt))
392 (with-syntax ((fmt* (augmented-format-string #'fmt))
393 (prefix prefix))
394 #'(format (guix-warning-port) (gettext fmt*)
395 (program-name) (program-name) prefix
396 args ...)))
397 ((warning (N_ singular plural n) args ...)
398 (and (string? (syntax->datum #'singular))
399 (string? (syntax->datum #'plural)))
400 (with-syntax ((s (augmented-format-string #'singular))
401 (p (augmented-format-string #'plural))
402 (b prefix))
403 #'(format (guix-warning-port)
404 (ngettext s p n %gettext-domain)
405 (program-name) (program-name) b
406 args ...))))))
407
e49951eb
MW
408(define (guix-main arg0 . args)
409 (initialize-guix)
410 (let ()
411 (define (option? str) (string-prefix? "-" str))
412 (match args
413 (() (show-guix-usage) (exit 1))
414 (("--help") (show-guix-usage))
415 (("--version") (show-version-and-exit "guix"))
a2011be5 416 (((? option?) args ...) (show-guix-usage) (exit 1))
e49951eb 417 ((command args ...)
a2011be5
LC
418 (parameterize ((program-name command))
419 (apply run-guix-command
420 (string->symbol command)
421 args))))))
e49951eb 422
073c34d7 423;;; ui.scm ends here