Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Robustify R setting detection #1

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
275 changes: 88 additions & 187 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,229 +2,130 @@ name: Tests

on:
push:
branches:
- main
- master
branches: [main, master]
pull_request:
branches:
- main
- master

# cargo error handling is wrapped by ci-cargo, for simplicity and better logging.
# To use ci-cargo in a step, source ci-cargo.ps1 (using `. ./ci-cargo.ps`, note the extra dot in front).
# ci-cargo does not handle correctly -- separator, so wrap it in quotes ('--')
# A special named parameter called `ActionName` can be used to set the name of current ci-cargo action,
# used as `ci-cargo .... -ActionName "Called for documentation purposes"`.
branches: [main, master]

jobs:
# All tests
tests:
Crate-check:
runs-on: ${{ matrix.config.os }}

name: ${{ matrix.config.os }} (R-${{ matrix.config.r }} rust-${{ matrix.config.rust-version }})

name: Crate-check ${{ matrix.config.os }} (${{ matrix.config.r }} - ${{ matrix.config.rust-version }})

strategy:
fail-fast: false
matrix:
config:
- {
os: windows-latest,
r: "release",
rust-version: "stable-msvc",
rtools-version: "44",
features: "full-functionality",
}
- {
os: windows-latest,
r: "devel",
rust-version: "stable-msvc",
rtools-version: "44",
features: "default",
}
- {
os: windows-latest,
r: "oldrel",
rust-version: "stable-msvc",
rtools-version: "43",
features: "default",
}

- {
os: macOS-latest,
r: "release",
rust-version: "stable",
features: "full-functionality",
}
- {
os: ubuntu-latest,
r: "release",
rust-version: "stable",
features: "full-functionality",
check_fmt: true,
}
- {
os: ubuntu-latest,
r: "release",
rust-version: "nightly",
features: "full-functionality",
extra-args: ["-Zdoctest-xcompile"],
}
# R-devel requires LD_LIBRARY_PATH
- {
os: ubuntu-latest,
r: "devel",
rust-version: "stable",
features: "default",
}
- {
os: ubuntu-latest,
r: "oldrel",
rust-version: "stable",
features: "default",
}
- {os: windows-latest, r: 'release', rust-version: 'stable-msvc', target: 'x86_64-pc-windows-gnu', rtools-version: '44'}
- {os: windows-latest, r: 'devel', rust-version: 'stable-msvc', target: 'x86_64-pc-windows-gnu', rtools-version: '44'}
- {os: macOS-latest, r: 'release', rust-version: 'stable' }
- {os: ubuntu-latest, r: 'release', rust-version: 'stable' }
- {os: ubuntu-latest, r: 'devel', rust-version: 'stable' }

env:
R_REMOTES_NO_ERRORS_FROM_WARNINGS: true

# This environment variable enables support for pseudo multi-target cargo builds.
# Current stable Rust does not support multi-targeting,
# see https://github.com/rust-lang/cargo/issues/8176
# The variable is treated as a comma-separated list of valid Rust targets.
# 'default' value emits no '--target' flag.
# E.g.: BUILD_TARGETS=i686-pc-windows-gnu,x86_64-pc-windows-gnu builds two times,
# each time providing '--target=*-pc-windows-gnu' flag to cargo.
BUILD_TARGETS: default

RSPM: ${{ matrix.config.rspm }}
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}

# PowerShell core is available on all platforms and can be used to unify scripts
defaults:
run:
shell: pwsh

steps:
- uses: actions/checkout@v3

- name: Set up Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.config.rust-version }}
components: rustfmt, clippy

- uses: actions/checkout@v4

- name: Set up R
uses: r-lib/actions/setup-r@v2
with:
r-version: ${{ matrix.config.r }}
use-public-rspm: true
rtools-version: ${{ matrix.config.rtools-version }}
# TODO: enable RSPM when all the packages are available
use-public-rspm: false

- name: Set up Pandoc
uses: r-lib/actions/setup-pandoc@v2

- name: Configure Windows (R >= 4.2)
if: startsWith(runner.os, 'Windows') && matrix.config.r != '4.1'
run: |
if ("${{ matrix.config.rtools-version }}" -eq "42") {
$rtools_home = "C:\rtools42" # for R 4.2
} else {
$rtools_home = "C:\rtools43" # for R >= 4.3
}

# c.f. https://github.com/wch/r-source/blob/f1501504df8df1668a57d3a1b6f80167f24441d3/src/library/profile/Rprofile.windows#L70-L71
echo "${rtools_home}\x86_64-w64-mingw32.static.posix\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append ;
echo "${rtools_home}\usr\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append ;
echo "$(Rscript.exe -e 'cat(normalizePath(R.home()))')\bin\x64" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append ;

