From 59f289249f1b72a7eb7a51a68282563e4d1d79b2 Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Fri, 30 Nov 2012 15:49:06 -0700 Subject: [PATCH] raco pkg: user-specific and version-specific by default The default `raco pkg' mode should work right for a multiple-version installation (because everything in Racket should work in a multiple-version installation). Along the same lines, `raco pkg' should work if the installation directory is unwriteable. So, the default mode is user-specific and version-specific. Use `--shared' or `-s' for user-specific, all-version installs. By default, `raco pkg show' now shows packages installed in all three modes (installation-wide, user- and version- specific, and user-specific all-version). Use `-i', `-u', or `-s' to show just one of them. --- collects/planet2/lib.rkt | 71 +++++++++++++++----- collects/planet2/main.rkt | 41 ++++++++--- collects/planet2/scribblings/planet2.scrbl | 54 ++++++++++----- collects/tests/planet2/tests-conflicts.rkt | 4 ++ collects/tests/planet2/tests-remove.rkt | 14 ++-- collects/tests/planet2/tests-update-auto.rkt | 8 +-- 6 files changed, 138 insertions(+), 54 deletions(-) diff --git a/collects/planet2/lib.rkt b/collects/planet2/lib.rkt index 6c267f3a3c..e30bd4797c 100644 --- a/collects/planet2/lib.rkt +++ b/collects/planet2/lib.rkt @@ -28,6 +28,8 @@ (define current-install-system-wide? (make-parameter #f)) +(define current-install-version-specific? + (make-parameter #t)) (struct pkg-desc (source type name auto?)) @@ -79,9 +81,12 @@ (λ (ip) (copy-port ip op))))))) (define (pkg-dir) - (build-path (if (current-install-system-wide?) - (find-lib-dir) - (find-system-path 'addon-dir)) + (build-path (cond + [(current-install-system-wide?) (find-lib-dir)] + [(current-install-version-specific?) + (build-path (find-system-path 'addon-dir) (version))] + [else + (find-system-path 'addon-dir)]) "pkgs")) (define (pkg-config-file) (build-path (pkg-dir) "config.rktd")) @@ -92,8 +97,11 @@ (define (pkg-lock-file) (make-lock-file-name (pkg-db-file))) -(for-each make-directory* - (list (pkg-dir) (pkg-installed-dir))) +(define (link-version-regexp) + (cond + [(current-install-system-wide?) #f] + [(current-install-version-specific?) (regexp (regexp-quote (version)))] + [else #f])) (define (make-metadata-namespace) (make-base-empty-namespace)) @@ -249,11 +257,13 @@ (links pkg-dir #:remove? #t #:user? (not (current-install-system-wide?)) + #:version-regexp (link-version-regexp) #:root? #t)] [_ (links pkg-dir #:remove? #t #:user? (not (current-install-system-wide?)) + #:version-regexp (link-version-regexp) #:root? #t) (delete-directory/files pkg-dir)])) @@ -551,6 +561,27 @@ [else (error 'pkg "cannot infer package source type\n given: ~e" pkg)])) (define db (read-pkg-db)) + (define db+with-dbs + (let ([with-sys-wide (lambda (t) + (parameterize ([current-install-system-wide? #t]) + (t)))] + [with-vers-spec (lambda (t) + (parameterize ([current-install-version-specific? #t]) + (t)))] + [with-vers-all (lambda (t) + (parameterize ([current-install-version-specific? #f]) + (t)))] + [with-current (lambda (t) (t))]) + (cond + [(current-install-system-wide?) (list (cons db with-current))] + [(current-install-version-specific?) + (list (cons (with-sys-wide read-pkg-db) with-sys-wide) + (cons db with-current) + (cons (with-vers-all read-pkg-db) with-vers-all))] + [else + (list (cons (with-sys-wide read-pkg-db) with-sys-wide) + (cons (with-vers-spec read-pkg-db) with-vers-spec) + db)]))) (define (install-package/outer infos desc info) (match-define (pkg-desc pkg type orig-name auto?) desc) (match-define @@ -581,12 +612,16 @@ (file-exists? (build-path other-d f))))) (or ;; Compare with main installation's collections + ;; FIXME: this should check all collection paths that aren't + ;; from the package system. (and (file-exists? (build-path (find-collects-dir) c f)) (cons "racket" (build-path c f))) ;; Compare with installed packages - (for/or ([other-pkg (in-hash-keys db)] - #:unless (and updating? (equal? other-pkg pkg-name))) - (and (has-collection-file? (package-directory other-pkg)) + (for*/or ([db+with-db (in-list db+with-dbs)] + [other-pkg (in-hash-keys (car db+with-db))] + #:unless (and updating? (equal? other-pkg pkg-name))) + (and ((cdr db+with-db) + (lambda () (has-collection-file? (package-directory other-pkg)))) (cons other-pkg (build-path c f)))) ;; Compare with simultaneous installs (for/or ([other-pkg-info (in-list infos)] @@ -669,6 +704,7 @@ (dprintf "creating link to ~e" final-pkg-dir) (links final-pkg-dir #:user? (not (current-install-system-wide?)) + #:version-regexp (link-version-regexp) #:root? #t) (define this-pkg-info (pkg-info orig-pkg checksum auto?)) @@ -796,16 +832,17 @@ #:dep-behavior dep-behavior to-update)])) -(define (show-cmd) +(define (show-cmd indent) (let () (define db (read-pkg-db)) (define pkgs (sort (hash-keys db) string-ci<=?)) (table-display (list* - (list "Package(auto?)" "Checksum" "Source") + (list (format "~aPackage(auto?)" indent) "Checksum" "Source") (for/list ([pkg (in-list pkgs)]) (match-define (pkg-info orig-pkg checksum auto?) (hash-ref db pkg)) - (list (format "~a~a" + (list (format "~a~a~a" + indent pkg (if auto? "*" @@ -905,6 +942,8 @@ (contract-out [current-install-system-wide? (parameter/c boolean?)] + [current-install-version-specific? + (parameter/c boolean?)] [pkg-desc (-> string? (or/c #f 'file 'dir 'link 'file-url 'dir-url 'github 'name) @@ -913,26 +952,26 @@ pkg-desc?)] [config-cmd (-> boolean? list? - void)] + void?)] [create-cmd (-> string? path-string? - void)] + void?)] [update-packages (->* ((listof string?)) (#:dep-behavior dep-behavior/c #:all? boolean? #:deps? boolean?) - boolean?)] + (or/c #f (listof (or/c path-string? (non-empty-listof path-string?)))))] [remove-packages (->* ((listof string?)) (#:auto? boolean? #:force? boolean?) void)] [show-cmd - (-> void)] + (-> string? void)] [install-cmd (->* ((listof pkg-desc?)) (#:dep-behavior dep-behavior/c #:force? boolean? #:ignore-checksums? boolean?) - void)])) + (or/c #f (listof (or/c path-string? (non-empty-listof path-string?)))))])) diff --git a/collects/planet2/main.rkt b/collects/planet2/main.rkt index 4ac0664140..a67ca9a8c1 100644 --- a/collects/planet2/main.rkt +++ b/collects/planet2/main.rkt @@ -6,7 +6,7 @@ (define (setup no-setup? installation? setup-collects) (unless (or no-setup? - (getenv "PLT_PLANET2_NOSETUP")) + (not (member (getenv "PLT_PLANET2_NOSETUP") '(#f "")))) (setup:setup #:make-user? (not installation?) #:collections (and setup-collects @@ -28,6 +28,7 @@ [#:bool no-setup () ("Don't run 'raco setup' after changing packages" "(generally not a good idea)")] [#:bool installation ("-i") "Operate on the installation-wide package database"] + [#:bool shared ("-s") "Install user-specific packages as shared for all versions"] [(#:sym #f) deps () ("Specify the behavior for dependencies;" "options are:" @@ -44,7 +45,8 @@ "this is a global setting for all installs for this command, which means" "that it affects dependencies... so make sure the dependencies exist first")] #:args pkg-source - (parameterize ([current-install-system-wide? installation]) + (parameterize ([current-install-system-wide? installation] + [current-install-version-specific? (not shared)]) (with-package-lock (define setup-collects (install-cmd #:dep-behavior deps @@ -58,6 +60,7 @@ [#:bool no-setup () ("Don't run 'raco setup' after changing packages" "(generally not a good idea)")] [#:bool installation ("-i") "Operate on the installation-wide package database"] + [#:bool shared ("-s") "Operate on the user-specific all-version package database"] [#:bool all ("-a") ("Update all packages;" "only if no packages are given on the command line")] [(#:sym #f) deps () @@ -72,7 +75,8 @@ " search-auto: like 'search-ask' but does not ask for permission to install")] [#:bool update-deps () "Check named packages' dependencies for updates"] #:args pkgs - (parameterize ([current-install-system-wide? installation]) + (parameterize ([current-install-system-wide? installation] + [current-install-version-specific? (not shared)]) (with-package-lock (define setup-collects (update-packages pkgs @@ -86,10 +90,12 @@ [#:bool no-setup () ("Don't run 'raco setup' after changing packages" "(generally not a good idea)")] [#:bool installation ("-i") "Operate on the installation-wide package database"] + [#:bool shared ("-s") "Operate on the user-specific all-version package database"] [#:bool force () "Force removal of packages"] [#:bool auto () "Remove automatically installed packages with no dependencies"] #:args pkgs - (parameterize ([current-install-system-wide? installation]) + (parameterize ([current-install-system-wide? installation] + [current-install-version-specific? (not shared)]) (with-package-lock (remove-packages pkgs #:auto? auto @@ -97,17 +103,34 @@ (setup no-setup installation #f)))] [show "Show information about installed packages" - [#:bool installation ("-i") "Operate on the installation-wide package database"] + [#:bool installation ("-i") "Show only the installation-wide package database"] + [#:bool shared ("-s") "Show only the user-specific all-version package database"] + [#:bool user ("-u") "Show only the user- and version-specific package database"] #:args () - (parameterize ([current-install-system-wide? installation]) - (with-package-lock - (show-cmd)))] + (define only-mode (cond + [installation 'i] + [shared 's] + [user 'u] + [else #f])) + (for ([mode '(i s u)]) + (when (or (eq? mode only-mode) (not only-mode)) + (unless only-mode + (printf "~a\n" (case mode + [(i) "Installation-wide:"] + [(s) "User-specific, all-version:"] + [(u) "User-spcific, version-specific:"]))) + (parameterize ([current-install-system-wide? (eq? mode 'i)] + [current-install-version-specific? (eq? mode 'u)]) + (with-package-lock + (show-cmd (if only-mode "" " "))))))] [config "View and modify the package configuration" [#:bool installation ("-i") "Operate on the installation-wide package database"] + [#:bool shared ("-s") "Operate on the user-specific all-version package database"] [#:bool set () "Completely replace the value"] #:args key+vals - (parameterize ([current-install-system-wide? installation]) + (parameterize ([current-install-system-wide? installation] + [current-install-version-specific? (not shared)]) (with-package-lock (config-cmd set key+vals)))] [create diff --git a/collects/planet2/scribblings/planet2.scrbl b/collects/planet2/scribblings/planet2.scrbl index 06c9c0a098..5e3e23eae7 100644 --- a/collects/planet2/scribblings/planet2.scrbl +++ b/collects/planet2/scribblings/planet2.scrbl @@ -200,9 +200,10 @@ sub-sub-commands: inferred for each @nonterm{pkg-source}.} @item{@DFlag{no-setup} --- Does not run @exec{raco setup} after installation. This behavior is also the case if the - environment variable @envvar{PLT_PLANET2_NOSETUP} is set (to anything).} + environment variable @envvar{PLT_PLANET2_NOSETUP} is set to any non-empty value.} - @item{@DFlag{installation} or @Flag{i} --- Install system-wide rather than user-local.} + @item{@DFlag{installation} or @Flag{i} --- Install system-wide, rather than user-local.} + @item{@DFlag{shared} or @Flag{s} --- Install for all versions, rather than user-local and version-specific.} @item{@DFlag{deps} @nonterm{behavior} --- Selects the behavior for dependencies, where @nonterm{behavior} is one of @itemlist[ @@ -235,6 +236,7 @@ the following @nonterm{option}s: @itemlist[ @item{@DFlag{no-setup} --- Same as for @exec{install}.} @item{@DFlag{installation} or @Flag{i} --- Same as for @exec{install}.} + @item{@DFlag{shared} or @Flag{s} --- Same as for @exec{install}.} @item{@DFlag{deps} @nonterm{behavior} --- Same as for @exec{install}.} @item{@DFlag{all} or @Flag{a} --- Update all packages, if no packages are given in the argument list.} @item{@DFlag{update-deps} --- Checks the named packages, and their dependencies (transitively) for updates.} @@ -248,15 +250,21 @@ listed, this command fails atomically. It accepts the following @nonterm{option} @itemlist[ @item{@DFlag{no-setup} --- Same as for @exec{install}.} @item{@DFlag{installation} or @Flag{i} --- Same as for @exec{install}.} + @item{@DFlag{shared} or @Flag{s} --- Same as for @exec{install}.} @item{@DFlag{force} --- Ignore dependencies when removing packages.} @item{@DFlag{auto} --- Remove packages that were installed by the @exec{search-auto} and @exec{search-ask} dependency behavior that are no longer required.} ] } -@item{@exec{show} @nonterm{option} ... --- Print information about currently installed packages. It accepts the following @nonterm{option}s: +@item{@exec{show} @nonterm{option} ... --- Print information about currently installed packages. + By default, packages are shown for all installation modes (installation-wide, + user- and version-specific, and user-specific all-version). + The command accepts the following @nonterm{option}s: @itemlist[ - @item{@DFlag{installation} or @Flag{i} --- Same as for @exec{install}.} + @item{@DFlag{installation} or @Flag{i} --- Show only installation-wide packages.} + @item{@DFlag{user} or @Flag{u} --- Show only user-specific, version-specific packages.} + @item{@DFlag{shared} or @Flag{s} --- Show only user-specific, all-version packages.} ] } @@ -265,6 +273,7 @@ View and modify package configuration options. It accepts the following @nonterm @itemlist[ @item{@DFlag{installation} or @Flag{i} --- Same as for @exec{install}.} + @item{@DFlag{shared} or @Flag{s} --- Same as for @exec{install}.} @item{@DFlag{set} --- Sets an option, rather than printing it.} ] @@ -547,31 +556,40 @@ the package manager. @subsection{Are package installations versioned with respect to the Racket version?} -No. When you install a package, it is installed for all -versions of Racket until you remove it. (In contrast, @|Planet1| +By default, when you install a package, it is installed for a specific +user and a specific version of Racket. + +You can use the @DFlag{installation} or @Flag{i} flag to install for +all users of a particular Racket installation; an installation-wide +package is not exactly version-specific, because the version of an +installation can change if it corresponds to a source-code checkout +that is periodically updated and rebuilt. + +Finally, you can use the @DFlag{shared} or @Flag{s} flag +with @exec{raco pkg} commands to install user-specific packages that +apply to all Racket versions that you run. (In contrast, @|Planet1| requires reinstallation of all packages every version change.) @subsection{Where and how are packages installed?} -User-local packages are in @racket[(build-path (find-system-path -'addon-dir) "pkgs")] and installation-wide packages are in +User-local and version-specific packages are in @racket[(build-path +(find-system-path 'addon-dir) (version) "pkgs")], user-local and +all-version packages are in @racket[(build-path (find-system-path +'addon-dir) "pkgs")], and installation-wide packages are in @racket[(build-path (find-lib-dir) "pkgs")]. They are linked as collection roots with @exec{raco link}. @subsection{How are user-local and installation-wide packages related?} -They are totally distinct: packages are not compared with one another -for conflicts. +User-local packages are checked against installation-wide packages +for conflicts. Installation-wide packages are checked only against +other installation-wide packages. -This is because it would be in-feasible to check them reliably. For -example, if a system package is being installed by user A, then how -could the system know that user B exists so B's packages could be -checked for conflicts? - -We anticipate that most users will only one kind of package. The -majority of users will employ user-local packages but classes or other -shared workspaces might exclusively employ installation-wide packages. +Beware that a new installation-wide package can invalidate previous +conflict checks for user-specific packages. Similarly, new +user-specific but all-version packages can invalidate previous +user-specific conflict checks for a different Racket version. @subsection{If packages have no version numbers, how can I update packages with error fixes, etc?} diff --git a/collects/tests/planet2/tests-conflicts.rkt b/collects/tests/planet2/tests-conflicts.rkt index cedc5bdbfa..bf60a7953e 100644 --- a/collects/tests/planet2/tests-conflicts.rkt +++ b/collects/tests/planet2/tests-conflicts.rkt @@ -38,6 +38,10 @@ $ "test -f test-pkgs/planet2-test1-conflict.zip" $ "raco pkg install test-pkgs/planet2-test1-conflict.zip" =exit> 1) + (shelly-install "conflicts are caught across sharing modes" "test-pkgs/planet2-test1.zip" + $ "test -f test-pkgs/planet2-test1-conflict.zip" + $ "raco pkg install -s test-pkgs/planet2-test1-conflict.zip" =exit> 1) + (shelly-wind $ "cp -r test-pkgs/planet2-test1 test-pkgs/planet2-test1-linking" (shelly-install* "conflicts are caught, even with a link" diff --git a/collects/tests/planet2/tests-remove.rkt b/collects/tests/planet2/tests-remove.rkt index ae35f0ad03..a8721480c3 100644 --- a/collects/tests/planet2/tests-remove.rkt +++ b/collects/tests/planet2/tests-remove.rkt @@ -20,18 +20,18 @@ (shelly-case "remove and show" (shelly-case "remove of not installed package fails" - $ "raco pkg show" =stdout> "Package(auto?) Checksum Source\n" + $ "raco pkg show -u" =stdout> "Package(auto?) Checksum Source\n" $ "raco pkg remove not-there" =exit> 1) (shelly-install "remove test" "test-pkgs/planet2-test1.zip") (shelly-install "remove of dep fails" "test-pkgs/planet2-test1.zip" - $ "raco pkg show" =stdout> #rx"Package\\(auto\\?\\) +Checksum +Source\nplanet2-test1 +[a-f0-9]+ +\\(file .+tests/planet2/test-pkgs/planet2-test1.zip\\)\n" + $ "raco pkg show -u" =stdout> #rx"Package\\(auto\\?\\) +Checksum +Source\nplanet2-test1 +[a-f0-9]+ +\\(file .+tests/planet2/test-pkgs/planet2-test1.zip\\)\n" $ "raco pkg install test-pkgs/planet2-test2.zip" - $ "raco pkg show" =stdout> #rx"Package\\(auto\\?\\) +Checksum +Source\nplanet2-test1 +[a-f0-9]+ +\\(file .+tests/planet2/test-pkgs/planet2-test1.zip\\)\nplanet2-test2 +[a-f0-9]+ +\\(file .+tests/planet2/test-pkgs/planet2-test2.zip\\)\n" + $ "raco pkg show -u" =stdout> #rx"Package\\(auto\\?\\) +Checksum +Source\nplanet2-test1 +[a-f0-9]+ +\\(file .+tests/planet2/test-pkgs/planet2-test1.zip\\)\nplanet2-test2 +[a-f0-9]+ +\\(file .+tests/planet2/test-pkgs/planet2-test2.zip\\)\n" $ "raco pkg remove planet2-test1" =exit> 1 $ "raco pkg remove planet2-test2" - $ "raco pkg show" =stdout> #rx"Package\\(auto\\?\\) +Checksum +Source\nplanet2-test1 +[a-f0-9]+ +\\(file .+tests/planet2/test-pkgs/planet2-test1.zip\\)\n") + $ "raco pkg show -u" =stdout> #rx"Package\\(auto\\?\\) +Checksum +Source\nplanet2-test1 +[a-f0-9]+ +\\(file .+tests/planet2/test-pkgs/planet2-test1.zip\\)\n") (shelly-install "remove of dep can be forced" "test-pkgs/planet2-test1.zip" $ "raco pkg install test-pkgs/planet2-test2.zip" @@ -59,14 +59,14 @@ $ "racket -e '(require planet2-test1)'" =exit> 1 $ "racket -e '(require planet2-test2)'" =exit> 1 $ "raco pkg install --deps search-auto test-pkgs/planet2-test2.zip" =exit> 0 - $ "raco pkg show" =stdout> #rx"Package\\(auto\\?\\) +Checksum +Source\nplanet2-test1\\* +[a-f0-9]+ +\\(pns planet2-test1\\)\nplanet2-test2 +[a-f0-9]+ +\\(file .+tests/planet2/test-pkgs/planet2-test2.zip\\)\n" + $ "raco pkg show -u" =stdout> #rx"Package\\(auto\\?\\) +Checksum +Source\nplanet2-test1\\* +[a-f0-9]+ +\\(pns planet2-test1\\)\nplanet2-test2 +[a-f0-9]+ +\\(file .+tests/planet2/test-pkgs/planet2-test2.zip\\)\n" $ "racket -e '(require planet2-test1)'" =exit> 0 $ "racket -e '(require planet2-test2)'" =exit> 0 $ "racket -e '(require planet2-test2/contains-dep)'" =exit> 0 $ "raco pkg remove planet2-test2" - $ "raco pkg show" =stdout> #rx"Package\\(auto\\?\\) +Checksum +Source\nplanet2-test1\\* +[a-f0-9]+ +\\(pns planet2-test1\\)\n" + $ "raco pkg show -u" =stdout> #rx"Package\\(auto\\?\\) +Checksum +Source\nplanet2-test1\\* +[a-f0-9]+ +\\(pns planet2-test1\\)\n" $ "racket -e '(require planet2-test1)'" =exit> 0 $ "raco pkg remove --auto" - $ "raco pkg show" =stdout> "Package(auto?) Checksum Source\n" + $ "raco pkg show -u" =stdout> "Package(auto?) Checksum Source\n" $ "racket -e '(require planet2-test1)'" =exit> 1 $ "racket -e '(require planet2-test2)'" =exit> 1))))) diff --git a/collects/tests/planet2/tests-update-auto.rkt b/collects/tests/planet2/tests-update-auto.rkt index 988b68956e..a52a59b19b 100644 --- a/collects/tests/planet2/tests-update-auto.rkt +++ b/collects/tests/planet2/tests-update-auto.rkt @@ -46,12 +46,12 @@ 'source "http://localhost:9999/pkg-a-first.plt")) $ "raco pkg install --deps search-auto pkg-b" =exit> 0 #rx"Package\\(auto\\?\\) Checksum Source\npkg-a\\* [a-f0-9]+ \\(pns pkg-a\\)\npkg-b [a-f0-9]+ \\(pns pkg-b\\)\n" + $ "raco pkg show -u" =stdout> #rx"Package\\(auto\\?\\) Checksum Source\npkg-a\\* [a-f0-9]+ \\(pns pkg-a\\)\npkg-b [a-f0-9]+ \\(pns pkg-b\\)\n" $ "racket -e '(require pkg-b)'" =exit> 43 $ "racket -e '(require pkg-a)'" =exit> 0 ;; remove auto doesn't do anything because everything is needed $ "raco pkg remove --auto" - $ "raco pkg show" =stdout> #rx"Package\\(auto\\?\\) Checksum Source\npkg-a\\* [a-f0-9]+ \\(pns pkg-a\\)\npkg-b [a-f0-9]+ \\(pns pkg-b\\)\n" + $ "raco pkg show -u" =stdout> #rx"Package\\(auto\\?\\) Checksum Source\npkg-a\\* [a-f0-9]+ \\(pns pkg-a\\)\npkg-b [a-f0-9]+ \\(pns pkg-b\\)\n" $ "racket -e '(require pkg-b)'" =exit> 43 $ "racket -e '(require pkg-a)'" =exit> 0 ;; pkg-a is now an auto @@ -63,9 +63,9 @@ $ "raco pkg update -a" =exit> 0 $ "racket -e '(require pkg-a)'" =exit> 43 $ "raco pkg remove pkg-b" - $ "raco pkg show" =stdout> #rx"Package\\(auto\\?\\) Checksum Source\npkg-a\\* [a-f0-9]+ \\(pns pkg-a\\)\n" + $ "raco pkg show -u" =stdout> #rx"Package\\(auto\\?\\) Checksum Source\npkg-a\\* [a-f0-9]+ \\(pns pkg-a\\)\n" $ "racket -e '(require pkg-b)'" =exit> 1 ;; pkg-a is now not needed $ "raco pkg remove --auto" - $ "raco pkg show" =stdout> "Package(auto?) Checksum Source\n" + $ "raco pkg show -u" =stdout> "Package(auto?) Checksum Source\n" $ "racket -e '(require pkg-a)'" =exit> 1)))