Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .Rprofile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
source("renv/activate.R")
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
.github
LICENSE
README.md
# From https://rstudio.github.io/renv/articles/docker.html#containerizing-an-existing-renv-project
renv/*
!renv/activate.R
!renv/settings.json
59 changes: 44 additions & 15 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,14 @@ apt-get -y install --no-install-recommends \
pkgconf \
# switching UID/GID inside container (for permissions)
gosu \
# Required by nlme \
libblas3 \
# Numerical library, dynamically linked in FORCE
libgsl0-dev \
# JSON parsing, dynamically linked in FORCE
libjansson-dev \
# Required by nlme
liblapack3 \
# build requirement for Rmarkdown
libuv1-dev \
# file locking, used in FORCE's bash scripts
Expand Down Expand Up @@ -80,8 +84,15 @@ apt-get install -y --no-install-recommends \
ccache \
# build requirement for OpenCV
cmake \
# build requirement for nlme
gfortran \
# build requirement for nlme
libblas-dev \
# build requirement for nlme
liblapack-dev \
ninja-build \
python3-pip
python3-pip \
r-cran-renv

# Install Python packages
RUN --mount=type=bind,source=requirements.txt,target=requirements.txt \
Expand All @@ -94,6 +105,37 @@ RUN --mount=type=bind,source=requirements.txt,target=requirements.txt \
exit 1) && \
pip3 install --root /build_thirdparty --break-system-packages --no-cache-dir -r requirements.txt

# Install R packages.
WORKDIR /work
RUN mkdir renv

# Packages installed: rmarkdown, echarts4r, snow, snowfall, getopt
#
# Updating the packages in the lock file:
# Rscript -e 'renv::update()' && \
# Rscript -e 'renv::snapshot()'
#
# Bootstrapping renv to generate the first renv.lock & settings:
# Rscript -e 'renv::init(); renv::settings$snapshot.type("all")' && \
# Rscript -e 'install.packages("package", Ncpus = parallel::detectCores(), repos="https://cloud.r-project.org"); if (!library(package, logical.return=T)) quit(save="no", status=10) && \
# Rscript -e 'renv::snapshot()'

COPY --link .Rprofile renv.lock ./
COPY --link renv/activate.R renv/activate.R
COPY --link renv/settings.json renv/settings.json

# Ccache size set from "ccache -s -v" after built from an empty cache.
# Other ccache settings from https://dirk.eddelbuettel.com/blog/2017/11/27/.
RUN --mount=type=cache,id=force-base-renv,target=/root/.cache \
export RENV_PATHS_LIBRARY=/usr/local/lib/R && \
export RENV_CONFIG_CACHE_SYMLINKS=FALSE && \
mkdir -p $HOME/.R $HOME/.config/ccache && \
echo -n "CCACHE=ccache\nCC=\$(CCACHE) gcc\nCXX=\$(CCACHE) g++\nCXX11=\$(CCACHE) g++\nCXX14=\$(CCACHE) g++\nCXX17=\$(CCACHE) g++\nFC=\$(CCACHE) gfortran\nF77=\$(CCACHE) gfortran\n" > $HOME/.R/Makevars && \
echo 'MAKEFLAGS = -j$(shell nproc)' >> $HOME/.R/Makevars && \
echo -n "max_size = 200M\nsloppiness = include_file_ctime\nhash_dir = false\n" > $HOME/.config/ccache/ccache.conf && \
R -s -e 'renv::restore()' && \
rm -rf $HOME/.R $HOME/.config/ccache

# Build OpenCV from source, only include the required parts.
RUN --mount=type=cache,id=force-base-opencv,target=/root/.cache \
ccache -M 20M && \
Expand Down Expand Up @@ -134,20 +176,7 @@ FROM internal_base AS builder
# Add login-script for UID/GID-remapping.
COPY --chown=root:root --link remap-user.sh /usr/local/bin/remap-user.sh

# Install R packages.
# Ccache size set from "ccache -s -v" after built from an empty cache.
# Other ccache settings from https://dirk.eddelbuettel.com/blog/2017/11/27/.
RUN --mount=type=cache,id=force-base-r,target=/root/.cache \
mkdir -p $HOME/.R $HOME/.config/ccache && \
echo -n "CCACHE=ccache\nCC=\$(CCACHE) gcc\nCXX=\$(CCACHE) g++\nCXX11=\$(CCACHE) g++\nCXX14=\$(CCACHE) g++\nCXX17=\$(CCACHE) g++\nFC=\$(CCACHE) gfortran\nF77=\$(CCACHE) gfortran\n" > $HOME/.R/Makevars && \
echo -n "max_size = 200M\nsloppiness = include_file_ctime\nhash_dir = false\n" > $HOME/.config/ccache/ccache.conf && \
Rscript -e 'install.packages("rmarkdown", Ncpus = parallel::detectCores(), repos="https://cloud.r-project.org"); if (!library(rmarkdown, logical.return=T)) quit(save="no", status=10)' && \
Rscript -e 'install.packages("echarts4r", Ncpus = parallel::detectCores(), repos="https://cloud.r-project.org"); if (!library(echarts4r, logical.return=T)) quit(save="no", status=10)' && \
Rscript -e 'install.packages("snow", Ncpus = parallel::detectCores(), repos="https://cloud.r-project.org"); if (!library(snow, logical.return=T)) quit(save="no", status=10)' && \
Rscript -e 'install.packages("snowfall", Ncpus = parallel::detectCores(), repos="https://cloud.r-project.org"); if (!library(snowfall, logical.return=T)) quit(save="no", status=10)' && \
Rscript -e 'install.packages("getopt", Ncpus = parallel::detectCores(), repos="https://cloud.r-project.org"); if (!library(getopt, logical.return=T)) quit(save="no", status=10)' && \
rm -rf $HOME/.R $HOME/.config/ccache

COPY --from=opencv_builder --link /usr/local/lib/R/R-*/* /usr/local/lib/R/site-library/
COPY --from=opencv_builder --link /build_thirdparty/usr/ /usr/