# Add target
rustup target add x86_64-pc-windows-gnu
echo "BUILD_TARGETS=x86_64-pc-windows-gnu" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append ;

# The following lines add two tweaks:
#
# 1. Change the linker name to "x86_64-w64-mingw32.static.posix-gcc.exe".
# 2. Add empty libgcc_s.a and libgcc_eh.a, and add them to the compiler's
# library search paths via `LIBRARY_PATH` envvar.
#
# The first tweak is needed because Rtools42 doesn't contain
# "x86_64-w64-mingw32-gcc," which `rustc` uses as the default linker
# for the `x86_64-pc-windows-gnu` target.
#
# If we use the Rtools' toolchain, the second tweak is also required.
# `rustc` adds `-lgcc_eh` and `-lgcc_s` flags to the compiler, but
# Rtools' GCC doesn't have `libgcc_eh` or `libgcc_s` due to the
# compilation settings. So, in order to please the compiler, we need
# to add empty `libgcc_eh` or `libgcc_s` to the library search paths.
#
# For more details, please refer to https://github.com/r-windows/rtools-packages/blob/2407b23f1e0925bbb20a4162c963600105236318/mingw-w64-gcc/PKGBUILD#L313-L316
- name: Set up Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.config.rust-version }}
components: rustfmt, clippy
targets: ${{ matrix.config.target }}

# Stuff here lifted from libR-sys workflows with thanks to authors.
# All configurations for Windows go here
# 1. Configure linker
# 2. Create libgcc_eh mock
# 3. Add R bin path to PATH
# 4. Add Rtools' GCC to PATH (required to find linker)
# 5. Add include path (required to resolve standard headers like stdio.h)
- name: Configure Windows
if: runner.os == 'Windows'
run: |
# Configure linker
echo "RUSTFLAGS=-C linker=x86_64-w64-mingw32.static.posix-gcc.exe" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append

# Create libgcc_eh mock
New-Item -Path libgcc_mock -Type Directory
New-Item -Path libgcc_mock\libgcc_eh.a -Type File
New-Item -Path libgcc_mock\libgcc_s.a -Type File

