============================================================================================================
About: The up-to-date document is available at https://rpubs.com/sherloconan/802481

1. Getting Started

  • Beginner: watch R-Ladies’ YouTube video, 90 minutes.

  • Intermediate: practise Siegert, Gabry, Lysy, and Goodrich’s vignette, 30 minutes.

  • Advanced: watch John Muschelli’s YouTube videos, 80 minutes.

  • Comprehensive: check Sahir Rai Bhatnagar’s summary from time to time.

 

devtools::has_devel()
available::available("PACKAGENAME", browse = FALSE) #See if a package name is available and appropriate

   

2. New Project

Create a regular R package by usethis::create_package(“~/PATH/PACKAGENAME”).

Create a Stan-based R package by

rstantools::rstan_create_package(path="~/PATH/PACKAGENAME")
usethis::use_gpl_license(version = 3)
file.remove('Read-and-delete-me')

 

2.1. Edit DESCRIPTION

Authors@R:
  c(person(given = "Zhengxiao",
           family = "Wei",
           role = c("aut", "cre"),
           email = "USER1@uvic.ca",
           comment = c(ORCID = "0000-0003-1866-2320")),
    person(given = "Farouk S.",
           family = "Nathoo",
           role = c("aut"),
           email = "USER2@uvic.ca",
           comment = c(ORCID = "0000-0002-2569-3507")),
    person(given = "Michael E. J.",
           family = "Masson",
           role = c("aut"),
           email = "USER3@uvic.ca",
           comment = c(ORCID = "0000-0002-5430-6078")))
  • Description: Neither title nor description should include either the name of your package or the word “package”. Remember to tab indent the newline.

Also, remember to edit the PACKAGENAME-package.R file in “~/PATH/PACKAGENAME/R”.

2023 update: Updated package doc. @docType "package" is deprecated. Please document "_PACKAGE" instead. Manually edit the PACKAGENAME-package.R file.

FROM

#' @docType package
#' @name PACKAGENAME-package
#' @aliases PACKAGENAME
NULL

TO

#' @keywords internal
"_PACKAGE"

 

2.2. Create R Script

Create R script as you always do (the icon in the upper left corner) and save to the path “~/PATH/PACKAGENAME/R”.

Remember to write the library name if it is not the base one, e.g., stats::cor.test() and rstan::summary().

Otherwise, e.g., usethis::use_package(“dplyr”) and usethis::use_pipe().

 

Advanced topics about S3 and S4 objects can be found in, e.g., Liquid Brain’s YouTube video, 20 minutes.

 

2.3. Create Stan File

Create Stan file as well but save to the path “~/PATH/PACKAGENAME/inst/stan”. The filename should not contain spaces or dashes and nor should it start with a number or utilize non-ASCII characters

Remember to leave a blank line after the Stan code.

Some Stan/rstan examples can be found in Hedibert Freitas Lopes’s post.

 

2023 update: Updated Stan deprecated syntax. Required rstan version 2.26 or later. See reference at https://mc-stan.org/docs/reference-manual/postfix-brackets-array-syntax.html

For instance, the syntax vector[C] Y[N]; is deprecated and will be removed in Stan 2.33. The postfix bracket notation for arrays is replaced by array[N] vector[C] Y;.

int s[C]; is replaced by array[C] int s;.

The syntax for declaring vector types, such as vector[N] b;, remains unchanged.

 

2.4. Document the R Function

Place the cursor inside the R function body, and click on the tool bar Code - Insert Roxygen Skeleton.

The first sentence will be the title. The next paragraph will be Description. And, the following paragraphs will all go to the Details section.

As for Examples, use

#' \dontrun{}

