;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2012, 2013, 2014, 2015 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2013 Mark H Weaver <mhw@netris.org>
;;; Copyright © 2013 Nikita Karetnikov <nikita@karetnikov.org>
;;; Copyright © 2014 Cyril Roelandt <tipecaml@gmail.com>
;;; Copyright © 2014 Cyrill Schenkel <cyrill.schenkel@gmail.com>
-;;; Copyright © 2014, 2015 Alex Kost <alezost@gmail.com>
+;;; Copyright © 2014, 2015, 2017 Alex Kost <alezost@gmail.com>
;;; Copyright © 2015 David Thompson <davet@gnu.org>
-;;; Copyright © 2015 Mathieu Lirzin <mthl@openmailbox.org>
+;;; Copyright © 2015, 2016 Mathieu Lirzin <mthl@gnu.org>
+;;; Copyright © 2016 Roel Janssen <roel@gnu.org>
+;;; Copyright © 2016 Benz Schenk <benz.schenk@uzh.ch>
;;;
;;; This file is part of GNU Guix.
;;;
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
(define-module (guix ui)
+ #:use-module (guix gexp)
#:use-module (guix utils)
#:use-module (guix store)
#:use-module (guix config)
#:use-module (guix packages)
#:use-module (guix profiles)
#:use-module (guix derivations)
+ #:use-module (guix combinators)
#:use-module (guix build-system)
#:use-module (guix serialization)
#:use-module ((guix build utils) #:select (mkdir-p))
#:use-module ((guix licenses) #:select (license? license-name))
+ #:use-module ((guix build syscalls) #:select (terminal-columns))
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-11)
#:use-module (srfi srfi-19)
#:use-module (texinfo)
#:use-module (texinfo plain-text)
#:use-module (texinfo string-utils)
- #:export (_
+ #:export (G_
N_
P_
report-error
warn-about-load-error
show-version-and-exit
show-bug-report-information
+ make-regexp*
string->number*
size->number
show-derivation-outputs
read/eval
read/eval-package-expression
location->string
- switch-symlinks
config-directory
fill-paragraph
texi->plain-text
package-description-string
+ package-synopsis-string
string->recutils
package->recutils
package-specification->name+version+output
string->generations
string->duration
+ matching-generations
+ display-generation
+ display-profile-content
+ display-profile-content-diff
+ roll-back*
+ switch-to-generation*
+ delete-generation*
run-guix-command
run-guix
program-name
guix-warning-port
warning
+ info
guix-main))
;;; Commentary:
;; Text domain for package synopses and descriptions.
"guix-packages")
-(define _ (cut gettext <> %gettext-domain))
+(define G_ (cut gettext <> %gettext-domain))
(define N_ (cut ngettext <> <> <> %gettext-domain))
(define (P_ msgid)
(syntax-case x ()
((name (underscore fmt) args (... ...))
(and (string? (syntax->datum #'fmt))
- (free-identifier=? #'underscore #'_))
+ (free-identifier=? #'underscore #'G_))
(with-syntax ((fmt* (augmented-format-string #'fmt))
(prefix (datum->syntax x prefix)))
#'(format (guix-warning-port) (gettext fmt*)
args (... ...))))))))
(define-diagnostic warning "warning: ") ; emit a warning
+(define-diagnostic info "")
(define-diagnostic report-error "error: ")
(define-syntax-rule (leave args ...)
(case on-error
((debug)
(newline)
- (display (_ "entering debugger; type ',bt' for a backtrace\n"))
+ (display (G_ "entering debugger; type ',bt' for a backtrace\n"))
(start-repl #:debug (make-debug (stack->vector stack) 0
(error-string frame args)
#f)))
"Report the failure to load FILE, a user-provided Scheme file.
ARGS is the list of arguments received by the 'throw' handler."
(match args
- (('system-error . _)
+ (('system-error . rest)
(let ((err (system-error-errno args)))
- (report-error (_ "failed to load '~a': ~a~%") file (strerror err))))
+ (report-error (G_ "failed to load '~a': ~a~%") file (strerror err))))
(('syntax-error proc message properties form . rest)
(let ((loc (source-properties->location properties)))
- (format (current-error-port) (_ "~a: error: ~a~%")
+ (format (current-error-port) (G_ "~a: error: ~a~%")
(location->string loc) message)))
(('srfi-34 obj)
- (report-error (_ "exception thrown: ~s~%") obj))
+ (if (message-condition? obj)
+ (report-error (G_ "~a~%")
+ (gettext (condition-message obj)
+ %gettext-domain))
+ (report-error (G_ "exception thrown: ~s~%") obj)))
((error args ...)
- (report-error (_ "failed to load '~a':~%") file)
+ (report-error (G_ "failed to load '~a':~%") file)
(apply display-error frame (current-error-port) args))))
(define (warn-about-load-error file args) ;FIXME: factorize with ↑
"Report the failure to load FILE, a user-provided Scheme file, without
exiting. ARGS is the list of arguments received by the 'throw' handler."
(match args
- (('system-error . _)
+ (('system-error . rest)
(let ((err (system-error-errno args)))
- (warning (_ "failed to load '~a': ~a~%") file (strerror err))))
+ (warning (G_ "failed to load '~a': ~a~%") file (strerror err))))
(('syntax-error proc message properties form . rest)
(let ((loc (source-properties->location properties)))
- (format (current-error-port) (_ "~a: warning: ~a~%")
+ (format (current-error-port) (G_ "~a: warning: ~a~%")
(location->string loc) message)))
(('srfi-34 obj)
- (warning (_ "failed to load '~a': exception thrown: ~s~%")
- file obj))
+ (if (message-condition? obj)
+ (warning (G_ "failed to load '~a': ~a~%")
+ file
+ (gettext (condition-message obj) %gettext-domain))
+ (warning (G_ "failed to load '~a': exception thrown: ~s~%")
+ file obj)))
((error args ...)
- (warning (_ "failed to load '~a':~%") file)
+ (warning (G_ "failed to load '~a':~%") file)
(apply display-error #f (current-error-port) args))))
(define (install-locale)
(lambda _
(setlocale LC_ALL ""))
(lambda args
- (warning (_ "failed to install locale: ~a~%")
+ (warning (G_ "failed to install locale: ~a~%")
(strerror (system-error-errno args))))))
(define (initialize-guix)
"Display version information for COMMAND and `(exit 0)'."
(simple-format #t "~a (~a) ~a~%"
command %guix-package-name %guix-version)
- (display (_ "Copyright (C) 2015 the Guix authors
+ (format #t "Copyright ~a 2017 ~a"
+ ;; TRANSLATORS: Translate "(C)" to the copyright symbol
+ ;; (C-in-a-circle), if this symbol is available in the user's
+ ;; locale. Otherwise, do not translate "(C)"; leave it as-is. */
+ (G_ "(C)")
+ (G_ "the Guix authors\n"))
+ (display (G_"\
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
(exit 0))
(define (show-bug-report-information)
- (format #t (_ "
+ ;; TRANSLATORS: The placeholder indicates the bug-reporting address for this
+ ;; package. Please add another line saying "Report translation bugs to
+ ;; ...\n" with the address for translation bugs (typically your translation
+ ;; team's web or email address).
+ (format #t (G_ "
Report bugs to: ~a.") %guix-bug-report-address)
- (format #t (_ "
+ (format #t (G_ "
~a home page: <~a>") %guix-package-name %guix-home-page-url)
- (display (_ "
+ (display (G_ "
General help using GNU software: <http://www.gnu.org/gethelp/>"))
(newline))
+(define (augmented-system-error-handler file)
+ "Return a 'system-error' handler that mentions FILE in its message."
+ (lambda (key proc fmt args errno)
+ ;; Augment the FMT and ARGS with information about TARGET (this
+ ;; information is missing as of Guile 2.0.11, making the exception
+ ;; uninformative.)
+ (apply throw key proc "~A: ~S"
+ (list (strerror (car errno)) file)
+ (list errno))))
+
+(define-syntax-rule (error-reporting-wrapper proc (args ...) file)
+ "Wrap PROC such that its 'system-error' exceptions are augmented to mention
+FILE."
+ (let ((real-proc (@ (guile) proc)))
+ (lambda (args ...)
+ (catch 'system-error
+ (lambda ()
+ (real-proc args ...))
+ (augmented-system-error-handler file)))))
+
(set! symlink
;; We 'set!' the global binding because (gnu build ...) modules and similar
;; typically don't use (guix ui).
- (let ((real-symlink (@ (guile) symlink)))
- (lambda (target link)
- "This is a 'symlink' replacement that provides proper error reporting."
- (catch 'system-error
- (lambda ()
- (real-symlink target link))
- (lambda (key proc fmt args errno)
- ;; Augment the FMT and ARGS with information about LINK (this
- ;; information is missing as of Guile 2.0.11, making the exception
- ;; uninformative.)
- (apply throw key proc "~A: ~S"
- (list (strerror (car errno)) link)
- (list errno)))))))
+ (error-reporting-wrapper symlink (source target) target))
(set! copy-file
;; Note: here we use 'set!', not #:replace, because UIs typically use
;; 'copy-recursively', which doesn't use (guix ui).
- (let ((real-copy-file (@ (guile) copy-file)))
- (lambda (source target)
- "This is a 'copy-file' replacement that provides proper error reporting."
- (catch 'system-error
- (lambda ()
- (real-copy-file source target))
- (lambda (key proc fmt args errno)
- ;; Augment the FMT and ARGS with information about TARGET (this
- ;; information is missing as of Guile 2.0.11, making the exception
- ;; uninformative.)
- (apply throw key proc "~A: ~S"
- (list (strerror (car errno)) target)
- (list errno)))))))
+ (error-reporting-wrapper copy-file (source target) target))
+
+(set! canonicalize-path
+ (error-reporting-wrapper canonicalize-path (file) file))
+
+
+(define (make-regexp* regexp . flags)
+ "Like 'make-regexp' but error out if REGEXP is invalid, reporting the error
+nicely."
+ (catch 'regular-expression-syntax
+ (lambda ()
+ (apply make-regexp regexp flags))
+ (lambda (key proc message . rest)
+ (leave (G_ "'~a' is not a valid regular expression: ~a~%")
+ regexp message))))
(define (string->number* str)
"Like `string->number', but error out with an error message on failure."
(or (string->number str)
- (leave (_ "~a: invalid number~%") str)))
+ (leave (G_ "~a: invalid number~%") str)))
(define (size->number str)
"Convert STR, a storage measurement representation such as \"1024\" or
str))
(num (string->number numstr)))
(unless num
- (leave (_ "invalid number: ~a~%") numstr))
+ (leave (G_ "invalid number: ~a~%") numstr))
((compose inexact->exact round)
(* num
("ZB" (expt 10 21))
("YB" (expt 10 24))
("" 1)
- (_
- (leave (_ "unknown unit: ~a~%") unit)))))))
+ (x
+ (leave (G_ "unknown unit: ~a~%") unit)))))))
(define (call-with-error-handling thunk)
"Call THUNK within a user-friendly error handler."
+ (define (port-filename* port)
+ ;; 'port-filename' returns #f for non-file ports, but it raises an
+ ;; exception for file ports that are closed. Work around that.
+ (and (not (port-closed? port))
+ (port-filename port)))
+
(guard (c ((package-input-error? c)
(let* ((package (package-error-package c))
(input (package-error-invalid-input c))
(file (location-file location))
(line (location-line location))
(column (location-column location)))
- (leave (_ "~a:~a:~a: package `~a' has an invalid input: ~s~%")
+ (leave (G_ "~a:~a:~a: package `~a' has an invalid input: ~s~%")
file line column
(package-full-name package) input)))
((package-cross-build-system-error? c)
(let* ((package (package-error-package c))
(loc (package-location package))
(system (package-build-system package)))
- (leave (_ "~a: ~a: build system `~a' does not support cross builds~%")
+ (leave (G_ "~a: ~a: build system `~a' does not support cross builds~%")
(location->string loc)
(package-full-name package)
(build-system-name system))))
+ ((gexp-input-error? c)
+ (let ((input (package-error-invalid-input c)))
+ (leave (G_ "~s: invalid G-expression input~%")
+ (gexp-error-invalid-input c))))
((profile-not-found-error? c)
- (leave (_ "profile '~a' does not exist~%")
+ (leave (G_ "profile '~a' does not exist~%")
(profile-error-profile c)))
((missing-generation-error? c)
- (leave (_ "generation ~a of profile '~a' does not exist~%")
+ (leave (G_ "generation ~a of profile '~a' does not exist~%")
(missing-generation-error-generation c)
(profile-error-profile c)))
((nar-error? c)
(let ((file (nar-error-file c))
(port (nar-error-port c)))
(if file
- (leave (_ "corrupt input while restoring '~a' from ~s~%")
- file (or (port-filename port) port))
- (leave (_ "corrupt input while restoring archive from ~s~%")
- (or (port-filename port) port)))))
+ (leave (G_ "corrupt input while restoring '~a' from ~s~%")
+ file (or (port-filename* port) port))
+ (leave (G_ "corrupt input while restoring archive from ~s~%")
+ (or (port-filename* port) port)))))
((nix-connection-error? c)
- (leave (_ "failed to connect to `~a': ~a~%")
+ (leave (G_ "failed to connect to `~a': ~a~%")
(nix-connection-error-file c)
(strerror (nix-connection-error-code c))))
((nix-protocol-error? c)
;; FIXME: Server-provided error messages aren't i18n'd.
- (leave (_ "build failed: ~a~%")
+ (leave (G_ "build failed: ~a~%")
(nix-protocol-error-message c)))
((derivation-missing-output-error? c)
- (leave (_ "reference to invalid output '~a' of derivation '~a'~%")
+ (leave (G_ "reference to invalid output '~a' of derivation '~a'~%")
(derivation-missing-output c)
(derivation-file-name (derivation-error-derivation c))))
+ ((file-search-error? c)
+ (leave (G_ "file '~a' could not be found in these \
+directories:~{ ~a~}~%")
+ (file-search-error-file-name c)
+ (file-search-error-search-path c)))
((message-condition? c)
;; Normally '&message' error conditions have an i18n'd message.
- (leave (_ "~a~%")
+ (leave (G_ "~a~%")
(gettext (condition-message c) %gettext-domain))))
;; Catch EPIPE and the likes.
(catch 'system-error
thunk
(lambda (key proc format-string format-args . rest)
- (leave (_ "~a: ~a~%") proc
+ (leave (G_ "~a: ~a~%") proc
(apply format #f format-string format-args))))))
(define-syntax-rule (leave-on-EPIPE exp ...)
(lambda ()
(call-with-input-string str read))
(lambda args
- (leave (_ "failed to read expression ~s: ~s~%")
+ (leave (G_ "failed to read expression ~s: ~s~%")
str args)))))
(catch #t
(lambda ()
(eval exp (force %guix-user-module)))
(lambda args
- (report-error (_ "failed to evaluate expression '~a':~%") exp)
+ (report-error (G_ "failed to evaluate expression '~a':~%") exp)
(match args
(('syntax-error proc message properties form . rest)
- (report-error (_ "syntax error: ~a~%") message))
+ (report-error (G_ "syntax error: ~a~%") message))
(('srfi-34 obj)
- (report-error (_ "exception thrown: ~s~%") obj))
+ (if (message-condition? obj)
+ (report-error (G_ "~a~%")
+ (gettext (condition-message obj)
+ %gettext-domain))
+ (report-error (G_ "exception thrown: ~s~%") obj)))
((error args ...)
(apply display-error #f (current-error-port) args))
(what? #f))
error."
(match (read/eval str)
((? package? p) p)
- (_
- (leave (_ "expression ~s does not evaluate to a package~%")
+ (x
+ (leave (G_ "expression ~s does not evaluate to a package~%")
str))))
(define (show-derivation-outputs derivation)
(derivation-outputs derivation))))
(define* (show-what-to-build store drv
- #:key dry-run? (use-substitutes? #t))
+ #:key dry-run? (use-substitutes? #t)
+ (mode (build-mode normal)))
"Show what will or would (depending on DRY-RUN?) be built in realizing the
-derivations listed in DRV. Return #t if there's something to build, #f
-otherwise. When USE-SUBSTITUTES?, check and report what is prerequisites are
-available for download."
+derivations listed in DRV using MODE, a 'build-mode' value. Return #t if
+there's something to build, #f otherwise. When USE-SUBSTITUTES?, check and
+report what is prerequisites are available for download."
(define substitutable?
;; Call 'substitutation-oracle' upfront so we don't end up launching the
;; substituter many times. This makes a big difference, especially when
;; DRV is a long list as is the case with 'guix environment'.
(if use-substitutes?
- (substitution-oracle store drv)
+ (substitution-oracle store drv #:mode mode)
(const #f)))
(define (built-or-substitutable? drv)
(let-values (((b d)
(derivation-prerequisites-to-build
store drv
+ #:mode mode
#:substitutable? substitutable?)))
(values (append b build)
(append d download))))
(define (right-arrow port)
"Return either a string containing the 'RIGHT ARROW' character, or an ASCII
replacement if PORT is not Unicode-capable."
- (with-fluids ((%default-port-encoding (port-encoding port)))
- (let ((arrow "→"))
- (catch 'encoding-error
- (lambda ()
- (call-with-output-string
- (lambda (port)
- (set-port-conversion-strategy! port 'error)
- (display arrow port))))
- (lambda (key . args)
- "->")))))
+ (let ((encoding (port-encoding port))
+ (arrow "→"))
+ (catch 'encoding-error
+ (lambda ()
+ (call-with-output-string
+ (lambda (port)
+ (set-port-encoding! port encoding)
+ (set-port-conversion-strategy! port 'error)
+ (display arrow port))))
+ (lambda (key . args)
+ "->"))))
(define* (show-manifest-transaction store manifest transaction
#:key dry-run?)
"The following packages will be removed:~%~{~a~%~}~%"
len)
remove))))
- (_ #f))
+ (x #f))
(match downgrade
(((($ <manifest-entry> name old-version)
. ($ <manifest-entry> _ new-version output item)) ..1)
"The following packages will be downgraded:~%~{~a~%~}~%"
len)
downgrade))))
- (_ #f))
+ (x #f))
(match upgrade
(((($ <manifest-entry> name old-version)
. ($ <manifest-entry> _ new-version output item)) ..1)
"The following packages will be upgraded:~%~{~a~%~}~%"
len)
upgrade))))
- (_ #f))
+ (x #f))
(match install
((($ <manifest-entry> name version output item _) ..1)
(let ((len (length name))
"The following packages will be installed:~%~{~a~%~}~%"
len)
install))))
- (_ #f))))
+ (x #f))))
(define-syntax with-error-handling
(syntax-rules ()
(define (location->string loc)
"Return a human-friendly, GNU-standard representation of LOC."
(match loc
- (#f (_ "<unknown location>"))
+ (#f (G_ "<unknown location>"))
(($ <location> file line column)
(format #f "~a:~a:~a" file line column))))
-(define (switch-symlinks link target)
- "Atomically switch LINK, a symbolic link, to point to TARGET. Works
-both when LINK already exists and when it does not."
- (let ((pivot (string-append link ".new")))
- (symlink target pivot)
- (rename-file pivot link)))
-
(define (config-directory)
"Return the name of the configuration directory, after making sure that it
exists. Honor the XDG specs,
(lambda args
(let ((err (system-error-errno args)))
;; ERR is necessarily different from EEXIST.
- (leave (_ "failed to create configuration directory `~a': ~a~%")
+ (leave (G_ "failed to create configuration directory `~a': ~a~%")
dir (strerror err)))))))
(define* (fill-paragraph str width #:optional (column 0))
(match (string-fold maybe-break
`(,column 0 ())
str)
- ((_ _ chars)
+ ((column newlines chars)
(list->string (reverse chars)))))
\f
;;;
(define %text-width
- (make-parameter (or (and=> (getenv "WIDTH") string->number)
- 80)))
+ (make-parameter (terminal-columns)))
(set! (@@ (texinfo plain-text) wrap*)
;; XXX: Monkey patch this private procedure to let 'package->recutils'
(with-fluids ((%default-port-encoding "UTF-8"))
(stexi->plain-text (texi-fragment->stexi str))))
+(define (package-field-string package field-accessor)
+ "Return a plain-text representation of PACKAGE field."
+ (and=> (field-accessor package)
+ (compose texi->plain-text P_)))
+
(define (package-description-string package)
"Return a plain-text representation of PACKAGE description field."
- (and=> (package-description package)
- (compose texi->plain-text P_)))
+ (package-field-string package package-description))
+
+(define (package-synopsis-string package)
+ "Return a plain-text representation of PACKAGE synopsis field."
+ (package-field-string package package-synopsis))
(define (string->recutils str)
"Return a version of STR where newlines have been replaced by newlines
(define* (package->recutils p port #:optional (width (%text-width)))
"Write to PORT a `recutils' record of package P, arranging to fit within
WIDTH columns."
+ (define width*
+ ;; The available number of columns once we've taken into account space for
+ ;; the initial "+ " prefix.
+ (if (> width 2) (- width 2) width))
+
(define (dependencies->recutils packages)
(let ((list (string-join (map package-full-name
(sort packages package<?)) " ")))
(string->recutils
- (fill-paragraph list width
+ (fill-paragraph list width*
(string-length "dependencies: ")))))
(define (package<? p1 p2)
(dependencies->recutils (filter package? inputs)))))
(format port "location: ~a~%"
(or (and=> (package-location p) location->string)
- (_ "unknown")))
+ (G_ "unknown")))
;; Note: Starting from version 1.6 or recutils, hyphens are not allowed in
;; field identifiers.
((? license? license)
(license-name license))
(x
- (_ "unknown"))))
+ (G_ "unknown"))))
(format port "synopsis: ~a~%"
(string-map (match-lambda
(#\newline #\space)
(chr chr))
- (or (and=> (package-synopsis p) P_)
+ (or (and=> (package-synopsis-string p) P_)
"")))
(format port "~a~2%"
(string->recutils
(string-trim-right
- (parameterize ((%text-width width))
+ (parameterize ((%text-width width*))
(texi->plain-text
(string-append "description: "
(or (and=> (package-description p) P_)
(make-time time-duration 0
(* 3600 hours (string->number (match:substring match 1)))))
- (cond ((string-match "^([0-9]+)d$" str)
+ (cond ((string-match "^([0-9]+)s$" str)
+ =>
+ (lambda (match)
+ (make-time time-duration 0
+ (string->number (match:substring match 1)))))
+ ((string-match "^([0-9]+)h$" str)
+ (lambda (match)
+ (hours->duration 1 match)))
+ ((string-match "^([0-9]+)d$" str)
=>
(lambda (match)
(hours->duration 24 match)))
(hours->duration (* 24 30) match)))
(else #f)))
+(define* (matching-generations str profile
+ #:key (duration-relation <=))
+ "Return the list of available generations matching a pattern in STR. See
+'string->generations' and 'string->duration' for the list of valid patterns.
+When STR is a duration pattern, return all the generations whose ctime has
+DURATION-RELATION with the current time."
+ (define (valid-generations lst)
+ (define (valid-generation? n)
+ (any (cut = n <>) (generation-numbers profile)))
+
+ (fold-right (lambda (x acc)
+ (if (valid-generation? x)
+ (cons x acc)
+ acc))
+ '()
+ lst))
+
+ (define (filter-generations generations)
+ (match generations
+ (() '())
+ (('>= n)
+ (drop-while (cut > n <>)
+ (generation-numbers profile)))
+ (('<= n)
+ (valid-generations (iota n 1)))
+ ((lst ..1)
+ (valid-generations lst))
+ (x #f)))
+
+ (define (filter-by-duration duration)
+ (define (time-at-midnight time)
+ ;; Return TIME at midnight by setting nanoseconds, seconds, minutes, and
+ ;; hours to zeros.
+ (let ((d (time-utc->date time)))
+ (date->time-utc
+ (make-date 0 0 0 0
+ (date-day d) (date-month d)
+ (date-year d) (date-zone-offset d)))))
+
+ (define generation-ctime-alist
+ (map (lambda (number)
+ (cons number
+ (time-second
+ (time-at-midnight
+ (generation-time profile number)))))
+ (generation-numbers profile)))
+
+ (match duration
+ (#f #f)
+ (res
+ (let ((s (time-second
+ (subtract-duration (time-at-midnight (current-time))
+ duration))))
+ (delete #f (map (lambda (x)
+ (and (duration-relation s (cdr x))
+ (first x)))
+ generation-ctime-alist))))))
+
+ (cond ((string->generations str)
+ =>
+ filter-generations)
+ ((string->duration str)
+ =>
+ filter-by-duration)
+ (else #f)))
+
+(define (display-generation profile number)
+ "Display a one-line summary of generation NUMBER of PROFILE."
+ (unless (zero? number)
+ (let ((header (format #f (G_ "Generation ~a\t~a") number
+ (date->string
+ (time-utc->date
+ (generation-time profile number))
+ "~b ~d ~Y ~T")))
+ (current (generation-number profile)))
+ (if (= number current)
+ ;; TRANSLATORS: The word "current" here is an adjective for
+ ;; "Generation", as in "current generation". Use the appropriate
+ ;; gender where applicable.
+ (format #t (G_ "~a\t(current)~%") header)
+ (format #t "~a~%" header)))))
+
+(define (display-profile-content-diff profile gen1 gen2)
+ "Display the changed packages in PROFILE GEN2 compared to generation GEN2."
+
+ (define (equal-entry? first second)
+ (string= (manifest-entry-item first) (manifest-entry-item second)))
+
+ (define (display-entry entry prefix)
+ (match entry
+ (($ <manifest-entry> name version output location _)
+ (format #t " ~a ~a\t~a\t~a\t~a~%" prefix name version output location))))
+
+ (define (list-entries number)
+ (manifest-entries (profile-manifest (generation-file-name profile number))))
+
+ (define (display-diff profile old new)
+ (display-generation profile new)
+ (let ((added (lset-difference
+ equal-entry? (list-entries new) (list-entries old)))
+ (removed (lset-difference
+ equal-entry? (list-entries old) (list-entries new))))
+ (for-each (cut display-entry <> "+") added)
+ (for-each (cut display-entry <> "-") removed)
+ (newline)))
+
+ (display-diff profile gen1 gen2))
+
+(define (display-profile-content profile number)
+ "Display the packages in PROFILE, generation NUMBER, in a human-readable
+way."
+ (for-each (match-lambda
+ (($ <manifest-entry> name version output location _)
+ (format #t " ~a\t~a\t~a\t~a~%"
+ name version output location)))
+
+ ;; Show most recently installed packages last.
+ (reverse
+ (manifest-entries
+ (profile-manifest (generation-file-name profile number))))))
+
+(define (display-generation-change previous current)
+ (format #t (G_ "switched from generation ~a to ~a~%") previous current))
+
+(define (roll-back* store profile)
+ "Like 'roll-back', but display what is happening."
+ (call-with-values
+ (lambda ()
+ (roll-back store profile))
+ display-generation-change))
+
+(define (switch-to-generation* profile number)
+ "Like 'switch-generation', but display what is happening."
+ (let ((previous (switch-to-generation profile number)))
+ (display-generation-change previous number)))
+
+(define (delete-generation* store profile generation)
+ "Like 'delete-generation', but display what is going on."
+ (format #t (G_ "deleting ~a~%")
+ (generation-file-name profile generation))
+ (delete-generation store profile generation))
+
(define* (package-specification->name+version+output spec
#:optional (output "out"))
"Parse package specification SPEC and return three value: the specified
optionally contain a version number and an output name, as in these examples:
guile
- guile-2.0.9
+ guile@2.0.9
guile:debug
- guile-2.0.9:debug
+ guile@2.0.9:debug
"
(let*-values (((name sub-drv)
(match (string-rindex spec #\:)
(define (show-guix-usage)
(format (current-error-port)
- (_ "Try `guix --help' for more information.~%"))
+ (G_ "Try `guix --help' for more information.~%"))
(exit 1))
(define (command-files)
(define (show-guix-help)
(define (internal? command)
- (member command '("substitute" "authenticate" "offload")))
+ (member command '("substitute" "authenticate" "offload"
+ "perform-download")))
- (format #t (_ "Usage: guix COMMAND ARGS...
+ (format #t (G_ "Usage: guix COMMAND ARGS...
Run COMMAND with ARGS.\n"))
(newline)
- (format #t (_ "COMMAND must be one of the sub-commands listed below:\n"))
+ (format #t (G_ "COMMAND must be one of the sub-commands listed below:\n"))
(newline)
;; TODO: Display a synopsis of each command.
(format #t "~{ ~a~%~}" (sort (remove internal? (commands))
(resolve-interface `(guix scripts ,command)))
(lambda -
(format (current-error-port)
- (_ "guix: ~a: command not found~%") command)
+ (G_ "guix: ~a: command not found~%") command)
(show-guix-usage))))
(let ((command-main (module-ref module
(symbol-append 'guix- command))))
(parameterize ((program-name command))
- (apply command-main args))))
+ ;; Disable canonicalization so we don't don't stat unreasonably.
+ (with-fluids ((%file-port-name-canonicalization #f))
+ (apply command-main args)))))
(define (run-guix . args)
"Run the 'guix' command defined by command line ARGS.
(match args
(()
(format (current-error-port)
- (_ "guix: missing command name~%"))
+ (G_ "guix: missing command name~%"))
(show-guix-usage))
((or ("-h") ("--help"))
(show-guix-help))
(show-version-and-exit "guix"))
(((? option? o) args ...)
(format (current-error-port)
- (_ "guix: unrecognized option '~a'~%") o)
+ (G_ "guix: unrecognized option '~a'~%") o)
(show-guix-usage))
+ (("help" command)
+ (apply run-guix-command (string->symbol command)
+ '("--help")))
(("help" args ...)
(show-guix-help))
((command args ...)