raco pkg: change --scope-dir search-path handling

Formerly, `--scope-dir` would include only the specified directory in
the search path for already installed packages, etc., which means that
it would only work right as a kind of installation scope that is a
step beyond "installation" on the "user"-to-"installation" spectrum.
The `'pkgs-search-dirs` confiugration entry, meanwhile, provides more
control over search ordering in installation scope. Make `--scope-dir`
work more consistently with that search-path configration.

This change also makes "instllation"-scope operations use the search
path more consistently, since some actions used to use the whole
search list while others pruned any prefix before the main
installation directory in the search list.
This commit is contained in:
Matthew Flatt 2018-09-10 06:14:34 -06:00
parent 292dac4e51
commit 0e2ad7d596
5 changed files with 88 additions and 33 deletions

View File

@ -398,9 +398,13 @@ all users of the Racket installation.
A directory path can be used as a @tech{package scope}, in which case A directory path can be used as a @tech{package scope}, in which case
package operations affect the set of packages installations in the package operations affect the set of packages installations in the
directory. An installation can be configured to include the directory. An installation can be configured to include the directory
directory in its search path for installed packages (see in its search path for installed packages (see @secref["config-file"
@secref["config-file" #:doc raco-doc]). #:doc raco-doc]). When a directory path is used as a @tech{package
scope}, operations such as dependency checking will use all paths in
the configured search path starting with the one that is designed as a
@tech{package scope}; if the designated path is not in the configured
search path, then the dierctory by itself is used as the search path.
Conflict checking disallows installation of the same or conflicting Conflict checking disallows installation of the same or conflicting
package in different scopes, but if such a configuration is forced, package in different scopes, but if such a configuration is forced,

View File

@ -78,10 +78,22 @@ directory}:
"pkg/scribblings/pkg.scrbl")]{package scope}. It defaults to "pkg/scribblings/pkg.scrbl")]{package scope}. It defaults to
@filepath{pkgs} in the main shared-file directory.} @filepath{pkgs} in the main shared-file directory.}
@item{@indexed-racket['pkgs-search-dirs] --- like @item{@indexed-racket['pkgs-search-dirs] --- similar to
@racket['lib-search-dirs], but for packages in @exec{installation} @racket['lib-search-dirs], but for packages in roughly
@tech[#:doc '(lib "pkg/scribblings/pkg.scrbl")]{package @exec{installation} @tech[#:doc '(lib
scope}.} "pkg/scribblings/pkg.scrbl")]{package scope}. More precisely, a
@racket[#f] value in the list is replaced with the directory
specified by @racket['pkgs-dir], and that point in the search
list corresponds to @exec{installation} scope. Paths before or
after a @racket[#f] value in the list can be selected as a
scopes to start searches at that path's point in the list.
Directories listed in @racket['pkgs-search-dirs] typically oblige
a corresponding entry in @racket['links-search-files], where
the corresponding entry is @filepath{links.rktd} within the
directory.
@history[#:changed "7.0.0.19" @elem{Adapt the package-search path in
a general way for a directory scope.}]}
@item{@indexed-racket['bin-dir] --- a path, string, or byte string for the @item{@indexed-racket['bin-dir] --- a path, string, or byte string for the
installation's directory containing executables. It defaults to a installation's directory containing executables. It defaults to a

View File

@ -0,0 +1,27 @@
#lang racket/base
(require racket/cmdline
setup/dirs)
;; This module is meant to be run via "tests-scope.rkt". It adds the
;; given paths to the package-search list, and it adds "links.rktd" in
;; those paths to the links-search list.
(command-line
#:args
path
(unless (null? path)
(let ([paths path]
[file (build-path (find-config-dir) "config.rktd")])
(define ht (call-with-input-file* file read))
(define new-ht
(hash-set (hash-set ht
'pkgs-search-dirs
(append paths (hash-ref ht 'pkgs-search-dirs '(#f))))
'links-search-files
(append (for/list ([path (in-list paths)])
(path->string (build-path path "links.rktd")))
(hash-ref ht 'links-search-files '(#f)))))
(call-with-output-file*
file
#:exists 'truncate/replace
(lambda (o) (write new-ht o))))))

View File

@ -3,7 +3,8 @@
"util.rkt" "util.rkt"
pkg/lib pkg/lib
setup/dirs setup/dirs
racket/format) racket/format
racket/file)
(this-test-is-run-by-the-main-test) (this-test-is-run-by-the-main-test)
@ -78,4 +79,22 @@
$ "racket -l racket/base -l pkg-test1/number -e '(number)'" =stdout> "2\n" $ "racket -l racket/base -l pkg-test1/number -e '(number)'" =stdout> "2\n"
$ "raco pkg remove pkg-test1" =stdout> #rx"Inferred package scope: installation") $ "raco pkg remove pkg-test1" =stdout> #rx"Inferred package scope: installation")
(initialize-catalogs)
(define alone-dir (make-temporary-file "alone~a" 'directory))
(define in-search-dir (make-temporary-file "in-search~a" 'directory))
(shelly-case
"directory as a package scope"
$ "raco pkg config -i --set catalogs http://localhost:9990 http://localhost:9991"
$ "raco pkg install -i pkg-test1"
$ "racket -l pkg-test1" =stdout> #rx"main loaded"
;; won't find "base" on catalog:
$ (~a "raco pkg install --scope-dir "alone-dir" --auto pkg-test2") =exit> 1 =stderr> #rx"cannot find package.*base"
$ "racket -l pkg-test2" =exit> 1 =stderr> #rx"collection not found"
$ (~a "racket -l tests/pkg/test-scope-add "in-search-dir)
;; will install "pkg-test2" without an extra "pkg-test1" or "base":
$ (~a "raco pkg install --scope-dir "in-search-dir" --auto pkg-test2")
$ "racket -l pkg-test2" =stdout> #rx"pkg-test2/main loaded")
(delete-directory/files alone-dir)
(delete-directory/files in-search-dir)
))) )))

View File

@ -37,20 +37,10 @@
;; read all packages in this scope or wider ;; read all packages in this scope or wider
(define (merge-pkg-dbs [scope (current-pkg-scope)]) (define (merge-pkg-dbs [scope (current-pkg-scope)])
(define (merge-next-pkg-dbs scope) (for/fold ([ht #hash()]) ([m-scope (in-list (reverse (get-scope-list scope)))])
(parameterize ([current-pkg-scope scope]) (define db (read-pkgs-db m-scope (current-pkg-scope-version)))
(merge-pkg-dbs scope))) (for/fold ([ht ht]) ([(k v) (in-hash db)])
(if (path? scope) (hash-set ht k v))))
(read-pkg-db)
(case scope
[(installation)
(for*/hash ([dir (in-list (get-pkgs-search-dirs))]
[(k v) (read-pkgs-db dir)])
(values k v))]
[(user)
(define db (read-pkgs-db 'user (current-pkg-scope-version)))
(for/fold ([ht (merge-next-pkg-dbs 'installation)]) ([(k v) (in-hash db)])
(hash-set ht k v))])))
;; Finds the scope, in which `pkg-name' is installed; returns 'dir, ;; Finds the scope, in which `pkg-name' is installed; returns 'dir,
;; 'installation, a path, or #f (where #f means "not installed"). If ;; 'installation, a path, or #f (where #f means "not installed"). If
@ -149,19 +139,22 @@
(and (path? scope) (and (path? scope)
(build-path scope "links.rktd"))) (build-path scope "links.rktd")))
(define (get-scope-list) (define (get-scope-list [current-scope (current-pkg-scope)])
;; Get a list of scopes suitable for searches with respect to ;; Get a list of scopes suitable for searches with respect to
;; the current scope ;; the current scope
(define current-scope (current-pkg-scope)) (or
(if (path? current-scope) ;; Exploit the fact that `member` returns a list starting
(list current-scope) ;; with the found element:
(member current-scope (member current-scope
(append '(user) (append '(user)
(let ([main (find-pkgs-dir)]) (let ([main (find-pkgs-dir)])
(for/list ([d (get-pkgs-search-dirs)]) (for/list ([d (get-pkgs-search-dirs)])
(if (equal? d main) (if (equal? d main)
'installation 'installation
d))))))) d)))))
;; In case the specified scope wasn't in the list,
;; then make a search path that has just that scope:
(list current-scope)))
(define (pkg-directory pkg-name #:cache [cache #f]) (define (pkg-directory pkg-name #:cache [cache #f])
;; Warning: takes locks individually. ;; Warning: takes locks individually.