Setting up a package from scratch
First, the simplest case: suppose you have some pure R{} code and maybe data that you'd like to make into a package called "Splendid". The bare-minimum steps you need are:-
- Make sure all the code & data lives in a single task called "Splendid".
cd
to the task above "Splendid"- call
maintain.packages( Splendid)
- Call
pre.install( Splendid)
to create a "source package" in a subdirectory of Splendid's task directory. The subdirectory will be called "Splendid". - Make sure you have all the R{} build tools installed and on your path-- see "R-exts" for details
- From a command prompt, run "R CMD build Splendid" (Windows: "R CMD build --binary Splendid") to create the installable version-- a file "Splendid_1.0.tar.gz" (Windows: "Splendid_1.0.zip") in the task directory.
- Install the package however you would normally. In Windows, I do this from inside R{}, via the "Packages/Install from local Zip" option, after "R CMD build --binary" in the previous step.
- Call
library(Splendid)
; your package will be loaded for use, and is also ready for live-editing.
Your package will probably just about work now, but the result won't yet be perfect. The first thing is that you will need to give it a sensible DESCRIPTION file. mvbutils
creates a default even if you don't, but it won't be what you really want, as you'll realize if you type library( help=Splendid)
. Easiest is to copy the default DESCRIPTION file from the new subdirectory "Splendid" into Splendid's home directory, then change it with a text editor. Apart from the obvious changes (your name etc), the most important field to add is "Depends:", to say what other packages are needed by Splendid (called "Imports:" if your package is NAMESPACEd-- see below). Once this modified DESCRIPTION file is sitting in Splendid's home directory, you will rarely need to change it again.
The additional steps you'll likely need are these:
- Copy and edit the DESCRIPTION file (see above)
- Provide documentation (see below)
- Namespace the package (see below)
- Sort out any C/Fortran source code, pre-compiled code, demos, and other additional files (see
pre.install
) - Move any subtasks of Splendid to one level up the task hierarchy (see
maintain.packages
)
Once you have set up "Splendid" so that maintain.packages
works, you should never need to cd
directly into "Splendid" again.Converting an existing package
Suppose you have already have a source package "hardway", and would like to try maintaining it via mvbutils
. You'll need to create a task package, then create a new version of the source package, then re-install it. The first step is to call unpackage( hardway)
to creat the task package "hardway" in a subdirectory of the current task. Plain-text documentation will be attached to functions, or stored as ".doc" text objects. All functions and documentation must thereafter be edited using fixr
. The full sequence is something like:
# Create task package in subdirectory of current:
unpackage( "path/to/source/package/hardway")
#
# Load image into memory:
maintain.packages( hardway)
#
# Make new version of source package:
pre.install( hardway, ...) # use dir= to control where new source pkg goes
# Now RCMD BUILD and INSTALL "hardway"...
# ...in Windows, I use R CMD build -binary and then the Menu commands...
# ..."Packages/Install from local zip file"
#
# If all goes well, then:
library( hardway) # off yer go
If you get problems after maintain.packages
, you might need unmaintain.package( hardway)
to clear out the in-memory copy of the new task package.Documentation
Documentation for functions can be stored as plain text just after a function's source code. The easiest way to add skeletal documentation to your function brilliant
, is fixr( brilliant, new.doc=TRUE)
; see flatdoc
if you want to understand what's going on. The format is almost exactly as displayed in plain-text help, i.e. from help(..., help_type="text")
in R{} >= 2.10, or help(...,chmhelp=FALSE,htmlhelp=FALSE)
prior to that. My recommendation is to just start writing something that looks reasonable, and see whether it works; to test the appearance, you'll need to run patch.install
to update the help system, as explained below in MAINTAINING.A.PACKAGE. If you run into problems with writing documentation for your functions, then refer to doc2Rd
for further details of format, such as how to document several functions in the same file.
You can also provide three other types of documentation, for: (i) general use of your package (please do! it helps the user a lot; packages where the doco PDF consists only of an alphabetical list of functions/objects are a pain); (ii) more specific aspects of usage that are not tied to individual functions, such as this file; and (iii) datasets. These types of documentation should be stored in the package as text objects whose name ends in ".doc"; examples of the three types could be "Splendid.package.doc", "glitzograms.with.Splendid.doc", and "earlobes.doc" if you have a dataset earlobes
. See doc2Rd
for format details.
If your package is namespaced (see below), you must document every function and dataset that the user will see, but you don't need to document any others.
Documentation of functions can be checked using tools::codoc
(qv).Namespaces
Packages really should be namespaced. The payoff for the end user is control over name clashes in different packages, and less clutter. The payoff for you is that you don't have to waste time documenting functions that aren't visible to the end user. Namespacing isn't compulsory, and the simplest case described above doesn't add a namespace. However, namespacing with mvbutils
is trivial; just add a function .onload
with this definition: function( libname, pkgname) {}
. (If you want to load compiled code, use the body of mvbutils:::generic.dll.loader
instead of the empty braces.) The only user-visible functions and datasets will be those that are named or aliased explicitly in your documentation; all others will be hidden. The only other recommended change if your package is namespaced, is for the DESCRIPTION file to say "Imports:" rather than "Depends:".Maintaining a package
Once you have successfully gotten your package installed and loaded the first time, you should rarely need to call RCMD BUILD or INSTALL again, except when you are about to distribute to others. After calling maintain.packages
and library
in an R{} session, you can modify, add and delete functions, datasets, and documentation in your package, and these changes will mostly be immediately manifested in the package within the session-- this is "live editing". To update the installed package on disk accordingly, call patch.install( Splendid)
-- this also calls pre.install
to update the source package, updates the help system in the current session, and does a few other synchronizations.
maintain.packages( Splendid)
loads your task package into an environment ..Splendid
, which lives in the "mvb.session.info" workspace on the search path. You can move objects to and from other tasks via move
, with ..Splendid
as the from
or to
argument; you can create functions and text via fixr
with argument pkg="Splendid"
; you can delete objects via rm.pkg
with argument pkg="Splendid"
. Intrepid users can also create things directly, via ..Splendid$newthing <<- ...<="" code="">. In most cases, you will be prompted afterwards for whether to update the task package, but you can always do yourself via Save.pos( ..Splendid)
. Note that only these updates only update the task package. To update the source package using the task package, call pre.install
; to update the installed package (and the source package), call patch.install
.
In rare cases, you may find that maintain.packages( Splendid)
fails. If that happens, there won't be a ..Splendid
environment, which means you can't fix whatever caused the load failure. The load failure is (invariably in my experience) caused by a "hidden" attempt to load a namespaced package, which is failing for yet another reason, usually something in its .onLoad
; that package might or might not be "Splendid" itself. If you can work out what other package is trying to load itself-- say "badpack"-- you can temporarily get round the problem by making use of the character vector partial.namespaces
, which lives in the "mvb.session.info" search environment, as follows:
partial.namespaces <<- c(="" partial.namespaces,="" "badpack")="" that="" will="" prevent="" execution="" of="" "badpack"'s="" .onLoad<-><->
. "badpack" won't be properly loaded, but at least the task package will be loaded into ..Splendid
, so that you can make a start on the problem. If you can't work out which package is causing the trouble, try
partial.namespaces <<- "every="" package"="" after="" that,="" no="" namespaced="" package="" will="" load="" properly,="" even="" if="" it="" is="" flawless;="" so="" remember="" to="" clear="" partial.namespaces <<- null<="" code=""> before resuming normal service.
You might also find find.lurking.envs
useful, via eapply( ..Splendid, find.lurking.envs)
; this will show any functions (or other things) in ..Splendid
that have accidentally acquired a non-standard environment such as a namespace, which can trigger a "hidden" package load attempt. The environment for all functions in ..Splendid
should probably be .GlobalEnv
; the environments in the loaded package will be different, of course.<-><->
Different r versions
You might need to distribute different versions of your package to go with different R{} versions. (This happened with the change from R{} 2.9 to 2.10; the Rdoc format changed unbackcompatibly.) The subdir
argument can be used to create different source package versions. Presumably you'll install the results into different R{} libraries.Customizing package creation
You can customize many aspects of the mvbutils package-creation process, by adding a function pre.install.hook.Splendid
to your package. See pre.install
for further details.