πŸ”—Quick Start: Mimicking the R Build Terminal

πŸ”—For x86_64 (Intel/AMD) builds

Open the MSYS2 UCRT64 shell that ships with Rtools45. On a default install this is:

c:\rtools45\ucrt64.exe

Or from cmd.exe:

c:\rtools45\usr\bin\bash.exe --login -i

Once inside, verify the environment:

which gcc        # should resolve to /ucrt64/bin/gcc or similar
which make       # should resolve to the MSYS2 make
echo $MSYSTEM    # should say UCRT64

πŸ”—For aarch64 (ARM) builds

Open the MSYS2 CLANG64 shell from Rtools45-aarch64:

c:\rtools45-aarch64\clangarm64.exe

Or:

c:\rtools45-aarch64\usr\bin\bash.exe --login -i

Verify:

which clang      # should resolve inside the toolchain
echo $MSYSTEM    # should say CLANGARM64

πŸ”—What the Build System Actually Does

πŸ”—Shell: sh (MSYS2), not cmd.exe

The R build Makefiles set:

SHELL = sh

(in src/gnuwin32/fixed/Makeconf, line 9)

This sh is the MSYS2 Bourne shell (/usr/bin/sh) shipped inside Rtools. All Make recipe lines execute through it. This gives you POSIX shell semantics (pipes, redirects, sed, which, etc.) even on Windows.

Important distinction: when R is running (not building) and a user calls system(), R uses COMSPEC (typically cmd.exe /c ...) instead. See src/gnuwin32/run.c.

πŸ”—Toolchain discovery: everything must be on PATH

Since Rtools42, R assumes the compiler toolchain is on PATH. There is no hardcoded path to gcc or any tool. From MkRules.rules:

β€œSince Rtools42, Rtools assumes that the compiler toolchain (e.g. gcc, as, … for Intel targets) is on PATH”

The key PATH entries for Rtools45 x86_64 are:

c:/rtools45/x86_64-w64-mingw32.static.posix/bin   # gcc, g++, gfortran, ar, etc.
c:/rtools45/usr/bin                                 # sh, make, sed, tar, etc.

For aarch64:

c:/rtools45-aarch64/aarch64-w64-mingw32.static.posix/bin   # clang, flang, lld, etc.
c:/rtools45-aarch64/usr/bin                                  # sh, make, sed, tar, etc.

These paths come from Rcmd_environ (lines 39-42) and are what the Rtools MSYS2 shell launchers set up for you automatically.

πŸ”—LOCAL_SOFT: auto-detected from compiler location

LOCAL_SOFT points to the root of the Rtools software collection (headers and static libraries). It is computed dynamically from wherever gcc/clang lives:

LOCAL_SOFT ?= $(shell which `echo $(CC) | sed -e 's/ .*//g'` | sed -e 's!/bin/[^/]\+!!g')

In practice:

  • x86_64: LOCAL_SOFT = /ucrt64 (i.e. c:/rtools45/x86_64-w64-mingw32.static.posix)
  • aarch64: LOCAL_SOFT = /clangarm64 (i.e. c:/rtools45-aarch64/aarch64-w64-mingw32.static.posix)

You can override it with R_CUSTOM_TOOLS_SOFT.


πŸ”—Key Environment Variables

VariablePurposeDefault
MSYSTEMSet by MSYS2 shell launcher; controls which toolchain prefix is activeUCRT64 or CLANGARM64
RTOOLS45_HOMEOverride Rtools install locationc:/rtools45
RTOOLS45_AARCH64_HOMEOverride aarch64 Rtools locationc:/rtools45-aarch64
R_CUSTOM_TOOLS_SOFTOverride LOCAL_SOFT for packages(unset)
R_CUSTOM_TOOLS_PATHOverride the Rtools PATH entries(unset)
USE_LLVMSet to 1 to use clang/flang instead of gcc/gfortran(unset)
WIN64 for x86_64 subarch layout; empty for aarch64 (no subarch)64
BINPREF / BINPREF64Prefix for tool names (for cross-compilation)(empty)
MSYS2_ENV_CONV_EXCLR sets this to R_ARCH to prevent MSYS2 path manglingset in C code

