Using renv is an excellent choice for maintaining a clean and reproducible R environment on macOS. Here, I will share my experiences and provide a guide on setting up R on macOS. The post is divided into the following sections:

  1. Installing system dependencies required for R libraries using Homebrew.
  2. Installing R libraries using renv.
  3. Saving and restoring R environments using renv.
  4. Installing R libraries hosted on private repositories.
  5. Building R libraries from source using Makevars.

Let’s get started!

Installing Homebrew

1# install xcode CLI
2xcode-select --install
3# install homebrew
4/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Installing system dependencies

Create a Brewfile for batch installation of R, RStudio and the required system libraries

 1brew "ccache"     # Object-file caching compiler wrapper
 2brew "coreutils"  # GNU File, Shell, and Text utilities
 3brew "gdal"       # Geospatial Data Abstraction Library
 4brew "geos"       # Geometry Engine
 5brew "gfortran"   # GNU compiler collection
 6brew "graphviz"   # Graph visualization software from AT&T and Bell Labs
 7brew "libgit2"    # C library of Git core methods that is re-entrant and linkable
 8brew "libjpeg"    # Image manipulation library
 9brew "libomp"     # LLVM's OpenMP runtime library
10brew "libxml2"    # GNOME XML library
11brew "llvm"       # Next-gen compiler infrastructure
12brew "openblas"   # Optimized BLAS library
13brew "openssl"    # OpenSSL GIO module for glib
14brew "poppler"    # PDF rendering library (based on the xpdf-3.0 code base)
15brew "proj"       # Cartographic Projections Library
16brew "readline"   # Library for command-line editing
17brew "unixodbc"   # ODBC 3 connectivity for UNIX
18brew "xz"         # General-purpose data compression with high compression ratio
19brew "zlib"       # General-purpose lossless data-compression library
20brew "r"
21cask "rstudio"

Then run the installation using the following brew command,

1brew bundle install --force --file Brewfile -v

Installing R libraries

To set up a reproducible R environment, I recommend using renv. Here’s an example of how to create a new environment and install the signature.tools.lib library:

  1. Set the current project folder as the working directory:
1setwd("/path/to/your/project/folder")
  1. Install the renv library if you haven’t already:
1install.packages("renv")
  1. Initialize and activate the renv environment:
1library(renv)
2renv::init()
3renv::activate()

By using renv, only the renv library itself will be installed in the system-wide location, such as /usr/local/Cellar/r/ (Intel macOS) or /opt/homebrew/Cellar/r/ (Apple Silicon).

Project-specific libraries, on the other hand, will be installed into the renv base located at ~/Library/Caches/org.R-project.R. When creating a new environment, renv links the libraries from its base into the renv folder within the project directory. This approach maintains a clean system base and allows for sharing the renv space across different R environments.

To install project-specific R libraries, you can utilize the renv::install() function. For instance, if you want to install signature.tools.lib, it has the following dependencies:

 1renv::install("bioc::VariantAnnotation")
 2renv::install("bioc::BSgenome.Hsapiens.UCSC.hg38")
 3renv::install("bioc::BSgenome.Hsapiens.1000genomes.hs37d5")
 4renv::install("bioc::BSgenome.Mmusculus.UCSC.mm10")
 5renv::install("bioc::BSgenome.Cfamiliaris.UCSC.canFam3")
 6renv::install("bioc::SummarizedExperiment")
 7renv::install("bioc::BiocGenerics")
 8renv::install("bioc::GenomeInfoDb")
 9renv::install("NMF")
10renv::install("foreach")
11renv::install("doParallel")
12renv::install("lpSolve")
13renv::install("ggplot2")
14renv::install("cluster")
15renv::install("methods")
16renv::install("stats")
17renv::install("linxihui/NNLM")
18renv::install("nnls")
19renv::install("GenSA")
20renv::install("gmp")
21renv::install("plyr")
22renv::install("RCircos")
23renv::install("scales")
24renv::install("bioc::GenomicRanges")
25renv::install("bioc::IRanges")
26renv::install("bioc::BSgenome")
27renv::install("readr")
28renv::install("doRNG")
29renv::install("combinat")
30renv::install("Nik-Zainal-Group/signature.tools.lib.dev")
31renv::install("Nik-Zainal-Group/teachingmutationalsignatures")

Saving and restoring R environments

To instruct renv to save the environment you described, you can use the renv::snapshot() function. However, please note that renv only saves libraries that are explicitly called in your code. If a library is not referenced anywhere in your code, renv will not include it in the snapshot.

To ensure all the required libraries are saved, you can create a separate file, let’s call it libraries.R, where you call and load all the necessary libraries. This way, renv will detect these library calls and include them in the snapshot.

Here’s an example of how you can structure the libraries.R file:

1# libraries.R
2library(signature.tools.lib)
3# Call and load any other required libraries here

