raco pkg update: allow packages sources as replacements

When an argument to `raco pkg update` is a package source,
use it to place the currently installed package.

Also, make the set of available command-line arguments more
consistent, especially for `raco pkg install` and `raco pkg update`.

Finally, fix the `--update-deps` flag, including checking
the dependencies of each updated packages based on then update,
instead of the pre-updated package.
This commit is contained in:
Matthew Flatt 2013-08-16 21:14:31 -06:00
parent 6baf90e3be
commit 72a4191aa9
10 changed files with 760 additions and 446 deletions

View File

@ -86,7 +86,7 @@ package:
The ``Checksum'' column reports the specific ``version'' of each
package that is installed. A package can have a @tech{version} in a
more traditional sense, but the @tech{checksum} is the ``version'' as
far as the package system is concerned. When you request an upgrade,
far as the package system is concerned. When you request an update,
then a package installation is updated if the current implementation
of the package has a different @tech{checksum} than the installed
package, whether or not the package author adjusted the package's
@ -194,7 +194,7 @@ actual package implementation, so each package installed from a
@tech{package catalog} is actually installed from a @filepath{.zip}
file, Github repository, etc. Registering with a @tech{package
catalog} is just a way of making your package easier to find and
upgrade.
update.
@; ----------------------------------------
@ -226,6 +226,14 @@ those dependencies. The end result is that @command-ref{update} might
report a version-mismatch error that forces you to request more
package updates than you originally requested.
Normally, you provide @tech{package names} to
@command-ref{update}. More generally, you can provide a @tech{package
source} to @command-ref{update}. In that case, a package with the same
name must be installed already, and the installed package is replaced
with the specified one. Replacing a package with a new @tech{package
source} is a generalization of fetching a replacement package that has
a new @tech{checksum} at a previously specified source.
@; ----------------------------------------
@section[#:tag "how-to-create"]{Creating Packages}

View File

@ -195,14 +195,17 @@ port, unless @racket[quiet?] is true.
The package lock must be held; see @racket[with-pkg-lock].}
@defproc[(pkg-update [names (listof string?)]
@defproc[(pkg-update [names (listof (or/c string? pkg-desc?))]
[#:dep-behavior dep-behavior
(or/c #f 'fail 'force 'search-ask 'search-auto)
#f]
[#:all? all? boolean? #f]
[#:deps? deps? boolean? #f]
[#:force? force? boolean? #f]
[#:ignore-checksums? ignore-checksums? boolean? #f]
[#:quiet? boolean? quiet? #f]
[#:strip strip (or/c #f 'source 'binary) #f])
[#:strip strip (or/c #f 'source 'binary) #f]
[#:link-dirs? link-dirs? boolean? #f])
(or/c 'skip
#f
(listof (or/c path-string?
@ -211,6 +214,10 @@ The package lock must be held; see @racket[with-pkg-lock].}
Implements @racket[pkg-update-command]. The result is the same as for
@racket[pkg-install].
A string in @racket[names] refers to an installed package that should
be checked for updates. A @racket[pkg-desc] in @racket[names] indicates
a package source that should replace the current installation.
The package lock must be held; see @racket[with-pkg-lock].}

View File

@ -291,10 +291,6 @@ sub-commands.
@item{@DFlag{auto} --- Shorthand for @exec{@DFlag{deps} search-auto}.}
@item{@DFlag{force} --- Ignores conflicts (unsafe).}
@item{@DFlag{ignore-checksums} --- Ignores errors verifying package @tech{checksums} (unsafe).}
@item{@DFlag{link} --- Implies @exec{--type dir} (and overrides any specified type),
and links the existing directory as an installed package, instead of copying the
directory's content to install. Directory @tech{package sources} are treated as links
@ -317,10 +313,6 @@ sub-commands.
@item{@DFlag{source} --- Strips built elements of a package before installing, and implies @DFlag{copy}.}
@item{@DFlag{skip-installed} --- Ignore any @nonterm{pkg-source}
whose name corresponds to an already-installed package, except for promoting auto-installed
packages to explicitly installed.}
@item{@DFlag{scope} @nonterm{scope} --- Selects the @tech{package scope} for installation, where @nonterm{scope} is one of
@itemlist[
@item{@exec{installation} --- Install packages for all users of a Racket installation, rather than user-specific.}
@ -337,6 +329,14 @@ sub-commands.
@item{@DFlag{catalog} @nonterm{catalog} --- Use @nonterm{catalog} instead of of the currently configured
@tech{package catalogs}.}
@item{@DFlag{skip-installed} --- Ignore any @nonterm{pkg-source}
whose name corresponds to an already-installed package, except for promoting auto-installed
packages to explicitly installed.}
@item{@DFlag{force} --- Ignores conflicts (unsafe).}
@item{@DFlag{ignore-checksums} --- Ignores errors verifying package @tech{checksums} (unsafe).}
@item{@DFlag{no-setup} --- Does not run @exec{raco setup} after installation. This behavior is also the case if the
environment variable @envvar{PLT_PKG_NOSETUP} is set to any non-empty value.}
@ -345,31 +345,46 @@ sub-commands.
@subcommand{@command/toc{update} @nonterm{option} ... @nonterm{pkg} ...
--- Checks the specified packages for
@tech{package updates}. If an update is found, but it cannot be
installed (e.g. it conflicts with another installed package), then
this command fails without installing any of the @nonterm{pkg}s
(or their dependencies).
@subcommand{@command/toc{update} @nonterm{option} ... @nonterm{pkg-source} ...
--- Checks the specified package names for @tech{package updates} or
replaces existing package installations with the given sources. If an
update or replacement cannot be installed (e.g. it conflicts with
another installed package), then this command fails without installing
any of the @nonterm{pkg-source}s (or their dependencies).
If a @tech{package scope} is not specified, the scope is inferred from
the given @nonterm{pkg}s.
the given @nonterm{pkg-source}s.
The @exec{update} sub-command accepts
the following @nonterm{option}s:
@itemlist[
@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 plus their dependencies (transitively) for updates.}
@item{@DFlag{update-deps} --- Checks dependencies (transitively) for updates.}
@item{@DFlag{lookup} --- Checks Causes a @tech{package name} as a @nonterm{pkg-source} to be used
as a replacement, instead of the name of a installed package that may have updates.
(If the named package was installed through a package name, then there's effectively
no difference.)}
@item{@DFlag{type} @nonterm{type} or @Flag{t} @nonterm{type} --- Same as for @command-ref{install}.}
@item{@DFlag{name} @nonterm{pkg} or @Flag{n} @nonterm{pkg} --- Same as for @command-ref{install}.}
@item{@DFlag{deps} @nonterm{behavior} --- Same as for @command-ref{install}.}
@item{@DFlag{auto} --- Shorthand for @exec{@DFlag{deps} search-auto} plus @DFlag{update-deps}.}
@item{@DFlag{link} --- Same as for @command-ref{install}.}
@item{@DFlag{static-link} --- Same as for @command-ref{install}.}
@item{@DFlag{binary} --- Same as for @command-ref{install}.}
@item{@DFlag{copy} --- Same as for @command-ref{install}.}
@item{@DFlag{source} --- Same as for @command-ref{install}.}
@item{@DFlag{scope} @nonterm{scope} --- Selects a @tech{package scope}, the same as for @command-ref{install}.}
@item{@Flag{i} or @DFlag{installation} --- Shorthand for @exec{--scope installation}.}
@item{@Flag{u} or @DFlag{user} --- Shorthand for @exec{--scope user}.}
@item{@DFlag{scope-dir} @nonterm{dir} --- Selects @nonterm{dir} as the @tech{package scope}, the same as for @command-ref{install}.}
@item{@DFlag{catalog} @nonterm{catalog} --- Same as for @command-ref{install}.}
@item{@DFlag{force} --- Same as for @command-ref{install}.}
@item{@DFlag{ignore-checksums} --- Same as for @command-ref{install}.}
@item{@DFlag{no-setup} --- Same as for @command-ref{install}.}
@item{@DFlag{binary} --- Same as for @command-ref{install}.}
@item{@DFlag{source} --- Same as for @command-ref{install}.}
@item{@DFlag{jobs} @nonterm{n} or @Flag{j} @nonterm{n} --- Same as for @command-ref{install}.}
]
}
@ -390,7 +405,8 @@ the given @nonterm{pkg}s.
(leaving auto-installed packages as such). Combined with @DFlag{auto}, removes
packages for which there are no dependencies.}
@item{@DFlag{force} --- Ignore dependencies when removing packages.}
@item{@DFlag{auto} --- Remove auto-installed packages (i.e., installed by the @exec{search-auto} or @exec{search-ask}
@item{@DFlag{auto} --- In addition to removing each @nonterm{pkg},
remove auto-installed packages (i.e., installed by the @exec{search-auto} or @exec{search-ask}
dependency behavior, or demoted via @DFlag{demote}) that are no longer required by any
explicitly installed package.}
@item{@DFlag{scope} @nonterm{scope} --- Selects a @tech{package scope}, the same as for @command-ref{install}.}
@ -440,8 +456,6 @@ the given @nonterm{pkg}s.
@item{@DFlag{deps} @nonterm{behavior} --- Same as for @command-ref{install}, except that @exec{search-auto} is
the default.}
@item{@DFlag{force} --- Same as for @command-ref{install}.}
@item{@DFlag{ignore-checksums} --- Same as for @command-ref{install}.}
@item{@DFlag{binary} --- Same as for @command-ref{install}.}
@item{@DFlag{source} --- Same as for @command-ref{install}.}
@item{@DFlag{scope} @nonterm{scope} --- Same as for @command-ref{install}.}
@ -449,6 +463,8 @@ the given @nonterm{pkg}s.
@item{@Flag{u} or @DFlag{user} --- Shorthand for @exec{--scope user}.}
@item{@DFlag{scope-dir} @nonterm{dir} --- Select @nonterm{dir} as the @tech{package scope}.}
@item{@DFlag{catalog} @nonterm{catalog} --- Same as for @command-ref{install}.}
@item{@DFlag{force} --- Same as for @command-ref{install}.}
@item{@DFlag{ignore-checksums} --- Same as for @command-ref{install}.}
@item{@DFlag{no-setup} --- Same as for @command-ref{install}.}
@item{@DFlag{jobs} @nonterm{n} or @Flag{j} @nonterm{n} --- Same as for @command-ref{install}.}
]
@ -497,9 +513,10 @@ View and modify configuration of the package manager itself, with the following
as the default value at @exec{user} scope.}
@item{@Flag{i} or @DFlag{installation} --- Shorthand for @exec{--scope installation}.}
@item{@Flag{u} or @DFlag{user} --- Shorthand for @exec{--scope user}.}
@item{@DFlag{scope-dir} @nonterm{dir} --- Same as for @command-ref{install}.}
]
The valid keys are:
The valid @nonterm{key}s are:
@itemlist[
@item{@exec{catalogs} --- A list of URLs for @tech{package catalogs}.}
@item{@exec{default-scope} --- Either @exec{installation} or @exec{user}.
@ -530,7 +547,8 @@ View and modify configuration of the package manager itself, with the following
@item{@DFlag{modules} --- Show the modules that are implemented by a package.}
@item{@DFlag{catalog} @nonterm{catalog} --- Query @nonterm{catalog} instead of the currently configured
@tech{package catalogs}.}
@item{@DFlag{version} @nonterm{version} --- Query catalogs for a result specific to @nonterm{version},
@item{@DFlag{version} @nonterm{version} or @Flag{v} @nonterm{version} --- Query catalogs
for a result specific to @nonterm{version},
instead of the installation's Racket version.}
]
}
@ -559,7 +577,8 @@ View and modify configuration of the package manager itself, with the following
over new information.}
@item{@DFlag{override} --- Changes merging so that new information takes precedence
over information already in @nonterm{dest-catalog}.}
@item{@DFlag{version} @nonterm{version} --- Copy catalog results specific to @nonterm{version}
@item{@DFlag{version} @nonterm{version} or @Flag{v} @nonterm{version} --- Copy catalog
results specific to @nonterm{version}
(for catalogs that make a distinction), instead of the installation's Racket version.}
]
}

View File

@ -1,4 +1,4 @@
#lang racket/base
(printf "pkg-test3/main loaded\n")
(printf "pkg-test3/main version 3 loaded\n")
(exit 0)

View File

@ -44,6 +44,7 @@
(shelly-create "racket-conflict" "tgz")
(shelly-create "pkg-test3" "zip")
(shelly-create "pkg-test3-v2" "zip")
(shelly-create "pkg-test3-v3" "zip")
$ "raco pkg create --format txt test-pkgs/pkg-test1" =exit> 1

View File

@ -25,6 +25,31 @@
(shelly-install "local packages can't be updated (directory)"
"test-pkgs/pkg-test1/"
$ "raco pkg update pkg-test1" =exit> 1)
(shelly-wind
$ "mkdir -p test-pkgs/update-test"
$ "cp -f test-pkgs/pkg-test1-v2.zip test-pkgs/update-test/pkg-test1.zip"
(shelly-install* "packages can be replaced with local packages (file)"
"test-pkgs/pkg-test1.zip"
"pkg-test1"
$ "racket -e '(require pkg-test1/update)'" =exit> 42
$ "raco pkg update test-pkgs/update-test/pkg-test1.zip"
$ "racket -e '(require pkg-test1/update)'" =exit> 43)
(finally
$ "rm -f test-pkgs/update-test/pkg-test1.zip"))
(shelly-install "packages can be replaced with local packages (file + name)"
"test-pkgs/pkg-test1.zip"
$ "racket -e '(require pkg-test1/update)'" =exit> 42
$ "raco pkg update --name pkg-test1 test-pkgs/pkg-test1-v2.zip"
$ "racket -e '(require pkg-test1/update)'" =exit> 43)
(shelly-install "packages can be replaced with local packages (directory)"
"test-pkgs/pkg-test1.zip"
$ "racket -e '(require pkg-test1/update)'" =exit> 42
$ "raco pkg update --name pkg-test1 test-pkgs/pkg-test1-v2"
$ "racket -e '(require pkg-test1/update)'" =exit> 43)
(shelly-install "replacement checksum can be checked"
"test-pkgs/pkg-test1.zip"
$ "raco pkg update test-pkgs/pkg-test1.zip" =stdout> "No updates available\n")
(shelly-wind
$ "mkdir -p test-pkgs/update-test"
$ "cp -f test-pkgs/pkg-test1.zip test-pkgs/update-test/pkg-test1.zip"
@ -32,7 +57,7 @@
(shelly-install* "remote packages can be updated"
"http://localhost:9999/update-test/pkg-test1.zip"
"pkg-test1"
$ "raco pkg update pkg-test1" =exit> 0 =stdout> "Downloading checksum\nNo updates available\n"
$ "raco pkg update pkg-test1" =exit> 0 =stdout> "Downloading checksum for pkg-test1\nNo updates available\n"
$ "racket -e '(require pkg-test1/update)'" =exit> 42
$ "cp -f test-pkgs/pkg-test1-v2.zip test-pkgs/update-test/pkg-test1.zip"
$ "cp -f test-pkgs/pkg-test1-v2.zip.CHECKSUM test-pkgs/update-test/pkg-test1.zip.CHECKSUM"
@ -49,7 +74,7 @@
(shelly-install* "remote packages can be updated, single-collection to multi-collection"
"test-pkgs/pkg-test1.zip http://localhost:9999/update-test/pkg-test3.zip"
"pkg-test1 pkg-test3"
$ "raco pkg update pkg-test3" =exit> 0 =stdout> "Downloading checksum\nNo updates available\n"
$ "raco pkg update pkg-test3" =exit> 0 =stdout> "Downloading checksum for pkg-test3\nNo updates available\n"
$ "cp -f test-pkgs/pkg-test3-v2.zip test-pkgs/update-test/pkg-test3.zip"
$ "cp -f test-pkgs/pkg-test3-v2.zip.CHECKSUM test-pkgs/update-test/pkg-test3.zip.CHECKSUM"
$ "raco pkg update pkg-test3" =exit> 0
@ -65,7 +90,7 @@
(shelly-install* "remote packages can be updated, multi-colelction to single-collection"
"test-pkgs/pkg-test1.zip http://localhost:9999/update-test/pkg-test3.zip"
"pkg-test1 pkg-test3"
$ "raco pkg update pkg-test3" =exit> 0 =stdout> "Downloading checksum\nNo updates available\n"
$ "raco pkg update pkg-test3" =exit> 0 =stdout> "Downloading checksum for pkg-test3\nNo updates available\n"
$ "cp -f test-pkgs/pkg-test3.zip test-pkgs/update-test/pkg-test3.zip"
$ "cp -f test-pkgs/pkg-test3.zip.CHECKSUM test-pkgs/update-test/pkg-test3.zip.CHECKSUM"
$ "raco pkg update pkg-test3" =exit> 0
@ -78,11 +103,14 @@
$ "mkdir -p test-pkgs/update-test"
$ "cp -f test-pkgs/pkg-test1.zip test-pkgs/update-test/pkg-test1.zip"
$ "cp -f test-pkgs/pkg-test1.zip.CHECKSUM test-pkgs/update-test/pkg-test1.zip.CHECKSUM"
$ "cp -f test-pkgs/pkg-test2.zip test-pkgs/update-test/pkg-test2.zip"
$ "cp -f test-pkgs/pkg-test2.zip.CHECKSUM test-pkgs/update-test/pkg-test2.zip.CHECKSUM"
(shelly-install* "update deps"
"http://localhost:9999/update-test/pkg-test1.zip"
"pkg-test1"
$ "raco pkg install test-pkgs/pkg-test2.zip"
$ "raco pkg update --update-deps pkg-test2" =exit> 0 =stdout> "Downloading checksum\nNo updates available\n"
$ "raco pkg install http://localhost:9999/update-test/pkg-test2.zip"
$ "raco pkg update --update-deps pkg-test2" =exit> 0
=stdout> "Downloading checksum for pkg-test2\nDownloading checksum for pkg-test1\nNo updates available\n"
$ "racket -e '(require pkg-test1/update)'" =exit> 42
$ "cp -f test-pkgs/pkg-test1-v2.zip test-pkgs/update-test/pkg-test1.zip"
$ "cp -f test-pkgs/pkg-test1-v2.zip.CHECKSUM test-pkgs/update-test/pkg-test1.zip.CHECKSUM"
@ -97,11 +125,87 @@
$ "mkdir -p test-pkgs/update-test"
$ "cp -f test-pkgs/pkg-test1.zip test-pkgs/update-test/pkg-test1.zip"
$ "cp -f test-pkgs/pkg-test1.zip.CHECKSUM test-pkgs/update-test/pkg-test1.zip.CHECKSUM"
(shelly-install* "update all is default"
$ "cp -f test-pkgs/pkg-test3.zip test-pkgs/update-test/pkg-test3.zip"
$ "cp -f test-pkgs/pkg-test3.zip.CHECKSUM test-pkgs/update-test/pkg-test3.zip.CHECKSUM"
(shelly-install* "update original and deps"
"http://localhost:9999/update-test/pkg-test1.zip"
"pkg-test1"
$ "raco pkg install http://localhost:9999/update-test/pkg-test3.zip"
$ "raco pkg update --update-deps pkg-test3" =exit> 0
=stdout> "Downloading checksum for pkg-test3\nDownloading checksum for pkg-test1\nNo updates available\n"
$ "racket -e '(require pkg-test1/update)'" =exit> 42
$ "cp -f test-pkgs/pkg-test1-v2.zip test-pkgs/update-test/pkg-test1.zip"
$ "cp -f test-pkgs/pkg-test1-v2.zip.CHECKSUM test-pkgs/update-test/pkg-test1.zip.CHECKSUM"
$ "cp -f test-pkgs/pkg-test3-v2.zip test-pkgs/update-test/pkg-test3.zip"
$ "cp -f test-pkgs/pkg-test3-v2.zip.CHECKSUM test-pkgs/update-test/pkg-test3.zip.CHECKSUM"
$ "raco pkg update --update-deps pkg-test3" =exit> 0
$ "racket -e '(require pkg-test1/update)'" =exit> 43
$ "racket -e '(require pkg-test3)'" =stdout> #rx"version 2 loaded"
$ "raco pkg remove pkg-test3")
(finally
$ "rm -f test-pkgs/update-test/pkg-test1.zip"
$ "rm -f test-pkgs/update-test/pkg-test1.zip.CHECKSUM"))
(shelly-wind
$ "mkdir -p test-pkgs/update-test"
$ "cp -f test-pkgs/pkg-test1.zip test-pkgs/update-test/pkg-test1.zip"
$ "cp -f test-pkgs/pkg-test1.zip.CHECKSUM test-pkgs/update-test/pkg-test1.zip.CHECKSUM"
$ "cp -f test-pkgs/pkg-test3.zip test-pkgs/update-test/pkg-test3.zip"
$ "cp -f test-pkgs/pkg-test3.zip.CHECKSUM test-pkgs/update-test/pkg-test3.zip.CHECKSUM"
(shelly-install* "update original, where update has no deps"
"http://localhost:9999/update-test/pkg-test1.zip"
"pkg-test1"
$ "raco pkg install http://localhost:9999/update-test/pkg-test3.zip"
$ "raco pkg update --update-deps pkg-test3" =exit> 0
=stdout> "Downloading checksum for pkg-test3\nDownloading checksum for pkg-test1\nNo updates available\n"
$ "racket -e '(require pkg-test1/update)'" =exit> 42
$ "cp -f test-pkgs/pkg-test1-v2.zip test-pkgs/update-test/pkg-test1.zip"
$ "cp -f test-pkgs/pkg-test1-v2.zip.CHECKSUM test-pkgs/update-test/pkg-test1.zip.CHECKSUM"
$ "cp -f test-pkgs/pkg-test3-v3.zip test-pkgs/update-test/pkg-test3.zip"
$ "cp -f test-pkgs/pkg-test3-v3.zip.CHECKSUM test-pkgs/update-test/pkg-test3.zip.CHECKSUM"
$ "raco pkg update --update-deps pkg-test3" =exit> 0
$ "racket -e '(require pkg-test1/update)'" =exit> 42
$ "racket -e '(require pkg-test3)'" =stdout> #rx"version 3 loaded"
$ "raco pkg remove pkg-test3")
(finally
$ "rm -f test-pkgs/update-test/pkg-test1.zip"
$ "rm -f test-pkgs/update-test/pkg-test1.zip.CHECKSUM"))
(shelly-wind
$ "mkdir -p test-pkgs/update-test"
$ "cp -f test-pkgs/pkg-test1.zip test-pkgs/update-test/pkg-test1.zip"
$ "cp -f test-pkgs/pkg-test1.zip.CHECKSUM test-pkgs/update-test/pkg-test1.zip.CHECKSUM"
$ "cp -f test-pkgs/pkg-test3-v3.zip test-pkgs/update-test/pkg-test3.zip"
$ "cp -f test-pkgs/pkg-test3-v3.zip.CHECKSUM test-pkgs/update-test/pkg-test3.zip.CHECKSUM"
(shelly-install* "update and get updates for newly introduced deps"
"http://localhost:9999/update-test/pkg-test1.zip"
"pkg-test1"
$ "raco pkg install http://localhost:9999/update-test/pkg-test3.zip"
$ "racket -e '(require pkg-test3)'" =stdout> #rx"version 3 loaded"
$ "raco pkg update --update-deps pkg-test3" =exit> 0
=stdout> "Downloading checksum for pkg-test3\nNo updates available\n"
$ "racket -e '(require pkg-test1/update)'" =exit> 42
$ "cp -f test-pkgs/pkg-test1-v2.zip test-pkgs/update-test/pkg-test1.zip"
$ "cp -f test-pkgs/pkg-test1-v2.zip.CHECKSUM test-pkgs/update-test/pkg-test1.zip.CHECKSUM"
$ "cp -f test-pkgs/pkg-test3.zip test-pkgs/update-test/pkg-test3.zip"
$ "cp -f test-pkgs/pkg-test3.zip.CHECKSUM test-pkgs/update-test/pkg-test3.zip.CHECKSUM"
$ "raco pkg update --update-deps pkg-test3" =exit> 0
$ "racket -e '(require pkg-test1/update)'" =exit> 43
$ "racket -e '(require pkg-test3)'" =stdout> #rx"main loaded"
$ "raco pkg remove pkg-test3")
(finally
$ "rm -f test-pkgs/update-test/pkg-test1.zip"
$ "rm -f test-pkgs/update-test/pkg-test1.zip.CHECKSUM"))
(shelly-wind
$ "mkdir -p test-pkgs/update-test"
$ "cp -f test-pkgs/pkg-test1.zip test-pkgs/update-test/pkg-test1.zip"
$ "cp -f test-pkgs/pkg-test1.zip.CHECKSUM test-pkgs/update-test/pkg-test1.zip.CHECKSUM"
(shelly-install* "update all"
"http://localhost:9999/update-test/pkg-test1.zip"
"pkg-test1"
$ "raco pkg install test-pkgs/pkg-test2.zip"
$ "raco pkg update -a" =exit> 0 =stdout> "Downloading checksum\nNo updates available\n"
$ "raco pkg update -a" =exit> 0 =stdout> "Downloading checksum for pkg-test1\nNo updates available\n"
$ "racket -e '(require pkg-test1/update)'" =exit> 42
$ "cp -f test-pkgs/pkg-test1-v2.zip test-pkgs/update-test/pkg-test1.zip"
$ "cp -f test-pkgs/pkg-test1-v2.zip.CHECKSUM test-pkgs/update-test/pkg-test1.zip.CHECKSUM"

View File

@ -412,13 +412,26 @@
(hash 'source (db:pkg-source pkg)
'checksum (db:pkg-source pkg))))
(define (remote-package-checksum pkg download-printf)
(define (remote-package-checksum pkg download-printf pkg-name)
(match pkg
[`(catalog ,pkg-name)
(hash-ref (package-catalog-lookup pkg-name #f download-printf) 'checksum)]
[`(url ,pkg-url-str)
(package-url->checksum pkg-url-str
#:download-printf download-printf)]))
#:download-printf download-printf
#:pkg-name pkg-name)]))
(define (checksum-for-pkg-source pkg-source type pkg-name download-printf)
(cond
[(or (eq? type 'file-url) (eq? type 'dir-url) (eq? type 'github))
(remote-package-checksum `(url ,pkg-source) download-printf pkg-name)]
[(eq? type 'file)
(define checksum-pth (format "~a.CHECKSUM" pkg-source))
(or (and (file-exists? checksum-pth)
(file->string checksum-pth))
(and (file-exists? pkg-source)
(call-with-input-file* pkg-source sha1)))]
[else #f]))
(define (write-file-hash! file new-db)
(unless (eq? (pkg-lock-held) 'exclusive)
@ -825,7 +838,7 @@
(define orig-pkg `(url ,pkg))
(define checksum (or given-checksum
(remote-package-checksum orig-pkg download-printf)))
(remote-package-checksum orig-pkg download-printf pkg-name)))
(define info
(update-install-info-orig-pkg
(match type
@ -1158,6 +1171,8 @@
#:old-descs [old-descs empty]
#:pre-succeed [pre-succeed void]
#:dep-behavior [dep-behavior #f]
#:update-deps? [update-deps? #f]
#:update-cache [update-cache #f]
#:updating? [updating? #f]
#:ignore-checksums? [ignore-checksums? #f]
#:skip-installed? [skip-installed? #f]
@ -1184,14 +1199,16 @@
(delete-directory/files pkg-dir)))
(define (format-deps update-deps)
(format-list (for/list ([ud (in-list update-deps)])
(format "~a (have ~a, need ~a)"
(car ud)
(caddr ud)
(cadddr ud)))))
(if (pkg-desc? ud)
(pkg-desc-name ud)
(format "~a (have ~a, need ~a)"
(car ud)
(caddr ud)
(cadddr ud))))))
(define (show-dependencies deps update? auto? conversation)
(unless quiet?
(printf/flush "The following ~a packages are listed as dependencies of ~a~a:~a\n"
(if update? "out-of-date" "uninstalled")
(printf/flush "The following~a packages are listed as dependencies of ~a~a:~a\n"
(if update? " out-of-date" " uninstalled")
pkg-name
(if (or auto? (eq? conversation 'always-yes))
(format "\nand they will be ~a~a"
@ -1331,6 +1348,30 @@
[(no)
(clean!)
(pkg-error "missing dependencies\n missing packages:~a" (format-list unsatisfied-deps))])]))]
[(and
update-deps?
(let ()
(define deps (get-all-deps metadata-ns pkg-dir))
(define update-pkgs
(append-map (λ (dep)
(define name (dependency->name dep))
(define this-platform? (dependency-this-platform? dep))
(or (and this-platform?
(not (hash-ref simultaneous-installs name #f))
((packages-to-update download-printf current-scope-db
#:must-update? #f #:deps? #t
#:update-cache update-cache
#:namespace metadata-ns)
name))
null))
deps))
(and (not (empty? update-pkgs))
update-pkgs)))
=> (lambda (update-pkgs)
(show-dependencies update-pkgs #t #f 'always-yes)
(raise (vector #t infos update-pkgs
(λ () (for-each (compose (remove-package quiet?) pkg-desc-name) update-pkgs))
install-conversation update-conversation)))]
[(and
(not (eq? dep-behavior 'force))
(let ()
@ -1393,8 +1434,12 @@
;; Try updates:
(define update-pkgs (map car update-deps))
(define (make-pre-succeed)
(define db (read-pkg-db))
(let ([to-update (filter-map (update-package download-printf db) update-pkgs)])
(define db current-scope-db)
(let ([to-update (append-map (packages-to-update download-printf db
#:deps? update-deps?
#:update-cache update-cache
#:namespace metadata-ns)
update-pkgs)])
(λ () (for-each (compose (remove-package quiet?) pkg-desc-name) to-update))))
(match (or dep-behavior
(if name?
@ -1592,6 +1637,8 @@
#:skip-installed? [skip-installed? #f]
#:pre-succeed [pre-succeed void]
#:dep-behavior [dep-behavior #f]
#:update-deps? [update-deps? #f]
#:update-cache [update-cache #f]
#:updating? [updating? #f]
#:quiet? [quiet? #f]
#:install-conversation [install-conversation #f]
@ -1621,13 +1668,17 @@
#:force? force
#:ignore-checksums? ignore-checksums?
#:dep-behavior dep-behavior
#:update-deps? update-deps?
#:update-cache update-cache
#:pre-succeed (lambda () (pre-succeed) (more-pre-succeed))
#:updating? updating?
#:install-conversation inst-conv
#:update-conversation updt-conv
#:strip strip-mode
(for/list ([dep (in-list deps)])
(pkg-desc dep #f #f #t)))])])
(if (pkg-desc? dep)
dep
(pkg-desc dep #f #f #t))))])])
(install-packages
#:old-infos old-infos
#:old-descs old-descs
@ -1635,6 +1686,8 @@
#:ignore-checksums? ignore-checksums?
#:skip-installed? skip-installed?
#:dep-behavior dep-behavior
#:update-deps? update-deps?
#:update-cache update-cache
#:pre-succeed pre-succeed
#:updating? updating?
#:quiet? quiet?
@ -1644,42 +1697,126 @@
#:link-dirs? link-dirs?
new-descs)))
(define ((update-is-possible? db) pkg-name)
(match-define (pkg-info orig-pkg checksum _)
(package-info pkg-name #:db db))
(define ty (first orig-pkg))
(not (member ty '(link static-link dir file))))
;; Determine packages to update, starting with `pkg-name'. If `pkg-name'
;; needs to be updated, return it in a list. Otherwise, if `deps?',
;; then return a list of dependencies that need to be updated.
;; (If a package needs to be updated, wait until the update
;; has been inspected for further dependencies.)
;; If `must-installed?', then complain if the package is not
;; installed inthe current scope.
;; If `must-update?', then complain if the package is not
;; updatable.
;; The `update-cache' argument is used to cache which packages
;; are already being updated and downloaded checksums.
(define ((packages-to-update download-printf db
#:must-installed? [must-installed? #t]
#:must-update? [must-update? #t]
#:deps? [deps? #f]
#:namespace metadata-ns
#:update-cache update-cache)
pkg-name)
(cond
[(pkg-desc? pkg-name)
;; Infer the package-source type and name:
(define-values (inferred-name type) (package-source->name+type
(pkg-desc-source pkg-name)
(pkg-desc-type pkg-name)))
(define name (or (pkg-desc-name pkg-name)
inferred-name))
;; Check that the package is installed, and get current checksum:
(define info (package-info name #:db db))
(if (or (not (pkg-info-checksum info))
(not (equal? (pkg-info-checksum info)
(checksum-for-pkg-source (pkg-desc-source pkg-name)
type
name download-printf))))
;; Update:
(begin
(hash-set! update-cache (pkg-desc-source pkg-name) #t)
(list (pkg-desc (pkg-desc-source pkg-name)
(pkg-desc-type pkg-name)
name
(pkg-desc-auto? pkg-name))))
;; No update needed, but maybe check dependencies:
(if deps?
((packages-to-update download-printf db
#:must-update? #f
#:deps? #t
#:update-cache update-cache
#:namespace metadata-ns)
pkg-name)
null))]
[(eq? #t (hash-ref update-cache pkg-name #f))
;; package is already being updated
null]
;; A string indicates that package source that should be
;; looked up in the installed packages to get the old source
;; for getting the checksum:
[(package-info pkg-name #:db db must-update?)
=>
(lambda (m)
(match-define (pkg-info orig-pkg checksum auto?) m)
(match orig-pkg
[`(,(or 'link 'static-link) ,_)
(if must-update?
(pkg-error (~a "cannot update linked packages\n"
" package name: ~a\n"
" package source: ~a")
pkg-name
orig-pkg)
null)]
[`(dir ,_)
(if must-update?
(pkg-error (~a "cannot update packages installed locally;\n"
" package was installed via a local directory\n"
" package name: ~a")
pkg-name)
null)]
[`(file ,_)
(if must-update?
(pkg-error (~a "cannot update packages installed locally;\n"
" package was installed via a local file\n"
" package name: ~a")
pkg-name)
null)]
[`(,_ ,orig-pkg-source)
(define new-checksum
(or (hash-ref update-cache pkg-name #f)
(remote-package-checksum orig-pkg download-printf pkg-name)))
;; Record downloaded checksum:
(hash-set! update-cache pkg-name new-checksum)
(or (and new-checksum
(not (equal? checksum new-checksum))
(begin
;; Update it:
(hash-set! update-cache pkg-name #t)
;; Flush cache of downloaded checksums, in case
;; there was a race between our checkig and updates on
;; the catalog server:
(clear-checksums-in-cache! update-cache)
;; FIXME: the type shouldn't be #f here; it should be
;; preseved from install time:
(list (pkg-desc orig-pkg-source #f pkg-name auto?))))
(if deps?
;; Check dependencies
(append-map
(packages-to-update download-printf db
#:must-update? #f
#:deps? #t
#:update-cache update-cache
#:namespace metadata-ns)
((package-dependencies metadata-ns db) pkg-name))
null))]))]
[else null]))
(define ((update-package download-printf db) pkg-name)
(match-define (pkg-info orig-pkg checksum auto?)
(package-info pkg-name #:db db))
(match orig-pkg
[`(,(or 'link 'static-link) ,_)
(pkg-error (~a "cannot update linked packages\n"
" package name: ~a\n"
" package source: ~a")
pkg-name
orig-pkg)]
[`(dir ,_)
(pkg-error (~a "cannot update packages installed locally;\n"
" package was installed via a local directory\n"
" package name: ~a")
pkg-name)]
[`(file ,_)
(pkg-error (~a "cannot update packages installed locally;\n"
" package was installed via a local file\n"
" package name: ~a")
pkg-name)]
[`(,_ ,orig-pkg-source)
(define new-checksum
(remote-package-checksum orig-pkg download-printf))
(and new-checksum
(not (equal? checksum new-checksum))
;; FIXME: the type shouldn't be #f here; it should be
;; preseved from install time:
(pkg-desc orig-pkg-source #f pkg-name auto?))]))
(define (clear-checksums-in-cache! update-cache)
(define l (for/list ([(k v) (in-hash update-cache)]
#:when (string? v))
k))
(for ([k (in-list l)]) (hash-remove! update-cache k)))
(define ((package-dependencies metadata-ns db) pkg-name)
(define ((package-dependencies metadata-ns db) pkg-name)
(map dependency->name
(filter dependency-this-platform?
(get-all-deps metadata-ns (pkg-directory* pkg-name #:db db)))))
@ -1687,23 +1824,27 @@
(define (pkg-update in-pkgs
#:all? [all? #f]
#:dep-behavior [dep-behavior #f]
#:deps? [deps? #f]
#:force? [force? #f]
#:ignore-checksums? [ignore-checksums? #f]
#:deps? [update-deps? #f]
#:quiet? [quiet? #f]
#:strip [strip-mode #f])
#:strip [strip-mode #f]
#:link-dirs? [link-dirs? #f])
(define download-printf (if quiet? void printf))
(define metadata-ns (make-metadata-namespace))
(define db (read-pkg-db))
(define pkgs
(cond
[(and all? (empty? in-pkgs))
(filter (update-is-possible? db) (hash-keys db))]
[deps?
(append-map
(package-dependencies metadata-ns db)
in-pkgs)]
[else
in-pkgs]))
(define to-update (filter-map (update-package download-printf db) pkgs))
(define all-mode? (and all? (empty? in-pkgs)))
(define pkgs (cond
[all-mode? (hash-keys db)]
[else in-pkgs]))
(define update-cache (make-hash))
(define to-update (append-map (packages-to-update download-printf db
#:must-update? (not all-mode?)
#:deps? (or update-deps?
all-mode?) ; avoid races
#:update-cache update-cache
#:namespace metadata-ns)
pkgs))
(cond
[(empty? to-update)
(unless quiet?
@ -1719,8 +1860,13 @@
#:updating? #t
#:pre-succeed (λ () (for-each (compose (remove-package quiet?) pkg-desc-name) to-update))
#:dep-behavior dep-behavior
#:update-deps? update-deps?
#:update-cache update-cache
#:quiet? quiet?
#:strip strip-mode
#:force? force?
#:ignore-checksums? ignore-checksums?
#:link-dirs? link-dirs?
to-update)]))
(define (pkg-show indent
@ -1868,7 +2014,7 @@
[(list)
(pkg-error "config key not provided")]
[_
(pkg-error "multiple config keys provided")])]))
(pkg-error "multiple config keys provided (not in value-setting mode)")])]))
(define (create-as-is create:format pkg-name dir orig-dir
#:quiet? [quiet? #f]
@ -1963,7 +2109,9 @@
(create-as-is create:format name dest-dir dir
#:hide-src? #t
#:quiet? quiet?
#:dest archive-dest-dir))
#:dest (if archive-dest-dir
(path->complete-path archive-dest-dir)
(current-directory))))
(lambda ()
(delete-directory/files tmp-dir))))
@ -2499,12 +2647,15 @@
#:dest (or/c (and/c path-string? complete-path?) #f))
void?)]
[pkg-update
(->* ((listof string?))
(->* ((listof (or/c string? pkg-desc?)))
(#:dep-behavior dep-behavior/c
#:all? boolean?
#:deps? boolean?
#:quiet? boolean?
#:strip (or/c #f 'source 'binary))
#:force? boolean?
#:ignore-checksums? boolean?
#:strip (or/c #f 'source 'binary)
#:link-dirs? boolean?)
(or/c #f 'skip (listof (or/c path-string? (non-empty-listof path-string?)))))]
[pkg-remove
(->* ((listof string?))

View File

@ -3,13 +3,15 @@
racket/list
racket/format
racket/path
racket/splicing
raco/command-name
setup/dirs
net/url
"name.rkt"
"lib.rkt"
"commands.rkt"
(prefix-in setup: setup/setup))
(prefix-in setup: setup/setup)
(for-syntax racket/base))
(define (setup no-setup? setup-collects jobs)
(unless (or (eq? setup-collects 'skip)
@ -35,7 +37,7 @@
;; Selects scope from `given-scope' through `user' arguments, or infers
;; a scope from `pkgs' if non-#f, and then calls `thunk'.
(define (call-with-package-scope who given-scope scope-dir installation user pkgs thunk)
(define (call-with-package-scope who given-scope scope-dir installation user pkgs pkgs-type thunk)
(define scope
(case given-scope
[(installation user) given-scope]
@ -53,7 +55,8 @@
(with-pkg-lock/read-only
(define-values (pkg scope)
(for/fold ([prev-pkg #f] [prev-scope #f]) ([pkg (in-list pkgs)])
(define scope (find-pkg-installation-scope pkg))
(define pkg-name (package-source->name pkg pkgs-type))
(define scope (find-pkg-installation-scope pkg-name))
(cond
[(not prev-pkg) (values pkg scope)]
[(equal? scope prev-scope) (values prev-pkg prev-scope)]
@ -83,336 +86,361 @@
[(regexp-match? #rx"^[a-zA-Z]*://" s) (string->url s)]
[else (path->url (path->complete-path s))]))
(commands
"This tool is used for managing installed packages."
"pkg-~a-command"
[install
"Install packages"
#:once-each
[(#:sym type [file dir file-url dir-url github name] #f) type ("-t")
("Type of <pkg-source>;"
"valid <types>s are: file, dir, file-url, dir-url, github, or name;"
"if not specified, the type is inferred syntactically")]
[(#:str name #f) name ("-n") ("Name of package, instead of inferred"
"(makes sense only when a single <pkg-source> is given)")]
#:once-any
[(#:sym mode [fail force search-ask search-auto] #f) deps ()
("Specify the behavior for dependencies, with <mode> as one of"
" fail: cancels the installation if dependencies are unmet"
" (default for most packages)"
" force: installs the package despite missing dependencies"
" search-ask: looks for the dependencies on your package naming services"
" (default if package is a package name) and asks if you would"
" like it installed"
" search-auto: like 'search-ask' but does not ask for permission to install")]
[#:bool auto () "Shorthand for `--deps search-auto'"]
#:once-each
[#:bool force () "Ignores conflicts"]
[#:bool ignore-checksums () "Ignores checksums"]
#:once-any
[#:bool link () ("Link a directory package source in place (default for a directory)")]
[#:bool static-link () ("Link in place, promising collections do not change")]
[#:bool copy () ("Treat directory sources the same as other sources")]
[#:bool source () ("Strip package's built elements before installing; implies --copy")]
[#:bool binary () ("Strip packages' source elements before installing; implies --copy")]
#:once-each
[#:bool skip-installed () ("Skip a <pkg-source> if already installed")]
#:once-any
[(#:sym scope [installation user] #f) scope ()
("Select package <scope>, one of"
" installation: Install for all users of the Racket installation"
" user: Install as user-specific for an installation version/name")]
[#:bool installation ("-i") "Shorthand for `--scope installation'"]
[#:bool user ("-u") "Shorthand for `--scope user'"]
[(#:str dir #f) scope-dir () "Install for package scope <dir>"]
#:once-each
[(#:str catalog #f) catalog () "Use <catalog> instead of configured catalogs"]
[#:bool no-setup () ("Don't run `raco setup' after changing packages (usually"
"not a good idea)")]
[(#:num n #f) jobs ("-j") "Setup with <n> parallel jobs"]
#:args pkg-source
(call-with-package-scope
'install
scope scope-dir installation user #f
(lambda ()
(unless (or (not name) (package-source->name name))
((current-pkg-error) (format "~e is an invalid package name" name)))
(define setup-collects
(with-pkg-lock
(parameterize ([current-pkg-catalogs (and catalog
(list (catalog->url catalog)))])
(define link-dirs? (not (or copy source binary)))
(pkg-install #:dep-behavior (if auto 'search-auto deps)
#:force? force
#:ignore-checksums? ignore-checksums
#:skip-installed? skip-installed
#:strip (or (and source 'source) (and binary 'binary))
#:link-dirs? link-dirs?
(for/list ([p (in-list pkg-source)])
(define a-type (or (and link 'link)
(and static-link 'static-link)
(and (eq? type 'dir) link-dirs? 'link)
type))
(pkg-desc p a-type name #f))))))
(setup no-setup setup-collects jobs)))]
[update
"Update packages"
#:once-each
[#:bool all ("-a") ("Update all packages;"
"only if no packages are given on the command line")]
[#:bool update-deps () "Check named packages' dependencies for updates"]
#:once-any
[(#:sym mode [fail force search-ask search-auto] #f) deps ()
("Specify the behavior for dependencies, with <mode> as one of"
" fail: cancels the installation if dependencies are unmet"
" (default for most packages)"
" force: installs the package despite missing dependencies"
" search-ask: looks for the dependencies on your package naming services"
" (default if package is an package name) and asks if you would"
" like it installed"
" search-auto: like 'search-ask' but does not ask for permission to install")]
[#:bool auto () "Shorthand for `--deps search-auto' plus `--update-deps'"]
#:once-any
[(#:sym scope [installation user] #f) scope ()
("Select package scope, one of"
" installation: Update only for all users of the Racket installation"
" user: Update only user-specific for an installation version/name")]
[#:bool installation ("-i") "Shorthand for `--scope installation'"]
[#:bool user ("-u") "Shorthand for `--scope user'"]
[(#:str dir #f) scope-dir () "Update for package scope <dir>"]
#:once-any
[#:bool source () ("Strip built elements of the package before installing")]
[#:bool binary () ("Strip source elements of the package before installing")]
#:once-each
[#:bool no-setup () ("Don't run `raco setup' after changing packages (usually"
"not a good idea)")]
[(#:num n #f) jobs ("-j") "Setup with <n> parallel jobs"]
#:args pkg
(call-with-package-scope
'update
scope scope-dir installation user pkg
(lambda ()
(define setup-collects
(with-pkg-lock
(pkg-update pkg
#:all? all
#:dep-behavior (if auto 'search-auto deps)
#:deps? (or update-deps auto)
#:strip (or (and source 'source) (and binary 'binary)))))
(setup no-setup setup-collects jobs)))]
[remove
"Remove packages"
#:once-each
[#:bool demote () "Demote to automatically installed, instead of removing"]
[#:bool force () "Force removal of packages"]
[#:bool auto () "Remove automatically installed packages with no dependencies"]
#:once-any
[(#:sym scope [installation user] #f) scope ()
("Select package <scope>, one of"
" installation: Remove packages for all users of the Racket installation"
" user: Remove user-specific for an installation version/name")]
[#:bool installation ("-i") "Shorthand for `--scope installation'"]
[#:bool user ("-u") "Shorthand for `--scope user'"]
[(#:str dir #f) scope-dir () "Remove for package scope <dir>"]
#:once-each
[#:bool no-setup () ("Don't run `raco setup' after changing packages (usually"
"not a good idea)")]
[(#:num n #f) jobs ("-j") "Setup with <n> parallel jobs"]
#:args pkg
(call-with-package-scope
'remove
scope scope-dir installation user pkg
(lambda ()
(define setup-collects
(with-pkg-lock
(pkg-remove pkg
#:demote? demote
#:auto? auto
#:force? force)))
(setup no-setup setup-collects jobs)))]
[show
"Show information about installed packages"
#:once-each
[#:bool all ("-a") "Show auto-installed packages, too"]
[#:bool dir ("-d") "Show the directory where the package is installed"]
#:once-any
[(#:sym scope [installation user] #f) scope ()
("Show only for package <scope>, one of"
" installation: Show only for all users of the Racket installation"
" user: Show only user-specific for an installation version/name")]
[(#:str vers #f) version ("-v") "Show user-specific for installation <vers>"]
[#:bool installation ("-i") "Shorthand for `--scope installation'"]
[#:bool user ("-u") "Shorthand for `--scope user'"]
[(#:str dir #f) scope-dir () "Show only for package scope <dir>"]
#:args ()
(define only-mode (case scope
[(installation user) scope]
[else
(cond
[scope-dir (path->complete-path scope-dir)]
[installation 'installation]
[user 'user]
[else (if version 'user #f)])]))
(for ([mode (if only-mode
(list only-mode)
(append (let ([main (find-pkgs-dir)])
(reverse
(for/list ([d (get-pkgs-search-dirs)])
(if (equal? d main)
'installation
(simple-form-path d)))))
'(user)))])
(when (or (equal? mode only-mode) (not only-mode))
(unless only-mode
(printf "~a\n" (case mode
[(installation) "Installation-wide:"]
[(user) (format "User-specific for installation ~s:"
(or version (get-installation-name)))]
[else (format "~a:" mode)])))
(parameterize ([current-pkg-scope mode]
[current-pkg-error (pkg-error 'show)]
[current-pkg-scope-version (or version (get-installation-name))])
(with-pkg-lock/read-only
(pkg-show (if only-mode "" " ")
#:auto? all
#:directory? dir)))))]
[migrate
"Install packages installed for other version/name"
#:once-each
[(#:sym mode [fail force search-ask search-auto] #f) deps ()
("Specify the behavior for dependencies, with <mode> as one of"
" fail: cancels the installation if dependencies are unmet"
" force: installs the package despite missing dependencies"
" search-ask: looks for the dependencies on your package naming services"
" and asks if you would like it installed"
" search-auto: (the default) like 'search-ask' but does not ask for"
" permission to install")]
[#:bool force () "Ignores conflicts"]
[#:bool ignore-checksums () "Ignores checksums"]
#:once-any
[#:bool source () ("Strip built elements of the package before installing")]
[#:bool binary () ("Strip source elements of the package before installing")]
#:once-any
[(#:sym scope [installation user] #f) scope ()
("Select package <scope>, one of"
" installation: Install for all users of the Racket installation"
" user: Install as user-specific for an installation version/name")]
[#:bool installation ("-i") "Shorthand for `--scope installation'"]
[#:bool user ("-u") "Shorthand for `--scope user'"]
[(#:str dir #f) scope-dir () "Install for package scope <dir>"]
#:once-each
[(#:str catalog #f) catalog () "Use <catalog> instead of configured catalogs"]
[#:bool no-setup () ("Don't run `raco setup' after changing packages (usually"
"not a good idea)")]
[(#:num n #f) jobs ("-j") "Setup with <n> parallel jobs"]
#:args (from-version)
(call-with-package-scope
'migrate
scope scope-dir installation user #f
(lambda ()
(define setup-collects
(with-pkg-lock
(parameterize ([current-pkg-catalogs (and catalog
(list (catalog->url catalog)))])
(pkg-migrate from-version
#:dep-behavior deps
#:force? force
#:ignore-checksums? ignore-checksums
#:strip (or (and source 'source) (and binary 'binary))))))
(setup no-setup setup-collects jobs)))]
[create
"Bundle package from a directory or installed package"
#:once-any
[#:bool from-dir () "Treat <directory-or-package> as a directory (the default)"]
[#:bool from-install () "Treat <directory-or-package> as a package name"]
#:once-any
[(#:sym fmt [zip tgz plt] #f) format ()
("Select the format of the package to be created;"
"valid <fmt>s are: zip (the default), tgz, plt")]
[#:bool manifest () "Creates a manifest file for a directory, rather than an archive"]
#:once-any
[#:bool as-is () "Bundle the directory/package as-is (the default)"]
[#:bool source () "Bundle sources only"]
[#:bool binary () "Bundle bytecode and rendered documentation without sources"]
[#:bool built () "Bundle sources, bytecode and rendered documentation"]
#:once-each
[(#:str dest-dir #f) dest () "Create output files in <dest-dir>"]
#:args (directory-or-package)
(parameterize ([current-pkg-error (pkg-error 'create)])
(pkg-create (if manifest 'MANIFEST (or format 'zip))
directory-or-package
#:dest (and dest
(path->complete-path dest))
#:source (cond
[from-install 'name]
[else 'dir])
#:mode (cond
[source 'source]
[binary 'binary]
[built 'built]
[else 'as-is])))]
[config
"View and modify the package manager's configuration"
#:once-each
[#:bool set () "Completely replace the value"]
#:once-any
[(#:sym scope [installation user] #f) scope ()
("Select configuration <scope>, one of"
" installation: Operate on the installation-wide package configuration"
" user: Operate on the user-specific for an installation name")]
[#:bool installation ("-i") "Shorthand for `--scope installation'"]
[#:bool user ("-u") "Shorthand for `--scope user'"]
#:args key/val
(call-with-package-scope
'config
scope #f installation user #f
(lambda ()
(if set
(with-pkg-lock
(pkg-config #t key/val))
(with-pkg-lock/read-only
(pkg-config #f key/val)))))]
[catalog-show
"Show package information as reported by a catalog"
#:once-any
[(#:str catalog #f) catalog () "Use <catalog> instead of configured catalogs"]
#:once-each
[#:bool all () "Show all packages"]
[#:bool only-names () "Show only package names"]
[#:bool modules () "Show implemented modules"]
[(#:str vers #f) version ("-v") "Show result for Racket <vers>"]
#:args pkg-name
(when (and all (pair? pkg-name))
((pkg-error 'catalog-show) "both `--all' and package names provided"))
(parameterize ([current-pkg-catalogs (and catalog
(list (catalog->url catalog)))]
[current-pkg-error (pkg-error 'catalog-show)]
[current-pkg-scope-version (or version
(current-pkg-scope-version))])
(pkg-catalog-show pkg-name
#:all? all
#:only-names? only-names
#:modules? modules))]
[catalog-copy
"Copy/merge package name catalogs"
#:once-each
[#:bool from-config () "Include currently configured catalogs last"]
#:once-any
[#:bool force () "Force replacement fo existing file/directory"]
[#:bool merge () "Merge to existing database"]
#:once-each
[#:bool override () "While merging, override existing with new"]
[(#:str vers #f) version ("-v") "Copy information suitable for Racket <vers>"]
#:args catalog
(parameterize ([current-pkg-error (pkg-error 'catalog-copy)])
(when (null? catalog)
((current-pkg-error) "need a destination catalog"))
(parameterize ([current-pkg-scope-version (or version
(current-pkg-scope-version))])
(pkg-catalog-copy (drop-right catalog 1)
(last catalog)
#:from-config? from-config
#:force? force
#:merge? merge
#:override? override)))])
(splicing-let ()
(define-syntax (make-commands stx)
(syntax-case stx ()
[(_ #:scope-flags (scope-flags ...)
#:job-flags (job-flags ...)
#:catalog-flags (catalog-flags ...)
#:install-type-flags (install-type-flags ...)
#:install-dep-flags (install-dep-flags ...)
#:install-dep-desc (install-dep-desc ...)
#:install-force-flags (install-force-flags ...)
#:install-copy-flags (install-copy-flags ...)
#:install-copy-defns (install-copy-defns ...))
(with-syntax ([([scope-flags ...]
[job-flags ...]
[catalog-flags ...]
[install-type-flags ...]
[(install-dep-flags ... (dep-desc ...))]
[install-force-flags ...]
[install-copy-flags ...]
[install-copy-defns ...])
(syntax-local-introduce #'([scope-flags ...]
[job-flags ...]
[catalog-flags ...]
[install-type-flags ...]
[install-dep-flags ...]
[install-force-flags ...]
[install-copy-flags ...]
[install-copy-defns ...]))])
#`(commands
"This tool is used for managing installed packages."
"pkg-~a-command"
;; ----------------------------------------
[install
"Install packages"
#:once-any
install-type-flags ...
#:once-any
[install-dep-flags ...
(dep-desc ...
install-dep-desc ...)]
[#:bool auto () "Shorthand for `--deps search-auto'"]
#:once-any
install-copy-flags ...
#:once-any
scope-flags ...
#:once-each
catalog-flags ...
[#:bool skip-installed () ("Skip a <pkg-source> if already installed")]
install-force-flags ...
job-flags ...
#:args pkg-source
install-copy-defns ...
(call-with-package-scope
'install
scope scope-dir installation user #f a-type
(lambda ()
(unless (or (not name) (package-source->name name))
((current-pkg-error) (format "~e is an invalid package name" name)))
(define setup-collects
(with-pkg-lock
(parameterize ([current-pkg-catalogs (and catalog
(list (catalog->url catalog)))])
(pkg-install #:dep-behavior (if auto 'search-auto deps)
#:force? force
#:ignore-checksums? ignore-checksums
#:skip-installed? skip-installed
#:strip (or (and source 'source) (and binary 'binary))
#:link-dirs? link-dirs?
(for/list ([p (in-list pkg-source)])
(pkg-desc p a-type name #f))))))
(setup no-setup setup-collects jobs)))]
;; ----------------------------------------
[update
"Update packages"
#:once-each
[#:bool all ("-a") ("Update all packages if no <pkg-source> is given")]
[#:bool update-deps () "Also update all dependencies"]
[#:bool lookup () "For each name <pkg-source>, look up in catalog"]
#:once-any
install-type-flags ...
#:once-any
[install-dep-flags ...
(dep-desc ...
install-dep-desc ...)]
[#:bool auto () "Shorthand for `--deps search-auto' plus `--update-deps'"]
#:once-any
install-copy-flags ...
#:once-any
scope-flags ...
#:once-each
catalog-flags ...
install-force-flags ...
job-flags ...
#:args pkg-source
install-copy-defns ...
(call-with-package-scope
'update
scope scope-dir installation user pkg-source #f
(lambda ()
(define setup-collects
(with-pkg-lock
(parameterize ([current-pkg-catalogs (and catalog
(list (catalog->url catalog)))])
(pkg-update (for/list ([pkg-source (in-list pkg-source)])
(cond
[lookup
(pkg-desc pkg-source a-type name #f)]
[else
(define-values (pkg-name pkg-type)
(package-source->name+type pkg-source a-type))
(if (eq? pkg-type 'name)
pkg-name
(pkg-desc pkg-source a-type name #f))]))
#:all? all
#:dep-behavior (if auto 'search-auto deps)
#:force? force
#:ignore-checksums? ignore-checksums
#:deps? (or update-deps auto)
#:strip (or (and source 'source) (and binary 'binary))
#:link-dirs? link-dirs?))))
(setup no-setup setup-collects jobs)))]
;; ----------------------------------------
[remove
"Remove packages"
#:once-each
[#:bool demote () "Demote to auto-installed, instead of removing"]
[#:bool force () "Remove even if package has dependents"]
[#:bool auto () "Also remove auto-installed packages that have no dependents"]
#:once-any
scope-flags ...
#:once-each
job-flags ...
#:args pkg
(call-with-package-scope
'remove
scope scope-dir installation user pkg 'name
(lambda ()
(define setup-collects
(with-pkg-lock
(pkg-remove pkg
#:demote? demote
#:auto? auto
#:force? force)))
(setup no-setup setup-collects jobs)))]
;; ----------------------------------------
[show
"Show information about installed packages"
#:once-each
[#:bool all ("-a") "Show auto-installed packages, too"]
[#:bool dir ("-d") "Show the directory where the package is installed"]
#:once-any
scope-flags ...
[(#:str vers #f) version ("-v") "Show user-specific for installation <vers>"]
#:args ()
(define only-mode (case scope
[(installation user) scope]
[else
(cond
[scope-dir (path->complete-path scope-dir)]
[installation 'installation]
[user 'user]
[else (if version 'user #f)])]))
(for ([mode (if only-mode
(list only-mode)
(append (let ([main (find-pkgs-dir)])
(reverse
(for/list ([d (get-pkgs-search-dirs)])
(if (equal? d main)
'installation
(simple-form-path d)))))
'(user)))])
(when (or (equal? mode only-mode) (not only-mode))
(unless only-mode
(printf "~a\n" (case mode
[(installation) "Installation-wide:"]
[(user) (format "User-specific for installation ~s:"
(or version (get-installation-name)))]
[else (format "~a:" mode)])))
(parameterize ([current-pkg-scope mode]
[current-pkg-error (pkg-error 'show)]
[current-pkg-scope-version (or version (get-installation-name))])
(with-pkg-lock/read-only
(pkg-show (if only-mode "" " ")
#:auto? all
#:directory? dir)))))]
;; ----------------------------------------
[migrate
"Install packages installed for other version/name"
#:once-each
[install-dep-flags ...
(dep-desc ...
"where the default is `search-auto'")]
#:once-any
[#:bool source () ("Strip built elements of the package before installing")]
[#:bool binary () ("Strip source elements of the package before installing")]
#:once-any
scope-flags ...
#:once-each
catalog-flags ...
install-force-flags ...
job-flags ...
#:args (from-version)
(call-with-package-scope
'migrate
scope scope-dir installation user #f #f
(lambda ()
(define setup-collects
(with-pkg-lock
(parameterize ([current-pkg-catalogs (and catalog
(list (catalog->url catalog)))])
(pkg-migrate from-version
#:dep-behavior deps
#:force? force
#:ignore-checksums? ignore-checksums
#:strip (or (and source 'source) (and binary 'binary))))))
(setup no-setup setup-collects jobs)))]
;; ----------------------------------------
[create
"Bundle package from a directory or installed package"
#:once-any
[#:bool from-dir () "Treat <directory-or-package> as a directory (the default)"]
[#:bool from-install () "Treat <directory-or-package> as a package name"]
#:once-any
[(#:sym fmt [zip tgz plt] #f) format ()
("Select the format of the package to be created;"
"valid <fmt>s are: zip (the default), tgz, plt")]
[#:bool manifest () "Creates a manifest file for a directory, rather than an archive"]
#:once-any
[#:bool as-is () "Bundle the directory/package as-is (the default)"]
[#:bool source () "Bundle sources only"]
[#:bool binary () "Bundle bytecode and rendered documentation without sources"]
[#:bool built () "Bundle sources, bytecode and rendered documentation"]
#:once-each
[(#:str dest-dir #f) dest () "Create output files in <dest-dir>"]
#:args (directory-or-package)
(parameterize ([current-pkg-error (pkg-error 'create)])
(pkg-create (if manifest 'MANIFEST (or format 'zip))
directory-or-package
#:dest (and dest
(path->complete-path dest))
#:source (cond
[from-install 'name]
[else 'dir])
#:mode (cond
[source 'source]
[binary 'binary]
[built 'built]
[else 'as-is])))]
;; ----------------------------------------
[config
"View and modify the package manager's configuration"
#:once-each
[#:bool set () "Set <key> to <val> ..."]
#:once-any
scope-flags ...
#:args (key . val)
(call-with-package-scope
'config
scope scope-dir installation user #f #f
(lambda ()
(if set
(with-pkg-lock
(pkg-config #t (cons key val)))
(with-pkg-lock/read-only
(pkg-config #f (cons key val))))))]
;; ----------------------------------------
[catalog-show
"Show package information as reported by a catalog"
#:once-each
[#:bool all () "Show all packages"]
[#:bool only-names () "Show only package names"]
[#:bool modules () "Show implemented modules"]
catalog-flags ...
[(#:str vers #f) version ("-v") "Show result for Racket <vers>"]
#:args pkg-name
(when (and all (pair? pkg-name))
((pkg-error 'catalog-show) "both `--all' and package names provided"))
(parameterize ([current-pkg-catalogs (and catalog
(list (catalog->url catalog)))]
[current-pkg-error (pkg-error 'catalog-show)]
[current-pkg-scope-version (or version
(current-pkg-scope-version))])
(pkg-catalog-show pkg-name
#:all? all
#:only-names? only-names
#:modules? modules))]
;; ----------------------------------------
[catalog-copy
"Copy/merge package name catalogs"
#:once-each
[#:bool from-config () "Include currently configured catalogs last"]
#:once-any
[#:bool force () "Force replacement fo existing file/directory"]
[#:bool merge () "Merge to existing database"]
#:once-each
[#:bool override () "While merging, override existing with new"]
[(#:str vers #f) version ("-v") "Copy information suitable for Racket <vers>"]
#:args catalog
(parameterize ([current-pkg-error (pkg-error 'catalog-copy)])
(when (null? catalog)
((current-pkg-error) "need a destination catalog"))
(parameterize ([current-pkg-scope-version (or version
(current-pkg-scope-version))])
(pkg-catalog-copy (drop-right catalog 1)
(last catalog)
#:from-config? from-config
#:force? force
#:merge? merge
#:override? override)))]))]))
(make-commands
#:scope-flags
([(#:sym scope [installation user] #f) scope ()
("Select package <scope>, one of"
" installation: for all users of the Racket installation"
" user: as user-specific for an installation version/name")]
[#:bool installation ("-i") "Shorthand for `--scope installation'"]
[#:bool user ("-u") "Shorthand for `--scope user'"]
[(#:str dir #f) scope-dir () "Select package scope <dir>"])
#:job-flags
([#:bool no-setup () ("Don't run `raco setup' after changing packages (usually"
"not a good idea)")]
[(#:num n #f) jobs ("-j") "Setup with <n> parallel jobs"])
#:catalog-flags
([(#:str catalog #f) catalog () "Use <catalog> instead of configured catalogs"])
#:install-type-flags
([(#:sym type [file dir file-url dir-url github name] #f) type ("-t")
("Type of <pkg-source>;"
"valid <types>s are: file, dir, file-url, dir-url, github, or name;"
"if not specified, the type is inferred syntactically")]
[(#:str name #f) name ("-n") ("Name of package, instead of inferred"
"(makes sense only when a single <pkg-source> is given)")])
#:install-dep-flags
([(#:sym mode [fail force search-ask search-auto] #f) deps ()
("Specify the behavior for uninstalled dependencies, with"
"<mode> as one of"
" fail: cancels if dependencies are not installed"
" force: continues despite missing dependencies"
" search-ask: looks for dependencies in the package catalogs"
" and asks for permission to auto-install"
" search-auto: like `search-ask', but does not ask for permission")])
#:install-dep-desc
("where the default is `search-ask' if <pkg-source> is a package name"
"or `fail' otherwise")
#:install-force-flags
([#:bool force () "Ignores conflicts"]
[#:bool ignore-checksums () "Ignores checksums"])
#:install-copy-flags
([#:bool link () ("Link a directory package source in place (default for a directory)")]
[#:bool static-link () ("Link in place, promising collections do not change")]
[#:bool copy () ("Treat directory sources the same as other sources")]
[#:bool source () ("Strip package's built elements before installing; implies --copy")]
[#:bool binary () ("Strip packages' source elements before installing; implies --copy")])
#:install-copy-defns
[(define link-dirs? (not (or copy source binary)))
(define a-type (or (and link 'link)
(and static-link 'static-link)
(and (eq? type 'dir) link-dirs? 'link)
type))]))

View File

@ -52,7 +52,8 @@
(define github-client_secret (make-parameter #f))
(define (package-url->checksum pkg-url-str [query empty]
#:download-printf [download-printf void])
#:download-printf [download-printf void]
#:pkg-name [pkg-name "package"])
(define pkg-url
(string->url pkg-url-str))
(match (url-scheme pkg-url)
@ -94,7 +95,7 @@
(hash-ref (hash-ref b 'commit) 'sha)))]
[_
(define u (string-append pkg-url-str ".CHECKSUM"))
(download-printf "Downloading checksum\n")
(download-printf "Downloading checksum for ~a\n" pkg-name)
(log-pkg-debug "Downloading checksum as ~a" u)
(call/input-url+200 (string->url u)
port->string)]))

View File

@ -62,7 +62,7 @@
body ...
#:handlers
(λ (_ . formals) final-expr)
(ensure-list (pimap symbol->string 'formals))
(pimap symbol->string 'formals)
(λ (help-string)
(for-each (λ (l) (display l) (newline)) (wrap-to-count long-description 80))
(newline)
@ -102,18 +102,13 @@
[extra (build-string (- n l) (λ (n) #\space))])
(string-append str extra)))
(define (ensure-list x)
(if (or (null? x) (pair? x))
x
(list x)))
;; pimap : (A -> B) improper-listof A -> improper-listof B
;; pimap : (A -> B) improper-listof A -> listof B
(define (pimap f pil)
(cond
[(null? pil) '()]
[(pair? pil) (cons (pimap f (car pil))
[(pair? pil) (cons (f (car pil))
(pimap f (cdr pil)))]
[else (f pil)]))
[else (list (f pil))]))
;; wrap-to-count : string nat -> (listof string)
;; breaks str into substrings such that no substring