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.
This commit is contained in:
Matthew Flatt 2012-11-30 15:49:06 -07:00
parent 7712d1a15b
commit 59f289249f
6 changed files with 138 additions and 54 deletions

View File

@ -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?)))))]))

View File

@ -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

View File

@ -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?}

View File

@ -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"

View File

@ -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)))))

View File

@ -46,12 +46,12 @@
'source
"http://localhost:9999/pkg-a-first.plt"))
$ "raco pkg install --deps search-auto pkg-b" =exit> 0 <input= "y\n"
$ "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
;; 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)))