πŸ”—Configuration File Chain

When you run make in src/gnuwin32/, here is what happens:

MkRules.dist          # shipped defaults (template, not read directly)
    |
    v
MkRules.local         # your overrides (copy from .dist, uncomment what you need)
    +
MkRules.rules         # runtime logic: compiler detection, LOCAL_SOFT, tool paths
    =
MkRules               # generated by concatenating .local + .rules
    |
    v
fixed/etc/Makeconf    # template for installed R's Makeconf (@ tokens substituted)
    |
    v  (via fixed/Makefile sed substitutions)
$R_HOME/etc/Makeconf  # final Makeconf shipped with R

πŸ”—What MkRules.rules computes automatically

  • CC = $(BINPREF)$(CCBASE) $(M_ARCH): compiler command
  • CCBASE = gcc or clang (if USE_LLVM is set)
  • FC = gfortran or flang
  • AR, NM, RANLIB, DLLTOOL, RESCOMP: all prefixed with BINPREF and optionally LLVMPREF (llvm-) for LLVM builds
  • TEXI2DVI = env COMSPEC= texi2dvi: clears COMSPEC to work around an MSYS2 bug
  • PKG_CONFIG = $(BINPREF)pkg-config: for library detection

πŸ”—Step-by-Step: Set Up a Terminal That Matches the Build Environment

Just launch the appropriate .exe:

  • x86_64: c:\rtools45\ucrt64.exe
  • aarch64: c:\rtools45-aarch64\clangarm64.exe

This sets MSYSTEM, PATH, and everything else correctly.

πŸ”—Option B: Manual setup from cmd.exe or PowerShell

@echo off
set RTOOLS45_HOME=c:\rtools45
set PATH=%RTOOLS45_HOME%\x86_64-w64-mingw32.static.posix\bin;%RTOOLS45_HOME%\usr\bin;%PATH%
set MSYSTEM=UCRT64

rem Launch the MSYS2 sh (what make uses as SHELL)
%RTOOLS45_HOME%\usr\bin\bash.exe --login

For aarch64, replace paths and set MSYSTEM=CLANGARM64.

πŸ”—Option C: From an existing MSYS2 bash, set up for R building

export PATH="/c/rtools45/x86_64-w64-mingw32.static.posix/bin:/c/rtools45/usr/bin:$PATH"

# Navigate to the R source
cd /c/path/to/r-source/src/gnuwin32

# Create MkRules.local if you haven't (copy from dist, edit as needed)
cp MkRules.dist MkRules.local

# Build
make all recommended

πŸ”—Option D: Register as a Windows Terminal profile

To make the Rtools MSYS2 shell available (and optionally the default) in Windows Terminal, add a profile to %LOCALAPPDATA%\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json:

{
  "guid": "{ede0cb62-8f00-4a05-8058-c00da0637b98}",
  "name": "Rtools45 UCRT64 (x86_64)",
  "commandline": "C:\\rtools45\\usr\\bin\\bash.exe --login -i",
  "icon": "C:\\rtools45\\ucrt64.ico",
  "hidden": false,
  "startingDirectory": "%USERPROFILE%",
  "environment": {
    "MSYSTEM": "UCRT64",
    "CHERE_INVOKING": "1",
    "MSYS2_PATH_TYPE": "inherit"
  }
}

Place the profile inside profiles.list. To make it the default, set defaultProfile at the top of the settings file to the same GUID.