# De-sudo this image
Expand Down
119 changes: 119 additions & 0 deletions UPDATE-R.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Updating R Package Dependencies

The pinned R dependencies are managed via [renv](https://rstudio.github.io/renv/).
`renv.lock` records the exact version of every package (direct and transitive).

To update it, spin up an ephemeral container from the same base image used by the
Dockerfile, mount this repository into it, and run the renv commands there.
The only file that needs to be committed afterwards is the updated `renv.lock`.

---

## Procedure

### 1. Build a local helper image with R and build dependencies

Run this once from the root of the repository (re-run whenever the base image
SHA in the `FROM` line of the Dockerfile changes):

```bash
docker build -t gdal-r-update - <<'EOF'
FROM ghcr.io/osgeo/gdal:ubuntu-small-3.11.3
RUN DEBIAN_FRONTEND=noninteractive apt-get update -qq && \
apt-get install -y --no-install-recommends \
r-base \
r-cran-renv \
gfortran \
libblas-dev \
liblapack-dev \
libuv1-dev \
pandoc
EOF
```

This installs R and all build dependencies as root inside a throwaway image.

---

### 2. Launch an interactive container

```bash
docker run --rm -it \
-v "$(pwd)":/work \
-u "$(id -u):$(id -g)" \
-e HOME=/tmp \
gdal-r-update \
bash
```

Running as your own UID/GID (`-u`) ensures that any files written to the
mounted repository — including the updated `renv.lock` — are owned by you.
`-e HOME=/tmp` gives renv a writable home directory inside the container.

---

### 3. Restore the current state

```bash
cd /work
export RENV_PATHS_LIBRARY=/tmp/renv-lib # keep packages out of the mounted volume
Rscript -e 'renv::restore()'
```

This installs all packages at the versions currently recorded in `renv.lock`.
It is required before updating so that renv knows what is installed.

---

### 4. Update packages

**Update all packages to the latest CRAN versions:**

```bash
Rscript -e 'renv::update()'
```

**Update a single package:**

```bash
Rscript -e 'renv::update("echarts4r")'
```

**Install a new package and add it to the lockfile:**

```bash
Rscript -e 'install.packages("newpkg")'
```

---

### 5. Write the new versions to the lockfile

```bash
Rscript -e 'renv::snapshot()'
```

This overwrites `renv.lock` with the currently installed versions.
Because `/work` is a bind mount, the file is updated directly on your host.

---

### 6. Commit

```bash
git add renv.lock
git commit -m "renv: update R package dependencies"
```

---

## Notes

- The directly required packages are: `rmarkdown`, `echarts4r`, `snow`, `snowfall`, `getopt`.
All other entries in `renv.lock` are their transitive dependencies.
- `snapshot.type` is set to `"all"` in `renv/settings.json`, so every installed
package (including indirect dependencies) is captured in the lockfile.
- `renv/library/` is listed in `renv/.gitignore` and must not be committed.
- The `R.Version` field in `renv.lock` reflects the R version inside the container.
Make sure the base image SHA in the `docker run` command matches the one in the
`FROM` line of the Dockerfile to keep R versions consistent.
Loading