The {cranitor} R Package
I have recently released the unofficial cranitor package for R.
The goal of cranitor is to maintain the backend of a CRAN – that is, the folder structure and metadata needed for install.packages
and remotes::install_version
.
As an example, consider a CRAN with the packages foo
(with versions 0.0.1 and 0.0.2) and bar
(with version 0.0.1).
The backend of such a CRAN with source versions and Windows versions for R 3.6.x are the following folder structure and files:
.
├── bin
│ └── windows
│ └── contrib
│ └── 3.6
│ ├── bar_0.0.1.zip
│ └── foo_0.0.2.zip
└── src
└── contrib
├── Archive
│ └── foo
│ └── foo_0.0.1.tar.gz
├── bar_0.0.1.tar.gz
├── foo_0.0.2.tar.gz
├── Meta
│ └── archive.rds
├── PACKAGES
├── PACKAGES.gz
└── PACKAGES.rds
In more details:
- The folder
src/contrib
contains the latest version of each package in tar.gz
format – the result of devtools::build(binary = FALSE)
.
- The folder
bin/windows/contrib
contains the latest version of each package in zip
format – the result of devtools::build(binary = TRUE)
on Windows.
- The folder
stc/contrib/Archive
contains all versions of packages that are not the latest version.
- The different
PACKAGES
files are metadata for install.packages
.
- The
archive.rds
is for remotes::install_version
.
The difference between the binary zip
versions and the tar.gz
versions is that the binary versions are compiled – both byte compilation of the R code and compilation of any C/C++/Fortran code.
The latter is the reason that it is so much faster to install packages on Windows than on Linux, which use the tar.gz
versions.
A CRAN can also have a bin/macosx
folder with binary packages compiled for macOS.
Just as for Windows, the binary packages for macOS can only be created with R on macOS.
Using a CRAN
The cranitor package does not handle hosting of the CRAN.
But any hosting service should be capable of hosting a simple folder structure.
In the tests of cranitor I use the servr package to test that I can install packages in the expected way.
I am actually quite pleased with how easily I can run a server as part of the tests.
When trying out a homemade CRAN it can be tempting to try it out on a local computer.
But beware that a CRAN is meant/expected to be served over the http protocol.
Base R does support a “file protocol” by specifying the path in this manner:
cran_path <- file.path("file://", normalizePath(cran_root, winslash = "/"))
normalizePath
is used to get the correct number of slashes.
This works with install.packages
:
install.packages("foo", type = "source", repos = cran_path)
However, it does not work with remotes::install_version
.
remotes::install_version("foo", version = "0.0.1", repos = cran_path)
On Linux I get an error like this:
Downloading package from url: file:////path/to/cran/src/contrib/Archive/foo/foo_0.0.1.tar.gz
Error in utils::download.file(url, path, method = method, quiet = quiet, :
cannot open URL 'file:////path/to/cran/src/contrib/Archive/foo/foo_0.0.1.tar.gz'
On Windows the error looks like this:
tar.exe: Error opening archive: truncated gzip input
Warning messages:
1: In utils::untar(tarfile, ...) :
‘tar.exe -xf "C:\Users\robert\AppData\Local\Temp\RtmpQTtWcH\file2b5c166e5518.tar.gz" -C "C:/Users/robert/AppData/Local/Temp/RtmpQTtWcH/remotes2b5c59f945e8"’ returned error code 1
2: In system(cmd, intern = TRUE) :
running command 'tar.exe -tf "C:\Users\robert\AppData\Local\Temp\RtmpQTtWcH\file2b5c166e5518.tar.gz"' had status 1
Location aware packages
My usecase of a local CRAN is to host internal packages.
To make a package aware of which CRAN it is located in the DESCRIPTION
file should include the field
Repository: <CRAN url>
If a package depends on other packages from a specific CRAN it can be specified that R should also look here by including yet another field:
Additional_repositories: <CRAN url>
In the build pipelines we use to test and upload our R packages to our local CRAN these fields are set automatically.