diff options
Diffstat (limited to 'guix/scripts/package.scm')
-rw-r--r-- | guix/scripts/package.scm | 695 |
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?) |