(no space before { ) to prevent from running them (when checking).

 

2.5. Include Data

usethis::use_data_raw()

## code to prepare `DATASET` dataset goes here
recall.long <- data.frame(
  "Subject" = factor(paste("s", rep(1:10,3), sep="")),
  "Level" = factor(rep(c("Level1", "Level2", "Level3"), each = 10)),
  "Response" = c(10,6,11,22,16,15,1,12,9,8,
                 13,8,14,23,18,17,1,15,12,9,
                 13,8,14,25,20,17,4,17,12,12))

usethis::use_data(recall.long, overwrite = TRUE, compress = "xz")


recall.wide <- data.frame(
  "Level1" = c(10,6,11,22,16,15,1,12,9,8),
  "Level2" = c(13,8,14,23,18,17,1,15,12,9),
  "Level3" = c(13,8,14,25,20,17,4,17,12,12))
rownames(recall.wide) <- paste("s", c(1:10), sep="")

usethis::use_data(recall.wide, overwrite = TRUE, compress = "xz")

This DATASET.R file will be located in “~/PATH/PACKAGENAME/data-raw”.

 

2.6. Document Data

See https://r-pkgs.org/data.html. Basically, it is an R script saved along with the R function file(s) in “~/PATH/PACKAGENAME/R”.

Do not use @source \doi{10.3758/BF03210951}, because this will increase the build runtime. See my stackflow answer.

 

2.7. Create .onAttach

Save an R script (usually named as zzz.R) in “~/PATH/PACKAGENAME/R”. This can print a message whenever the package is loaded.

.onAttach <- function(...) {
  packageStartupMessage("For execution on a local, multicore CPU with excess RAM we recommend calling\n",
                        "options(mc.cores = parallel::detectCores()).\n")
  if (.Platform$OS.type == "windows") {
    packageStartupMessage("Do not specify '-march=native' in 'LOCAL_CPPFLAGS' or a Makevars file")
  }
}

   

2.8. Check NAMESPACE

example(source)
try(roxygen2::roxygenize(load_code = sourceDir), silent = TRUE)
pkgbuild::compile_dll()
roxygen2::roxygenize()

The third command may take a while to run. It complies the C++ code.

Now, open NAMESPACE and see if it looks like this

import(Rcpp)
import(methods)
importFrom(rstan,sampling)
useDynLib(PACKAGENAME, .registration = TRUE)

 

2.9. Edit .Rbuildignore

usethis::use_build_ignore("[.]xlsx$", escape = FALSE)

Or, manually edit the file (yes, it is OK to do so).

^.*\.Rproj$
^\.Rproj\.user$
^LICENSE\.md$
^data-raw$
^\.travis\.yml$
^\.DS_Store$
^\.o$
^\.so$
^\.dll$
^\.Rhistory$
^README\.Rmd$
^README\.md$
^\.git$
^appveyor\.yml$
^codecov\.yml$
^_pkgdown\.yml$
^docs$
^pkgdown$
^\.github$
^cran-comments\.md$

   

3. Vignettes

To be included…

   

4. GitHub Version Control

Click on the tool bar Tools - Version Control - Project Setup - Git/SVN and select Git. The RStudio session will restart. Register a GitHub account and open https://github.com/settings/tokens to generate the token. Create a new (GitHub) project and make sure it has been granted permissions such as gist, repo, user, and workflow.

Open the terminal APP, go the package directory cd /PATH/PACKAGENAME (if you do not know how to form a path, just drag the package PACKAGENAME folder to the terminal APP and it will be displayed).

git remote add origin https://github.com/USER/PACKAGENAME.git
git branch -M main
git push -u origin main

Create README.Rmd.

usethis::use_readme_rmd()

It is time to show your markdown skill. 😃

 

4.1. Solve Error 403

This means you may have multiple GitHub accounts. For a macOS user, open the Keychain Access APP and search “github”. Open the one with “internet password” and edit it. The password should be your token (not the GitHub account password or desktop password).

 

4.2. Edit .gitignore

.DS_Store
.Rproj.user
.Rhistory
.RData
*.o

See the HDInterval package for reference.

 

4.3. Edit DESCRIPTION

Add GitHub url.


URL: https://github.com/USER/PACKAGENAME
BugReports: https://github.com/USER/PACKAGENAME/issues

Nevertheless, the commit, push, and pull requests are not discussed here. [2021 update: the “master” branch is now called “main”]

 

5. Check

Write the .Rd file to the man (stands for “manual”) folder.

devtools::document()
?PACKAGENAME::FUNC #see documentation

devtools::check(args = c('--as-cran'))

Now, carefully read https://r-pkgs.org/r-cmd-check.html. Review the old and know the new.

CRAN provides an automated service for checking R packages on windows.

devtools::check_win_release()
devtools::check_win_devel()
devtools::check_win_oldrelease()

 

5.1. Homebrew “checkbashisms”

If you see a warning like this (on macOS):

> checking top-level files ... WARNING
A complete check needs the 'checkbashisms' script.
See section ‘Configure and cleanup’ in the ‘Writing R Extensions’
manual.

Turn to my stackflow answer. The other two notes regarding “checking installed package size” (any package larger than 5Mb) and “checking for GNU extensions in Makefiles” (Stan-based) do not need to worry about.

 

5.2. Some common warnings and notes

Example One: Prevent using [[1]] as a list number in the documentation.

checking Rd cross-references ... WARNING
Missing link or links in documentation object 'FUNC.Rd':
‘0,1’ ‘1’ ‘2’ ‘3’

 

Example Two: Not hiding .travis.yml in .Rbuildignore.

checking for hidden files and directories ... NOTE
Found the following hidden files and directories:
 .travis.yml
These were most likely included in error.

 

Example Three: Writing too long one-line example in the documentation. Need to start a newline.

checking Rd line widths ... NOTE
Rd file '***.Rd':
 \examples lines wider than 100 characters:
These lines will be truncated in the PDF manual.

 

Example Four: I accidentally wrote the year 2024 as 2023 in DESCRIPTION.

Check: CRAN incoming feasibility, Result: NOTE
  The Date field is over a month old.

   

6. Continuous Integration

6.1. Travis CI [2021 update: it is no longer completely free]

I am not using Travis CI anymore. You could jump to the next section 6.2.

Travis CI domain has been moved from travis-ci.org to travis-ci.com.

usethis::use_travis()

Overwrite pre-existing file ‘.travis.yml’? Enter 1. Then, it automatically adds Travis build status badge to ‘README.Rmd’ like this:

<!-- badges: start -->
[![Travis build status](https://travis-ci.com/USER/PACKAGENAME.svg?branch=main)](https://travis-ci.com/github/USER/PACKAGENAME)

Knit README.Rmd again to update README.md.

Open .travis.yml and replace the context with the following one.

language: r

addons:
  apt:
    sources:
      - ubuntu-toolchain-r-test
    packages:
      - g++-7
      - libv8-dev
env:
  - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7"

before_install:
  - mkdir -p ~/.R/
  - echo "CXX14 = g++-7 -fPIC -flto=2" >> ~/.R/Makevars
  - echo "CXX14FLAGS = -mtune=native -march=native -Wno-unused-variable -Wno-unused-function -Wno-unused-local-typedefs -Wno-ignored-attributes -Wno-deprecated-declarations -Wno-attributes -O3" >> ~/.R/Makevars

install: 
  - R -e "install.packages('rstan')"

script: 
  - R -e "rstan::stan_version()"

 

6.2. Appveyor [2023 update: it somehow fails the checks for Stan]

usethis::use_appveyor()

It automatically adds AppVeyor build status badge to ‘README.Rmd’ like this:

[![AppVeyor build status](https://ci.appveyor.com/api/projects/status/github/USER/PACKAGENAME?branch=main&svg=true)](https://ci.appveyor.com/project/USER/PACKAGENAME)
<!-- badges: end -->

And, it automatically adds ‘^appveyor\.yml$’ to ‘.Rbuildignore’. Knit README.Rmd again to update README.md.

Commit and push the changes to GitHub. Let Travis CI check on Linux and Appveyor check on Windows.

 

To remove continuous-integration/appveyor/branch - AppVeyor build failed, delete Webhooks under Settings and delete the ‘appveyor.yml’ file.

 

6.4. Code Coverage [optional]

usethis::use_coverage()

The covr package is required. It automatically adds ‘covr’ to Suggests field in DESCRIPTION; adds ‘^codecov\.yml$’ to ‘.Rbuildignore’; adds Codecov test coverage badge to ‘README.Rmd’; etc.

Open codecov.yml and manually add the following:

comment: false

language: R

sudo: false

cache: packages

after_success:

- Rscript -e 'covr::codecov()'

Now, you can access https://app.codecov.io/gh/USER/PACKAGENAME/

And, run the following line locally (it may take a while):

covr::codecov(token = "YOUR TOKEN")

Or, you can automate it by GitHub Actions, e.g.,

usethis::use_github_action("test-coverage")

which creates test-coverage.yaml in “~/PATH/PACKAGENAME/.github/workflows”.

on:
  push:
    branches: [main, master]
  pull_request:
    branches: [main, master]

name: test-coverage

jobs:
  test-coverage:
    runs-on: macOS-latest
    env:
      GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
    steps:
      - uses: actions/checkout@v2

      - uses: r-lib/actions/setup-r@master

      - uses: r-lib/actions/setup-pandoc@master

      - name: Query dependencies
        run: |
          install.packages('remotes')
          saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2)
          writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version")
        shell: Rscript {0}

      - name: Cache R packages
        uses: actions/cache@v1
        with:
          path: ${{ env.R_LIBS_USER }}
          key: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-${{ hashFiles('.github/depends.Rds') }}
          restore-keys: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-

      - name: Install dependencies
        run: |
          install.packages(c("remotes"))
          remotes::install_deps(dependencies = TRUE)
          remotes::install_cran("covr")
        shell: Rscript {0}

      - name: Test coverage
        run: covr::codecov()
        shell: Rscript {0}

   

7. Unit Test

To increase the codecov percentage, you need to test the the code in every possible if-statement.

usethis::use_testthat()

The testing R script(s) should be saved in “~/PATH/PACKAGENAME/tests/testthat” and named as “test-SOMETHING.R”. Use of context() is no longer recommended. Check some expected outputs from the usage.

test_that("Error handling - method arg", {
  expect_error(FUNC(data.wide = recall.wide, method = -1),
               "should be one of")
})

skip_on_cran()
skip_on_os(c("windows", "linux", "solaris"))

test_that("Within-subjects HDI width computed by (default) Method 1", {
  HDI <- FUNC(data.wide = recall.wide, seed = 277)
  width <- round(HDI$width, 5)
  expect_equal(width, 0.53877)
})

test_that("Within-subjects HDI width computed by Method 2", {
  w <- capture_warnings(HDI <- FUNC(data.wide = recall.wide, method = 2, seed = 277))
  expect_match(w, ".*treedepth", all = FALSE)
  expect_match(w, ".*pairs()", all = FALSE)
  width <- round(HDI$width, 5)
  expect_equal(width, 0.51502)
})

Note: Stan results will only be exactly reproducible if all of the following components are identical:

  • Stan version
  • Stan interface (RStan, PyStan, CmdStan) and version, plus version of interface language (R, Python, shell)
  • versions of included libraries (Boost and Eigen)
  • operating system version
  • computer hardware including CPU, motherboard and memory
  • C++ compiler, including version, compiler flags, and linked libraries
  • same configuration of call to Stan, including random seed, chain ID, initialization and data

See my Stan forum post.

Test the package.

devtools::test()
devtools::test_coverage()

   

8. Build

 

devtools::build()

Now, the .tar.gz file is ready to be shared with others.

 

Step 1. unzip the build file PACKAGENAME_version.tar.gz (xx KB) into PACKAGENAME_version.tar (xxx KB);

Step 2. continue to unzip the Step 1 file into a neat PACKAGENAME (xxx KB) folder;

Some decompression tools (such as Archive Utility) can directly unzip .tar.gz into the folder in one step.

 

8.1. Binary Build

To significantly speed up the installation, you can try the binary build using GitHub Actions.

See Karim Barigou’s post.

 

Download, copy, and paste the build.yaml and build-linux.yaml files to the path “~/PATH/PACKAGENAME/.github/workflows”. Commit and push to GitHub.

You should get two workflows on GitHub’s Actions tab, https://github.com/USER/PACKAGENAME/actions. Run these two workflows to generate three artifacts.

If the following errors occur for the Windows build, then delete all .o files from your src folder.

System command 'Rcmd.exe' failed, exit status: 1, stdout + stderr:
C:\rtools40\mingw32\bin\nm.exe: RcppExports.o: file format not recognized
...

 

Download the zip artifacts and unzip them.

Fork https://github.com/kabarigou/drat : open this website, and click on the upper right corner “Fork”.

Create a new R project on RStudio: the upper left second icon - Version Control - Git, input the forked repository url (https://github.com/USER/drat). “Create project as subdirectory of” means where you locally save this project.

On https://github.com/USER/drat/settings/pages, set the source as “Branch: master - /docs” and save. This step is important!

Edit README.md and docs/index.html content accordingly.

Delete the bin folder in the main directory. Delete the bin and src folders in the docs folder.

drat::insertPackage(file="~/PATH/PACKAGENAME_version.zip", repodir="./docs") #for Windows
drat::insertPackage(file="~/PATH/PACKAGENAME_version.tgz", repodir="./docs") #for Mac
drat::insertPackage(file="~/PATH/PACKAGENAME_version.tar.gz", repodir="./docs") #for Ubuntu

Note: it seems that R package is not backward compatible. If you upload the binary build which is created on R 4.0.5, it likely will not be installed on R 4.0.0.


strategy:
      fail-fast: false
      matrix:
        config:
          - {os: macOS-latest,   r: '4.1.0'}
          - {os: macOS-latest,   r: '4.0.1'}
          - {os: windows-latest,   r: '4.1.0'}
          - {os: windows-latest,   r: '4.0.1'}
          

   

9. Install

 

devtools::install("~/PATH/PACKAGENAME")

 

If the following message pops up, you can enter 3 to continue. (roughly 17 minutes)

“These packages have more recent versions available. It is recommended to update all of them. Which would you like to update? 1: All 2: CRAN packages only 3: None 4: R6 (2.5.0 -> 2.5.1) [CRAN] Enter one or more numbers, or an empty line to skip updates:”

 

Sometimes, it may display the same context in console for a while. Just wait for completion. Here, no need to worry about the compiling warnings regarding RcppEigen.

 

If only make a change to the R code or the documentation rather than the Stan file(s).

devtools::install("~/PATH/PACKAGENAME", quick = TRUE)

 

Install the binary pacakge by calling

install.packages("rstan")
install.packages("PACKAGENAME", repos = "https://user.github.io/drat", type = "binary")

   

10. Uninstall

 

remove.packages("PACKAGENAME")

   

11. Release

Follow the tutorial.

Create a cran-comments.md file and push it to GitHub. Make sure this file is in .Rbuildignore. If you do not know how to create a .md file, just download and edit one from any GitHub repository of an R package, e.g., ‘httr’.

## Test environments

* local: darwin18.6.0
* travis: 4.0.2
* appveyor: windows-x86_64-release
* win-builder: windows-x86_64-oldrel, release, devel
* GitHub Actions: macOS-latest-release, windows-latest-release, ubuntu-latest-devel, ubuntu-latest-release, ubuntu-latest-oldrel 

## R CMD check results
0 errors | 0 warnings | 2 notes

Any package is larger than 5Mb.
> checking installed package size ... NOTE
installed size is  11.8Mb
sub-directories of 1Mb or more:
  libs   11.4Mb

This is a Stan-based R package.
> checking for GNU extensions in Makefiles ... NOTE
GNU make is a SystemRequirements.

## Downstream dependencies

There are currently no downstream dependencies for this package.

If everything looks good, you can upload the package bundle (the .tar.gz file by calling devtools::build()) to the CRAN submission form. Based on my experience, CRAN staff respond to my submission by email in a day. Hopefully, the package goes live on CRAN on that same day. In about three days, the binary packages will also be automatically built on CRAN.

Add any comments to cran-comments.md.

For the future development, create a NEWS.md file and push it to GitHub. Also, make sure this file is in .Rbuildignore.

   

12. Update and Misc.

 

How to fix image not found (the shared library was built with an earlier version of R)

Error in dyn.load(dll_copy_file) : unable to load shared object …

devtools::clean_dll()