summaryrefslogtreecommitdiff
path: root/guix/scripts/package.scm
diff options
context:
space:
mode:
Diffstat (limited to 'guix/scripts/package.scm')
-rw-r--r--guix/scripts/package.scm695
1 files changed, 407 insertions, 288 deletions
diff --git a/guix/scripts/package.scm b/guix/scripts/package.scm
index 5c7c165cbb..008ae53b47 100644
--- a/guix/scripts/package.scm
+++ b/guix/scripts/package.scm
@@ -25,6 +25,7 @@
#:use-module (guix packages)
#:use-module (guix utils)
#:use-module (guix config)
+ #:use-module (guix records)
#:use-module ((guix build utils) #:select (directory-exists? mkdir-p))
#:use-module ((guix ftp-client) #:select (ftp-open))
#:use-module (ice-9 ftw)
@@ -33,6 +34,7 @@
#:use-module (ice-9 regex)
#:use-module (ice-9 vlist)
#:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-9)
#:use-module (srfi srfi-11)
#:use-module (srfi srfi-19)
#:use-module (srfi srfi-26)
@@ -49,10 +51,10 @@
;;;
-;;; User environment.
+;;; User profile.
;;;
-(define %user-environment-directory
+(define %user-profile-directory
(and=> (getenv "HOME")
(cut string-append <> "/.guix-profile")))
@@ -67,30 +69,125 @@
;; coexist with Nix profiles.
(string-append %profile-directory "/guix-profile"))
+
+;;;
+;;; Manifests.
+;;;
+
+(define-record-type <manifest>
+ (manifest entries)
+ manifest?
+ (entries manifest-entries)) ; list of <manifest-entry>
+
+;; Convenient alias, to avoid name clashes.
+(define make-manifest manifest)
+
+(define-record-type* <manifest-entry> manifest-entry
+ make-manifest-entry
+ manifest-entry?
+ (name manifest-entry-name) ; string
+ (version manifest-entry-version) ; string
+ (output manifest-entry-output ; string
+ (default "out"))
+ (path manifest-entry-path) ; store path
+ (dependencies manifest-entry-dependencies ; list of store paths
+ (default '()))
+ (inputs manifest-entry-inputs ; list of inputs to build
+ (default '()))) ; this entry
+
(define (profile-manifest profile)
"Return the PROFILE's manifest."
- (let ((manifest (string-append profile "/manifest")))
- (if (file-exists? manifest)
- (call-with-input-file manifest read)
- '(manifest (version 1) (packages ())))))
+ (let ((file (string-append profile "/manifest")))
+ (if (file-exists? file)
+ (call-with-input-file file read-manifest)
+ (manifest '()))))
+
+(define (manifest->sexp manifest)
+ "Return a representation of MANIFEST as an sexp."
+ (define (entry->sexp entry)
+ (match entry
+ (($ <manifest-entry> name version path output (deps ...))
+ (list name version path output deps))))
-(define (manifest-packages manifest)
- "Return the packages listed in MANIFEST."
(match manifest
+ (($ <manifest> (entries ...))
+ `(manifest (version 1)
+ (packages ,(map entry->sexp entries))))))
+
+(define (sexp->manifest sexp)
+ "Parse SEXP as a manifest."
+ (match sexp
(('manifest ('version 0)
('packages ((name version output path) ...)))
- (zip name version output path
- (make-list (length name) '())))
+ (manifest
+ (map (lambda (name version output path)
+ (manifest-entry
+ (name name)
+ (version version)
+ (output output)
+ (path path)))
+ name version output path)))
;; Version 1 adds a list of propagated inputs to the
;; name/version/output/path tuples.
(('manifest ('version 1)
- ('packages (packages ...)))
- packages)
+ ('packages ((name version output path deps) ...)))
+ (manifest
+ (map (lambda (name version output path deps)
+ (manifest-entry
+ (name name)
+ (version version)
+ (output output)
+ (path path)
+ (dependencies deps)))
+ name version output path deps)))
(_
(error "unsupported manifest format" manifest))))
+(define (read-manifest port)
+ "Return the packages listed in MANIFEST."
+ (sexp->manifest (read port)))
+
+(define (write-manifest manifest port)
+ "Write MANIFEST to PORT."
+ (write (manifest->sexp manifest) port))
+
+(define (remove-manifest-entry name lst)
+ "Remove the manifest entry named NAME from LST."
+ (remove (match-lambda
+ (($ <manifest-entry> entry-name)
+ (string=? name entry-name)))
+ lst))
+
+(define (manifest-remove manifest names)
+ "Remove entries for each of NAMES from MANIFEST."
+ (make-manifest (fold remove-manifest-entry
+ (manifest-entries manifest)
+ names)))
+
+(define (manifest-installed? manifest name)
+ "Return #t if MANIFEST has an entry for NAME, #f otherwise."
+ (define (->bool x)
+ (not (not x)))
+
+ (->bool (find (match-lambda
+ (($ <manifest-entry> entry-name)
+ (string=? entry-name name)))
+ (manifest-entries manifest))))
+
+(define (manifest=? m1 m2)
+ "Return #t if manifests M1 and M2 are equal. This differs from 'equal?' in
+that the 'inputs' field is ignored for the comparison, since it is know to
+have no effect on the manifest contents."
+ (equal? (manifest->sexp m1)
+ (manifest->sexp m2)))
+
+
+;;;
+;;; Profiles.
+;;;
+
(define (profile-regexp profile)
"Return a regular expression that matches PROFILE's name and number."
(make-regexp (string-append "^" (regexp-quote (basename profile))
@@ -157,17 +254,9 @@ case when generations have been deleted (there are \"holes\")."
0
(generation-numbers profile)))
-(define (profile-derivation store packages)
- "Return a derivation that builds a profile (a user environment) with
-all of PACKAGES, a list of name/version/output/path/deps tuples."
- (define packages*
- ;; Turn any package object in PACKAGES into its output path.
- (map (match-lambda
- ((name version output path (deps ...))
- `(,name ,version ,output ,path
- ,(map input->name+path deps))))
- packages))
-
+(define (profile-derivation store manifest)
+ "Return a derivation that builds a profile (aka. 'user environment') with
+the given MANIFEST."
(define builder
`(begin
(use-modules (ice-9 pretty-print)
@@ -178,33 +267,29 @@ all of PACKAGES, a list of name/version/output/path/deps tuples."
(let ((output (assoc-ref %outputs "out"))
(inputs (map cdr %build-inputs)))
- (format #t "building user environment `~a' with ~a packages...~%"
+ (format #t "building profile '~a' with ~a packages...~%"
output (length inputs))
- (union-build output inputs)
+ (union-build output inputs
+ #:log-port (%make-void-port "w"))
(call-with-output-file (string-append output "/manifest")
(lambda (p)
- (pretty-print '(manifest (version 1)
- (packages ,packages*))
- p))))))
-
- (define ensure-valid-input
- ;; If a package object appears in the given input, turn it into a
- ;; derivation path.
- (match-lambda
- ((name (? package? p) sub-drv ...)
- `(,name ,(package-derivation (%store) p) ,@sub-drv))
- (input
- input)))
-
- (build-expression->derivation store "user-environment"
+ (pretty-print ',(manifest->sexp manifest) p))))))
+
+ (build-expression->derivation store "profile"
(%current-system)
builder
(append-map (match-lambda
- ((name version output path deps)
- `((,name ,path)
- ,@(map ensure-valid-input
- deps))))
- packages)
+ (($ <manifest-entry> name version
+ output path deps (inputs ..1))
+ (map (cute lower-input
+ (%store) <>)
+ inputs))
+ (($ <manifest-entry> name version
+ output path deps)
+ ;; Assume PATH and DEPS are
+ ;; already valid.
+ `((,name ,path) ,@deps)))
+ (manifest-entries manifest))
#:modules '((guix build union))))
(define (generation-number profile)
@@ -214,9 +299,13 @@ all of PACKAGES, a list of name/version/output/path/deps tuples."
(compose string->number (cut match:substring <> 1)))
0))
+(define (generation-file-name profile generation)
+ "Return the file name for PROFILE's GENERATION."
+ (format #f "~a-~a-link" profile generation))
+
(define (link-to-empty-profile generation)
"Link GENERATION, a string, to the empty profile."
- (let* ((drv (profile-derivation (%store) '()))
+ (let* ((drv (profile-derivation (%store) (manifest '())))
(prof (derivation->output-path drv "out")))
(when (not (build-derivations (%store) (list drv)))
(leave (_ "failed to build the empty profile~%")))
@@ -227,8 +316,7 @@ all of PACKAGES, a list of name/version/output/path/deps tuples."
"Atomically switch PROFILE to the previous generation."
(let* ((number (generation-number profile))
(previous-number (previous-generation-number profile number))
- (previous-generation (format #f "~a-~a-link"
- profile previous-number)))
+ (previous-generation (generation-file-name profile previous-number)))
(format #t (_ "switching from generation ~a to ~a~%")
number previous-number)
(switch-symlinks profile previous-generation)))
@@ -237,8 +325,7 @@ all of PACKAGES, a list of name/version/output/path/deps tuples."
"Roll back to the previous generation of PROFILE."
(let* ((number (generation-number profile))
(previous-number (previous-generation-number profile number))
- (previous-generation (format #f "~a-~a-link"
- profile previous-number))
+ (previous-generation (generation-file-name profile previous-number))
(manifest (string-append previous-generation "/manifest")))
(cond ((not (file-exists? profile)) ; invalid profile
(leave (_ "profile '~a' does not exist~%")
@@ -256,7 +343,7 @@ all of PACKAGES, a list of name/version/output/path/deps tuples."
(define (generation-time profile number)
"Return the creation time of a generation in the UTC format."
(make-time time-utc 0
- (stat:ctime (stat (format #f "~a-~a-link" profile number)))))
+ (stat:ctime (stat (generation-file-name profile number)))))
(define* (matching-generations str #:optional (profile %current-profile)
#:key (duration-relation <=))
@@ -325,8 +412,8 @@ DURATION-RELATION with the current time."
(else #f)))
(define (find-packages-by-description rx)
- "Search in SYNOPSIS and DESCRIPTION using RX. Return a list of
-matching packages."
+ "Return the list of packages whose name, synopsis, or description matches
+RX."
(define (same-location? p1 p2)
;; Compare locations of two packages.
(equal? (package-location p1) (package-location p2)))
@@ -337,7 +424,8 @@ matching packages."
(define matches?
(cut regexp-exec rx <>))
- (if (or (and=> (package-synopsis package)
+ (if (or (matches? (gettext (package-name package)))
+ (and=> (package-synopsis package)
(compose matches? gettext))
(and=> (package-description package)
(compose matches? gettext)))
@@ -349,6 +437,16 @@ matching packages."
(package-name p2))))
same-location?))
+(define* (lower-input store input #:optional (system (%current-system)))
+ "Lower INPUT so that it contains derivations instead of packages."
+ (match input
+ ((name (? package? package))
+ `(,name ,(package-derivation store package system)))
+ ((name (? package? package) output)
+ `(,name ,(package-derivation store package system)
+ ,output))
+ (_ input)))
+
(define (input->name+path input)
"Convert the name/package/sub-drv tuple INPUT to a name/store-path tuple."
(let loop ((input input))
@@ -402,6 +500,76 @@ return its return value."
(format (current-error-port) " interrupted by signal ~a~%" SIGINT)
#f))))
+
+;;;
+;;; Package specifications.
+;;;
+
+(define newest-available-packages
+ (memoize find-newest-available-packages))
+
+(define (find-best-packages-by-name name version)
+ "If version is #f, return the list of packages named NAME with the highest
+version numbers; otherwise, return the list of packages named NAME and at
+VERSION."
+ (if version
+ (find-packages-by-name name version)
+ (match (vhash-assoc name (newest-available-packages))
+ ((_ version pkgs ...) pkgs)
+ (#f '()))))
+
+(define* (specification->package+output spec #:optional (output "out"))
+ "Find the package and output specified by SPEC, or #f and #f; 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
+
+If SPEC does not specify a version number, return the preferred newest
+version; if SPEC does not specify an output, return OUTPUT."
+ (define (ensure-output p sub-drv)
+ (if (member sub-drv (package-outputs p))
+ sub-drv
+ (leave (_ "package `~a' lacks output `~a'~%")
+ (package-full-name p)
+ sub-drv)))
+
+ (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)))
+ (match (find-best-packages-by-name name version)
+ ((p)
+ (values p (ensure-output p sub-drv)))
+ ((p p* ...)
+ (warning (_ "ambiguous package specification `~a'~%")
+ spec)
+ (warning (_ "choosing ~a from ~a~%")
+ (package-full-name p)
+ (location->string (package-location p)))
+ (values p (ensure-output p sub-drv)))
+ (()
+ (leave (_ "~a: package not found~%") spec)))))
+
+(define (upgradeable? name current-version current-path)
+ "Return #t if there's a version of package NAME newer than CURRENT-VERSION,
+or if the newest available version is equal to CURRENT-VERSION but would have
+an output path different than CURRENT-PATH."
+ (match (vhash-assoc name (newest-available-packages))
+ ((_ candidate-version pkg . rest)
+ (case (version-compare candidate-version current-version)
+ ((>) #t)
+ ((<) #f)
+ ((=) (let ((candidate-path (derivation->output-path
+ (package-derivation (%store) pkg))))
+ (not (string=? current-path candidate-path))))))
+ (#f #f)))
+
(define ftp-open*
;; Memoizing version of `ftp-open'. The goal is to avoid initiating a new
;; FTP connection for each package, esp. since most of them are to the same
@@ -437,26 +605,31 @@ but ~a is available upstream~%")
((getaddrinfo-error ftp-error) #f)
(else (apply throw key args))))))
-(define* (search-path-environment-variables packages profile
+
+;;;
+;;; Search paths.
+;;;
+
+(define* (search-path-environment-variables entries profile
#:optional (getenv getenv))
"Return environment variable definitions that may be needed for the use of
-PACKAGES in PROFILE. Use GETENV to determine the current settings and report
-only settings not already effective."
+ENTRIES, a list of manifest entries, in PROFILE. Use GETENV to determine the
+current settings and report only settings not already effective."
;; Prefer ~/.guix-profile to the real profile directory name.
- (let ((profile (if (and %user-environment-directory
+ (let ((profile (if (and %user-profile-directory
(false-if-exception
- (string=? (readlink %user-environment-directory)
+ (string=? (readlink %user-profile-directory)
profile)))
- %user-environment-directory
+ %user-profile-directory
profile)))
;; The search path info is not stored in the manifest. Thus, we infer the
;; search paths from same-named packages found in the distro.
- (define package-in-manifest->package
+ (define manifest-entry->package
(match-lambda
- ((name version _ ...)
+ (($ <manifest-entry> name version)
(match (append (find-packages-by-name name version)
(find-packages-by-name name))
((p _ ...) p)
@@ -478,16 +651,16 @@ only settings not already effective."
variable
(string-join directories separator)))))))
- (let* ((packages (filter-map package-in-manifest->package packages))
+ (let* ((packages (filter-map manifest-entry->package entries))
(search-paths (delete-duplicates
(append-map package-native-search-paths
packages))))
(filter-map search-path-definition search-paths))))
-(define (display-search-paths packages profile)
+(define (display-search-paths entries profile)
"Display the search path environment variables that may need to be set for
-PACKAGES, in the context of PROFILE."
- (let ((settings (search-path-environment-variables packages profile)))
+ENTRIES, a list of manifest entries, in the context of PROFILE."
+ (let ((settings (search-path-environment-variables entries profile)))
(unless (null? settings)
(format #t (_ "The following environment variable definitions may be needed:~%"))
(format #t "~{ ~a~%~}" settings))))
@@ -633,6 +806,110 @@ Install, remove, or upgrade PACKAGES in a single transaction.\n"))
(cons `(query list-available ,(or arg ""))
result)))))
+(define (options->installable opts manifest)
+ "Given MANIFEST, the current manifest, and OPTS, the result of 'args-fold',
+return the new list of manifest entries."
+ (define (deduplicate deps)
+ ;; Remove duplicate entries from DEPS, a list of propagated inputs, where
+ ;; each input is a name/path tuple.
+ (define (same? d1 d2)
+ (match d1
+ ((_ p1)
+ (match d2
+ ((_ p2) (eq? p1 p2))
+ (_ #f)))
+ ((_ p1 out1)
+ (match d2
+ ((_ p2 out2)
+ (and (string=? out1 out2)
+ (eq? p1 p2)))
+ (_ #f)))))
+
+ (delete-duplicates deps same?))
+
+ (define (package->manifest-entry p output)
+ ;; Return a manifest entry for the OUTPUT of package P.
+ (check-package-freshness p)
+ ;; When given a package via `-e', install the first of its
+ ;; outputs (XXX).
+ (let* ((output (or output (car (package-outputs p))))
+ (path (package-output (%store) p output))
+ (deps (deduplicate (package-transitive-propagated-inputs p))))
+ (manifest-entry
+ (name (package-name p))
+ (version (package-version p))
+ (output output)
+ (path path)
+ (dependencies (map input->name+path deps))
+ (inputs (cons (list (package-name p) p output)
+ deps)))))
+
+ (define upgrade-regexps
+ (filter-map (match-lambda
+ (('upgrade . regexp)
+ (make-regexp (or regexp "")))
+ (_ #f))
+ opts))
+
+ (define packages-to-upgrade
+ (match upgrade-regexps
+ (()
+ '())
+ ((_ ...)
+ (let ((newest (find-newest-available-packages)))
+ (filter-map (match-lambda
+ (($ <manifest-entry> name version output path _)
+ (and (any (cut regexp-exec <> name)
+ upgrade-regexps)
+ (upgradeable? name version path)
+ (let ((output (or output "out")))
+ (call-with-values
+ (lambda ()
+ (specification->package+output name output))
+ list))))
+ (_ #f))
+ (manifest-entries manifest))))))
+
+ (define to-upgrade
+ (map (match-lambda
+ ((package output)
+ (package->manifest-entry package output)))
+ packages-to-upgrade))
+
+ (define packages-to-install
+ (filter-map (match-lambda
+ (('install . (? package? p))
+ (list p "out"))
+ (('install . (? string? spec))
+ (and (not (store-path? spec))
+ (let-values (((package output)
+ (specification->package+output spec)))
+ (and package (list package output)))))
+ (_ #f))
+ opts))
+
+ (define to-install
+ (append (map (match-lambda
+ ((package output)
+ (package->manifest-entry package output)))
+ packages-to-install)
+ (filter-map (match-lambda
+ (('install . (? package?))
+ #f)
+ (('install . (? store-path? path))
+ (let-values (((name version)
+ (package-name->name+version
+ (store-path-package-name path))))
+ (manifest-entry
+ (name name)
+ (version version)
+ (output #f)
+ (path path))))
+ (_ #f))
+ opts)))
+
+ (append to-upgrade to-install))
+
;;;
;;; Entry point.
@@ -653,67 +930,6 @@ Install, remove, or upgrade PACKAGES in a single transaction.\n"))
(let ((out (derivation->output-path (%guile-for-build))))
(not (valid-path? (%store) out))))
- (define newest-available-packages
- (memoize find-newest-available-packages))
-
- (define (find-best-packages-by-name name version)
- (if version
- (find-packages-by-name name version)
- (match (vhash-assoc name (newest-available-packages))
- ((_ version pkgs ...) pkgs)
- (#f '()))))
-
- (define* (find-package name #:optional (output "out"))
- ;; Find the package NAME; NAME may contain a version number and a
- ;; sub-derivation name. If the version number is not present,
- ;; return the preferred newest version. If the sub-derivation name is not
- ;; present, use OUTPUT.
- (define request name)
-
- (define (ensure-output p sub-drv)
- (if (member sub-drv (package-outputs p))
- p
- (leave (_ "package `~a' lacks output `~a'~%")
- (package-full-name p)
- sub-drv)))
-
- (let*-values (((name sub-drv)
- (match (string-rindex name #\:)
- (#f (values name output))
- (colon (values (substring name 0 colon)
- (substring name (+ 1 colon))))))
- ((name version)
- (package-name->name+version name)))
- (match (find-best-packages-by-name name version)
- ((p)
- (list name (package-version p) sub-drv (ensure-output p sub-drv)
- (package-transitive-propagated-inputs p)))
- ((p p* ...)
- (warning (_ "ambiguous package specification `~a'~%")
- request)
- (warning (_ "choosing ~a from ~a~%")
- (package-full-name p)
- (location->string (package-location p)))
- (list name (package-version p) sub-drv (ensure-output p sub-drv)
- (package-transitive-propagated-inputs p)))
- (()
- (leave (_ "~a: package not found~%") request)))))
-
- (define (upgradeable? name current-version current-path)
- ;; Return #t if there's a version of package NAME newer than
- ;; CURRENT-VERSION, or if the newest available version is equal to
- ;; CURRENT-VERSION but would have an output path different than
- ;; CURRENT-PATH.
- (match (vhash-assoc name (newest-available-packages))
- ((_ candidate-version pkg . rest)
- (case (version-compare candidate-version current-version)
- ((>) #t)
- ((<) #f)
- ((=) (let ((candidate-path (derivation->output-path
- (package-derivation (%store) pkg))))
- (not (string=? current-path candidate-path))))))
- (#f #f)))
-
(define (ensure-default-profile)
;; Ensure the default profile symlink and directory exist and are
;; writable.
@@ -725,11 +941,11 @@ more information.~%"))
(exit 1))
;; Create ~/.guix-profile if it doesn't exist yet.
- (when (and %user-environment-directory
+ (when (and %user-profile-directory
%current-profile
(not (false-if-exception
- (lstat %user-environment-directory))))
- (symlink %current-profile %user-environment-directory))
+ (lstat %user-profile-directory))))
+ (symlink %current-profile %user-profile-directory))
(let ((s (stat %profile-directory #f)))
;; Attempt to create /…/profiles/per-user/$USER if needed.
@@ -767,48 +983,17 @@ more information.~%"))
(define verbose? (assoc-ref opts 'verbose?))
(define profile (assoc-ref opts 'profile))
- (define (canonicalize-deps deps)
- ;; Remove duplicate entries from DEPS, a list of propagated inputs,
- ;; where each input is a name/path tuple.
- (define (same? d1 d2)
- (match d1
- ((_ p1)
- (match d2
- ((_ p2) (eq? p1 p2))
- (_ #f)))
- ((_ p1 out1)
- (match d2
- ((_ p2 out2)
- (and (string=? out1 out2)
- (eq? p1 p2)))
- (_ #f)))))
-
- (delete-duplicates deps same?))
-
- (define (same-package? tuple name out)
- (match tuple
- ((tuple-name _ tuple-output _ ...)
- (and (equal? name tuple-name)
- (equal? out tuple-output)))))
-
- (define (package->tuple p)
- ;; Convert package P to a tuple.
- ;; When given a package via `-e', install the first of its
- ;; outputs (XXX).
- (let* ((out (car (package-outputs p)))
- (path (package-output (%store) p out))
- (deps (package-transitive-propagated-inputs p)))
- `(,(package-name p)
- ,(package-version p)
- ,out
- ,p
- ,(canonicalize-deps deps))))
+ (define (same-package? entry name output)
+ (match entry
+ (($ <manifest-entry> entry-name _ entry-output _ ...)
+ (and (equal? name entry-name)
+ (equal? output entry-output)))))
(define (show-what-to-remove/install remove install dry-run?)
;; Tell the user what's going to happen in high-level terms.
;; TODO: Report upgrades more clearly.
(match remove
- (((name version _ path _) ..1)
+ ((($ <manifest-entry> name version _ path _) ..1)
(let ((len (length name))
(remove (map (cut format #f " ~a-~a\t~a" <> <> <>)
name version path)))
@@ -825,7 +1010,7 @@ more information.~%"))
remove))))
(_ #f))
(match install
- (((name version output path _) ..1)
+ ((($ <manifest-entry> name version output path _) ..1)
(let ((len (length name))
(install (map (cut format #f " ~a-~a\t~a\t~a" <> <> <> <>)
name version output path)))
@@ -846,15 +1031,15 @@ more information.~%"))
(generation-number profile))
(define (display-and-delete number)
- (let ((generation (format #f "~a-~a-link" profile number)))
+ (let ((generation (generation-file-name profile number)))
(unless (zero? number)
(format #t (_ "deleting ~a~%") generation)
(delete-file generation))))
(define (delete-generation number)
(let* ((previous-number (previous-generation-number profile number))
- (previous-generation (format #f "~a-~a-link"
- profile previous-number)))
+ (previous-generation
+ (generation-file-name profile previous-number)))
(cond ((zero? number)) ; do not delete generation 0
((and (= number current-generation-number)
(not (file-exists? previous-generation)))
@@ -909,126 +1094,59 @@ more information.~%"))
(_ #f))
opts))
(else
- (let* ((installed (manifest-packages (profile-manifest profile)))
- (upgrade-regexps (filter-map (match-lambda
- (('upgrade . regexp)
- (make-regexp (or regexp "")))
- (_ #f))
- opts))
- (upgrade (if (null? upgrade-regexps)
- '()
- (let ((newest (find-newest-available-packages)))
- (filter-map
- (match-lambda
- ((name version output path _)
- (and (any (cut regexp-exec <> name)
- upgrade-regexps)
- (upgradeable? name version path)
- (find-package name
- (or output "out"))))
- (_ #f))
- installed))))
- (install (append
- upgrade
- (filter-map (match-lambda
- (('install . (? package? p))
- (package->tuple p))
- (('install . (? store-path?))
- #f)
- (('install . package)
- (find-package package))
+ (let* ((manifest (profile-manifest profile))
+ (install* (options->installable opts manifest))
+ (remove (filter-map (match-lambda
+ (('remove . package)
+ package)
(_ #f))
- opts)))
- (drv (filter-map (match-lambda
- ((name version sub-drv
- (? package? package)
- (deps ...))
- (check-package-freshness package)
- (package-derivation (%store) package))
- (_ #f))
- install))
- (install*
- (append
- (filter-map (match-lambda
- (('install . (? package? p))
- #f)
- (('install . (? store-path? path))
- (let-values (((name version)
- (package-name->name+version
- (store-path-package-name
- path))))
- `(,name ,version #f ,path ())))
- (_ #f))
- opts)
- (map (lambda (tuple drv)
- (match tuple
- ((name version sub-drv _ (deps ...))
- (let ((output-path
- (derivation->output-path
- drv sub-drv)))
- `(,name ,version ,sub-drv ,output-path
- ,(canonicalize-deps deps))))))
- install drv)))
- (remove (filter-map (match-lambda
- (('remove . package)
- package)
- (_ #f))
- opts))
- (remove* (filter-map (cut assoc <> installed) remove))
- (packages
+ opts))
+ (remove* (filter (cut manifest-installed? manifest <>)
+ remove))
+ (entries
(append install*
(fold (lambda (package result)
(match package
- ((name _ out _ ...)
- (filter (negate
- (cut same-package? <>
- name out))
- result))))
- (fold alist-delete installed remove)
- install*))))
-
- (when (equal? profile %current-profile)
- (ensure-default-profile))
-
- (show-what-to-remove/install remove* install* dry-run?)
- (show-what-to-build (%store) drv
- #:use-substitutes? (assoc-ref opts 'substitutes?)
- #:dry-run? dry-run?)
-
- (or dry-run?
- (and (build-derivations (%store) drv)
- (let* ((prof-drv (profile-derivation (%store) packages))
- (prof (derivation->output-path prof-drv))
- (old-drv (profile-derivation
- (%store) (manifest-packages
- (profile-manifest profile))))
- (old-prof (derivation->output-path old-drv))
- (number (generation-number profile))
-
- ;; Always use NUMBER + 1 for the new profile,
- ;; possibly overwriting a "previous future
- ;; generation".
- (name (format #f "~a-~a-link"
- profile (+ 1 number))))
- (if (string=? old-prof prof)
- (when (or (pair? install) (pair? remove))
- (format (current-error-port)
- (_ "nothing to be done~%")))
- (and (parameterize ((current-build-output-port
- ;; Output something when Guile
- ;; needs to be built.
- (if (or verbose? (guile-missing?))
- (current-error-port)
- (%make-void-port "w"))))
- (build-derivations (%store) (list prof-drv)))
- (let ((count (length packages)))
+ (($ <manifest-entry> name _ out _ ...)
+ (filter (negate
+ (cut same-package? <>
+ name out))
+ result))))
+ (manifest-entries
+ (manifest-remove manifest remove))
+ install*)))
+ (new (make-manifest entries)))
+
+ (when (equal? profile %current-profile)
+ (ensure-default-profile))
+
+ (if (manifest=? new manifest)
+ (format (current-error-port) (_ "nothing to be done~%"))
+ (let ((prof-drv (profile-derivation (%store) new)))
+ (show-what-to-remove/install remove* install* dry-run?)
+ (show-what-to-build (%store) (list prof-drv)
+ #:use-substitutes?
+ (assoc-ref opts 'substitutes?)
+ #:dry-run? dry-run?)
+
+ (or dry-run?
+ (let* ((prof (derivation->output-path prof-drv))
+ (number (generation-number profile))
+
+ ;; Always use NUMBER + 1 for the new profile,
+ ;; possibly overwriting a "previous future
+ ;; generation".
+ (name (generation-file-name profile
+ (+ 1 number))))
+ (and (build-derivations (%store) (list prof-drv))
+ (let ((count (length entries)))
(switch-symlinks name prof)
(switch-symlinks profile name)
(format #t (N_ "~a package in profile~%"
"~a packages in profile~%"
count)
count)
- (display-search-paths packages
+ (display-search-paths entries
profile)))))))))))
(define (process-query opts)
@@ -1049,15 +1167,15 @@ more information.~%"))
(format #t (_ "~a\t(current)~%") header)
(format #t "~a~%" header)))
(for-each (match-lambda
- ((name version output location _)
+ (($ <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-packages
+ (manifest-entries
(profile-manifest
- (format #f "~a-~a-link" profile number)))))
+ (generation-file-name profile number)))))
(newline)))
(cond ((not (file-exists? profile)) ; XXX: race condition
@@ -1082,9 +1200,9 @@ more information.~%"))
(('list-installed regexp)
(let* ((regexp (and regexp (make-regexp regexp)))
(manifest (profile-manifest profile))
- (installed (manifest-packages manifest)))
+ (installed (manifest-entries manifest)))
(for-each (match-lambda
- ((name version output path _)
+ (($ <manifest-entry> name version output path _)
(when (or (not regexp)
(regexp-exec regexp name))
(format #t "~a\t~a\t~a\t~a~%"
@@ -1125,9 +1243,9 @@ more information.~%"))
(('search-paths)
(let* ((manifest (profile-manifest profile))
- (packages (manifest-packages manifest))
- (settings (search-path-environment-variables packages
- profile
+ (entries (manifest-entries manifest))
+ (packages (map manifest-entry-name entries))
+ (settings (search-path-environment-variables entries profile
(const #f))))
(format #t "~{~a~%~}" settings)
#t))
@@ -1139,6 +1257,7 @@ more information.~%"))
(with-error-handling
(parameterize ((%store (open-connection)))
(set-build-options (%store)
+ #:print-build-trace #f
#:fallback? (assoc-ref opts 'fallback?)
#:use-substitutes?
(assoc-ref opts 'substitutes?)