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
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:
│ └── windows
│ └── contrib
│ └── 3.6
│ ├── bar_0.0.1.zip
│ └── foo_0.0.2.zip
│ └── foo
│ └── foo_0.0.1.tar.gz
│ └── archive.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
archive.rds is for
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
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("foo", type = "source", repos = cran_path)
However, it does not work with
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
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.