Learn R Programming

mvbutils (version 2.5.4)

mvbutils.packaging.tools: How to create & maintain packages with mvbutils

Description

This document covers:
  • usingmvbutilsto create a new package from scratch;
  • usingmvbutilsto maintain a package you've created (e.g. edit it while using it);
  • converting an existing package intomvbutils-compatible format;
  • how to customize the package-creation process.
For clarity, the simplest usage is presented first in each case. For how to do things differently, first look further down this document, then in the documentation for pre.install and perhaps doc2Rd. You need to understand cd and fixr before trying any of this.

Arguments

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".
  • cdto the task above "Splendid"
  • callmaintain.packages( Splendid)
  • Callpre.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.
  • Calllibrary(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 (seepre.install)
  • Move any subtasks of Splendid to one level up the task hierarchy (seemaintain.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.