racket/collects/planet/planet.scrbl
Eli Barzilay 672910f27b Lots of bad TAB eliminations.
I started from tabs that are not on the beginning of lines, and in
several places I did further cleanings.

If you're worried about knowing who wrote some code, for example, if you
get to this commit in "git blame", then note that you can use the "-w"
flag in many git commands to ignore whitespaces.  For example, to see
per-line authors, use "git blame -w <file>".  Another example: to see
the (*much* smaller) non-whitespace changes in this (or any other)
commit, use "git log -p -w -1 <sha1>".
2012-11-07 11:22:20 -05:00

828 lines
35 KiB
Racket

#lang scribble/manual
@(require
(for-label
racket/base
scribble/manual
planet/config
planet/util
planet/version
planet/syntax
planet/scribble)
scribble/bnf)
@(define-syntax-rule (eg (code resl) ...)
(interaction
(eval:alts code resl)
...))
@title{PLaneT: Automatic Package Distribution}
@author["Jacob Matthews" "Robert Bruce Findler"]
The @PLaneT system is a method for automatically sharing code packages,
both as libraries and as full applications, that gives every user of a
@PLaneT client the illusion of having a local copy of every code
package on the server. It
consists of @link["http://planet.racket-lang.org/"]{the central @PLaneT
package repository}, a server that holds all PLaneT packages, and
the PLaneT client, built into Racket, which transparently
interacts with the server on your behalf when necessary.
@table-of-contents[]
@section{Using PLaneT}
To use a @PLaneT package in a program, require it using the
@racket[planet] @racket[require] form (see @(secref "require" #:doc
'(lib "scribblings/reference/reference.scrbl")) for a full reference
on the features of the @racket[require] statement in general and the
exact allowed grammar of PLaneT require statements). Here we explain
how to use PLaneT by example.
@subsection[#:tag "finding-a-package"]{Finding a Package}
If you are new to PLaneT, the first thing to do is visit
@link["http://planet.racket-lang.org/"]{the PLaneT repository web site}
and see what packages are available. People contribute new PLaneT
packages all the time --- if you want to be notified whenever a new or
updated package is released, you can subscribe to the
(announcement-only)
@link["http://mailman.cs.uchicago.edu/mailman/listinfo/planet-announce"]{PLaneT-announce mailing list}
or use an RSS reader to subscribe to
@link["http://planet.racket-lang.org/300/planet.rss"]{PLaneT's RSS feed}.
To use a package from PLaneT in your program, the easiest thing to do
is copy the @racket[require] code snippet off of that package's page
and paste it ino your program. For instance, to use Schematics'
@link["http://planet.racket-lang.org/users/schematics/spgsql.plt"]{spgsql.plt}
package (a library for interacting with the
@link["http://www.postgresql.org/"]{PostgresQL} database), as of this
writing you would copy and paste the line:
@racketblock[(require (planet "spgsql.rkt" ("schematics" "spgsql.plt" 2 3)))]
into your program. This line requires the file @filepath{spgsql.rkt} in package
version 2.3 of the @filepath{spgsql.plt} package written by
@filepath{schematics}. That does two things: first, it downloads and
installs a version of @filepath{spgsql.plt} that is compatible with
package version 2.3 from @link["http://planet.racket-lang.org/"]{the
central PLaneT repository} if a compatible version hasn't already been
installed. Second, it requires the module in file @filepath{spgsql.rkt}
from that package, making all of its exported bindings available for use.
Unlike with most package-distribution systems, package downloading and
installation in PLaneT is @emph{transparent}: there's no need for
you to do anything special the first time you want to use a package,
and there's no need for you to even know whether or not a particular
package is installed on your computer or the computers where your code
will be deployed.
If you want to find all of the latest versions of the
packages that planet has available, visit
@centered{@url{http://planet.racket-lang.org/servlets/pkg-info.ss}}
It returns a list matching the contract
@racketblock[(listof (list/c string?
string?
(list/c exact-positive-integer?
exact-nonnegative-integer?)))]
Each sublist represents
the latest version of one of the packages and contains the
userid, the package name (including ".plt"), and
the version (major and minor numbers).
@subsection{Shorthand Syntax}
The code snippet above can also be written using a new shorter syntax:
@racketblock[(require (planet schematics/spgsql:2:3/spgsql))]
The two forms behave identically. In the abbreviated syntax, however,
it is illegal to write the trailing @filepath{.rkt} suffix on the file
name to be required or the trailing @filepath{.plt} on the package file
name. (They are mandatory for the long-form syntax.) It is also legal
in the abbreviated syntax to omit a filename to be required entirely;
in that case, PLaneT requires the file @filepath{main.rkt} in the given
package.
@subsection{Networking troubles}
Sometimes, when PLaneT tries to download and install a
package for the first time, your operating system may block
it from access to the network. If you are uncomfortable
giving DrRacket free access to the network (or if your
attempts to do so do not seem to work), then you can use
your browser to manually install a planet package.
To see how this works, lets assume you want to install the PLAI package
and @racketblock[(require (planet plai/plai:1))] is not working for you.
@itemize[
@item{First,
fire up a command-line window and use @tt{raco planet url} to
determine the url for downloading the package.
To find the url for version @tt{(1 1)} of the plai package,
do this:
@tt{% raco planet url plai plai.plt 1 1}
and get this as a response:
@tt{http://planet.racket-lang.org/servlets/planet-servlet.rkt?lang=%224.1.5.3%22&name=%22plai.plt%22&maj=1&min-lo=1&min-hi=%23f&path=%28%22plai%22%29}}
@item{Copy and paste that url into your browser, which
should trigger the dowload of a file called
@tt{plai.plt}. Note that your browser will probably try to
call the file something else. Rename it to @tt{plai.plt}.}
@item{Now run the command-line tool one more time to install the plt file:
@tt{% raco planet fileinject plai plai.plt 1 1}
This command should be run from the same directory where you saved @tt{plai.plt}.
This command may fail, since version @tt{(1 1)} of the PLAI
package depends on @tt{cce/scheme:4:1}. If it does, simply
repeat the above steps for that package first, and then
continue with the @tt{fileinject} command for PLAI.}
@item{Finally, to check that the installation is successful,
run @tt{raco planet show}. You should see output like this
(possibly with slightly different version numbers, if the
packages have been updated since this was written):
@verbatim{
Normally-installed packages:
cce scheme.plt 4 1
plai plai.plt 1 1
}}
]
Once that is complete, PLaneT will use that version of the
package for any subsequent @racket[require]s and won't try
to use the network.
@subsection{Fine-Grained Control Over Package Imports}
The PLaneT client is designed to balance two competing goals:
transparent upgradability and control over the effect of a package
requirement. To that end, the most basic PLaneT require form offers
maximum upgradability, but several more specialized forms allow
finer-grained control over what versions of the named package may be
downloaded.
@margin-note{Package versions should not be confused with program or library
versions; a @italic{package version} is a PLaneT-specific version
number that encodes backwards-compatibility information.}
The most basic planet require line, which is what is used in the form
@racketblock[(require (planet "spgsql.rkt" ("schematics" "spgsql.plt" 2 3)))]
in longhand notation, or
@racketblock[(require (planet schematics/spgsql:2:3/spgsql))]
in shorthand notation, should be read ``Require from PLaneT
@italic{any} release of Schematics' @filepath{spgsql.plt} package that
is backwards-compatible with package version 2.3.'' (The actual
package version used is determined by @seclink["search-order"]{the
PLaneT search order}.) To signal this explicitly, it is possible to
write
@racketblock[(require (planet "spgsql.rkt" ("schematics" "spgsql.plt" 2 (+ 3))))]
or
@racketblock[(require (planet schematics/spgsql:2:>=3/spgsql))]
both of which mean the same thing as the first pair of require lines.
@margin-note{See @secref{backwards-compatibility} for a more detailed discussion of
backwards-compatibility obligations for PLaneT packages.}
The notion of ``backwards-compatibility'' has a specific meaning in
PLaneT: by definition, for the purposes of automation, a package is
considered to be backwards-compatible with any other package of the
same owner, name, and major version, and any @italic{lower} minor
version. Package maintainers are responsible for marking new releases
that break backwards-compatibility by incrementing their major-version
number. This means that all of the above require specifications will
match any release of @filepath{unlib.plt} with major package version 3
(and any minor version), but will @italic{never} match releases of
@filepath{unlib.plt} with higher (or lower) major version numbers.
Of course a package author may make a mistake and introduced a
backwards-incompatibility unintentionally, or may fix a bug that code
in third-party libraries was already working around. In those cases,
it may help to make use of the ``upper bound'' form of the planet
require, in longhand form:
@racketblock[(require (planet "reduction-semantics.rkt"
("robby" "redex.plt" 4 (- 3))))]
and using shorthand notation:
@racketblock[(require (planet robby/redex:4:<=3/reduction-semantics))]
In this require line, any version of the package @filepath{redex.plt}
from package version 4.0 to package version 4.3 will match the require
spec (though as with any PLaneT require specification,
@seclink["search-order"]{the PLaneT package search order} determines
which package is actually loaded).
It is also possible to specify both an upper and a lower bound, using
the planet require's ``range'' form:
@racketblock[(require (planet "test.rkt" ("schematics" "schemeunit.plt" 2 (9 10))))]
or
@racketblock[(require (planet schematics/schemeunit:2:9-10/test))]
This form matches any package in the specified range (inclusive on
both ends), in this example the specifications match either package
version 2.9 or 2.10 of the @filepath{schemeunit.plt} package, but do
not match version with higher or lower minor version numbers (or any
other major version number).
Using the range form, it is possible to require a specific version of
a package as a special case (choosing the upper and lower bounds to be
equal), but this is a common enough case that it has special support
with the ``exact-match'' form:
@racketblock[(require (planet "unzip.rkt" ("dherman" "zip.plt" 2 (= 1))))]
or
@racketblock[(require (planet dherman/zip:2:=1/unzip))]
match only the exact package version 2.1 of the @filepath{zip.plt} package.
@;@subsection{Linkage}
@;@subsection{The Diamond Property}
@subsection{Monitoring PLaneT's progress}
PLaneT logs information about what it is doing to the @tt{info}
log (via @racket[log-info]).
In DrRacket, you can view the logs from the @onscreen{Show Log}
menu item in the @onscreen{View} menu, and Racket's logging output
can be controlled via command-line options and via environment
variables. See
@secref["logging" #:doc '(lib "scribblings/reference/reference.scrbl")]
for more details.
@section[#:tag "search-order"]{The PLaneT Search Order}
PLaneT has four strategies it uses in order to match a request with an
appropriate package that.
@subsection{Previous Linkage}
Whenever a file requires a package via PLaneT and that requirement is
satisfied, the system makes a note of exactly which package satisfied
that requirement and from then on always uses that exact same package,
even if a newer version is available. This is done to prevent "magic
upgrades" in which a program stops working after installation because
an unrelated package was installed. Such connections are called links
and are stored in a user-specific table called the linkage table.
@subsection{Acceptable Local Package}
If the PLaneT client doesn't have any previous linkage information, it
checks its list of already-installed PLaneT packages for one that
meets the requirement, and uses it if available. Both PLaneT-installed
packages and packages established through a development link
(see @secref{devlinks})
are checked simultaneously at this stage.
@subsection{Acceptable Remote Package}
If there is no acceptable local package, the PLaneT client sends
a request to the PLaneT server for a new package that would satisfy
the requirement. The server then finds the newest matching package
and sends it back to the client, which then installs it and uses
it to satisfy the original requirement.
@subsection{Cached Installation Archive}
If the remote server cannot be contacted (or fails in any way to
deliver an acceptable package), the PLaneT client consults the
uninstalled-packages cache, a cache of all previously-downloaded
packages, even those that are not currently installed. Racket
users who frequently upgrade their installations may have many
packages downloaded but not installed at any given time; this step
is intended to ensure that these users can still run programs even
if they temporarily lose network connection.
@section[#:tag "cmdline"]{The @exec{raco planet} Command-Line Tool}
The @exec{raco planet} command-line tool allows a command-line interface to
the most commonly-performed PLaneT tasks. It is invoked from the
command line as
@commandline{raco planet @italic{subcommand} @italic{arg} ...}
where @italic{subcommand} is a subcommand from the following list, and
@exec{@italic{arg}} is a sequence of arguments determined by that subcommand:
@(define (cmd name desc)
@item{@(seclink name (exec name)): @desc})
@itemize[
@cmd["create"]{create a PLaneT archive from a directory}
@cmd["install"]{download and install a given package}
@cmd["remove"]{remove the specified package from the local cache}
@cmd["show"]{list the packages installed in the local cache}
@cmd["clearlinks"]{clear the linkage table, allowing upgrades}
@cmd["fileinject"]{install a local file to the planet cache}
@cmd["link"]{create a development link}
@cmd["unlink"]{remove development link associated with the given package}
@cmd["fetch"]{download a package file without installing it}
@cmd["url"]{get a URL for the given package}
@cmd["open"]{unpack the contents of the given package}
@cmd["structure"]{display the structure of a given .plt archive}
@cmd["print"]{display a file within of the given .plt archive}]
Each of these commands is described in more detail below. All the
functionality of the command-line tool is also provided with a programmatic interface by
@seclink["util.rkt"]{the @filepath{util.rkt} library}.
@subsection[#:tag "create"]{@exec{create}}
Usage:
@commandline{raco planet create [ <option> ... ] <path>}
Create a PLaneT archive in the current directory whose contents are the
directory @exec{<path>}.
@exec{<option>} is one of:
@itemize[
@item{@exec{-f, --force}: force a package to be created even if its info.rkt file contains
errors.}]
@subsection[#:tag "install"]{@exec{install}}
Usage:
@commandline{raco planet install <owner> <pkg> <maj> <min>}
Download and install the package that @racket[(require (planet "file.rkt" (<owner> <pkg> <maj> <min>)))]
would install.
@subsection[#:tag "remove"]{@exec{remove}}
Usage:
@commandline{raco planet remove [ <option> ... ] <owner> <pkg> <maj> <min>}
Remove the specified package from the local cache, optionally also removing its
distribution file.
@exec{<option>} is one of:
@itemize[
@item{@exec{-e, --erase}: also remove the package's distribution file from the
uninstalled-package cache}]
@subsection[#:tag "show"]{@exec{show}}
Usage:
@commandline{raco planet show [ <option> ... ]}
List the packages installed in the local cache.
@exec{<option>} is one of:
@itemize[
@item{@exec{-p, --packages}: show packages only (default)}
@item{@exec{-l, --linkage}: show linkage table only}
@item{@exec{-a, --all}: show packages and linkage}]
@subsection[#:tag "clearlinks"]{@exec{clearlinks}}
Usage:
@commandline{raco planet clearlinks}
Clear the linkage table, allowing upgrades.
@subsection[#:tag "fileinject"]{@exec{fileinject}}
Usage:
@commandline{raco planet fileinject <owner> <plt-file> <maj> <min>}
Install local file <plt-file> into the planet cache as though it had been
downloaded from the planet server. It is treated as though it had the given owner name as its owner name,
the given file's filename as the its package name, and the given major and minor version numbers.
@subsection[#:tag "link"]{@exec{link}}
Usage:
@commandline{raco planet link <owner> <pkg> <maj> <min> <path>}
Create a development link (see @secref{devlinks}) between the given
package specifier and the specified directory name.
@subsection[#:tag "unlink"]{@exec{unlink}}
Usage:
@commandline{raco planet unlink [ <option> ] <owner> <pkg> <maj> <min>}
Remove any development link (see @secref{devlinks}) associated with
the given package.
@exec{<option>} can only be:
@itemize[@item{@exec{-q, --quiet}: don't signal an error on nonexistent links}]
@subsection[#:tag "fetch"]{@exec{fetch}}
Usage:
@commandline{raco planet fetch <owner> <pkg> <maj> <min>}
Download the given package file from the central PLaneT repository without installing it.
@subsection[#:tag "url"]{@exec{url}}
Usage:
@commandline{raco planet url <owner> <pkg> <maj> <min>}
Get a URL for the given package.
This is never necessary for normal use of planet, but may be helpful in some
circumstances for retrieving packages.
@subsection[#:tag "open"]{@exec{open}}
Usage:
@commandline{raco planet open <plt-file> <target>}
Unpack the contents of the given package into the given directory without
installing.
This command is not necessary for normal use of planet. It is intended to allow
you to inspect package contents offline without needing to install the package.
@subsection[#:tag "structure"]{@exec{structure}}
Usage:
@commandline{raco planet structure <plt-file>}
Print the structure of the PLaneT archive named by <plt-file> to the standard
output port.
This command does not unpack or install the named .plt file.
@subsection[#:tag "print"]{@exec{print}}
Usage:
@commandline{raco planet print <plt-file> <path>}
Print the contents of the file named by <path>, which must be a relative path
within the PLaneT archive named by <plt-file>, to the standard output port.
This command does not unpack or install the named .plt file.
@section[#:tag "hash-lang-planet"]{The @racketmodname[planet] Language}
@defmodulelang[planet]
When used with @hash-lang[], @racketmodname[planet] must be followed
by a short-form PLaneT path. The path is used in the same way that
@hash-lang[] uses plain identifiers: @racketidfont{/lang/reader} is
added to the given path to determine a module that supplies a module
reader.
The @racketmodname[planet] module (as opposed to the reader used with
@hash-lang[]) implements the @exec{raco planet} command-line tool.
@include-section["private/util.scrbl"]
@section{Developing Packages for PLaneT}
To put a package on PLaneT, or release an upgrade to an
already-existing package:
@subsection{Write Your Package}
PLaneT can distribute whatever programs you write, but keep
these guidelines in mind as you write:
@itemize[
@item{Organize your code into modules. Since the PLaneT client is
integrated into the @racket[require] form, it works best if your code
is arranged into modules.}
@item{When one module in your program depends on another, it is best
to require it using the relative-file-name form rather than the
planet require form. For instance, if your program contains files
primary.rkt and helper.rkt where primary.rkt requires helper, use the form
@racket[(require "helper.rkt")]
instead of
@racket[(require (planet "helper.rkt" ("username" "packagename.plt" 1 0)))]
in files that will also be a part of the package.}]
@subsubsection[#:tag "devlinks"]{Development Links}
To aid development, PLaneT allows users to establish direct
associations between a particular planet package
with an arbitrary directory on the filesystem, for instance connecting the package named by the require line
@racket[(require (planet "file.rkt" ("my" "mypackage.plt" 1 0)))]
to the directory @filepath{/home/myname/svn/mypackages/devel/}.
These associations are intended to allow developers to use their own
directory structures, version control systems, and so on while still
being able to use the packages they create as though they were
distributed directly by PLaneT. Development links are local to a
particular user and repository (but not to a particular Racket minor
revision).
To establish a development link, use the @exec{raco planet} command-line tool:
@commandline{raco planet link myname mypackage.plt 1 0 ~/svn/mypackages/devel}
Once you are finished developing a package, you should remove any
development links you have established for it, again using the planet
command-line tool:
@commandline{raco planet unlink myname mypackage.plt 1 0}
You may alternately use the functions @racket[add-hard-link] and @racket[remove-hard-link].
@subsection{Prepare Your Distribution}
@subsubsection{Arrange Files Into a Directory}
Make sure that all source files, documentation, etc. that you want to
be a part of the package are in a single directory and its
subdirectories. Furthermore make sure that nothing else, @italic{e.g.}
unneeded backup files, is in that directory (with the exception that
the meta-subdirectories and files Git/Subversion/CVS uses are
automatically skipped by the packaging tool).
@subsubsection{Create Documentation [Optional]}
Use Scribble to write documentation for your package. See
@secref["scribble.rkt"] for macros that ensure proper bindings and version
numbers in documentation for @|PLaneT| packages, and
@other-manual['(lib "scribblings/scribble/scribble.scrbl")]
for instructions on how to write Scribble documentation.
When testing your documentation, set up a development link and use
@centered{@tt{raco setup -P <owner> <package-name> <maj> <min>}}
with arguments based on the development link to build and
test your documentation.
@italic{Note:} Always use @racket[this-package-in] in @racket[for-label]
bindings when documenting @|PLaneT| packages, and always use the bindings in
@racketmodname[planet/scribble] rather than @racketmodname[scribble/manual].
These macros automatically produce @racket[planet]-based module paths with
appropriate version numbers. Other @racket[require] subforms and Scribble
declarations may refer to the wrong version of a package, or may not be
recognized as part of a @|PLaneT| package at all when documentation is produced.
@subsubsection{Create an @filepath{info.rkt} File [Optional]}
If you put a file named @filepath{info.rkt} in your package's root directory, the
PLaneT system (as well as the rest of the Racket tool suite) will
look in it for descriptive metadata about your package. The PLaneT
system looks for certain names in that file:
@itemize[
@item{The @indexed-racket['blurb] field: If present, the blurb field
should contain a list of XHTML fragments encoded as x-expressions (see
the xml collection for details) that PLaneT will use as a short
description of your project.}
@item{The @indexed-racket['release-notes] field: If present, the
release-notes field should contain a list of XHTML fragments encoded
as x-expressions (see the xml collection for details) that PLaneT will
use as a short description of what's new in this release of your
package.}
@item{The @indexed-racket['categories] field: If present, the categories
field should be a list of symbols corresponding to the categories
under which this package should be listed.
The valid categories are:
@itemize[
@item{@indexed-racket['devtools]: Development Tools}
@item{@indexed-racket['net]: Networking and Protocols}
@item{@indexed-racket['media]: Graphics and Audio}
@item{@indexed-racket['xml]: XML-Related}
@item{@indexed-racket['datastructures]: Data Structures and Algorithms}
@item{@indexed-racket['io]: Input/Output and Filesystem}
@item{@indexed-racket['scientific]: Mathematical and Scientific}
@item{@indexed-racket['system]: Hardware/Operating System-Specific Tools}
@item{@indexed-racket['ui]: Textual and Graphical User Interface}
@item{@indexed-racket['metaprogramming]: Metaprogramming Tools}
@item{@indexed-racket['planet]: PLaneT-Related}
@item{@indexed-racket['misc]: Miscellaneous}]
If you put symbols other than these the categories field, they will be
ignored. If you put no legal symbols in the categories field or do not
include this field in your info.rkt file, your package will be
categorized as "Miscellaneous."}
@item{The @indexed-racket['can-be-loaded-with] field:
If present, the can-be-loaded-with field should be a quoted datum of
one of the following forms:
@BNF[(list @racket[can-be-loaded-with]
@racket['all]
@racket['none]
@racket[(list 'all-except 'VER-SPEC ...)]
@racket[(list 'only 'VER-SPEC ...)])
(list @racket[VER-SPEC]
@racket[Nat]
@racket[(Nat MINOR)])
(list @racket[MINOR]
@racket[Nat]
@racket[(Nat Nat)]
@racket[(= Nat)]
@racket[(+ Nat)]
@racket[(- Nat)])]
where @racket[VER-SPEC] is a PLaneT package version specification
in a manner like using @racket[planet] in @racket[require].
Depending on your package's behavior, it may or may not be okay for
multiple versions of the same package to be loaded at one time on the
entire system --- for instance, if your package relies on writing to a
particular file and assumes that nothing else writes to that same
file, then multiple versions of the same package being loaded
simultaneously may be a problem. This field allows you to specify
whether your package can be loaded simultaneously with older versions
of itself. If its value is @indexed-racket['all], then the package may be
loaded with any older version. If it is @indexed-racket['none], then it
may not be loaded with older versions at all. If it is @racket[(list
'all-except VER-SPEC ...)] then any package except those that match
one of the given @racket[VER-SPEC] forms may be loaded with this package; if it
is @racket[(list 'only VER-SPEC ...)] then only packages that match
one of the given @racket[VER-SPEC] forms may be loaded with this package.
When checking to see if a package may be loaded, PLaneT compares it to
all other currently-loaded instances of the same package with any
version: for each comparison, it checks to see if the newer package's
can-be-loaded-with field allows the older package to be loaded. If all
such comparisons succeed then the new package may be loaded; otherwise
PLaneT signals an error.
The default for this field is @indexed-racket['none] as a conservative
protection measure. For many packages it is safe to set this field to
@indexed-racket['all].}
@item{The @indexed-racket['homepage] field:
If present, the URL field should be a string corresponding to a URL
for the package. PLaneT provides this link with the description of your
package on the main PLaneT web page.}
@item{The @indexed-racket['primary-file] field:
If present, the primary-file field should be a either a string
corresponding to the name (without path) of the main Racket source
file of your package, or a list of such strings. The PLaneT web page
corresponding to this package will present all files listed here as
interface files for your package; it will give direct links to each
package and a listing of all names provided by the package along with
their contracts (if present).
If you include only a single string, it will be used as the require
line printed on your package's page. If you include a list of strings,
then the first legal file string in the list will be used.}
@item{The @indexed-racket['required-core-version] field: If present, the
required-core-version field should be a string with the same syntax as
the output of the @racket[version] function. Defining this field
indicates that PLaneT should only allow users of a version of Racket
equal to or more recent than the version specified by this field. This
allows you finer-grained control of your package's core-language
requirements than its inclusion in a particular repository; for
instance, setting this field to @racket["5.1.3"] would cause the PLaneT server
not to serve it to Racket v5.1.2 or older clients.}
@item{The @indexed-racket['version] field:
If present, the version field should be a string that describes the
version number of this code that should be presented to users (e.g.,
@racket["0.15 alpha"]). This field does not override or in any way interact
with your package's package version number, which is assigned by
PLaneT, but may be useful to users.}
@item{The @indexed-racket['repositories] field: If present, the repositories
field should be a list consisting of some subset of the strings
@racket["4.x"] and @racket["3xx"]. The string @racket["4.x"] indicates
that this package should be included in the v4.x repository (which
contains packages that are intended to run in Racket and PLT Scheme versions at
or above version 4.0, including the 5.0 series), and the string @racket["3xx"] indicates that
the package should be included in the v3xx repository (containing
packages intended to run in PLT Scheme versions in the 3xx series). A
single package (and a single version of a package) may be included in
multiple repositories with the same PLaneT version number.}]
In addition, PLaneT uses the setup-plt installer to install packages
on client machines, so most fields it looks for can be included with
their usual effects. In particular, adding a @indexed-racket['name]
field indicates that the Racket files in the package should be
compiled during installation; it is a good idea to add it.
An example info.rkt file looks like this:
@racketmod[
setup/infotab
(define name "My Application")
(define blurb
'("My application runs 60% faster on 20% less peanut "
"butter. It even shows a fancy graphic!"))
(define primary-file "my-app.rkt")
(define categories '(system xml))
]
See @secref[#:doc '(lib "scribblings/raco/raco.scrbl") "info.rkt"]
for more information on @filepath{info.rkt} files.
@subsection{Build a Distribution Archive}
@itemlist[#:style
'ordered
@item{So that the next step can find @racket[for-label] documentation
in your own package, first set up a development link
(if it is not already set), using
@commandline{raco planet link <owner> pkg.plt> <maj> <min> <path-to-files>}
This step is not necessary if your package has no documentation.}
@item{Use the planet command-line tool in its archive-creation mode to
create a planet archive:
@commandline{raco planet create /home/jacobm/my-app/}
This will create a planet archive named @filepath{my-app.plt} in the current
directory whose contents are the contents of @filepath{/home/jacobm/my-app} and
all its subdirectories.
Alternately, you can run @racket[make-planet-archive] with the name of the directory
you've prepared as its argument:
@racket[(make-planet-archive "/home/jacobm/my-app/")]
This function will build a packaged version of your directory and
return the path to that package. The path will always be a file named
@filepath{X.plt}, where @filepath{X} is the name of the directory you
gave to @racket[make-planet-archive], located in that same directory.}
@item{Remove the development link from the first step (assuming you added one) using
@commandline{raco planet unlink <owner> <packagename.plt> <maj> <min>}}
@item{Now test that your archive file works as intended using the
planet command-line tool in its install mode:
@commandline{raco planet fileinject <owner> <path to .plt file> <maj> <min>}
installs the specified file into your local PLaneT cache as
though it had been downloaded from the PLaneT server with the given
owner name and major and minor versions. After you run this command,
you can require your package on your local machine using
@racket[(require (planet <file> (<owner> <.plt file name> <maj> <min>)))]
to verify everything works.}
@item{Finally, use
@commandline{raco planet remove <owner> <.plt file name> <maj> <min>}
to remove the test package from your local cache. (Not removing it is
safe as long as you use the same name and version numbers the package
will have on the PLaneT server; otherwise you may experience
problems.)}]
@subsection[#:tag "backwards-compatibility"]{Determine Your Package's Backwards-Compatibility}
If you are updating a previously-released package, you must decide
whether your package is a backwards-compatible change or not. A rule
of thumb is to remember that modules written to work with the
previously-released version of your package should unmodified with the
new package. This means that at a minimum, a backwards compatible
update should:
@itemize[
@item{Contain all the same Racket source files in that the previous
version contained in directories intended for public access}
@item{In each public file, provide at least all the bindings that the
previous version provided}
@item{For each name provided with a contract (see @(secref #:doc '(lib
"scribblings/guide/guide.scrbl") "contracts" )), provide it
with a contract that is at least as permissive as the previous
contract}]
A backwards-compatible upgrade may, however:
@itemize[
@item{Change any behavior that
reasonable consumers of your package would not consider guaranteed
(@italic{e.g.}, by fixing bugs or improving the efficiency of
operations).}
@item{Remove files in clearly-marked private
sections. By convention, the contents of any directory called
@filepath{private} are considered private and should not be relied
upon by external users of your package.}
@item{Extend the set of names exported by a module.}]
Currently these rules are guidelines only, but in the future some or
all of them may be enforced programmatically. Ultimately, though, no
technical device can precisely capture what it means for a package to
be backwards-compatible with a previous version, so you should use your
best judgment.
@subsection{Submit Your Package}
Go to @link["http://planet.racket-lang.org/"]{the central PLaneT
package repository web page} and click on the link marked "contribute
a package / log in" in the upper-right-hand corner. If you have not
yet created an account, then do so on that page by providing your
name, a user name, an email address, and a password and then
responding to the confirmation message delivered to the email address
you provide.
Once you have an account, then if this is a new package then upload it
using the "Contribute a package" section in your user account page. If
this is a package update then click "update this package" next to its
name in the "Manage your packages" section of your user account page,
then upload the .plt file and indicate on the form whether your update
is backwards-compatible with the prior version or not.