https://github.com/winvector/examplerpackage
Example of how to build a simple R package
https://github.com/winvector/examplerpackage
Last synced: 4 months ago
JSON representation
Example of how to build a simple R package
- Host: GitHub
- URL: https://github.com/winvector/examplerpackage
- Owner: WinVector
- Created: 2020-10-18T20:14:42.000Z (over 4 years ago)
- Default Branch: main
- Last Pushed: 2020-11-27T19:43:19.000Z (over 4 years ago)
- Last Synced: 2024-12-27T02:14:03.042Z (6 months ago)
- Language: R
- Size: 363 KB
- Stars: 2
- Watchers: 4
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.Rmd
Awesome Lists containing this project
README
---
title: "README"
output: github_document
---[ExampleRPackage](https://github.com/WinVector/ExampleRPackage) package README.
What you will need for this lesson is:
* A working `R` of version at least as new as `3.6`, with `4.*` preferred.
* A network connection.
* To install the `R` packages listed in the next subsection.
* A text editor or IDE (integrated development environment) for editing `R` files.
* For the source-control steps a `git` client (either command line or graphical).
* Some ability to delete files/directories.
* A command-line shell able to run `R CMD` steps.## Configuring your machine
Instructions on how to configure a machine are given here
* [How to configure Linux for R work](https://github.com/WinVector/ExampleRPackage/blob/main/extras/setting_up_a_Linux_machine.md).
* [How to configure OSX for R work](https://github.com/WinVector/ExampleRPackage/blob/main/extras/setting_up_a_MacOS_machine.md).
* [How to configure Windows for R work](https://github.com/WinVector/ExampleRPackage/blob/main/extras/setting_up_a_Windows_machine.md).
These steps require some knowledge of working with your computer, network access, disk space, and admin rights. We strongly advise you take the steps in this section before class. We also strongly advise taking the trouble to run these steps. Having full control of a package-enabled R environment is very powerful. Please reach out for help if you are stuck on steps.Once you have your machine configured start up `R` and install the packages we will be using.
## A word of warning
There are many systems and tutorials that sit on top of higher order R tools. These can be useful, but, one must remember in R ["Writing R Extensions"](https://cran.r-project.org/doc/manuals/R-exts.html) is the primary source for how to develop R packges.
## Packages we assume you have installed
We assume your machine has a current working R, command-shell (bash / zsh), and text editor (emacs, vim, or other).
Start R and run the following.
```{r, eval=FALSE}
install.packages(c(
"tinytest", # for running tests
"roxygen2", # to generate manuals from comments
"knitr", # to generate vignettes from markdown
"rmarkdown", # to convert markdown formats, may not need this
"wrapr" # example for argument list checking
))
```## The file structure of this project
```
DESCRIPTION # the main project control fileR # project source code directory
/example_function.R # our example function
/package_help.R # package documentation
README.Rmd # package documentation source
README.md # package documentation, produced from README.Rmdinst # installed items distributed with package
/tinytest # package test directory
/tinytest/test_ExampleRPackage.R # the one test we have nowvignettes # where we place longer documents
/Example_Vignette.Rmd # Example markdown documenttests # test trigger, do not edit
/tinytest.R # test trigger, do not edit.Rbuildignore # file that tells the builder what files to ignore
.gitignore # file that tells version control what files to skip
NAMESPACE # project imports/exports, produced by roxygen2
man # roxygen2 generated documentation
/ExampleRPackage.Rd # generated package documentation
/example_function.Rd # generated function documentationExampleRPackage.Rproj # (optional) config file for RStudio
```## How to copy this project
### From GitHub
Point browser to [https://github.com/WinVector/ExampleRPackage](https://github.com/WinVector/ExampleRPackage) and press "Fork" in the upper right portion of the page.
### Copying the project at the command line (`base`/`zsh` for Linux, MacOS, or Windows with Windows Subsystem for Linux)
From a `bash`/`zsh` command line:
```
# get a copy of the repository
git clone https://github.com/WinVector/ExampleRPackage.git# remove the .git directory to sever it from the original repository
cd ExampleRPackage
rm -rf .git# start up a new git repository
git init .
git add -A .
git commit -m"example package"
```Then associate the package with your own repository by creating an new empty repository on GitHub and following their "preexisting project" instructions (adding a remote).
## Procedures for working with packages.
For all of these steps we are assuming your current directory is the top-level of the package you
are working with. This can be accomplished with the `setwd()` command. Alternately one can use an `RStudio`
project, which largely keeps track of the working directory.### Build Package
We will run this with our working directory inside our package (please see `getwd()`/`setwd()` for how to navigate between directories in R). This will produce the file `ExampleRPackage_0.1.0.tar.gz`.
```{r}
library(wrapr)# assumes our current directory is our package
# see setwd()/getwd() for how to change directories
rebuild_current_package_and_attach <- function(
..., # not used, force later arguments to bind by name
package_dir = getwd(), # default to package is current directory.
lib = .libPaths()[[1]] # where to attach package
) {
wrapr::stop_if_dot_args(substitute(list(...)), "rebuild_current_package")
start_time <- date()
message(paste("rebuild_current_package working in directory", package_dir))
message(paste("rebuild_current_package working in lib", lib))
# get package name from current directory
pkg_name <- tail(strsplit(package_dir, .Platform$file.sep)[[1]], n = 1)
message(paste("rebuild_current_package working on package", pkg_name))
# get into a clean state with no package installed/attached
detach_str <- paste0('package:', pkg_name)
res <- tryCatch(do.call(detach, list(detach_str)), error = function(e) e)
if(pkg_name %in% rownames(installed.packages())) {
remove.packages(pkg_name, lib = lib)
}
# regenerate man/.Rd files from roxygen comments
res_text <- capture.output(suppressMessages(roxygen2::roxygenize(package.dir = package_dir)))
# rebuild a source distribution of package
res <- system(paste("R CMD build", package_dir),
intern = TRUE)
# find the tar name
matches <- res[grepl(pkg_name, res)]
pkg_pattern <- paste0(pkg_name, '[_.0-9]+', '.tar.gz')
matches <- matches[grepl(pkg_pattern, matches)]
if(length(matches) != 1) {
stop("having trouble finding package tar name from R CMD build output")
}
str_span <- regexpr(pkg_pattern, matches)
tar_name <- substr(matches, str_span, str_span + attr(str_span, 'match.length') - 1)
# install the package from the source distribution
res_text <- capture.output(install.packages(tar_name, repos = NULL, verbose = FALSE, quiet = TRUE))
# attach package for use
res_text <- capture.output(library(pkg_name, character.only = TRUE))
end_time <- date()
return(list(
pkg_name = pkg_name,
tar_name = tar_name,
package_dir = package_dir,
package_dir = package_dir,
lib = lib,
start_time = start_time,
end_time = end_time
))
}
``````{r, eval=FALSE}
# re-document, re-build, re-install, and re-attach package
# write only name of package and tar file name into current work-space
(unpack[pkg_name, tar_name] := rebuild_current_package_and_attach())# rebuild_current_package working in directory /Users/johnmount/Documents/work/ExampleRPackage
# rebuild_current_package working in lib /Users/johnmount/Library/R/4.0/library
# rebuild_current_package working on package ExampleRPackage
#
# $pkg_name
# [1] "ExampleRPackage"
#
# $tar_name
# [1] "ExampleRPackage_0.1.0.tar.gz"
#
# $package_dir
# [1] "/Users/johnmount/Documents/work/ExampleRPackage"
#
# $package_dir
# [1] "/Users/johnmount/Documents/work/ExampleRPackage"
#
# $lib
# [1] "/Users/johnmount/Library/R/4.0/library"
#
# $start_time
# [1] "Fri Nov 27 11:13:31 2020"
#
# $end_time
# [1] "Fri Nov 27 11:13:35 2020"
``````{r, echo=FALSE}
# get into state as if last block had been run
library(ExampleRPackage)
pkg_name <- "ExampleRPackage"
tar_name <- "ExampleRPackage_0.1.0.tar.gz"
``````{r}
ls()print(pkg_name)
print(tar_name)
```Probably want to restart `R` at this point and re-attach the package with `library(ExampleRPackage)`.
### Build `README.md` from `README.Rmd`
```{r, eval=FALSE}
knitr::knit("README.Rmd")
```Some time after rebuilding `README.md` you may want to rebuild the package again to make sure the new copy is included in the package tar.
### Run tests
This package is already setup to use `tinytest`. Existing packages can be configured to work with `tinytest` by running `tinytest::setup_tinytest('.')`.
```{r}
# test directory of tests
dir <- system.file('tinytest',
package = 'ExampleRPackage',
mustWork = TRUE)
print(dir)test_text <- capture.output(tinytest::run_test_dir(
dir,
verbose = TRUE,
color = FALSE))
cat(paste(test_text, collapse = '\n'))
```### Check package
```{r}
check_text <- system(paste("R CMD check", tar_name),
intern = TRUE)
cat(paste(check_text, collapse = '\n'))
```## Example code
```{r}
example_function(3)
```## Sharing packages
### Install from file
```{r, eval=FALSE}
install.packages(tar_name, repos = NULL)
```### Install from tar on github
```{r, eval=FALSE}
# get this path by copying Github's download link
url <- "https://github.com/WinVector/ExampleRPackage/raw/main/ExampleRPackage_0.1.0.tar.gz"
install.packages(url, repos = NULL)
```### Install package from github
```{r, eval=FALSE}
remotes::install_github("https://github.com/WinVector/ExampleRPackage")
```## Submitting to CRAN
Submitting to CRAN is a specialized step. First your package must pass all checks and all CRAN rules (most of which are listed in ["Writing R Extensions"](https://cran.r-project.org/doc/manuals/R-exts.html)). It is important to note the following:
* The purpose of CRAN, as stated by some members, is a reliable experience for users electing to take packages from CRAN. CRAN considers unreliability in single extension packages as reputation risk for R/CRAN itself. Note this is different purpose than serving package authors.
* CRAN is *not* a test system, the assumption is you are only submitting packages that should pass all requirements to CRAN- not using CRAN to confirm the package meets requirements.
* All initial package submissions to CRAN are hand-checked with a emphasis on the package purpose and description.
* Minimizing dependencies (and not having non-CRAN dependencies) is a must. I believe CRAN currently checks if packages import more than 20 non-default dependencies and raises on issue.
* Do not get "lawyerly" with CRAN. CRAN feedback tends to have a purpose. It may appear to the non-cognoscenti that some packages have variances or are allowed around the rules. Either this is not the case, or it is a point of strong negative contention. In either case it is *not* a good idea to attempt to copy bad practices from other packages.
* CRAN packages must be testable in isolation and free of file-system side-effects.For now I am going to leave CRAN submissions as a "to be covered later" topic.