guix package: Better separate option processing.

* guix/scripts/package.scm (find-package): Rename to...
  (specification->package+output): ... this.  Rename 'name' parmameter
  to 'spec'.  Return a package and output name instead of a tuple.
  (options->installable): New procedure
  (guix-package)[process-actions]: Use it, and remove corresponding
  code.
This commit is contained in:
Ludovic Courtès 2013-10-30 15:26:14 +01:00
parent f506ed920c
commit edac884624
1 changed files with 185 additions and 164 deletions

View File

@ -421,41 +421,43 @@ VERSION."
((_ version pkgs ...) pkgs) ((_ version pkgs ...) pkgs)
(#f '())))) (#f '()))))
(define* (find-package name #:optional (output "out")) (define* (specification->package+output spec #:optional (output "out"))
"Find the package NAME; NAME may contain a version number and a "Find the package and output specified by SPEC, or #f and #f; SPEC may
sub-derivation name. If the version number is not present, return the optionally contain a version number and an output name, as in these examples:
preferred newest version. If the sub-derivation name is not present, use
OUTPUT."
(define request name)
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) (define (ensure-output p sub-drv)
(if (member sub-drv (package-outputs p)) (if (member sub-drv (package-outputs p))
p sub-drv
(leave (_ "package `~a' lacks output `~a'~%") (leave (_ "package `~a' lacks output `~a'~%")
(package-full-name p) (package-full-name p)
sub-drv))) sub-drv)))
(let*-values (((name sub-drv) (let*-values (((name sub-drv)
(match (string-rindex name #\:) (match (string-rindex spec #\:)
(#f (values name output)) (#f (values spec output))
(colon (values (substring name 0 colon) (colon (values (substring spec 0 colon)
(substring name (+ 1 colon)))))) (substring spec (+ 1 colon))))))
((name version) ((name version)
(package-name->name+version name))) (package-name->name+version name)))
(match (find-best-packages-by-name name version) (match (find-best-packages-by-name name version)
((p) ((p)
(list name (package-version p) sub-drv (ensure-output p sub-drv) (values p (ensure-output p sub-drv)))
(package-transitive-propagated-inputs p)))
((p p* ...) ((p p* ...)
(warning (_ "ambiguous package specification `~a'~%") (warning (_ "ambiguous package specification `~a'~%")
request) spec)
(warning (_ "choosing ~a from ~a~%") (warning (_ "choosing ~a from ~a~%")
(package-full-name p) (package-full-name p)
(location->string (package-location p))) (location->string (package-location p)))
(list name (package-version p) sub-drv (ensure-output p sub-drv) (values p (ensure-output p sub-drv)))
(package-transitive-propagated-inputs p)))
(() (()
(leave (_ "~a: package not found~%") request))))) (leave (_ "~a: package not found~%") spec)))))
(define (upgradeable? name current-version current-path) (define (upgradeable? name current-version current-path)
"Return #t if there's a version of package NAME newer than CURRENT-VERSION, "Return #t if there's a version of package NAME newer than CURRENT-VERSION,
@ -707,6 +709,112 @@ Install, remove, or upgrade PACKAGES in a single transaction.\n"))
(cons `(query list-available ,(or arg "")) (cons `(query list-available ,(or arg ""))
result))))) result)))))
(define (options->installable opts installed)
"Given INSTALLED, the set of currently installed packages, and OPTS, the
result of 'args-fold', return two values: the new list of manifest entries,
and the list of derivations that need to be built."
(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* (package->tuple p #:optional output)
;; Convert package P to a manifest tuple.
;; When given a package via `-e', install the first of its
;; outputs (XXX).
(check-package-freshness p)
(let* ((output (or output (car (package-outputs p))))
(path (package-output (%store) p output))
(deps (package-transitive-propagated-inputs p)))
`(,(package-name p)
,(package-version p)
,output
,path
,(canonicalize-deps 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
((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))
installed)))))
(define to-upgrade
(map (match-lambda
((package output)
(package->tuple 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->tuple 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))))
`(,name ,version #f ,path ())))
(_ #f))
opts)))
(define derivations
(map (match-lambda
((package output)
;; FIXME: We should really depend on just OUTPUT rather than on all
;; the outputs of PACKAGE.
(package-derivation (%store) package)))
(append packages-to-install packages-to-upgrade)))
(values (append to-upgrade to-install) derivations))
;;; ;;;
;;; Entry point. ;;; Entry point.
@ -780,43 +888,12 @@ more information.~%"))
(define verbose? (assoc-ref opts 'verbose?)) (define verbose? (assoc-ref opts 'verbose?))
(define profile (assoc-ref opts 'profile)) (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) (define (same-package? tuple name out)
(match tuple (match tuple
((tuple-name _ tuple-output _ ...) ((tuple-name _ tuple-output _ ...)
(and (equal? name tuple-name) (and (equal? name tuple-name)
(equal? out tuple-output))))) (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 (show-what-to-remove/install remove install dry-run?) (define (show-what-to-remove/install remove install dry-run?)
;; Tell the user what's going to happen in high-level terms. ;; Tell the user what's going to happen in high-level terms.
;; TODO: Report upgrades more clearly. ;; TODO: Report upgrades more clearly.
@ -922,127 +999,71 @@ more information.~%"))
(_ #f)) (_ #f))
opts)) opts))
(else (else
(let* ((installed (manifest-packages (profile-manifest profile))) (let*-values (((installed)
(upgrade-regexps (filter-map (match-lambda (manifest-packages (profile-manifest profile)))
(('upgrade . regexp) ((install* drv)
(make-regexp (or regexp ""))) (options->installable opts installed)))
(_ #f)) (let* ((remove (filter-map (match-lambda
opts)) (('remove . package)
(upgrade (if (null? upgrade-regexps) package)
'()
(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))
(_ #f)) (_ #f))
opts))) opts))
(drv (filter-map (match-lambda (remove* (filter-map (cut assoc <> installed) remove))
((name version sub-drv (packages
(? package? package) (append install*
(deps ...)) (fold (lambda (package result)
(check-package-freshness package) (match package
(package-derivation (%store) package)) ((name _ out _ ...)
(_ #f)) (filter (negate
install)) (cut same-package? <>
(install* name out))
(append result))))
(filter-map (match-lambda (fold alist-delete installed remove)
(('install . (? package? p)) install*))))
#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
(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) (when (equal? profile %current-profile)
(ensure-default-profile)) (ensure-default-profile))
(show-what-to-remove/install remove* install* dry-run?) (show-what-to-remove/install remove* install* dry-run?)
(show-what-to-build (%store) drv (show-what-to-build (%store) drv
#:use-substitutes? (assoc-ref opts 'substitutes?) #:use-substitutes? (assoc-ref opts 'substitutes?)
#:dry-run? dry-run?) #:dry-run? dry-run?)
(or dry-run? (or dry-run?
(and (build-derivations (%store) drv) (and (build-derivations (%store) drv)
(let* ((prof-drv (profile-derivation (%store) packages)) (let* ((prof-drv (profile-derivation (%store) packages))
(prof (derivation->output-path prof-drv)) (prof (derivation->output-path prof-drv))
(old-drv (profile-derivation (old-drv (profile-derivation
(%store) (manifest-packages (%store) (manifest-packages
(profile-manifest profile)))) (profile-manifest profile))))
(old-prof (derivation->output-path old-drv)) (old-prof (derivation->output-path old-drv))
(number (generation-number profile)) (number (generation-number profile))
;; Always use NUMBER + 1 for the new profile, ;; Always use NUMBER + 1 for the new profile,
;; possibly overwriting a "previous future ;; possibly overwriting a "previous future
;; generation". ;; generation".
(name (format #f "~a-~a-link" (name (format #f "~a-~a-link"
profile (+ 1 number)))) profile (+ 1 number))))
(if (string=? old-prof prof) (if (string=? old-prof prof)
(when (or (pair? install) (pair? remove)) (when (or (pair? install*) (pair? remove))
(format (current-error-port) (format (current-error-port)
(_ "nothing to be done~%"))) (_ "nothing to be done~%")))
(and (parameterize ((current-build-output-port (and (parameterize ((current-build-output-port
;; Output something when Guile ;; Output something when Guile
;; needs to be built. ;; needs to be built.
(if (or verbose? (guile-missing?)) (if (or verbose? (guile-missing?))
(current-error-port) (current-error-port)
(%make-void-port "w")))) (%make-void-port "w"))))
(build-derivations (%store) (list prof-drv))) (build-derivations (%store) (list prof-drv)))
(let ((count (length packages))) (let ((count (length packages)))
(switch-symlinks name prof) (switch-symlinks name prof)
(switch-symlinks profile name) (switch-symlinks profile name)
(format #t (N_ "~a package in profile~%" (format #t (N_ "~a package in profile~%"
"~a packages in profile~%" "~a packages in profile~%"
count) count)
count) count)
(display-search-paths packages (display-search-paths packages
profile))))))))))) profile))))))))))))
(define (process-query opts) (define (process-query opts)
;; Process any query specified by OPTS. Return #t when a query was ;; Process any query specified by OPTS. Return #t when a query was