#lang scribble/doc
@(require "mz.rkt"
          (for-label setup/dirs
                     setup/collection-search))

@title[#:tag "collects"]{Libraries and Collections}

A @deftech{library} is a @racket[module] declaration for use by multiple
programs. Racket further groups libraries into @deftech{collections}.
Typically, collections are added via @deftech{packages} (see
@other-doc['(lib "pkg/scribblings/pkg.scrbl")]); the package manager
works outside of the Racket core, but it configures the core run-time
system through @tech{collection links files}.

Libraries in collections are referenced through @racket[lib] paths
(see @racket[require]) or symbolic shorthands. For example, the
following module uses the @filepath{getinfo.rkt} library module from
the @filepath{setup} collection, and the @filepath{cards.rkt} library
module from the @filepath{games} collection's @filepath{cards}
subcollection:

@racketmod[
racket
(require (lib "setup/getinfo.rkt")
         (lib "games/cards/cards.rkt"))
....
]

This example is more compactly and more commonly written using
symbolic shorthands:

@racketmod[
racket
(require setup/getinfo
         games/cards/cards)
....
]

When an identifier @racket[_id] is used in a @racket[require] form, it
is converted to @racket[(lib _rel-string)] where @racket[_rel-string]
is the string form of @racket[_id].

A @racket[_rel-string] in @racket[(lib _rel-string)] consists of one
or more path elements that name collections, and then a final path
element that names a library file; the path elements are separated by
@litchar{/}. If @racket[_rel-string] contains no @litchar{/}s, then
@litchar{/main.rkt} is implicitly appended to the path. If
@racket[_rel-string] contains @litchar{/} but does not end with a file
suffix, then @litchar{.rkt} is implicitly appended to the path.

Libraries also can be distributed via @|PLaneT| packages. Such
libraries are referenced through a @racket[planet] module path (see
@racket[require]) and are downloaded by Racket on demand, instead of
referenced through @tech{collections}.

The translation of a @racket[planet] or @racket[lib] path to a
@racket[module] declaration is determined by the @tech{module name
resolver}, as specified by the @racket[current-module-name-resolver]
parameter.

@; ----------------------------------------------------------------------

@section[#:tag "collects-search"]{Collection Search Configuration}

For the default @tech{module name resolver}, the search path for
collections is determined by the
@racket[current-library-collection-links] parameter and the
@racket[current-library-collection-paths] parameter:

@itemlist[

 @item{The most primitive @tech{collection}-based modules are located
       in @filepath{collects} directory relative to the Racket
       executable. Libraries for a collection are grouped within a
       directory whose name matches the collection name. The path to
       the @filepath{collects} directory is normally included in
       @racket[current-library-collection-paths].}

 @item{Collection-based libraries also can be installed other
       directories, perhaps user-specific, that are structured like
       the @filepath{collects} directory. Those additional directories
       can be included in the
       @racket[current-library-collection-paths] parameter either
       dynamically, through command-line arguments to @exec{racket},
       or by setting the @envvar{PLTCOLLECTS} environment variable;
       see @racket[find-library-collection-paths].}

 @item{@tech{Collection links files} provide a mapping from top-level
       collection names to directories, plus additional
       @filepath{collects}-like directories (that have subdirectories
       with names that match collection names). Each @tech{collection
       links file} to be searched is referenced by the
       @racket[current-library-collection-links] parameter; the parameter
       references the file, and not the file's content, so
       that changes to the file can be detected and affect later
       module resolution. See also
       @racket[find-library-collection-links].}

 @item{The @racket[current-library-collection-links] parameter's value
       can also include hash tables that provide the same content as
       @tech{collection links files}: a mapping from collection names
       in symbol form to a list of paths for the collection, or from
       @racket[#f] to a list of @filepath{collects}-like paths.}

 @item{Finally, the @racket[current-library-collection-links]
       parameter's value includes @racket[#f] to indicate the point in
       the search process at which the @tech{module-name resolver} should
       check @racket[current-library-collection-paths] relative to the
       files and hash tables in @racket[current-library-collection-links].}

]

To resolve a module reference @racket[_rel-string], the default
@tech{module name resolver} searches collection links in
@racket[current-library-collection-links] from first to last to locate
the first directory that contains @racket[_rel-string], splicing a
search through in @racket[current-library-collection-paths] where in
@racket[current-library-collection-links] contains @racket[#f].  The
filesystem tree for each element in the link table and search path is
effectively @deftech[#:key "collection splicing"]{spliced} together with the filesystem trees of other path
elements that correspond to the same collection. Some Racket tools
rely on unique resolution of module path names, so an installation and
configuration should not allow multiple files to match the same
collection and file combination.

The value of the @racket[current-library-collection-links] parameter
is initialized by the @exec{racket} executable to the result of
@racket[(find-library-collection-links)], and the value of the
@racket[current-library-collection-paths] parameter is initialized to
the result of @racket[(find-library-collection-paths)].

@; ----------------------------------------------------------------------

@section[#:tag "links-file"]{Collection Links}

@deftech{Collection links files} are used by
@racket[collection-file-path], @racket[collection-path], and the
default @tech{module name resolver} to locate collections before
trying the @racket[(current-library-collection-paths)] search
path. The @tech{collection links files} to use are determined by the
@racket[current-library-collection-links] parameter, which is
initialized to the result of @racket[find-library-collection-links].

A @tech{collection links file} is @racket[read] with default reader
parameter settings to obtain a list. Every element of the list must be
a link specification with one of the forms @racket[(list _string
_encoded-path)], @racket[(list _string _encoded-path _regexp)], @racket[(list 'root
_encoded-path)], @racket[(list 'root _encoded-path _regexp)], @racket[(list 'static-root
_encoded-path)], @racket[(list 'static-root _encoded-path _regexp)].
A @racket[_string] names a
top-level @tech{collection}, in which case @racket[_encoded-path] describes a path
that can be used as the collection's path (directly, as opposed to a
subdirectory of @racket[_encoded-path] named by @racket[_string]). A
@racket['root] entry, in contrast, acts like an path in
@racket[(current-library-collection-paths)].  A
@racket['static-root] entry is like a @racket['root] entry, but
where the immediate content of the directory is assumed not to change unless the
@tech{collection links file} changes.
Each @racket[_encoded-path] is either a string, a
byte string that is converted to a path with @racket[bytes->path],
or a list of relative path-element byte strings, @racket['up], and @racket['same]
indicators that are combined with @racket[build-path] with the byte
strings converted with @racket[bytes->path-element].
If @racket[_encoded-path] describes a
relative path, it is relative to the directory containing the
@tech{collection links file}. If @racket[_regexp] is specified in a
link, then the link is used only if @racket[(regexp-match?  _regexp
(version))] produces a true result.

A single top-level collection can have multiple links in a
@tech{collection links file}, and any number of @racket['root] entries
can appear. The corresponding paths are effectively spliced together,
since the paths are tried in order to locate a file or sub-collection.

The @exec{raco link} command-link tool can display, install, and
remove links in a @tech{collection links file}. See @secref[#:doc
raco-doc "link"] in @other-manual[raco-doc] for more information.

@history[#:changed "8.1.0.6" @elem{Changed @racket[_encoded-path] to
                                   allow bytes strings and lists.}]

@; ----------------------------------------

@section[#:tag "collects-api"]{Collection Paths and Parameters}

@defproc[(find-library-collection-paths [pre-extras (listof path-string?) null]
                                        [post-extras (listof path-string?) null]
                                        [config hash? (read-installation-configuration-table)]
                                        [name (get-installation-name config)])
         (listof path?)]{

Produces a list of paths, which is normally used to initialize
@racket[current-library-collection-paths], as follows:

@itemize[

@item{The path produced by @racket[(build-path (find-system-path
    'addon-dir) name "collects")] is the first element of the
  default collection path list, unless the value of the
  @racket[use-user-specific-search-paths] parameter is @racket[#f].}

 @item{Extra directories provided in @racket[pre-extras] are included
  next to the default collection path list, converted to complete
  paths relative to the executable.}

 @item{If the directory specified by @racket[(find-system-path
    'collects-dir)] is absolute, or if it is relative (to the
  executable) and it exists, then it is added to the end of the
  default collection path list.}

 @item{Extra directories provided in @racket[post-extras] are included
  last in the default collection path list, converted to complete
  paths relative to the executable.}

 @item{If @racket[config] has a value for
  @racket['collects-search-dirs], then it is used in place of the
  default collection path list (as constructed by the preceding three
  bullets), and the default is spliced in place of any @racket[#f]
  within the @racket['collects-search-dirs] list. If @racket[config]
  does not have a @racket['collects-search-dirs] value, then the
  default collection path list is used.}

 @item{If the @indexed-envvar{PLTCOLLECTS} environment variable is
  defined, it is combined with the default list using
  @racket[path-list-string->path-list], as long as the value of
  @racket[use-user-specific-search-paths] is true. If it is not
  defined or if the value @racket[use-user-specific-search-paths] is
  @racket[#f], the collection path list as constructed by the
  preceding four bullets is used directly.

  Note that on @|AllUnix|, paths are separated by @litchar{:}, and
  on Windows by @litchar{;}.  Also,
  @racket[path-list-string->path-list] splices the default paths at an
  empty path, for example, with many Unix shells you can set
  @envvar{PLTCOLLECTS} to @tt{":`pwd`"}, @tt{"`pwd`:"}, or
  @tt{"`pwd`"} to specify search the current directory after, before,
  or instead of the default paths, respectively.}

]

@history[#:changed "8.4.0.3" @elem{Added the @racket[config] and
                                   @racket[name] arguments.}]}

@defproc[(find-library-collection-links [config hash? (read-installation-configuration-table)]
                                        [name (get-installation-name config)])
         (listof (or/c #f (and/c path? complete-path?)))]{

Produces a list of paths and @racket[#f], which is normally used to
initialize @racket[current-library-collection-links], as follows:

@itemlist[

 @item{The list starts with @racket[#f], which causes the default
       @tech{module name resolver}, @racket[collection-file-path],
       and @racket[collection-path] to try paths in
       @racket[current-library-collection-paths] before
       @tech{collection links files}.}

 @item{As long as the values of
       @racket[use-user-specific-search-paths] and
       @racket[use-collection-link-paths] are true, the second element
       in the result list is the path of the user--specific
       @tech{collection links file}, which is @racket[(build-path
       (find-system-path 'addon-dir) name "links.rktd")] by default,
       but it can be replaced by a @racket['links-file] value in
       @racket[config].}

 @item{As long as the value of @racket[use-collection-link-paths] is
       true, the rest of the list contains a result like that of
       @racket[get-links-search-files], but using @racket[config] if
       supplied instead of reading the installation's
       @filepath{config.rktd} file. Typically, that result is a list
       with a single path, @racket[(build-path (find-config-dir)
       "links.rktd")].}

]

@history[#:changed "8.4.0.3" @elem{Added the @racket[config] and
                                   @racket[name] arguments.}]}


@defproc*[([(collection-file-path [file path-string?] [collection path-string?] ...+
                                  [#:check-compiled? check-compiled? any/c
                                                     (regexp-match? #rx"[.]rkt$" file)])
            path?]
           [(collection-file-path [file path-string?] [collection path-string?] ...+
                                  [#:fail fail-proc (string? . -> . any)]
                                  [#:check-compiled? check-compiled? any/c
                                                     (regexp-match? #rx"[.]rkt$" file)])
            any])]{

Returns the path to the file indicated by @racket[file] in the
collection specified by the @racket[collection]s, where the second
@racket[collection] (if any) names a sub-collection, and so on.  The
search uses the values of @racket[current-library-collection-links]
and @racket[current-library-collection-paths].

@margin-note{See also @racket[collection-search] in
             @racketmodname[setup/collection-search].}

If @racket[file] is not found, but @racket[file] ends in
@filepath{.rkt} and a file with the suffix @filepath{.ss} exists, then
the directory of the @filepath{.ss} file is used. If @racket[file] is
not found and the @filepath{.rkt}/@filepath{.ss} conversion does not
apply, but a directory corresponding to the @racket[collection]s is
found, then a path using the first such directory is
returned.

If @racket[check-compiled?] is true, then the search also depends on
@racket[use-compiled-file-paths] and
@racket[current-compiled-file-roots]; if @racket[file] is not found,
then a compiled form of @racket[file] with the suffix @filepath{.zo}
is checked in the same way as the default @tech{compiled-load
handler}.  If a compiled file is found, the result from
@racket[collection-file-path] reports the location that @racket[file]
itself would occupy (if it existed) for the found compiled file.

Finally, if the collection is not found, and if @racket[fail-proc] is
provided, then @racket[fail-proc] is applied to an error message (that
does not start @scheme["collection-file-path:"] or otherwise claim a
source), and its result is the result of
@racket[collection-file-path].  If @racket[fail-proc] is not provided
and the collection is not found, then the
@exnraise[exn:fail:filesystem].

@examples[(eval:alts (collection-file-path "main.rkt" "racket" "base")
                     (build-path "path" "to" "collects" "racket" "base" "main.rkt"))
          (eval:error (collection-file-path "sandwich.rkt" "bologna"))]

@history[#:changed "6.0.1.12" @elem{Added the @racket[check-compiled?] argument.}]}


@defproc*[([(collection-path [collection path-string?] ...+)
            path?]
           [(collection-path [collection path-string?] ...+
                             [#:fail fail-proc (string? . -> . any)])
            any])]{

  @deprecated[#:what "function" @racket[collection-file-path]]{
  @tech{Collection splicing} implies that a given collection can have
  multiple paths, such as when multiple @tech[#:doc
  '(lib "scribblings/guide/guide.scrbl")]{packages} provide modules for a
  collection.}

Like @racket[collection-file-path], but without a specified file name,
so that a directory indicated by @racket[collection]s is returned.

When multiple directories correspond to the collection, the first one
found in the search sequence (see @secref["collects-search"]) is returned.}


@defparam*[current-library-collection-paths paths
                                            (listof (and/c path-string? complete-path?))
                                            (listof (and/c path? complete-path?))]{

Parameter that determines a list of complete directory paths for
finding libraries (as referenced in @racket[require], for example)
through the default @tech{module name resolver} and for finding paths
through @racket[collection-path] and
@racket[collection-file-path]. See @secref["collects-search"] for more
information.}


@defparam*[current-library-collection-links paths
                                            (listof (or/c #f
                                                          (and/c path-string? complete-path?)
                                                          (hash/c (or/c (and/c symbol? module-path?) #f)
                                                                  (listof (and/c path-string? complete-path?)))))
                                            (listof (or/c #f
                                                          (and/c path? complete-path?)
                                                          (hash/c (or/c (and/c symbol? module-path?) #f)
                                                                  (listof (and/c path? complete-path?)))))]{


Parameter that determines @tech{collection links files}, additional
paths, and the relative search order of
@racket[current-library-collection-paths] for finding libraries (as
referenced in @racket[require], for example) through the default
@tech{module name resolver} and for finding paths through
@racket[collection-path] and @racket[collection-file-path]. See
@secref["collects-search"] for more information.}


@defboolparam[use-user-specific-search-paths on?]{

Parameter that determines whether user-specific paths, which are in
the directory produced by @racket[(find-system-path 'addon-dir)], are
included in search paths for collections and other files. For example,
the initial value of @racket[find-library-collection-paths] omits the
user-specific collection directory when this parameter's value is
@racket[#f].

If @Flag{U} or @DFlag{no-user-path} argument to @exec{racket}, then
@racket[use-user-specific-search-paths] is initialized to
@racket[#f].}


@defboolparam[use-collection-link-paths on?]{

Parameter that determines whether @tech{collection links files} are
included in the result of @racket[find-library-collection-links].

If this parameter's value is @racket[#f] on start-up, then
@tech{collection links files} are effectively disabled permanently for
the Racket process. In particular, if an empty string is provided as
the @Flag{X} or @DFlag{collects} argument to @exec{racket}, then not
only is @racket[current-library-collection-paths] initialized to the
empty list, but @racket[use-collection-link-paths] is initialized to
@racket[#f].}


@defproc[(read-installation-configuration-table) (and/c hash? immutable?)]{

Returns the content of the installation's @filepath{config.rktd} file
(see @secref["config-file" #:doc raco-doc]) as long as that content is
a @tech{hash table}, and otherwise returns an empty hash table.

@history[#:added "8.4.0.3"]}
