#lang scribble/doc @(require scribble/manual scribble/bnf scribble/eval (for-label racket) (for-label planet/config) (for-label planet/util)) @(define-syntax-rule (eg (code resl) ...) (interaction (eval:alts code resl) ...)) @title{@bold{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. @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{% 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{% 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 [