New-Item -Path .cargo -ItemType Directory -Force
$pwd_slash = echo "${PWD}" | % {$_ -replace '\\','/'}
@"
[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32.static.posix-gcc.exe"

[env]
LIBRARY_PATH = "${pwd_slash}/libgcc_mock"
"@ | Out-File -FilePath .cargo/config.toml -Encoding utf8 -Append ;
env:
RUST_TOOLCHAIN: ${{ matrix.config.rust-version }}

# TODO: Remove this runner when we drop the support for R < 4.2
- name: Configure Windows (R < 4.2)
if: startsWith(runner.os, 'Windows') && matrix.config.r == '4.1'
# 1. Add rust target
# 2. Add target name to the $targets variable
# 3. Add mingw32/mingw64 bin folders to PATH
# 4. Add R x64/i386 folders to PATH
echo "LIBRARY_PATH=${pwd_slash}/libgcc_mock" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append

# Add R bin path to PATH
echo "$(Rscript.exe -e 'cat(normalizePath(R.home()))')\bin\x64" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append ;

# Add Rtools' GCC to PATH
if ($env:RTOOLS_VERSION -eq '44') {
$mingw_root = "C:\rtools44\x86_64-w64-mingw32.static.posix"
} elseif ($env:RTOOLS_VERSION -eq '43') {
$mingw_root = "C:\rtools43\x86_64-w64-mingw32.static.posix"
} elseif ($env:RTOOLS_VERSION -eq '42') {
$mingw_root = "C:\rtools42\x86_64-w64-mingw32.static.posix"
}
echo "$mingw_root\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append

# Add include path
echo "LIBRSYS_LIBCLANG_INCLUDE_PATH=$mingw_root\include" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
env:
RUST_TARGET: ${{ matrix.config.target }}
RTOOLS_VERSION: ${{ matrix.config.rtools-version }}


# macOS configurations, mainly llvm and path to libclang
# Because of this R installation issue on macOS-11.0
# https://github.com/r-lib/actions/issues/200
# Symlinks to R/Rscript are not properly set up, so we do it by hand, using this trick
# https://github.com/r-lib/ps/commit/a24f2c4d1bdba63be14e7729b9ab81d0ed9f719e
# Environment variables are required fir Mac-OS-11.0, see
# https://github.com/extendr/libR-sys/issues/35
- name: Configure macOS
if: runner.os == 'macOS'
run: |
$targets=@()
if ($env:RUST_TOOLCHAIN -notlike "*x86_64*") {
rustup target add i686-pc-windows-gnu ;
$targets+="i686-pc-windows-gnu"
echo "${env:RTOOLS40_HOME}\mingw32\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append ;
echo "$(Rscript.exe -e 'cat(normalizePath(R.home()))')\bin\i386" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append ;
brew install llvm
echo "LIBCLANG_PATH=$(brew --prefix llvm)/lib" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
$env:LLVM_CONFIG_PATH = "$(brew --prefix llvm)/bin/llvm-config"
echo "LLVM_CONFIG_PATH=$env:LLVM_CONFIG_PATH" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
echo "LIBRSYS_LIBCLANG_INCLUDE_PATH=$(. $env:LLVM_CONFIG_PATH --libdir)/clang/$(. $env:LLVM_CONFIG_PATH --version)/include" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append

if ((Get-ChildItem -Path /usr/local/bin -Filter R | Measure-Object).Count -eq 0) {
echo "::warning:: Found no R symlink in /usr/local/bin, setting up manually..."
ln -s /Library/Frameworks/R.framework/Versions/Current/Resources/bin/R /usr/local/bin/
}
if ($env:RUST_TOOLCHAIN -notlike "*i686*") {
rustup target add x86_64-pc-windows-gnu ;
$targets+="x86_64-pc-windows-gnu"
echo "${env:RTOOLS40_HOME}\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append ;
echo "$(Rscript.exe -e 'cat(normalizePath(R.home()))')\bin\x64" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append ;
if ((Get-ChildItem -Path /usr/local/bin -Filter Rscript | Measure-Object).Count -eq 0) {
echo "::warning:: Found no Rscript symlink in /usr/local/bin, setting up manually..."
ln -s /Library/Frameworks/R.framework/Versions/Current/Resources/bin/Rscript /usr/local/bin/
}
echo "BUILD_TARGETS=$($targets -join ',')" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append ;
env:
RUST_TOOLCHAIN: ${{ matrix.config.rust-version }}

# This is required for ubuntu r-devel
# 'Del alias:R' removes 'R' alias which prevents running R
# 'Del alias:R' removes R alias which prevents running R
- name: Configure Linux
if: startsWith(runner.os, 'linux')
if: runner.os == 'linux'
run: |
Del alias:R
echo "LD_LIBRARY_PATH=$(R -s -e 'cat(normalizePath(R.home()))')/lib" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append

# Check code formatting. As this doesn't depend on the platform, do this only on one platform.
- name: Check code formatting
if: matrix.config.check_fmt
run: cargo fmt -- --check
env:
RUST_TARGET: ${{ matrix.config.target }}

# For each target in the BUILD_TARGETS comma-separated list, run cargo build with appropriate target
# Required by Windows builds, does not affect other platforms
- name: Build
run: |
. ./ci-cargo.ps1
foreach($target in ($env:BUILD_TARGETS).Split(',')) {
ci-cargo build $(if($target -ne 'default') {"--target=$target"} ) -ActionName "Building for $target target"
}
# For each target in the BUILD_TARGETS comma-separated list, run cargo test with appropriate target
# Required by Windows builds, does not affect other platforms
# ! ci-cargo requires '--' to be wrapped in quotes (passed as an explicit string)
- name: Run tests
- name: Run test
run: |
. ./ci-cargo.ps1
foreach($target in ($env:BUILD_TARGETS).Split(',')) {
# Note: no feature is specified, which means such features like graphics, serde, ndarray, and num-complex are not tested here.
ci-cargo test $(if($target -ne 'default') {"--target=$target"} ) '--' --nocapture -ActionName "Testing for $target target"
}

ci-cargo test -vv
env:
RUST_TARGET: ${{ matrix.config.target }}
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## [0.2.2] - 2025-02-19
### Fixed
- Rely on `R_HOME` env var first, then try `R RHOME`; panic if both fail.

## [0.2.1] - 2025-02-02
### Fixed
- Reformatted code to conform to Rust formatting standards (no functional changes).
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "r-src"
version = "0.2.1"
version = "0.2.2"
edition = "2021"
license = "MIT"
authors = ["Balasubramanian Narasimhan <[email protected]>"]
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ but has extra platform-specific dependencies for `Windows` such as

Two components are required to build the library:

1. [`R`](https://cran.r-project.org/): It needs to be installed and
available in the search path. On `Windows`, the `Rtools` binaries
are also expected to be on the search path.
1. [`R`](https://cran.r-project.org/): An environment variable
`R_HOME` indicating the value of `R_HOME` is first looked for and,
if not found, the command `R RHOME` is tried, so `R` must be on the
path for the latter. On `Windows`, the `Rtools` binaries are also
expected to be on the search path.
2. [`Rust`](https://www.rust-lang.org/learn/get-started): It is
recommended to install `Rust` using `rustup`; search path should
include `Rust` binaries.
Expand Down
Loading
Loading