Key points:

  • MSYS2_PATH_TYPE=inherit is required so the MSYS2 login shell prepends its toolchain paths to the Windows PATH instead of replacing it. This is what keeps cargo, rustc, R.exe, git, etc. reachable inside the shell.
  • CHERE_INVOKING=1 tells /etc/profile not to cd to $HOME, so the shell opens in whatever directory launched it (important for β€œOpen in Terminal” actions in Explorer / IDEs).
  • For the aarch64 toolchain, swap in C:\rtools45-aarch64\usr\bin\bash.exe and MSYSTEM=CLANGARM64. Only add that profile if Rtools45-aarch64 is actually installed. A missing commandline or icon path makes Windows Terminal warn on every startup.

πŸ”—Option E: Register as a VS Code / Positron terminal profile

The repo’s .vscode/settings.json ships Windows terminal profiles for UCRT64 (and CLANGARM64) using the same environment as Option D. Pick one from the + dropdown in the integrated terminal, or via the Command Palette β†’ Terminal: Create New Terminal (With Profile). .vscode/tasks.json also exposes them as Tasks: Run Task entries.


πŸ”—How R Invokes make for Package Installation

When you run R CMD INSTALL on a package with compiled code, R:

  1. Reads $R_HOME/etc/Makeconf (which has all the tool paths baked in)
  2. Reads the package’s Makevars.win (if present)
  3. Invokes make with SHELL = sh
  4. The compiler (gcc/clang) and all tools are found on PATH

The Rcmd_environ file sets up the PATH for this (lines 39-42 are uncommented during installer builds):

R_RTOOLS45_PATH="${RTOOLS45_HOME:-c:/rtools45}/x86_64-w64-mingw32.static.posix/bin;${RTOOLS45_HOME:-c:/rtools45}/usr/bin"
PATH="${R_CUSTOM_TOOLS_PATH:-${R_RTOOLS45_PATH}};${PATH}/"

πŸ”—Common Gotchas

  1. SHELL = sh, not bash. Make recipes use sh semantics. Bashisms (arrays, [[ ]], etc.) will fail in Makefile recipes.

  2. COMSPEC is cleared for texi2dvi. TEXI2DVI = env COMSPEC= texi2dvi works around an MSYS2 bug where texi2dvi gets confused by Windows COMSPEC.

  3. MSYS2 path conversion. MSYS2 auto-converts /c/foo to C:\foo in some contexts. R sets MSYS2_ENV_CONV_EXCL=R_ARCH in C code (src/gnuwin32/system.c) to prevent mangling of R_ARCH.

  4. No hardcoded Rtools path. If gcc isn’t on PATH, the build will fail. There is no fallback path detection.

  5. pkg-config is used by default (USE_PKG_CONFIG = 1) to find library flags. Falls back to hardcoded library lists if pkg-config is unavailable.

  6. Static linking. Rtools uses static libraries (the .static.posix in the toolchain name). DLLFLAGS includes -static-libgcc and libraries are linked statically from LOCAL_SOFT/lib.

  7. /dev/shm and /dev/mqueue warnings when launching from Windows Terminal. Starting an Rtools shell directly via bash.exe in Windows Terminal can print:

    mkdir: cannot create directory '/dev/shm': Read-only file system
    Creating /dev/shm directory failed.
    POSIX semaphores and POSIX shared memory will not work
    
    mkdir: cannot create directory '/dev/mqueue': Read-only file system
    Creating /dev/mqueue directory failed.
    POSIX message queues will not work

    The MSYS2 runtime tries to create these mounts on startup; when bash is launched outside the msys2_shell.cmd wrapper it can’t set them up against the virtual /dev tree. The warnings are benign for R package work. Nothing in the R build toolchain (gcc, make, R CMD INSTALL, cargo) uses POSIX semaphores or message queues.

    To silence them, launch through the wrapper instead of raw bash.exe in your Windows Terminal profile:

    "commandline": "C:\\rtools45\\msys2_shell.cmd -defterm -here -no-start -ucrt64"

    (use -clangarm64 for Rtools45-aarch64). The wrapper mounts /dev/shm and /dev/mqueue as tmpfs before bash starts. The Option D profile above works fine despite the warnings, so only switch wrappers if the noise bothers you.