2013-01-06 00:47:50 +01:00
|
|
|
|
;;; GNU Guix --- Functional package management for GNU
|
2015-01-10 00:39:59 +01:00
|
|
|
|
;;; Copyright © 2012, 2013, 2014, 2015 Ludovic Courtès <ludo@gnu.org>
|
Replace individual scripts with master 'guix' script.
* scripts/guix.in: New script.
* Makefile.am (bin_SCRIPTS): Add 'scripts/guix'. Remove 'guix-build',
'guix-download', 'guix-import', 'guix-package', and 'guix-gc'.
(MODULES): Add 'guix/scripts/build.scm', 'guix/scripts/download.scm',
'guix/scripts/import.scm', 'guix/scripts/package.scm', and
'guix/scripts/gc.scm'.
* configure.ac (AC_CONFIG_FILES): Add 'scripts/guix'. Remove 'guix-build',
'guix-download', 'guix-import', 'guix-package', and 'guix-gc'.
* guix-build.in, guix-download.in, guix-gc.in, guix-import.in,
guix-package.in: Remove shell script boilerplate. Move to guix-COMMAND.in
to guix/scripts/COMMAND.scm. Rename module from (guix-COMMAND) to
(guix scripts COMMAND). Change "guix-COMMAND" to "guix COMMAND" in
usage help string.
* pre-inst-env.in: Add "@abs_top_builddir@/scripts" to the front of $PATH.
Export $GUIX_UNINSTALLED.
* tests/guix-build.sh, tests/guix-daemon.sh, tests/guix-download.sh,
tests/guix-gc.sh, tests/guix-package.sh: Use "guix COMMAND" instead of
"guix-COMMAND".
* doc/guix.texi: Replace all occurrences of "guix-COMMAND" with
"guix COMMAND".
* po/POTFILES.in: Update.
2013-02-14 10:15:25 +01:00
|
|
|
|
;;; Copyright © 2013 Mark H Weaver <mhw@netris.org>
|
2013-04-21 10:08:40 +02:00
|
|
|
|
;;; Copyright © 2013 Nikita Karetnikov <nikita@karetnikov.org>
|
2015-09-10 11:37:36 +02:00
|
|
|
|
;;; Copyright © 2014 Cyril Roelandt <tipecaml@gmail.com>
|
|
|
|
|
;;; Copyright © 2014 Cyrill Schenkel <cyrill.schenkel@gmail.com>
|
2015-08-16 09:28:04 +02:00
|
|
|
|
;;; Copyright © 2014, 2015 Alex Kost <alezost@gmail.com>
|
2015-09-10 11:37:36 +02:00
|
|
|
|
;;; Copyright © 2015 David Thompson <davet@gnu.org>
|
2015-08-07 00:10:43 +02:00
|
|
|
|
;;; Copyright © 2015 Mathieu Lirzin <mthl@openmailbox.org>
|
2012-11-01 00:50:01 +01:00
|
|
|
|
;;;
|
2013-01-06 00:47:50 +01:00
|
|
|
|
;;; This file is part of GNU Guix.
|
2012-11-01 00:50:01 +01:00
|
|
|
|
;;;
|
2013-01-06 00:47:50 +01:00
|
|
|
|
;;; GNU Guix is free software; you can redistribute it and/or modify it
|
2012-11-01 00:50:01 +01:00
|
|
|
|
;;; under the terms of the GNU General Public License as published by
|
|
|
|
|
;;; the Free Software Foundation; either version 3 of the License, or (at
|
|
|
|
|
;;; your option) any later version.
|
|
|
|
|
;;;
|
2013-01-06 00:47:50 +01:00
|
|
|
|
;;; GNU Guix is distributed in the hope that it will be useful, but
|
2012-11-01 00:50:01 +01:00
|
|
|
|
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
;;; GNU General Public License for more details.
|
|
|
|
|
;;;
|
|
|
|
|
;;; You should have received a copy of the GNU General Public License
|
2013-01-06 00:47:50 +01:00
|
|
|
|
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
|
2012-11-01 00:50:01 +01:00
|
|
|
|
|
|
|
|
|
(define-module (guix ui)
|
|
|
|
|
#:use-module (guix utils)
|
|
|
|
|
#:use-module (guix store)
|
2012-11-03 21:19:43 +01:00
|
|
|
|
#:use-module (guix config)
|
2012-11-01 00:50:01 +01:00
|
|
|
|
#:use-module (guix packages)
|
2014-10-08 15:15:49 +02:00
|
|
|
|
#:use-module (guix profiles)
|
2013-02-20 23:41:24 +01:00
|
|
|
|
#:use-module (guix derivations)
|
2015-02-08 18:30:20 +01:00
|
|
|
|
#:use-module (guix build-system)
|
|
|
|
|
#:use-module (guix serialization)
|
2014-09-14 15:33:38 +02:00
|
|
|
|
#:use-module ((guix build utils) #:select (mkdir-p))
|
2013-02-01 13:16:27 +01:00
|
|
|
|
#:use-module ((guix licenses) #:select (license? license-name))
|
2015-06-19 14:57:44 +02:00
|
|
|
|
#:use-module (gnu system file-systems)
|
2013-02-01 13:16:27 +01:00
|
|
|
|
#:use-module (srfi srfi-1)
|
|
|
|
|
#:use-module (srfi srfi-11)
|
2013-09-19 13:07:39 +02:00
|
|
|
|
#:use-module (srfi srfi-19)
|
2012-11-01 00:50:01 +01:00
|
|
|
|
#:use-module (srfi srfi-26)
|
2015-05-25 18:25:19 +02:00
|
|
|
|
#:use-module (srfi srfi-31)
|
2012-11-01 00:50:01 +01:00
|
|
|
|
#:use-module (srfi srfi-34)
|
2014-02-21 17:45:04 +01:00
|
|
|
|
#:use-module (srfi srfi-35)
|
2013-05-10 12:33:18 +02:00
|
|
|
|
#:autoload (ice-9 ftw) (scandir)
|
2012-11-19 23:02:59 +01:00
|
|
|
|
#:use-module (ice-9 match)
|
2013-02-20 23:41:24 +01:00
|
|
|
|
#:use-module (ice-9 format)
|
2013-09-19 13:07:39 +02:00
|
|
|
|
#:use-module (ice-9 regex)
|
2015-05-25 22:52:41 +02:00
|
|
|
|
#:autoload (system repl repl) (start-repl)
|
|
|
|
|
#:autoload (system repl debug) (make-debug stack->vector)
|
2015-08-07 00:10:43 +02:00
|
|
|
|
#:use-module (texinfo)
|
|
|
|
|
#:use-module (texinfo plain-text)
|
|
|
|
|
#:use-module (texinfo string-utils)
|
2012-11-01 00:50:01 +01:00
|
|
|
|
#:export (_
|
|
|
|
|
N_
|
2014-06-13 17:30:40 +02:00
|
|
|
|
P_
|
2015-01-22 22:43:46 +01:00
|
|
|
|
report-error
|
2012-11-01 00:50:01 +01:00
|
|
|
|
leave
|
2015-05-18 13:49:44 +02:00
|
|
|
|
make-user-module
|
|
|
|
|
load*
|
2015-04-07 22:27:45 +02:00
|
|
|
|
warn-about-load-error
|
2012-11-03 21:19:43 +01:00
|
|
|
|
show-version-and-exit
|
2013-01-05 15:55:47 +01:00
|
|
|
|
show-bug-report-information
|
2015-10-28 15:53:17 +01:00
|
|
|
|
make-regexp*
|
2013-05-20 18:14:55 +02:00
|
|
|
|
string->number*
|
2014-04-08 22:01:44 +02:00
|
|
|
|
size->number
|
2015-07-23 15:08:39 +02:00
|
|
|
|
show-derivation-outputs
|
2013-02-20 23:41:24 +01:00
|
|
|
|
show-what-to-build
|
2015-05-21 00:42:35 +02:00
|
|
|
|
show-what-to-build*
|
2014-10-08 15:15:49 +02:00
|
|
|
|
show-manifest-transaction
|
2012-11-01 00:50:01 +01:00
|
|
|
|
call-with-error-handling
|
2012-11-19 23:02:59 +01:00
|
|
|
|
with-error-handling
|
2015-07-15 18:01:05 +02:00
|
|
|
|
leave-on-EPIPE
|
2013-11-18 23:08:20 +01:00
|
|
|
|
read/eval
|
2013-03-01 21:55:42 +01:00
|
|
|
|
read/eval-package-expression
|
2013-02-01 13:16:27 +01:00
|
|
|
|
location->string
|
2013-02-20 23:46:38 +01:00
|
|
|
|
config-directory
|
2013-02-01 13:16:27 +01:00
|
|
|
|
fill-paragraph
|
2015-09-20 12:27:23 +02:00
|
|
|
|
texi->plain-text
|
2015-08-07 00:10:43 +02:00
|
|
|
|
package-description-string
|
2013-02-01 13:16:27 +01:00
|
|
|
|
string->recutils
|
Replace individual scripts with master 'guix' script.
* scripts/guix.in: New script.
* Makefile.am (bin_SCRIPTS): Add 'scripts/guix'. Remove 'guix-build',
'guix-download', 'guix-import', 'guix-package', and 'guix-gc'.
(MODULES): Add 'guix/scripts/build.scm', 'guix/scripts/download.scm',
'guix/scripts/import.scm', 'guix/scripts/package.scm', and
'guix/scripts/gc.scm'.
* configure.ac (AC_CONFIG_FILES): Add 'scripts/guix'. Remove 'guix-build',
'guix-download', 'guix-import', 'guix-package', and 'guix-gc'.
* guix-build.in, guix-download.in, guix-gc.in, guix-import.in,
guix-package.in: Remove shell script boilerplate. Move to guix-COMMAND.in
to guix/scripts/COMMAND.scm. Rename module from (guix-COMMAND) to
(guix scripts COMMAND). Change "guix-COMMAND" to "guix COMMAND" in
usage help string.
* pre-inst-env.in: Add "@abs_top_builddir@/scripts" to the front of $PATH.
Export $GUIX_UNINSTALLED.
* tests/guix-build.sh, tests/guix-daemon.sh, tests/guix-download.sh,
tests/guix-gc.sh, tests/guix-package.sh: Use "guix COMMAND" instead of
"guix-COMMAND".
* doc/guix.texi: Replace all occurrences of "guix-COMMAND" with
"guix COMMAND".
* po/POTFILES.in: Update.
2013-02-14 10:15:25 +01:00
|
|
|
|
package->recutils
|
2013-11-01 16:57:48 +01:00
|
|
|
|
package-specification->name+version+output
|
2015-06-19 14:57:44 +02:00
|
|
|
|
specification->file-system-mapping
|
2013-09-19 13:07:39 +02:00
|
|
|
|
string->generations
|
|
|
|
|
string->duration
|
2015-10-26 19:03:56 +01:00
|
|
|
|
matching-generations
|
2015-10-26 21:16:20 +01:00
|
|
|
|
display-generation
|
|
|
|
|
display-profile-content
|
2015-10-26 23:01:06 +01:00
|
|
|
|
roll-back*
|
|
|
|
|
switch-to-generation*
|
|
|
|
|
delete-generation*
|
Replace individual scripts with master 'guix' script.
* scripts/guix.in: New script.
* Makefile.am (bin_SCRIPTS): Add 'scripts/guix'. Remove 'guix-build',
'guix-download', 'guix-import', 'guix-package', and 'guix-gc'.
(MODULES): Add 'guix/scripts/build.scm', 'guix/scripts/download.scm',
'guix/scripts/import.scm', 'guix/scripts/package.scm', and
'guix/scripts/gc.scm'.
* configure.ac (AC_CONFIG_FILES): Add 'scripts/guix'. Remove 'guix-build',
'guix-download', 'guix-import', 'guix-package', and 'guix-gc'.
* guix-build.in, guix-download.in, guix-gc.in, guix-import.in,
guix-package.in: Remove shell script boilerplate. Move to guix-COMMAND.in
to guix/scripts/COMMAND.scm. Rename module from (guix-COMMAND) to
(guix scripts COMMAND). Change "guix-COMMAND" to "guix COMMAND" in
usage help string.
* pre-inst-env.in: Add "@abs_top_builddir@/scripts" to the front of $PATH.
Export $GUIX_UNINSTALLED.
* tests/guix-build.sh, tests/guix-daemon.sh, tests/guix-download.sh,
tests/guix-gc.sh, tests/guix-package.sh: Use "guix COMMAND" instead of
"guix-COMMAND".
* doc/guix.texi: Replace all occurrences of "guix-COMMAND" with
"guix COMMAND".
* po/POTFILES.in: Update.
2013-02-14 10:15:25 +01:00
|
|
|
|
run-guix-command
|
2015-08-16 09:28:04 +02:00
|
|
|
|
run-guix
|
2013-04-11 22:30:06 +02:00
|
|
|
|
program-name
|
|
|
|
|
guix-warning-port
|
|
|
|
|
warning
|
Replace individual scripts with master 'guix' script.
* scripts/guix.in: New script.
* Makefile.am (bin_SCRIPTS): Add 'scripts/guix'. Remove 'guix-build',
'guix-download', 'guix-import', 'guix-package', and 'guix-gc'.
(MODULES): Add 'guix/scripts/build.scm', 'guix/scripts/download.scm',
'guix/scripts/import.scm', 'guix/scripts/package.scm', and
'guix/scripts/gc.scm'.
* configure.ac (AC_CONFIG_FILES): Add 'scripts/guix'. Remove 'guix-build',
'guix-download', 'guix-import', 'guix-package', and 'guix-gc'.
* guix-build.in, guix-download.in, guix-gc.in, guix-import.in,
guix-package.in: Remove shell script boilerplate. Move to guix-COMMAND.in
to guix/scripts/COMMAND.scm. Rename module from (guix-COMMAND) to
(guix scripts COMMAND). Change "guix-COMMAND" to "guix COMMAND" in
usage help string.
* pre-inst-env.in: Add "@abs_top_builddir@/scripts" to the front of $PATH.
Export $GUIX_UNINSTALLED.
* tests/guix-build.sh, tests/guix-daemon.sh, tests/guix-download.sh,
tests/guix-gc.sh, tests/guix-package.sh: Use "guix COMMAND" instead of
"guix-COMMAND".
* doc/guix.texi: Replace all occurrences of "guix-COMMAND" with
"guix COMMAND".
* po/POTFILES.in: Update.
2013-02-14 10:15:25 +01:00
|
|
|
|
guix-main))
|
2012-11-01 00:50:01 +01:00
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
;;;
|
|
|
|
|
;;; User interface facilities for command-line tools.
|
|
|
|
|
;;;
|
|
|
|
|
;;; Code:
|
|
|
|
|
|
|
|
|
|
(define %gettext-domain
|
2014-06-13 17:30:40 +02:00
|
|
|
|
;; Text domain for strings used in the tools.
|
2012-11-01 00:50:01 +01:00
|
|
|
|
"guix")
|
|
|
|
|
|
2014-06-13 17:30:40 +02:00
|
|
|
|
(define %package-text-domain
|
|
|
|
|
;; Text domain for package synopses and descriptions.
|
|
|
|
|
"guix-packages")
|
|
|
|
|
|
2012-11-01 00:50:01 +01:00
|
|
|
|
(define _ (cut gettext <> %gettext-domain))
|
|
|
|
|
(define N_ (cut ngettext <> <> <> %gettext-domain))
|
2015-09-06 15:47:06 +02:00
|
|
|
|
|
|
|
|
|
(define (P_ msgid)
|
|
|
|
|
"Return the translation of the package description or synopsis MSGID."
|
|
|
|
|
;; Descriptions/synopses might occasionally be empty strings, even if that
|
|
|
|
|
;; is something we try to avoid. Since (gettext "") can return a non-empty
|
|
|
|
|
;; string, explicitly check for that case.
|
|
|
|
|
(if (string-null? msgid)
|
|
|
|
|
msgid
|
|
|
|
|
(gettext msgid %package-text-domain)))
|
2012-11-01 00:50:01 +01:00
|
|
|
|
|
2013-04-22 13:24:16 +02:00
|
|
|
|
(define-syntax-rule (define-diagnostic name prefix)
|
|
|
|
|
"Create a diagnostic macro (i.e., NAME), which will prepend PREFIX to all
|
|
|
|
|
messages."
|
|
|
|
|
(define-syntax name
|
|
|
|
|
(lambda (x)
|
|
|
|
|
(define (augmented-format-string fmt)
|
|
|
|
|
(string-append "~:[~*~;guix ~a: ~]~a" (syntax->datum fmt)))
|
|
|
|
|
|
2013-05-30 00:49:34 +02:00
|
|
|
|
(syntax-case x ()
|
|
|
|
|
((name (underscore fmt) args (... ...))
|
|
|
|
|
(and (string? (syntax->datum #'fmt))
|
|
|
|
|
(free-identifier=? #'underscore #'_))
|
2013-04-22 13:24:16 +02:00
|
|
|
|
(with-syntax ((fmt* (augmented-format-string #'fmt))
|
|
|
|
|
(prefix (datum->syntax x prefix)))
|
|
|
|
|
#'(format (guix-warning-port) (gettext fmt*)
|
|
|
|
|
(program-name) (program-name) prefix
|
|
|
|
|
args (... ...))))
|
2013-05-30 00:49:34 +02:00
|
|
|
|
((name (N-underscore singular plural n) args (... ...))
|
2013-04-22 13:24:16 +02:00
|
|
|
|
(and (string? (syntax->datum #'singular))
|
2013-05-30 00:49:34 +02:00
|
|
|
|
(string? (syntax->datum #'plural))
|
|
|
|
|
(free-identifier=? #'N-underscore #'N_))
|
2013-04-22 13:24:16 +02:00
|
|
|
|
(with-syntax ((s (augmented-format-string #'singular))
|
|
|
|
|
(p (augmented-format-string #'plural))
|
|
|
|
|
(prefix (datum->syntax x prefix)))
|
|
|
|
|
#'(format (guix-warning-port)
|
|
|
|
|
(ngettext s p n %gettext-domain)
|
|
|
|
|
(program-name) (program-name) prefix
|
|
|
|
|
args (... ...))))))))
|
|
|
|
|
|
|
|
|
|
(define-diagnostic warning "warning: ") ; emit a warning
|
|
|
|
|
|
|
|
|
|
(define-diagnostic report-error "error: ")
|
|
|
|
|
(define-syntax-rule (leave args ...)
|
|
|
|
|
"Emit an error message and exit."
|
|
|
|
|
(begin
|
|
|
|
|
(report-error args ...)
|
|
|
|
|
(exit 1)))
|
|
|
|
|
|
2015-05-18 13:49:44 +02:00
|
|
|
|
(define (make-user-module modules)
|
|
|
|
|
"Return a new user module with the additional MODULES loaded."
|
|
|
|
|
;; Module in which the machine description file is loaded.
|
|
|
|
|
(let ((module (make-fresh-user-module)))
|
|
|
|
|
(for-each (lambda (iface)
|
|
|
|
|
(module-use! module (resolve-interface iface)))
|
|
|
|
|
modules)
|
|
|
|
|
module))
|
|
|
|
|
|
2015-05-25 22:52:41 +02:00
|
|
|
|
(define* (load* file user-module
|
|
|
|
|
#:key (on-error 'nothing-special))
|
2015-05-18 13:49:44 +02:00
|
|
|
|
"Load the user provided Scheme source code FILE."
|
2015-05-25 18:25:19 +02:00
|
|
|
|
(define (frame-with-source frame)
|
|
|
|
|
;; Walk from FRAME upwards until source location information is found.
|
|
|
|
|
(let loop ((frame frame)
|
|
|
|
|
(previous frame))
|
|
|
|
|
(if (not frame)
|
|
|
|
|
previous
|
|
|
|
|
(if (frame-source frame)
|
|
|
|
|
frame
|
|
|
|
|
(loop (frame-previous frame) frame)))))
|
|
|
|
|
|
2015-05-25 22:52:41 +02:00
|
|
|
|
(define (error-string frame args)
|
|
|
|
|
(call-with-output-string
|
|
|
|
|
(lambda (port)
|
|
|
|
|
(apply display-error frame port (cdr args)))))
|
|
|
|
|
|
|
|
|
|
(define tag
|
|
|
|
|
(make-prompt-tag "user-code"))
|
|
|
|
|
|
2015-05-18 13:49:44 +02:00
|
|
|
|
(catch #t
|
|
|
|
|
(lambda ()
|
2015-05-25 18:25:19 +02:00
|
|
|
|
;; XXX: Force a recompilation to avoid ABI issues.
|
2015-05-18 13:49:44 +02:00
|
|
|
|
(set! %fresh-auto-compile #t)
|
2015-05-25 18:25:19 +02:00
|
|
|
|
(set! %load-should-auto-compile #t)
|
2015-05-18 13:49:44 +02:00
|
|
|
|
|
|
|
|
|
(save-module-excursion
|
|
|
|
|
(lambda ()
|
|
|
|
|
(set-current-module user-module)
|
|
|
|
|
|
2015-05-25 18:25:19 +02:00
|
|
|
|
;; Hide the "auto-compiling" messages.
|
|
|
|
|
(parameterize ((current-warning-port (%make-void-port "w")))
|
2015-05-25 22:52:41 +02:00
|
|
|
|
(call-with-prompt tag
|
|
|
|
|
(lambda ()
|
|
|
|
|
;; Give 'load' an absolute file name so that it doesn't try to
|
|
|
|
|
;; search for FILE in %LOAD-PATH. Note: use 'load', not
|
|
|
|
|
;; 'primitive-load', so that FILE is compiled, which then allows us
|
|
|
|
|
;; to provide better error reporting with source line numbers.
|
|
|
|
|
(load (canonicalize-path file)))
|
|
|
|
|
(const #f))))))
|
2015-05-25 18:25:19 +02:00
|
|
|
|
(lambda _
|
|
|
|
|
;; XXX: Errors are reported from the pre-unwind handler below, but
|
|
|
|
|
;; calling 'exit' from there has no effect, so we call it here.
|
|
|
|
|
(exit 1))
|
|
|
|
|
(rec (handle-error . args)
|
|
|
|
|
;; Capture the stack up to this procedure call, excluded, and pass
|
|
|
|
|
;; the faulty stack frame to 'report-load-error'.
|
2015-05-25 22:52:41 +02:00
|
|
|
|
(let* ((stack (make-stack #t handle-error tag))
|
2015-05-25 18:25:19 +02:00
|
|
|
|
(depth (stack-length stack))
|
|
|
|
|
(last (and (> depth 0) (stack-ref stack 0)))
|
|
|
|
|
(frame (frame-with-source
|
|
|
|
|
(if (> depth 1)
|
|
|
|
|
(stack-ref stack 1) ;skip the 'throw' frame
|
|
|
|
|
last))))
|
2015-05-25 22:52:41 +02:00
|
|
|
|
|
|
|
|
|
(report-load-error file args frame)
|
|
|
|
|
|
|
|
|
|
(case on-error
|
|
|
|
|
((debug)
|
|
|
|
|
(newline)
|
|
|
|
|
(display (_ "entering debugger; type ',bt' for a backtrace\n"))
|
|
|
|
|
(start-repl #:debug (make-debug (stack->vector stack) 0
|
|
|
|
|
(error-string frame args)
|
|
|
|
|
#f)))
|
|
|
|
|
((backtrace)
|
|
|
|
|
(newline (current-error-port))
|
|
|
|
|
(display-backtrace stack (current-error-port)))
|
|
|
|
|
(else
|
|
|
|
|
#t))))))
|
2015-05-25 18:25:19 +02:00
|
|
|
|
|
|
|
|
|
(define* (report-load-error file args #:optional frame)
|
2015-05-25 22:52:41 +02:00
|
|
|
|
"Report the failure to load FILE, a user-provided Scheme file.
|
2015-04-07 22:07:25 +02:00
|
|
|
|
ARGS is the list of arguments received by the 'throw' handler."
|
|
|
|
|
(match args
|
|
|
|
|
(('system-error . _)
|
|
|
|
|
(let ((err (system-error-errno args)))
|
2015-05-25 22:52:41 +02:00
|
|
|
|
(report-error (_ "failed to load '~a': ~a~%") file (strerror err))))
|
2015-04-07 22:07:25 +02:00
|
|
|
|
(('syntax-error proc message properties form . rest)
|
|
|
|
|
(let ((loc (source-properties->location properties)))
|
|
|
|
|
(format (current-error-port) (_ "~a: error: ~a~%")
|
2015-05-25 22:52:41 +02:00
|
|
|
|
(location->string loc) message)))
|
2015-05-31 21:54:28 +02:00
|
|
|
|
(('srfi-34 obj)
|
|
|
|
|
(report-error (_ "exception thrown: ~s~%") obj))
|
2015-04-07 22:07:25 +02:00
|
|
|
|
((error args ...)
|
|
|
|
|
(report-error (_ "failed to load '~a':~%") file)
|
2015-05-25 22:52:41 +02:00
|
|
|
|
(apply display-error frame (current-error-port) args))))
|
2015-04-07 22:07:25 +02:00
|
|
|
|
|
2015-04-07 22:27:45 +02:00
|
|
|
|
(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 . _)
|
|
|
|
|
(let ((err (system-error-errno args)))
|
|
|
|
|
(warning (_ "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~%")
|
|
|
|
|
(location->string loc) message)))
|
2015-05-31 21:54:28 +02:00
|
|
|
|
(('srfi-34 obj)
|
|
|
|
|
(warning (_ "failed to load '~a': exception thrown: ~s~%")
|
|
|
|
|
file obj))
|
2015-04-07 22:27:45 +02:00
|
|
|
|
((error args ...)
|
|
|
|
|
(warning (_ "failed to load '~a':~%") file)
|
|
|
|
|
(apply display-error #f (current-error-port) args))))
|
|
|
|
|
|
2013-04-22 13:24:16 +02:00
|
|
|
|
(define (install-locale)
|
|
|
|
|
"Install the current locale settings."
|
|
|
|
|
(catch 'system-error
|
|
|
|
|
(lambda _
|
|
|
|
|
(setlocale LC_ALL ""))
|
|
|
|
|
(lambda args
|
|
|
|
|
(warning (_ "failed to install locale: ~a~%")
|
|
|
|
|
(strerror (system-error-errno args))))))
|
|
|
|
|
|
Replace individual scripts with master 'guix' script.
* scripts/guix.in: New script.
* Makefile.am (bin_SCRIPTS): Add 'scripts/guix'. Remove 'guix-build',
'guix-download', 'guix-import', 'guix-package', and 'guix-gc'.
(MODULES): Add 'guix/scripts/build.scm', 'guix/scripts/download.scm',
'guix/scripts/import.scm', 'guix/scripts/package.scm', and
'guix/scripts/gc.scm'.
* configure.ac (AC_CONFIG_FILES): Add 'scripts/guix'. Remove 'guix-build',
'guix-download', 'guix-import', 'guix-package', and 'guix-gc'.
* guix-build.in, guix-download.in, guix-gc.in, guix-import.in,
guix-package.in: Remove shell script boilerplate. Move to guix-COMMAND.in
to guix/scripts/COMMAND.scm. Rename module from (guix-COMMAND) to
(guix scripts COMMAND). Change "guix-COMMAND" to "guix COMMAND" in
usage help string.
* pre-inst-env.in: Add "@abs_top_builddir@/scripts" to the front of $PATH.
Export $GUIX_UNINSTALLED.
* tests/guix-build.sh, tests/guix-daemon.sh, tests/guix-download.sh,
tests/guix-gc.sh, tests/guix-package.sh: Use "guix COMMAND" instead of
"guix-COMMAND".
* doc/guix.texi: Replace all occurrences of "guix-COMMAND" with
"guix COMMAND".
* po/POTFILES.in: Update.
2013-02-14 10:15:25 +01:00
|
|
|
|
(define (initialize-guix)
|
2013-02-17 15:38:02 +01:00
|
|
|
|
"Perform the usual initialization for stand-alone Guix commands."
|
Replace individual scripts with master 'guix' script.
* scripts/guix.in: New script.
* Makefile.am (bin_SCRIPTS): Add 'scripts/guix'. Remove 'guix-build',
'guix-download', 'guix-import', 'guix-package', and 'guix-gc'.
(MODULES): Add 'guix/scripts/build.scm', 'guix/scripts/download.scm',
'guix/scripts/import.scm', 'guix/scripts/package.scm', and
'guix/scripts/gc.scm'.
* configure.ac (AC_CONFIG_FILES): Add 'scripts/guix'. Remove 'guix-build',
'guix-download', 'guix-import', 'guix-package', and 'guix-gc'.
* guix-build.in, guix-download.in, guix-gc.in, guix-import.in,
guix-package.in: Remove shell script boilerplate. Move to guix-COMMAND.in
to guix/scripts/COMMAND.scm. Rename module from (guix-COMMAND) to
(guix scripts COMMAND). Change "guix-COMMAND" to "guix COMMAND" in
usage help string.
* pre-inst-env.in: Add "@abs_top_builddir@/scripts" to the front of $PATH.
Export $GUIX_UNINSTALLED.
* tests/guix-build.sh, tests/guix-daemon.sh, tests/guix-download.sh,
tests/guix-gc.sh, tests/guix-package.sh: Use "guix COMMAND" instead of
"guix-COMMAND".
* doc/guix.texi: Replace all occurrences of "guix-COMMAND" with
"guix COMMAND".
* po/POTFILES.in: Update.
2013-02-14 10:15:25 +01:00
|
|
|
|
(install-locale)
|
2013-10-12 16:37:28 +02:00
|
|
|
|
(textdomain %gettext-domain)
|
2013-07-12 23:01:07 +02:00
|
|
|
|
|
|
|
|
|
;; Ignore SIGPIPE. If the daemon closes the connection, we prefer to be
|
|
|
|
|
;; notified via an EPIPE later.
|
|
|
|
|
(sigaction SIGPIPE SIG_IGN)
|
|
|
|
|
|
Replace individual scripts with master 'guix' script.
* scripts/guix.in: New script.
* Makefile.am (bin_SCRIPTS): Add 'scripts/guix'. Remove 'guix-build',
'guix-download', 'guix-import', 'guix-package', and 'guix-gc'.
(MODULES): Add 'guix/scripts/build.scm', 'guix/scripts/download.scm',
'guix/scripts/import.scm', 'guix/scripts/package.scm', and
'guix/scripts/gc.scm'.
* configure.ac (AC_CONFIG_FILES): Add 'scripts/guix'. Remove 'guix-build',
'guix-download', 'guix-import', 'guix-package', and 'guix-gc'.
* guix-build.in, guix-download.in, guix-gc.in, guix-import.in,
guix-package.in: Remove shell script boilerplate. Move to guix-COMMAND.in
to guix/scripts/COMMAND.scm. Rename module from (guix-COMMAND) to
(guix scripts COMMAND). Change "guix-COMMAND" to "guix COMMAND" in
usage help string.
* pre-inst-env.in: Add "@abs_top_builddir@/scripts" to the front of $PATH.
Export $GUIX_UNINSTALLED.
* tests/guix-build.sh, tests/guix-daemon.sh, tests/guix-download.sh,
tests/guix-gc.sh, tests/guix-package.sh: Use "guix COMMAND" instead of
"guix-COMMAND".
* doc/guix.texi: Replace all occurrences of "guix-COMMAND" with
"guix COMMAND".
* po/POTFILES.in: Update.
2013-02-14 10:15:25 +01:00
|
|
|
|
(setvbuf (current-output-port) _IOLBF)
|
|
|
|
|
(setvbuf (current-error-port) _IOLBF))
|
|
|
|
|
|
2012-11-03 21:19:43 +01:00
|
|
|
|
(define* (show-version-and-exit #:optional (command (car (command-line))))
|
|
|
|
|
"Display version information for COMMAND and `(exit 0)'."
|
|
|
|
|
(simple-format #t "~a (~a) ~a~%"
|
|
|
|
|
command %guix-package-name %guix-version)
|
2015-01-28 19:06:33 +01:00
|
|
|
|
(display (_ "Copyright (C) 2015 the Guix authors
|
2013-11-03 23:09:30 +01:00
|
|
|
|
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.
|
|
|
|
|
"))
|
2012-11-03 21:19:43 +01:00
|
|
|
|
(exit 0))
|
|
|
|
|
|
2013-01-05 15:55:47 +01:00
|
|
|
|
(define (show-bug-report-information)
|
|
|
|
|
(format #t (_ "
|
|
|
|
|
Report bugs to: ~a.") %guix-bug-report-address)
|
|
|
|
|
(format #t (_ "
|
|
|
|
|
~a home page: <~a>") %guix-package-name %guix-home-page-url)
|
|
|
|
|
(display (_ "
|
|
|
|
|
General help using GNU software: <http://www.gnu.org/gethelp/>"))
|
|
|
|
|
(newline))
|
|
|
|
|
|
2015-06-10 10:25:16 +02:00
|
|
|
|
(set! symlink
|
|
|
|
|
;; We 'set!' the global binding because (gnu build ...) modules and similar
|
|
|
|
|
;; typically don't use (guix ui).
|
2015-04-20 22:37:20 +02:00
|
|
|
|
(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"
|
2015-05-24 17:29:14 +02:00
|
|
|
|
(list (strerror (car errno)) link)
|
|
|
|
|
(list errno)))))))
|
2015-04-20 22:37:20 +02:00
|
|
|
|
|
2015-05-24 17:22:43 +02:00
|
|
|
|
(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)))))))
|
|
|
|
|
|
2015-10-28 15:53:17 +01:00
|
|
|
|
(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 (_ "'~a' is not a valid regular expression: ~a~%")
|
|
|
|
|
regexp message))))
|
|
|
|
|
|
2013-05-20 18:14:55 +02:00
|
|
|
|
(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)))
|
|
|
|
|
|
2014-04-08 22:01:44 +02:00
|
|
|
|
(define (size->number str)
|
|
|
|
|
"Convert STR, a storage measurement representation such as \"1024\" or
|
|
|
|
|
\"1MiB\", to a number of bytes. Raise an error if STR could not be
|
|
|
|
|
interpreted."
|
|
|
|
|
(define unit-pos
|
|
|
|
|
(string-rindex str char-set:digit))
|
|
|
|
|
|
|
|
|
|
(define unit
|
|
|
|
|
(and unit-pos (substring str (+ 1 unit-pos))))
|
|
|
|
|
|
|
|
|
|
(let* ((numstr (if unit-pos
|
|
|
|
|
(substring str 0 (+ 1 unit-pos))
|
|
|
|
|
str))
|
|
|
|
|
(num (string->number numstr)))
|
|
|
|
|
(unless num
|
|
|
|
|
(leave (_ "invalid number: ~a~%") numstr))
|
|
|
|
|
|
|
|
|
|
((compose inexact->exact round)
|
|
|
|
|
(* num
|
|
|
|
|
(match unit
|
2014-10-03 13:35:14 +02:00
|
|
|
|
((or "KiB" "K" "k") (expt 2 10))
|
|
|
|
|
((or "MiB" "M") (expt 2 20))
|
|
|
|
|
((or "GiB" "G") (expt 2 30))
|
|
|
|
|
((or "TiB" "T") (expt 2 40))
|
|
|
|
|
((or "PiB" "P") (expt 2 50))
|
|
|
|
|
((or "EiB" "E") (expt 2 60))
|
|
|
|
|
((or "ZiB" "Z") (expt 2 70))
|
|
|
|
|
((or "YiB" "Y") (expt 2 80))
|
|
|
|
|
("kB" (expt 10 3))
|
2014-04-08 22:01:44 +02:00
|
|
|
|
("MB" (expt 10 6))
|
|
|
|
|
("GB" (expt 10 9))
|
|
|
|
|
("TB" (expt 10 12))
|
2014-10-03 13:35:14 +02:00
|
|
|
|
("PB" (expt 10 15))
|
|
|
|
|
("EB" (expt 10 18))
|
|
|
|
|
("ZB" (expt 10 21))
|
|
|
|
|
("YB" (expt 10 24))
|
2014-04-08 22:01:44 +02:00
|
|
|
|
("" 1)
|
|
|
|
|
(_
|
|
|
|
|
(leave (_ "unknown unit: ~a~%") unit)))))))
|
|
|
|
|
|
2012-11-01 00:50:01 +01:00
|
|
|
|
(define (call-with-error-handling thunk)
|
|
|
|
|
"Call THUNK within a user-friendly error handler."
|
|
|
|
|
(guard (c ((package-input-error? c)
|
|
|
|
|
(let* ((package (package-error-package c))
|
|
|
|
|
(input (package-error-invalid-input c))
|
|
|
|
|
(location (package-location package))
|
|
|
|
|
(file (location-file location))
|
|
|
|
|
(line (location-line location))
|
|
|
|
|
(column (location-column location)))
|
2013-04-21 10:08:40 +02:00
|
|
|
|
(leave (_ "~a:~a:~a: package `~a' has an invalid input: ~s~%")
|
2012-11-01 00:50:01 +01:00
|
|
|
|
file line column
|
|
|
|
|
(package-full-name package) input)))
|
2013-05-27 23:41:35 +02:00
|
|
|
|
((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~%")
|
|
|
|
|
(location->string loc)
|
|
|
|
|
(package-full-name package)
|
|
|
|
|
(build-system-name system))))
|
2014-10-08 15:29:01 +02:00
|
|
|
|
((profile-not-found-error? c)
|
|
|
|
|
(leave (_ "profile '~a' does not exist~%")
|
|
|
|
|
(profile-error-profile c)))
|
|
|
|
|
((missing-generation-error? c)
|
|
|
|
|
(leave (_ "generation ~a of profile '~a' does not exist~%")
|
|
|
|
|
(missing-generation-error-generation c)
|
|
|
|
|
(profile-error-profile c)))
|
2015-02-08 18:30:20 +01:00
|
|
|
|
((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)))))
|
2013-03-07 19:29:12 +01:00
|
|
|
|
((nix-connection-error? c)
|
2013-04-21 10:08:40 +02:00
|
|
|
|
(leave (_ "failed to connect to `~a': ~a~%")
|
2013-03-07 19:29:12 +01:00
|
|
|
|
(nix-connection-error-file c)
|
|
|
|
|
(strerror (nix-connection-error-code c))))
|
2012-11-01 00:50:01 +01:00
|
|
|
|
((nix-protocol-error? c)
|
|
|
|
|
;; FIXME: Server-provided error messages aren't i18n'd.
|
2013-04-21 10:08:40 +02:00
|
|
|
|
(leave (_ "build failed: ~a~%")
|
2014-02-21 17:45:04 +01:00
|
|
|
|
(nix-protocol-error-message c)))
|
2015-01-24 22:50:40 +01:00
|
|
|
|
((derivation-missing-output-error? c)
|
|
|
|
|
(leave (_ "reference to invalid output '~a' of derivation '~a'~%")
|
|
|
|
|
(derivation-missing-output c)
|
|
|
|
|
(derivation-file-name (derivation-error-derivation c))))
|
2014-02-21 17:45:04 +01:00
|
|
|
|
((message-condition? c)
|
|
|
|
|
;; Normally '&message' error conditions have an i18n'd message.
|
2014-10-09 23:50:16 +02:00
|
|
|
|
(leave (_ "~a~%")
|
|
|
|
|
(gettext (condition-message c) %gettext-domain))))
|
2013-07-12 23:01:07 +02:00
|
|
|
|
;; Catch EPIPE and the likes.
|
|
|
|
|
(catch 'system-error
|
|
|
|
|
thunk
|
2014-04-04 20:37:27 +02:00
|
|
|
|
(lambda (key proc format-string format-args . rest)
|
|
|
|
|
(leave (_ "~a: ~a~%") proc
|
|
|
|
|
(apply format #f format-string format-args))))))
|
2012-11-01 00:50:01 +01:00
|
|
|
|
|
2015-07-15 18:01:05 +02:00
|
|
|
|
(define-syntax-rule (leave-on-EPIPE exp ...)
|
|
|
|
|
"Run EXP... in a context when EPIPE errors are caught and lead to 'exit'
|
|
|
|
|
with successful exit code. This is useful when writing to the standard output
|
|
|
|
|
may lead to EPIPE, because the standard output is piped through 'head' or
|
|
|
|
|
similar."
|
|
|
|
|
(catch 'system-error
|
|
|
|
|
(lambda ()
|
|
|
|
|
exp ...)
|
|
|
|
|
(lambda args
|
|
|
|
|
;; We really have to exit this brutally, otherwise Guile eventually
|
|
|
|
|
;; attempts to flush all the ports, leading to an uncaught EPIPE down
|
|
|
|
|
;; the path.
|
|
|
|
|
(if (= EPIPE (system-error-errno args))
|
|
|
|
|
(primitive-_exit 0)
|
|
|
|
|
(apply throw args)))))
|
|
|
|
|
|
2014-06-14 22:37:24 +02:00
|
|
|
|
(define %guix-user-module
|
|
|
|
|
;; Module in which user expressions are evaluated.
|
2014-06-14 23:23:56 +02:00
|
|
|
|
;; Compute lazily to avoid circularity with (guix gexp).
|
|
|
|
|
(delay
|
|
|
|
|
(let ((module (make-module)))
|
|
|
|
|
(beautify-user-module! module)
|
|
|
|
|
;; Use (guix gexp) so that one can use #~ & co.
|
|
|
|
|
(module-use! module (resolve-interface '(guix gexp)))
|
|
|
|
|
module)))
|
2014-06-14 22:37:24 +02:00
|
|
|
|
|
2013-11-18 23:08:20 +01:00
|
|
|
|
(define (read/eval str)
|
|
|
|
|
"Read and evaluate STR, raising an error if something goes wrong."
|
2013-03-01 21:55:42 +01:00
|
|
|
|
(let ((exp (catch #t
|
|
|
|
|
(lambda ()
|
|
|
|
|
(call-with-input-string str read))
|
|
|
|
|
(lambda args
|
|
|
|
|
(leave (_ "failed to read expression ~s: ~s~%")
|
|
|
|
|
str args)))))
|
2013-11-18 23:08:20 +01:00
|
|
|
|
(catch #t
|
|
|
|
|
(lambda ()
|
2014-06-14 23:23:56 +02:00
|
|
|
|
(eval exp (force %guix-user-module)))
|
2013-11-18 23:08:20 +01:00
|
|
|
|
(lambda args
|
2015-05-26 22:38:17 +02:00
|
|
|
|
(report-error (_ "failed to evaluate expression '~a':~%") exp)
|
|
|
|
|
(match args
|
|
|
|
|
(('syntax-error proc message properties form . rest)
|
|
|
|
|
(report-error (_ "syntax error: ~a~%") message))
|
2015-05-31 21:54:28 +02:00
|
|
|
|
(('srfi-34 obj)
|
|
|
|
|
(report-error (_ "exception thrown: ~s~%") obj))
|
2015-05-26 22:38:17 +02:00
|
|
|
|
((error args ...)
|
|
|
|
|
(apply display-error #f (current-error-port) args))
|
|
|
|
|
(what? #f))
|
|
|
|
|
(exit 1)))))
|
2013-11-18 23:08:20 +01:00
|
|
|
|
|
|
|
|
|
(define (read/eval-package-expression str)
|
|
|
|
|
"Read and evaluate STR and return the package it refers to, or exit an
|
|
|
|
|
error."
|
|
|
|
|
(match (read/eval str)
|
|
|
|
|
((? package? p) p)
|
|
|
|
|
(_
|
|
|
|
|
(leave (_ "expression ~s does not evaluate to a package~%")
|
|
|
|
|
str))))
|
2013-03-01 21:55:42 +01:00
|
|
|
|
|
2015-07-23 15:08:39 +02:00
|
|
|
|
(define (show-derivation-outputs derivation)
|
|
|
|
|
"Show the output file names of DERIVATION."
|
|
|
|
|
(format #t "~{~a~%~}"
|
|
|
|
|
(map (match-lambda
|
|
|
|
|
((out-name . out)
|
|
|
|
|
(derivation->output-path derivation out-name)))
|
|
|
|
|
(derivation-outputs derivation))))
|
|
|
|
|
|
2013-04-17 00:06:59 +02:00
|
|
|
|
(define* (show-what-to-build store drv
|
|
|
|
|
#:key dry-run? (use-substitutes? #t))
|
2013-02-20 23:41:24 +01:00
|
|
|
|
"Show what will or would (depending on DRY-RUN?) be built in realizing the
|
2013-02-22 21:08:06 +01:00
|
|
|
|
derivations listed in DRV. Return #t if there's something to build, #f
|
2013-04-17 00:06:59 +02:00
|
|
|
|
otherwise. When USE-SUBSTITUTES?, check and report what is prerequisites are
|
|
|
|
|
available for download."
|
2015-01-10 00:39:59 +01:00
|
|
|
|
(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)
|
|
|
|
|
(const #f)))
|
|
|
|
|
|
2014-05-19 23:08:43 +02:00
|
|
|
|
(define (built-or-substitutable? drv)
|
2015-01-24 22:50:40 +01:00
|
|
|
|
(or (null? (derivation-outputs drv))
|
|
|
|
|
(let ((out (derivation->output-path drv))) ;XXX: assume "out" exists
|
2014-05-19 23:08:43 +02:00
|
|
|
|
(or (valid-path? store out)
|
2015-01-10 00:39:59 +01:00
|
|
|
|
(substitutable? out)))))
|
2014-05-19 23:08:43 +02:00
|
|
|
|
|
2013-04-17 00:06:59 +02:00
|
|
|
|
(let*-values (((build download)
|
derivations: 'derivation' and related procedures return a single value.
* guix/derivations.scm (derivation->output-path,
derivation->output-paths): New procedures.
(derivation-path->output-path): Use 'derivation->output-path'.
(derivation-path->output-paths): Use 'derivation->output-paths'.
(derivation): Accept 'derivation?' objects as inputs. Return a single
value.
(build-derivations): New procedure.
(compiled-modules): Use 'derivation->output-paths'.
(build-expression->derivation)[source-path]: Add case for when the
input matches 'derivation?'.
[prologue]: Accept 'derivation?' objects in INPUTS.
[mod-dir, go-dir]: Use 'derivation->output-path'.
* guix/download.scm (url-fetch): Adjust to the single-value return.
* guix/packages.scm (package-output): Use 'derivation->output-path'.
* guix/scripts/build.scm (guix-build): When the argument is
'derivation-path?', pass it through 'read-derivation'.
Use 'derivation-file-name' to print out the .drv file names, and to
register them. Use 'derivation->output-path' instead of
'derivation-path->output-path'.
* guix/scripts/package.scm (roll-back): Adjust to the single-value
return.
(guix-package): Use 'derivation->output-path'.
* guix/ui.scm (show-what-to-build): Adjust to deal with 'derivation?'
objects instead of .drv file names.
* gnu/system/grub.scm (grub-configuration-file): Use
'derivation->output-path' instead of 'derivation-path->output-path'.
* gnu/system/vm.scm (qemu-image, system-qemu-image): Likewise.
* tests/builders.scm, tests/derivations.scm, tests/packages.scm,
tests/store.scm, tests/union.scm: Adjust to the new calling
convention.
* doc/guix.texi (Defining Packages, The Store, Derivations): Adjust
accordingly.
2013-09-18 17:01:40 +02:00
|
|
|
|
(fold2 (lambda (drv build download)
|
|
|
|
|
(let-values (((b d)
|
|
|
|
|
(derivation-prerequisites-to-build
|
|
|
|
|
store drv
|
2015-01-10 00:39:59 +01:00
|
|
|
|
#:substitutable? substitutable?)))
|
derivations: 'derivation' and related procedures return a single value.
* guix/derivations.scm (derivation->output-path,
derivation->output-paths): New procedures.
(derivation-path->output-path): Use 'derivation->output-path'.
(derivation-path->output-paths): Use 'derivation->output-paths'.
(derivation): Accept 'derivation?' objects as inputs. Return a single
value.
(build-derivations): New procedure.
(compiled-modules): Use 'derivation->output-paths'.
(build-expression->derivation)[source-path]: Add case for when the
input matches 'derivation?'.
[prologue]: Accept 'derivation?' objects in INPUTS.
[mod-dir, go-dir]: Use 'derivation->output-path'.
* guix/download.scm (url-fetch): Adjust to the single-value return.
* guix/packages.scm (package-output): Use 'derivation->output-path'.
* guix/scripts/build.scm (guix-build): When the argument is
'derivation-path?', pass it through 'read-derivation'.
Use 'derivation-file-name' to print out the .drv file names, and to
register them. Use 'derivation->output-path' instead of
'derivation-path->output-path'.
* guix/scripts/package.scm (roll-back): Adjust to the single-value
return.
(guix-package): Use 'derivation->output-path'.
* guix/ui.scm (show-what-to-build): Adjust to deal with 'derivation?'
objects instead of .drv file names.
* gnu/system/grub.scm (grub-configuration-file): Use
'derivation->output-path' instead of 'derivation-path->output-path'.
* gnu/system/vm.scm (qemu-image, system-qemu-image): Likewise.
* tests/builders.scm, tests/derivations.scm, tests/packages.scm,
tests/store.scm, tests/union.scm: Adjust to the new calling
convention.
* doc/guix.texi (Defining Packages, The Store, Derivations): Adjust
accordingly.
2013-09-18 17:01:40 +02:00
|
|
|
|
(values (append b build)
|
|
|
|
|
(append d download))))
|
2013-04-17 00:06:59 +02:00
|
|
|
|
'() '()
|
|
|
|
|
drv))
|
|
|
|
|
((build) ; add the DRV themselves
|
|
|
|
|
(delete-duplicates
|
derivations: 'derivation' and related procedures return a single value.
* guix/derivations.scm (derivation->output-path,
derivation->output-paths): New procedures.
(derivation-path->output-path): Use 'derivation->output-path'.
(derivation-path->output-paths): Use 'derivation->output-paths'.
(derivation): Accept 'derivation?' objects as inputs. Return a single
value.
(build-derivations): New procedure.
(compiled-modules): Use 'derivation->output-paths'.
(build-expression->derivation)[source-path]: Add case for when the
input matches 'derivation?'.
[prologue]: Accept 'derivation?' objects in INPUTS.
[mod-dir, go-dir]: Use 'derivation->output-path'.
* guix/download.scm (url-fetch): Adjust to the single-value return.
* guix/packages.scm (package-output): Use 'derivation->output-path'.
* guix/scripts/build.scm (guix-build): When the argument is
'derivation-path?', pass it through 'read-derivation'.
Use 'derivation-file-name' to print out the .drv file names, and to
register them. Use 'derivation->output-path' instead of
'derivation-path->output-path'.
* guix/scripts/package.scm (roll-back): Adjust to the single-value
return.
(guix-package): Use 'derivation->output-path'.
* guix/ui.scm (show-what-to-build): Adjust to deal with 'derivation?'
objects instead of .drv file names.
* gnu/system/grub.scm (grub-configuration-file): Use
'derivation->output-path' instead of 'derivation-path->output-path'.
* gnu/system/vm.scm (qemu-image, system-qemu-image): Likewise.
* tests/builders.scm, tests/derivations.scm, tests/packages.scm,
tests/store.scm, tests/union.scm: Adjust to the new calling
convention.
* doc/guix.texi (Defining Packages, The Store, Derivations): Adjust
accordingly.
2013-09-18 17:01:40 +02:00
|
|
|
|
(append (map derivation-file-name
|
2014-05-19 23:08:43 +02:00
|
|
|
|
(remove built-or-substitutable? drv))
|
2013-04-17 00:06:59 +02:00
|
|
|
|
(map derivation-input-path build))))
|
|
|
|
|
((download) ; add the references of DOWNLOAD
|
2013-07-01 00:29:22 +02:00
|
|
|
|
(if use-substitutes?
|
|
|
|
|
(delete-duplicates
|
|
|
|
|
(append download
|
|
|
|
|
(remove (cut valid-path? store <>)
|
|
|
|
|
(append-map
|
|
|
|
|
substitutable-references
|
|
|
|
|
(substitutable-path-info store
|
|
|
|
|
download)))))
|
|
|
|
|
download)))
|
2013-09-02 23:30:07 +02:00
|
|
|
|
;; TODO: Show the installed size of DOWNLOAD.
|
2013-02-20 23:41:24 +01:00
|
|
|
|
(if dry-run?
|
2013-04-17 00:06:59 +02:00
|
|
|
|
(begin
|
|
|
|
|
(format (current-error-port)
|
2013-05-07 13:28:11 +02:00
|
|
|
|
(N_ "~:[The following derivation would be built:~%~{ ~a~%~}~;~]"
|
|
|
|
|
"~:[The following derivations would be built:~%~{ ~a~%~}~;~]"
|
2013-04-17 00:06:59 +02:00
|
|
|
|
(length build))
|
|
|
|
|
(null? build) build)
|
|
|
|
|
(format (current-error-port)
|
2013-05-07 13:28:11 +02:00
|
|
|
|
(N_ "~:[The following file would be downloaded:~%~{ ~a~%~}~;~]"
|
|
|
|
|
"~:[The following files would be downloaded:~%~{ ~a~%~}~;~]"
|
2013-04-17 00:06:59 +02:00
|
|
|
|
(length download))
|
|
|
|
|
(null? download) download))
|
|
|
|
|
(begin
|
|
|
|
|
(format (current-error-port)
|
2013-05-07 13:28:11 +02:00
|
|
|
|
(N_ "~:[The following derivation will be built:~%~{ ~a~%~}~;~]"
|
|
|
|
|
"~:[The following derivations will be built:~%~{ ~a~%~}~;~]"
|
2013-04-17 00:06:59 +02:00
|
|
|
|
(length build))
|
|
|
|
|
(null? build) build)
|
|
|
|
|
(format (current-error-port)
|
2013-05-07 13:28:11 +02:00
|
|
|
|
(N_ "~:[The following file will be downloaded:~%~{ ~a~%~}~;~]"
|
|
|
|
|
"~:[The following files will be downloaded:~%~{ ~a~%~}~;~]"
|
2013-04-17 00:06:59 +02:00
|
|
|
|
(length download))
|
|
|
|
|
(null? download) download)))
|
|
|
|
|
(pair? build)))
|
2013-02-20 23:41:24 +01:00
|
|
|
|
|
2015-05-21 00:42:35 +02:00
|
|
|
|
(define show-what-to-build*
|
|
|
|
|
(store-lift show-what-to-build))
|
|
|
|
|
|
2014-10-08 15:15:49 +02:00
|
|
|
|
(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)
|
|
|
|
|
"->")))))
|
|
|
|
|
|
|
|
|
|
(define* (show-manifest-transaction store manifest transaction
|
|
|
|
|
#:key dry-run?)
|
|
|
|
|
"Display what will/would be installed/removed from MANIFEST by TRANSACTION."
|
|
|
|
|
(define (package-strings name version output item)
|
|
|
|
|
(map (lambda (name version output item)
|
|
|
|
|
(format #f " ~a~:[:~a~;~*~]\t~a\t~a"
|
|
|
|
|
name
|
|
|
|
|
(equal? output "out") output version
|
|
|
|
|
(if (package? item)
|
|
|
|
|
(package-output store item output)
|
|
|
|
|
item)))
|
|
|
|
|
name version output item))
|
|
|
|
|
|
|
|
|
|
(define → ;an arrow that can be represented on stderr
|
|
|
|
|
(right-arrow (current-error-port)))
|
|
|
|
|
|
|
|
|
|
(define (upgrade-string name old-version new-version output item)
|
|
|
|
|
(format #f " ~a~:[:~a~;~*~]\t~a ~a ~a\t~a"
|
|
|
|
|
name (equal? output "out") output
|
|
|
|
|
old-version → new-version
|
|
|
|
|
(if (package? item)
|
|
|
|
|
(package-output store item output)
|
|
|
|
|
item)))
|
|
|
|
|
|
2015-02-08 18:52:00 +01:00
|
|
|
|
(let-values (((remove install upgrade downgrade)
|
2014-10-08 15:15:49 +02:00
|
|
|
|
(manifest-transaction-effects manifest transaction)))
|
|
|
|
|
(match remove
|
|
|
|
|
((($ <manifest-entry> name version output item) ..1)
|
|
|
|
|
(let ((len (length name))
|
|
|
|
|
(remove (package-strings name version output item)))
|
|
|
|
|
(if dry-run?
|
|
|
|
|
(format (current-error-port)
|
|
|
|
|
(N_ "The following package would be removed:~%~{~a~%~}~%"
|
|
|
|
|
"The following packages would be removed:~%~{~a~%~}~%"
|
|
|
|
|
len)
|
|
|
|
|
remove)
|
|
|
|
|
(format (current-error-port)
|
|
|
|
|
(N_ "The following package will be removed:~%~{~a~%~}~%"
|
|
|
|
|
"The following packages will be removed:~%~{~a~%~}~%"
|
|
|
|
|
len)
|
|
|
|
|
remove))))
|
|
|
|
|
(_ #f))
|
2015-02-08 18:52:00 +01:00
|
|
|
|
(match downgrade
|
|
|
|
|
(((($ <manifest-entry> name old-version)
|
|
|
|
|
. ($ <manifest-entry> _ new-version output item)) ..1)
|
|
|
|
|
(let ((len (length name))
|
|
|
|
|
(downgrade (map upgrade-string
|
|
|
|
|
name old-version new-version output item)))
|
|
|
|
|
(if dry-run?
|
|
|
|
|
(format (current-error-port)
|
|
|
|
|
(N_ "The following package would be downgraded:~%~{~a~%~}~%"
|
|
|
|
|
"The following packages would be downgraded:~%~{~a~%~}~%"
|
|
|
|
|
len)
|
|
|
|
|
downgrade)
|
|
|
|
|
(format (current-error-port)
|
|
|
|
|
(N_ "The following package will be downgraded:~%~{~a~%~}~%"
|
|
|
|
|
"The following packages will be downgraded:~%~{~a~%~}~%"
|
|
|
|
|
len)
|
|
|
|
|
downgrade))))
|
|
|
|
|
(_ #f))
|
2014-10-08 15:15:49 +02:00
|
|
|
|
(match upgrade
|
|
|
|
|
(((($ <manifest-entry> name old-version)
|
|
|
|
|
. ($ <manifest-entry> _ new-version output item)) ..1)
|
|
|
|
|
(let ((len (length name))
|
|
|
|
|
(upgrade (map upgrade-string
|
|
|
|
|
name old-version new-version output item)))
|
|
|
|
|
(if dry-run?
|
|
|
|
|
(format (current-error-port)
|
|
|
|
|
(N_ "The following package would be upgraded:~%~{~a~%~}~%"
|
|
|
|
|
"The following packages would be upgraded:~%~{~a~%~}~%"
|
|
|
|
|
len)
|
|
|
|
|
upgrade)
|
|
|
|
|
(format (current-error-port)
|
|
|
|
|
(N_ "The following package will be upgraded:~%~{~a~%~}~%"
|
|
|
|
|
"The following packages will be upgraded:~%~{~a~%~}~%"
|
|
|
|
|
len)
|
|
|
|
|
upgrade))))
|
|
|
|
|
(_ #f))
|
|
|
|
|
(match install
|
|
|
|
|
((($ <manifest-entry> name version output item _) ..1)
|
|
|
|
|
(let ((len (length name))
|
|
|
|
|
(install (package-strings name version output item)))
|
|
|
|
|
(if dry-run?
|
|
|
|
|
(format (current-error-port)
|
|
|
|
|
(N_ "The following package would be installed:~%~{~a~%~}~%"
|
|
|
|
|
"The following packages would be installed:~%~{~a~%~}~%"
|
|
|
|
|
len)
|
|
|
|
|
install)
|
|
|
|
|
(format (current-error-port)
|
|
|
|
|
(N_ "The following package will be installed:~%~{~a~%~}~%"
|
|
|
|
|
"The following packages will be installed:~%~{~a~%~}~%"
|
|
|
|
|
len)
|
|
|
|
|
install))))
|
|
|
|
|
(_ #f))))
|
|
|
|
|
|
2012-11-01 00:50:01 +01:00
|
|
|
|
(define-syntax with-error-handling
|
|
|
|
|
(syntax-rules ()
|
|
|
|
|
"Run BODY within a user-friendly error condition handler."
|
|
|
|
|
((_ body ...)
|
|
|
|
|
(call-with-error-handling
|
|
|
|
|
(lambda ()
|
|
|
|
|
body ...)))))
|
|
|
|
|
|
2012-11-19 23:02:59 +01:00
|
|
|
|
(define (location->string loc)
|
|
|
|
|
"Return a human-friendly, GNU-standard representation of LOC."
|
|
|
|
|
(match loc
|
|
|
|
|
(#f (_ "<unknown location>"))
|
|
|
|
|
(($ <location> file line column)
|
|
|
|
|
(format #f "~a:~a:~a" file line column))))
|
|
|
|
|
|
2013-02-20 23:46:38 +01:00
|
|
|
|
(define (config-directory)
|
|
|
|
|
"Return the name of the configuration directory, after making sure that it
|
|
|
|
|
exists. Honor the XDG specs,
|
|
|
|
|
<http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>."
|
|
|
|
|
(let ((dir (and=> (or (getenv "XDG_CONFIG_HOME")
|
|
|
|
|
(and=> (getenv "HOME")
|
|
|
|
|
(cut string-append <> "/.config")))
|
|
|
|
|
(cut string-append <> "/guix"))))
|
|
|
|
|
(catch 'system-error
|
|
|
|
|
(lambda ()
|
2014-09-14 15:33:38 +02:00
|
|
|
|
(mkdir-p dir)
|
2013-02-20 23:46:38 +01:00
|
|
|
|
dir)
|
|
|
|
|
(lambda args
|
2014-09-14 15:33:38 +02:00
|
|
|
|
(let ((err (system-error-errno args)))
|
|
|
|
|
;; ERR is necessarily different from EEXIST.
|
|
|
|
|
(leave (_ "failed to create configuration directory `~a': ~a~%")
|
|
|
|
|
dir (strerror err)))))))
|
2013-02-20 23:46:38 +01:00
|
|
|
|
|
2013-02-01 13:16:27 +01:00
|
|
|
|
(define* (fill-paragraph str width #:optional (column 0))
|
|
|
|
|
"Fill STR such that each line contains at most WIDTH characters, assuming
|
|
|
|
|
that the first character is at COLUMN.
|
|
|
|
|
|
|
|
|
|
When STR contains a single line break surrounded by other characters, it is
|
|
|
|
|
converted to a space; sequences of more than one line break are preserved."
|
|
|
|
|
(define (maybe-break chr result)
|
|
|
|
|
(match result
|
|
|
|
|
((column newlines chars)
|
|
|
|
|
(case chr
|
|
|
|
|
((#\newline)
|
|
|
|
|
`(,column ,(+ 1 newlines) ,chars))
|
|
|
|
|
(else
|
2014-07-27 00:53:16 +02:00
|
|
|
|
(let* ((spaces (if (and (pair? chars) (eqv? (car chars) #\.)) 2 1))
|
|
|
|
|
(chars (case newlines
|
|
|
|
|
((0) chars)
|
|
|
|
|
((1)
|
|
|
|
|
(append (make-list spaces #\space) chars))
|
|
|
|
|
(else
|
|
|
|
|
(append (make-list newlines #\newline) chars))))
|
|
|
|
|
(column (case newlines
|
|
|
|
|
((0) column)
|
|
|
|
|
((1) (+ spaces column))
|
|
|
|
|
(else 0))))
|
2013-02-01 13:16:27 +01:00
|
|
|
|
(let ((chars (cons chr chars))
|
|
|
|
|
(column (+ 1 column)))
|
|
|
|
|
(if (> column width)
|
|
|
|
|
(let*-values (((before after)
|
|
|
|
|
(break (cut eqv? #\space <>) chars))
|
|
|
|
|
((len)
|
|
|
|
|
(length before)))
|
|
|
|
|
(if (<= len width)
|
|
|
|
|
`(,len
|
|
|
|
|
0
|
|
|
|
|
,(if (null? after)
|
|
|
|
|
before
|
2014-07-27 00:53:16 +02:00
|
|
|
|
(append before
|
|
|
|
|
(cons #\newline
|
|
|
|
|
(drop-while (cut eqv? #\space <>)
|
|
|
|
|
after)))))
|
2013-02-01 13:16:27 +01:00
|
|
|
|
`(,column 0 ,chars))) ; unbreakable
|
|
|
|
|
`(,column 0 ,chars)))))))))
|
|
|
|
|
|
|
|
|
|
(match (string-fold maybe-break
|
|
|
|
|
`(,column 0 ())
|
|
|
|
|
str)
|
|
|
|
|
((_ _ chars)
|
|
|
|
|
(list->string (reverse chars)))))
|
|
|
|
|
|
2013-11-01 16:57:48 +01:00
|
|
|
|
|
|
|
|
|
;;;
|
|
|
|
|
;;; Packages.
|
|
|
|
|
;;;
|
|
|
|
|
|
2015-08-07 00:10:43 +02:00
|
|
|
|
(define %text-width
|
|
|
|
|
(make-parameter (or (and=> (getenv "WIDTH") string->number)
|
|
|
|
|
80)))
|
|
|
|
|
|
|
|
|
|
(set! (@@ (texinfo plain-text) wrap*)
|
|
|
|
|
;; XXX: Monkey patch this private procedure to let 'package->recutils'
|
|
|
|
|
;; parameterize the fill of description field correctly.
|
|
|
|
|
(lambda strings
|
|
|
|
|
(let ((indent (fluid-ref (@@ (texinfo plain-text) *indent*))))
|
|
|
|
|
(fill-string (string-concatenate strings)
|
|
|
|
|
#:line-width (%text-width) #:initial-indent indent
|
|
|
|
|
#:subsequent-indent indent))))
|
|
|
|
|
|
|
|
|
|
(define (texi->plain-text str)
|
|
|
|
|
"Return a plain-text representation of texinfo fragment STR."
|
2015-09-24 21:56:42 +02:00
|
|
|
|
;; 'texi-fragment->stexi' uses a string port so make sure it's a
|
|
|
|
|
;; Unicode-capable one (see <http://bugs.gnu.org/11197>.)
|
|
|
|
|
(with-fluids ((%default-port-encoding "UTF-8"))
|
|
|
|
|
(stexi->plain-text (texi-fragment->stexi str))))
|
2015-08-07 00:10:43 +02:00
|
|
|
|
|
|
|
|
|
(define (package-description-string package)
|
|
|
|
|
"Return a plain-text representation of PACKAGE description field."
|
|
|
|
|
(and=> (package-description package)
|
|
|
|
|
(compose texi->plain-text P_)))
|
|
|
|
|
|
2013-02-01 13:16:27 +01:00
|
|
|
|
(define (string->recutils str)
|
|
|
|
|
"Return a version of STR where newlines have been replaced by newlines
|
|
|
|
|
followed by \"+ \", which makes for a valid multi-line field value in the
|
|
|
|
|
`recutils' syntax."
|
|
|
|
|
(list->string
|
|
|
|
|
(string-fold-right (lambda (chr result)
|
|
|
|
|
(if (eqv? chr #\newline)
|
|
|
|
|
(cons* chr #\+ #\space result)
|
|
|
|
|
(cons chr result)))
|
|
|
|
|
'()
|
|
|
|
|
str)))
|
|
|
|
|
|
2015-08-07 00:10:43 +02:00
|
|
|
|
(define* (package->recutils p port #:optional (width (%text-width)))
|
2013-02-01 13:16:27 +01:00
|
|
|
|
"Write to PORT a `recutils' record of package P, arranging to fit within
|
|
|
|
|
WIDTH columns."
|
2014-07-25 00:29:47 +02:00
|
|
|
|
(define (dependencies->recutils packages)
|
|
|
|
|
(let ((list (string-join (map package-full-name
|
|
|
|
|
(sort packages package<?)) " ")))
|
|
|
|
|
(string->recutils
|
|
|
|
|
(fill-paragraph list width
|
|
|
|
|
(string-length "dependencies: ")))))
|
|
|
|
|
|
2014-07-16 15:38:34 +02:00
|
|
|
|
(define (package<? p1 p2)
|
|
|
|
|
(string<? (package-full-name p1) (package-full-name p2)))
|
|
|
|
|
|
2013-02-01 13:16:27 +01:00
|
|
|
|
;; Note: Don't i18n field names so that people can post-process it.
|
|
|
|
|
(format port "name: ~a~%" (package-name p))
|
|
|
|
|
(format port "version: ~a~%" (package-version p))
|
2015-08-22 06:31:11 +02:00
|
|
|
|
(format port "outputs: ~a~%" (string-join (package-outputs p)))
|
2015-04-19 19:12:22 +02:00
|
|
|
|
(format port "systems: ~a~%"
|
|
|
|
|
(string-join (package-transitive-supported-systems p)))
|
2014-07-16 15:38:34 +02:00
|
|
|
|
(format port "dependencies: ~a~%"
|
|
|
|
|
(match (package-direct-inputs p)
|
2014-07-25 14:38:20 +02:00
|
|
|
|
(((labels inputs . _) ...)
|
|
|
|
|
(dependencies->recutils (filter package? inputs)))))
|
2013-02-01 13:16:27 +01:00
|
|
|
|
(format port "location: ~a~%"
|
|
|
|
|
(or (and=> (package-location p) location->string)
|
|
|
|
|
(_ "unknown")))
|
2014-01-13 18:49:26 +01:00
|
|
|
|
|
|
|
|
|
;; Note: Starting from version 1.6 or recutils, hyphens are not allowed in
|
|
|
|
|
;; field identifiers.
|
|
|
|
|
(format port "homepage: ~a~%" (package-home-page p))
|
|
|
|
|
|
2013-02-01 13:16:27 +01:00
|
|
|
|
(format port "license: ~a~%"
|
|
|
|
|
(match (package-license p)
|
|
|
|
|
(((? license? licenses) ...)
|
|
|
|
|
(string-join (map license-name licenses)
|
|
|
|
|
", "))
|
|
|
|
|
((? license? license)
|
|
|
|
|
(license-name license))
|
|
|
|
|
(x
|
|
|
|
|
(_ "unknown"))))
|
|
|
|
|
(format port "synopsis: ~a~%"
|
|
|
|
|
(string-map (match-lambda
|
|
|
|
|
(#\newline #\space)
|
|
|
|
|
(chr chr))
|
2014-06-13 17:30:40 +02:00
|
|
|
|
(or (and=> (package-synopsis p) P_)
|
2013-02-01 13:16:27 +01:00
|
|
|
|
"")))
|
2015-08-07 00:10:43 +02:00
|
|
|
|
(format port "~a~2%"
|
|
|
|
|
(string->recutils
|
|
|
|
|
(string-trim-right
|
|
|
|
|
(parameterize ((%text-width width))
|
|
|
|
|
(texi->plain-text
|
|
|
|
|
(string-append "description: "
|
|
|
|
|
(or (and=> (package-description p) P_)
|
|
|
|
|
""))))
|
|
|
|
|
#\newline))))
|
2013-02-01 13:16:27 +01:00
|
|
|
|
|
2013-09-19 13:07:39 +02:00
|
|
|
|
(define (string->generations str)
|
|
|
|
|
"Return the list of generations matching a pattern in STR. This function
|
|
|
|
|
accepts the following patterns: \"1\", \"1,2,3\", \"1..9\", \"1..\", \"..9\"."
|
|
|
|
|
(define (maybe-integer)
|
|
|
|
|
(let ((x (string->number str)))
|
|
|
|
|
(and (integer? x)
|
|
|
|
|
x)))
|
|
|
|
|
|
|
|
|
|
(define (maybe-comma-separated-integers)
|
|
|
|
|
(let ((lst (delete-duplicates
|
|
|
|
|
(map string->number
|
|
|
|
|
(string-split str #\,)))))
|
|
|
|
|
(and (every integer? lst)
|
|
|
|
|
lst)))
|
|
|
|
|
|
|
|
|
|
(cond ((maybe-integer)
|
|
|
|
|
=>
|
|
|
|
|
list)
|
|
|
|
|
((maybe-comma-separated-integers)
|
|
|
|
|
=>
|
|
|
|
|
identity)
|
|
|
|
|
((string-match "^([0-9]+)\\.\\.([0-9]+)$" str)
|
|
|
|
|
=>
|
|
|
|
|
(lambda (match)
|
|
|
|
|
(let ((s (string->number (match:substring match 1)))
|
|
|
|
|
(e (string->number (match:substring match 2))))
|
|
|
|
|
(and (every integer? (list s e))
|
|
|
|
|
(<= s e)
|
|
|
|
|
(iota (1+ (- e s)) s)))))
|
|
|
|
|
((string-match "^([0-9]+)\\.\\.$" str)
|
|
|
|
|
=>
|
|
|
|
|
(lambda (match)
|
|
|
|
|
(let ((s (string->number (match:substring match 1))))
|
|
|
|
|
(and (integer? s)
|
|
|
|
|
`(>= ,s)))))
|
|
|
|
|
((string-match "^\\.\\.([0-9]+)$" str)
|
|
|
|
|
=>
|
|
|
|
|
(lambda (match)
|
|
|
|
|
(let ((e (string->number (match:substring match 1))))
|
|
|
|
|
(and (integer? e)
|
|
|
|
|
`(<= ,e)))))
|
|
|
|
|
(else #f)))
|
|
|
|
|
|
|
|
|
|
(define (string->duration str)
|
|
|
|
|
"Return the duration matching a pattern in STR. This function accepts the
|
|
|
|
|
following patterns: \"1d\", \"1w\", \"1m\"."
|
|
|
|
|
(define (hours->duration hours match)
|
|
|
|
|
(make-time time-duration 0
|
|
|
|
|
(* 3600 hours (string->number (match:substring match 1)))))
|
|
|
|
|
|
|
|
|
|
(cond ((string-match "^([0-9]+)d$" str)
|
|
|
|
|
=>
|
|
|
|
|
(lambda (match)
|
|
|
|
|
(hours->duration 24 match)))
|
|
|
|
|
((string-match "^([0-9]+)w$" str)
|
|
|
|
|
=>
|
|
|
|
|
(lambda (match)
|
|
|
|
|
(hours->duration (* 24 7) match)))
|
|
|
|
|
((string-match "^([0-9]+)m$" str)
|
|
|
|
|
=>
|
|
|
|
|
(lambda (match)
|
|
|
|
|
(hours->duration (* 24 30) match)))
|
|
|
|
|
(else #f)))
|
|
|
|
|
|
2015-10-26 19:03:56 +01:00
|
|
|
|
(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))
|
|
|
|
|
(_ #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)))
|
|
|
|
|
|
2015-10-26 21:16:20 +01:00
|
|
|
|
(define (display-generation profile number)
|
|
|
|
|
"Display a one-line summary of generation NUMBER of PROFILE."
|
|
|
|
|
(unless (zero? number)
|
|
|
|
|
(let ((header (format #f (_ "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)
|
|
|
|
|
(format #t (_ "~a\t(current)~%") header)
|
|
|
|
|
(format #t "~a~%" header)))))
|
|
|
|
|
|
|
|
|
|
(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))))))
|
|
|
|
|
|
2015-10-26 23:01:06 +01:00
|
|
|
|
(define (display-generation-change previous current)
|
|
|
|
|
(format #t (_ "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 (_ "deleting ~a~%")
|
|
|
|
|
(generation-file-name profile generation))
|
|
|
|
|
(delete-generation store profile generation))
|
|
|
|
|
|
2013-11-01 16:57:48 +01:00
|
|
|
|
(define* (package-specification->name+version+output spec
|
|
|
|
|
#:optional (output "out"))
|
|
|
|
|
"Parse package specification SPEC and return three value: the specified
|
|
|
|
|
package name, version number (or #f), and output name (or OUTPUT). SPEC may
|
|
|
|
|
optionally contain a version number and an output name, as in these examples:
|
|
|
|
|
|
|
|
|
|
guile
|
|
|
|
|
guile-2.0.9
|
|
|
|
|
guile:debug
|
|
|
|
|
guile-2.0.9:debug
|
|
|
|
|
"
|
|
|
|
|
(let*-values (((name sub-drv)
|
|
|
|
|
(match (string-rindex spec #\:)
|
|
|
|
|
(#f (values spec output))
|
|
|
|
|
(colon (values (substring spec 0 colon)
|
|
|
|
|
(substring spec (+ 1 colon))))))
|
|
|
|
|
((name version)
|
|
|
|
|
(package-name->name+version name)))
|
|
|
|
|
(values name version sub-drv)))
|
|
|
|
|
|
2015-06-19 14:57:44 +02:00
|
|
|
|
(define (specification->file-system-mapping spec writable?)
|
|
|
|
|
"Read the SPEC and return the corresponding <file-system-mapping>. SPEC is
|
|
|
|
|
a string of the form \"SOURCE\" or \"SOURCE=TARGET\". The former specifies
|
|
|
|
|
that SOURCE from the host should be mounted at SOURCE in the other system.
|
|
|
|
|
The latter format specifies that SOURCE from the host should be mounted at
|
|
|
|
|
TARGET in the other system."
|
|
|
|
|
(let ((index (string-index spec #\=)))
|
|
|
|
|
(if index
|
|
|
|
|
(file-system-mapping
|
|
|
|
|
(source (substring spec 0 index))
|
|
|
|
|
(target (substring spec (+ 1 index)))
|
|
|
|
|
(writable? writable?))
|
|
|
|
|
(file-system-mapping
|
|
|
|
|
(source spec)
|
|
|
|
|
(target spec)
|
|
|
|
|
(writable? writable?)))))
|
|
|
|
|
|
2013-11-01 16:57:48 +01:00
|
|
|
|
|
|
|
|
|
;;;
|
|
|
|
|
;;; Command-line option processing.
|
|
|
|
|
;;;
|
|
|
|
|
|
Replace individual scripts with master 'guix' script.
* scripts/guix.in: New script.
* Makefile.am (bin_SCRIPTS): Add 'scripts/guix'. Remove 'guix-build',
'guix-download', 'guix-import', 'guix-package', and 'guix-gc'.
(MODULES): Add 'guix/scripts/build.scm', 'guix/scripts/download.scm',
'guix/scripts/import.scm', 'guix/scripts/package.scm', and
'guix/scripts/gc.scm'.
* configure.ac (AC_CONFIG_FILES): Add 'scripts/guix'. Remove 'guix-build',
'guix-download', 'guix-import', 'guix-package', and 'guix-gc'.
* guix-build.in, guix-download.in, guix-gc.in, guix-import.in,
guix-package.in: Remove shell script boilerplate. Move to guix-COMMAND.in
to guix/scripts/COMMAND.scm. Rename module from (guix-COMMAND) to
(guix scripts COMMAND). Change "guix-COMMAND" to "guix COMMAND" in
usage help string.
* pre-inst-env.in: Add "@abs_top_builddir@/scripts" to the front of $PATH.
Export $GUIX_UNINSTALLED.
* tests/guix-build.sh, tests/guix-daemon.sh, tests/guix-download.sh,
tests/guix-gc.sh, tests/guix-package.sh: Use "guix COMMAND" instead of
"guix-COMMAND".
* doc/guix.texi: Replace all occurrences of "guix-COMMAND" with
"guix COMMAND".
* po/POTFILES.in: Update.
2013-02-14 10:15:25 +01:00
|
|
|
|
(define (show-guix-usage)
|
|
|
|
|
(format (current-error-port)
|
2013-05-10 23:14:26 +02:00
|
|
|
|
(_ "Try `guix --help' for more information.~%"))
|
|
|
|
|
(exit 1))
|
Replace individual scripts with master 'guix' script.
* scripts/guix.in: New script.
* Makefile.am (bin_SCRIPTS): Add 'scripts/guix'. Remove 'guix-build',
'guix-download', 'guix-import', 'guix-package', and 'guix-gc'.
(MODULES): Add 'guix/scripts/build.scm', 'guix/scripts/download.scm',
'guix/scripts/import.scm', 'guix/scripts/package.scm', and
'guix/scripts/gc.scm'.
* configure.ac (AC_CONFIG_FILES): Add 'scripts/guix'. Remove 'guix-build',
'guix-download', 'guix-import', 'guix-package', and 'guix-gc'.
* guix-build.in, guix-download.in, guix-gc.in, guix-import.in,
guix-package.in: Remove shell script boilerplate. Move to guix-COMMAND.in
to guix/scripts/COMMAND.scm. Rename module from (guix-COMMAND) to
(guix scripts COMMAND). Change "guix-COMMAND" to "guix COMMAND" in
usage help string.
* pre-inst-env.in: Add "@abs_top_builddir@/scripts" to the front of $PATH.
Export $GUIX_UNINSTALLED.
* tests/guix-build.sh, tests/guix-daemon.sh, tests/guix-download.sh,
tests/guix-gc.sh, tests/guix-package.sh: Use "guix COMMAND" instead of
"guix-COMMAND".
* doc/guix.texi: Replace all occurrences of "guix-COMMAND" with
"guix COMMAND".
* po/POTFILES.in: Update.
2013-02-14 10:15:25 +01:00
|
|
|
|
|
2013-05-10 12:33:18 +02:00
|
|
|
|
(define (command-files)
|
|
|
|
|
"Return the list of source files that define Guix sub-commands."
|
|
|
|
|
(define directory
|
|
|
|
|
(and=> (search-path %load-path "guix.scm")
|
|
|
|
|
(compose (cut string-append <> "/guix/scripts")
|
|
|
|
|
dirname)))
|
|
|
|
|
|
2013-05-14 13:37:21 +02:00
|
|
|
|
(define dot-scm?
|
|
|
|
|
(cut string-suffix? ".scm" <>))
|
|
|
|
|
|
2013-05-10 12:33:18 +02:00
|
|
|
|
(if directory
|
2015-05-07 21:51:30 +02:00
|
|
|
|
(scandir directory dot-scm?)
|
2013-05-10 12:33:18 +02:00
|
|
|
|
'()))
|
|
|
|
|
|
|
|
|
|
(define (commands)
|
|
|
|
|
"Return the list of Guix command names."
|
|
|
|
|
(map (compose (cut string-drop-right <> 4)
|
|
|
|
|
basename)
|
|
|
|
|
(command-files)))
|
|
|
|
|
|
|
|
|
|
(define (show-guix-help)
|
2014-01-06 23:31:17 +01:00
|
|
|
|
(define (internal? command)
|
2015-03-25 10:34:27 +01:00
|
|
|
|
(member command '("substitute" "authenticate" "offload")))
|
2014-01-06 23:31:17 +01:00
|
|
|
|
|
2013-05-10 12:33:18 +02:00
|
|
|
|
(format #t (_ "Usage: guix COMMAND ARGS...
|
|
|
|
|
Run COMMAND with ARGS.\n"))
|
|
|
|
|
(newline)
|
|
|
|
|
(format #t (_ "COMMAND must be one of the sub-commands listed below:\n"))
|
|
|
|
|
(newline)
|
|
|
|
|
;; TODO: Display a synopsis of each command.
|
2014-01-06 23:31:17 +01:00
|
|
|
|
(format #t "~{ ~a~%~}" (sort (remove internal? (commands))
|
|
|
|
|
string<?))
|
2013-05-10 12:33:18 +02:00
|
|
|
|
(show-bug-report-information))
|
|
|
|
|
|
2013-04-11 22:30:06 +02:00
|
|
|
|
(define program-name
|
|
|
|
|
;; Name of the command-line program currently executing, or #f.
|
|
|
|
|
(make-parameter #f))
|
|
|
|
|
|
2013-05-10 12:14:01 +02:00
|
|
|
|
(define (run-guix-command command . args)
|
|
|
|
|
"Run COMMAND with the given ARGS. Report an error when COMMAND is not
|
|
|
|
|
found."
|
|
|
|
|
(define module
|
|
|
|
|
(catch 'misc-error
|
|
|
|
|
(lambda ()
|
|
|
|
|
(resolve-interface `(guix scripts ,command)))
|
|
|
|
|
(lambda -
|
2013-05-10 23:14:26 +02:00
|
|
|
|
(format (current-error-port)
|
|
|
|
|
(_ "guix: ~a: command not found~%") command)
|
|
|
|
|
(show-guix-usage))))
|
2013-05-10 12:14:01 +02:00
|
|
|
|
|
|
|
|
|
(let ((command-main (module-ref module
|
|
|
|
|
(symbol-append 'guix- command))))
|
|
|
|
|
(parameterize ((program-name command))
|
|
|
|
|
(apply command-main args))))
|
|
|
|
|
|
2015-08-16 09:28:04 +02:00
|
|
|
|
(define (run-guix . args)
|
|
|
|
|
"Run the 'guix' command defined by command line ARGS.
|
|
|
|
|
Unlike 'guix-main', this procedure assumes that locale, i18n support,
|
|
|
|
|
and signal handling has already been set up."
|
|
|
|
|
(define option? (cut string-prefix? "-" <>))
|
|
|
|
|
|
|
|
|
|
(match args
|
|
|
|
|
(()
|
|
|
|
|
(format (current-error-port)
|
|
|
|
|
(_ "guix: missing command name~%"))
|
|
|
|
|
(show-guix-usage))
|
|
|
|
|
((or ("-h") ("--help"))
|
|
|
|
|
(show-guix-help))
|
|
|
|
|
(("--version")
|
|
|
|
|
(show-version-and-exit "guix"))
|
|
|
|
|
(((? option? o) args ...)
|
|
|
|
|
(format (current-error-port)
|
|
|
|
|
(_ "guix: unrecognized option '~a'~%") o)
|
|
|
|
|
(show-guix-usage))
|
|
|
|
|
(("help" args ...)
|
|
|
|
|
(show-guix-help))
|
|
|
|
|
((command args ...)
|
|
|
|
|
(apply run-guix-command
|
|
|
|
|
(string->symbol command)
|
|
|
|
|
args))))
|
|
|
|
|
|
2013-04-11 22:30:06 +02:00
|
|
|
|
(define guix-warning-port
|
|
|
|
|
(make-parameter (current-warning-port)))
|
|
|
|
|
|
Replace individual scripts with master 'guix' script.
* scripts/guix.in: New script.
* Makefile.am (bin_SCRIPTS): Add 'scripts/guix'. Remove 'guix-build',
'guix-download', 'guix-import', 'guix-package', and 'guix-gc'.
(MODULES): Add 'guix/scripts/build.scm', 'guix/scripts/download.scm',
'guix/scripts/import.scm', 'guix/scripts/package.scm', and
'guix/scripts/gc.scm'.
* configure.ac (AC_CONFIG_FILES): Add 'scripts/guix'. Remove 'guix-build',
'guix-download', 'guix-import', 'guix-package', and 'guix-gc'.
* guix-build.in, guix-download.in, guix-gc.in, guix-import.in,
guix-package.in: Remove shell script boilerplate. Move to guix-COMMAND.in
to guix/scripts/COMMAND.scm. Rename module from (guix-COMMAND) to
(guix scripts COMMAND). Change "guix-COMMAND" to "guix COMMAND" in
usage help string.
* pre-inst-env.in: Add "@abs_top_builddir@/scripts" to the front of $PATH.
Export $GUIX_UNINSTALLED.
* tests/guix-build.sh, tests/guix-daemon.sh, tests/guix-download.sh,
tests/guix-gc.sh, tests/guix-package.sh: Use "guix COMMAND" instead of
"guix-COMMAND".
* doc/guix.texi: Replace all occurrences of "guix-COMMAND" with
"guix COMMAND".
* po/POTFILES.in: Update.
2013-02-14 10:15:25 +01:00
|
|
|
|
(define (guix-main arg0 . args)
|
|
|
|
|
(initialize-guix)
|
2015-08-16 09:28:04 +02:00
|
|
|
|
(apply run-guix args))
|
Replace individual scripts with master 'guix' script.
* scripts/guix.in: New script.
* Makefile.am (bin_SCRIPTS): Add 'scripts/guix'. Remove 'guix-build',
'guix-download', 'guix-import', 'guix-package', and 'guix-gc'.
(MODULES): Add 'guix/scripts/build.scm', 'guix/scripts/download.scm',
'guix/scripts/import.scm', 'guix/scripts/package.scm', and
'guix/scripts/gc.scm'.
* configure.ac (AC_CONFIG_FILES): Add 'scripts/guix'. Remove 'guix-build',
'guix-download', 'guix-import', 'guix-package', and 'guix-gc'.
* guix-build.in, guix-download.in, guix-gc.in, guix-import.in,
guix-package.in: Remove shell script boilerplate. Move to guix-COMMAND.in
to guix/scripts/COMMAND.scm. Rename module from (guix-COMMAND) to
(guix scripts COMMAND). Change "guix-COMMAND" to "guix COMMAND" in
usage help string.
* pre-inst-env.in: Add "@abs_top_builddir@/scripts" to the front of $PATH.
Export $GUIX_UNINSTALLED.
* tests/guix-build.sh, tests/guix-daemon.sh, tests/guix-download.sh,
tests/guix-gc.sh, tests/guix-package.sh: Use "guix COMMAND" instead of
"guix-COMMAND".
* doc/guix.texi: Replace all occurrences of "guix-COMMAND" with
"guix COMMAND".
* po/POTFILES.in: Update.
2013-02-14 10:15:25 +01:00
|
|
|
|
|
2012-11-01 00:50:01 +01:00
|
|
|
|
;;; ui.scm ends here
|