Skip to content

Commit

Permalink
raco pkg: change --scope-dir search-path handling
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mflatt committed Sep 10, 2018
1 parent 292dac4 commit 0e2ad7d
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 33 deletions.
10 changes: 7 additions & 3 deletions pkgs/racket-doc/pkg/scribblings/pkg.scrbl
Original file line number Diff line number Diff line change
Expand Up @@ -398,9 +398,13 @@ all users of the Racket installation.

A directory path can be used as a @tech{package scope}, in which case
package operations affect the set of packages installations in the
directory. An installation can be configured to include the
directory in its search path for installed packages (see
@secref["config-file" #:doc raco-doc]).
directory. An installation can be configured to include the directory
in its search path for installed packages (see @secref["config-file"
#: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
package in different scopes, but if such a configuration is forced,
Expand Down
20 changes: 16 additions & 4 deletions pkgs/racket-doc/scribblings/raco/config.scrbl
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,22 @@ directory}:
"pkg/scribblings/pkg.scrbl")]{package scope}. It defaults to
@filepath{pkgs} in the main shared-file directory.}

@item{@indexed-racket['pkgs-search-dirs] --- like
@racket['lib-search-dirs], but for packages in @exec{installation}
@tech[#:doc '(lib "pkg/scribblings/pkg.scrbl")]{package
scope}.}
@item{@indexed-racket['pkgs-search-dirs] --- similar to
@racket['lib-search-dirs], but for packages in roughly
@exec{installation} @tech[#:doc '(lib
"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
installation's directory containing executables. It defaults to a
Expand Down
27 changes: 27 additions & 0 deletions pkgs/racket-test/tests/pkg/test-scope-add.rkt
Original file line number Diff line number Diff line change
@@ -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))))))
21 changes: 20 additions & 1 deletion pkgs/racket-test/tests/pkg/tests-scope.rkt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"util.rkt"
pkg/lib
setup/dirs
racket/format)
racket/format
racket/file)

(this-test-is-run-by-the-main-test)

Expand Down Expand Up @@ -78,4 +79,22 @@
$ "racket -l racket/base -l pkg-test1/number -e '(number)'" =stdout> "2\n"
$ "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)

)))
43 changes: 18 additions & 25 deletions racket/collects/pkg/private/pkg-db.rkt
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,10 @@

;; read all packages in this scope or wider
(define (merge-pkg-dbs [scope (current-pkg-scope)])
(define (merge-next-pkg-dbs scope)
(parameterize ([current-pkg-scope scope])
(merge-pkg-dbs scope)))
(if (path? scope)
(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))])))
(for/fold ([ht #hash()]) ([m-scope (in-list (reverse (get-scope-list scope)))])
(define db (read-pkgs-db m-scope (current-pkg-scope-version)))
(for/fold ([ht ht]) ([(k v) (in-hash db)])
(hash-set ht k v))))

;; Finds the scope, in which `pkg-name' is installed; returns 'dir,
;; 'installation, a path, or #f (where #f means "not installed"). If
Expand Down Expand Up @@ -149,19 +139,22 @@
(and (path? scope)
(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
;; the current scope
(define current-scope (current-pkg-scope))
(if (path? current-scope)
(list current-scope)
(member current-scope
(append '(user)
(let ([main (find-pkgs-dir)])
(for/list ([d (get-pkgs-search-dirs)])
(if (equal? d main)
'installation
d)))))))
(or
;; Exploit the fact that `member` returns a list starting
;; with the found element:
(member current-scope
(append '(user)
(let ([main (find-pkgs-dir)])
(for/list ([d (get-pkgs-search-dirs)])
(if (equal? d main)
'installation
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])
;; Warning: takes locks individually.
Expand Down

0 comments on commit 0e2ad7d

Please sign in to comment.