By calling renv::snapshot() after sourcing the libraries.R file, renv will recognize the library calls and include the required libraries in the snapshot.

1# save renv.lock file
2renv::snapshot()

Saving the environment will generate a renv.lock.

To restore the environment on a new location or machine, you can follow these steps after copying the libraries.R and renv.lock files into a new project folder, then run the following commands:

1# if `renv` is not installed on the new system
2install.packages('renv')
3
4renv::init()
5# or
6renv::restore()

Installation from private repositories

When dealing with R libraries hosted on a private repository, additional authentication is required. One common method is to generate a Personal Access Token (PAT) to authenticate with the hosting service like Github.

To authenticate using the generated PAT.

1Sys.setenv(GITHUB_PAT = "PASTE_TOKEN_HERE")
2renv::install("private_repo/R_package")

Installation from source

To install R libraries from source, we need to instruct R where to find the required system dependencies. This is done by using a Makevars file. which is placed ~/.R/Makevars and looks like this.

 1# --------
 2# Makevars
 3# --------
 4
 5# General note
 6
 7# Homebrew bin / opt / lib locations
 8
 9### uncomment on Apple Silicon
10#HB=/opt/homebrew/bin
11#HO=/opt/homebrew/opt
12#HL=/opt/homebrew/lib
13#HI=/opt/homebrew/include
14
15### uncomment on Intel
16#HB=/usr/local/bin
17#HO=/usr/local/opt
18#HL=/usr/local/lib
19#HI=/usr/local/include
20
21# MacOS Xcode header location
22# (do "xcrun -show-sdk-path" in terminal to get path)
23XH=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk
24
25# ccache
26CCACHE=$(HB)/ccache
27
28# Make using all cores (set # to # of cores on your machine)
29MAKE=make -j4
30
31# GNU version
32GNU_VER=12
33
34# LLVM (Clang) compiler options
35CC=$(CCACHE) $(HO)/llvm/bin/clang
36CXX=$(CC)++
37CXX98=$(CC)++
38CXX11=$(CC)++
39CXX14=$(CC)++
40CXX17=$(CC)++
41
42# FORTRAN
43FC=$(CCACHE) $(HB)/gfortran-$(GNU_VER)
44F77=$(FC)
45FLIBS=-L$(HL)/gcc/$(GNU_VER) -lgfortran -lquadmath -lm
46
47# STD libraries
48CXX1XSTD=-std=c++0x
49CXX11STD=-std=c++11
50CXX14STD=-std=c++14
51CXX17STD=-std=c++17
52
53# FLAGS
54STD_FLAGS=-g -O3 -Wall -pedantic -mtune=native -pipe
55CFLAGS=$(STD_FLAGS)
56CXXFLAGS=$(STD_FLAGS)
57CXX98FLAGS=$(STD_FLAGS)
58CXX11FLAGS=$(STD_FLAGS)
59CXX14FLAGS=$(STD_FLAGS)
60CXX17FLAGS=$(STD_FLAGS)
61
62# Preprocessor FLAGS
63# NB: -isysroot refigures the include path to the Xcode SDK we set above
64CPPFLAGS=-isysroot $(XH) -I$(HI) \
65    -I$(HO)/llvm/include -I$(HO)/openssl/include \
66    -I$(HO)/gettext/include -I$(HO)/tcl-tk/include
67
68# Linker flags (suggested by homebrew)
69LDFLAGS+=-L$(HO)/llvm/lib -Wl,-rpath,$(HO)/llvm/lib
70
71# Flags for OpenMP support that should allow packages that want to use
72# OpenMP to do so (data.table), and other packages that bork with
73# -fopenmp flag (stringi) to be left alone
74SHLIB_OPENMP_CFLAGS=-fopenmp
75SHLIB_OPENMP_CXXFLAGS=-fopenmp
76SHLIB_OPENMP_CXX98FLAGS=-fopenmp
77SHLIB_OPENMP_CXX11FLAGS=-fopenmp
78SHLIB_OPENMP_CXX14FLAGS=-fopenmp
79SHLIB_OPENMP_CXX17FLAGS=-fopenmp
80SHLIB_OPENMP_FCFLAGS=-fopenmp
81SHLIB_OPENMP_FFLAGS=-fopenmp

There are three things we need to pay attention to in the file above (highlighted lines):

  1. Depending on the architecture of the macOS machine, Intel vs Apple Silicon (M1/2), we need to uncomment the corresponding lines.
  2. Run xcrun -show-sdk-path to make sure they are located at /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk
  3. To find the installed version of GCC, change directory into /usr/local/lib/gcc/ on an Intel macOS, or /opt/homebrew/lib/ on an Apple Silicon.

The mentioned method is useful for installing libraries like the data.table library that require OpenMP support to leverage multiprocessing capabilities. For more details on this approach, you can refer to the article located here.