Skip to content

Commit e83042f

Browse files
[R-package] Add support for R 4.0 (fixes #3064, fixes #3024) (#3065)
* [R-package] replaced gendef.exe with R code to create R.def (fixes #3064) * fix broken things * trying to add R4.0 docs * changed make * fixing make paths * update notes on environment variables * fix linting * fixes to CI * fixing build_cmd and other stuff * fix bad substitution * fix Azure Linux builds * I am bad at bash * simplifying * only testing R * getting better logs * mingw32 * docs * toolchain * using msys * fix visual studio condition * toolchain test * full CI * fix if-elses * bump allowed NOTEs * search for Rscript * updates to docs * use processx * fix mismatched arguments * move CI to GitHub Actions * minor changes * fix workflow file * fix templating * fix Azure DevOps * debugging windows builds * dont shQuote file name * all GitHub Actions jobs * Apply suggestions from code review Co-authored-by: Nikita Titov <nekit94-08@mail.ru> * minor cleanup * remove objdump printing * make file.remove() invisible * Apply suggestions from code review Co-authored-by: Nikita Titov <nekit94-08@mail.ru> * reduce duplicated paths in docs Co-authored-by: Nikita Titov <nekit94-08@mail.ru>
1 parent e706bec commit e83042f

File tree

12 files changed

+324
-33
lines changed

12 files changed

+324
-33
lines changed

.appveyor.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@ environment:
1515
matrix:
1616
- COMPILER: MINGW
1717
TASK: r-package
18-
- COMPILER: MSVC
18+
R_VERSION: 3.6
19+
TOOLCHAIN: MINGW
20+
- COMPILER: MSVC
1921
TASK: r-package
22+
R_VERSION: 4.0
23+
TOOLCHAIN: MSVC
2024
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
2125
- COMPILER: MSVC
2226
TASK: python

.ci/test_r_package.sh

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,26 @@ mkdir -p $R_LIB_PATH
77
echo "R_LIBS=$R_LIB_PATH" > ${HOME}/.Renviron
88
export PATH="$R_LIB_PATH/R/bin:$PATH"
99

10+
# Get details needed for installing R components
11+
#
12+
# NOTES:
13+
# * Linux builds on Azure use a container and don't need these details
14+
if ! { [[ $AZURE == "true" ]] && [[ $OS_NAME == "linux" ]]; }; then
15+
R_MAJOR_VERSION=( ${R_VERSION//./ } )
16+
if [[ "${R_MAJOR_VERSION}" == "3" ]]; then
17+
export R_MAC_VERSION=3.6.3
18+
export R_LINUX_VERSION="3.6.3-1bionic"
19+
export R_APT_REPO="bionic-cran35/"
20+
elif [[ "${R_MAJOR_VERSION}" == "4" ]]; then
21+
export R_MAC_VERSION=4.0.0
22+
export R_LINUX_VERSION="4.0.0-1.1804.0"
23+
export R_APT_REPO="bionic-cran40/"
24+
else
25+
echo "Unrecognized R version: ${R_VERSION}"
26+
exit -1
27+
fi
28+
fi
29+
1030
# installing precompiled R for Ubuntu
1131
# https://cran.r-project.org/bin/linux/ubuntu/#installation
1232
# adding steps from https://stackoverflow.com/a/56378217/3986677 to get latest version
@@ -18,7 +38,7 @@ if [[ $AZURE != "true" ]] && [[ $OS_NAME == "linux" ]]; then
1838
--keyserver keyserver.ubuntu.com \
1939
--recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9
2040
sudo add-apt-repository \
21-
"deb https://cloud.r-project.org/bin/linux/ubuntu bionic-cran35/"
41+
"deb https://cloud.r-project.org/bin/linux/ubuntu ${R_APT_REPO}"
2242
sudo apt-get update
2343
sudo apt-get install \
2444
--no-install-recommends \

.ci/test_r_package_windows.ps1

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,34 +29,60 @@ function Download-Miktex-Setup {
2929
Download-File-With-Retries $FileToDownload $destfile
3030
}
3131

32-
$env:R_WINDOWS_VERSION = "3.6.3"
3332
$env:R_LIB_PATH = "$env:BUILD_SOURCESDIRECTORY/RLibrary" -replace '[\\]', '/'
3433
$env:R_LIBS = "$env:R_LIB_PATH"
35-
$env:PATH = "$env:R_LIB_PATH/Rtools/bin;" + "$env:R_LIB_PATH/R/bin/x64;" + "$env:R_LIB_PATH/miktex/texmfs/install/miktex/bin/x64;" + $env:PATH
34+
$env:PATH = "$env:R_LIB_PATH/Rtools/bin;" + "$env:R_LIB_PATH/Rtools/usr/bin;" + "$env:R_LIB_PATH/R/bin/x64;" + "$env:R_LIB_PATH/miktex/texmfs/install/miktex/bin/x64;" + $env:PATH
3635
$env:CRAN_MIRROR = "https://cloud.r-project.org/"
3736
$env:CTAN_MIRROR = "https://ctan.math.illinois.edu/systems/win32/miktex"
3837
$env:CTAN_MIKTEX_ARCHIVE = "$env:CTAN_MIRROR/setup/windows-x64/"
3938
$env:CTAN_PACKAGE_ARCHIVE = "$env:CTAN_MIRROR/tm/packages/"
4039

40+
# Get details needed for installing R components
41+
#
42+
# NOTES:
43+
# * some paths and file names are different on R4.0
44+
$env:R_MAJOR_VERSION = $env:R_VERSION.split('.')[0]
45+
if ($env:R_MAJOR_VERSION -eq "3") {
46+
$env:RTOOLS_MINGW_BIN = "$env:R_LIB_PATH/Rtools/mingw_64/bin"
47+
$env:RTOOLS_EXE_FILE = "Rtools35.exe"
48+
$env:R_WINDOWS_VERSION = "3.6.3"
49+
} elseif ($env:R_MAJOR_VERSION -eq "4") {
50+
$env:RTOOLS_MINGW_BIN = "$env:R_LIB_PATH/Rtools/mingw64/bin"
51+
$env:RTOOLS_EXE_FILE = "rtools40-x86_64.exe"
52+
$env:R_WINDOWS_VERSION = "4.0.0"
53+
} else {
54+
Write-Output "[ERROR] Unrecognized R version: $env:R_VERSION"
55+
Check-Output $false
56+
}
57+
4158
if ($env:COMPILER -eq "MINGW") {
42-
$env:CXX = "$env:R_LIB_PATH/Rtools/mingw_64/bin/g++.exe"
43-
$env:CC = "$env:R_LIB_PATH/Rtools/mingw_64/bin/gcc.exe"
59+
$env:CXX = "$env:RTOOLS_MINGW_BIN/g++.exe"
60+
$env:CC = "$env:RTOOLS_MINGW_BIN/gcc.exe"
4461
}
4562

4663
cd $env:BUILD_SOURCESDIRECTORY
4764
tzutil /s "GMT Standard Time"
4865
[Void][System.IO.Directory]::CreateDirectory($env:R_LIB_PATH)
4966

50-
if ($env:COMPILER -eq "MINGW") {
67+
if ($env:TOOLCHAIN -eq "MINGW") {
5168
Write-Output "Telling R to use MinGW"
5269
$install_libs = "$env:BUILD_SOURCESDIRECTORY/R-package/src/install.libs.R"
53-
((Get-Content -path $install_libs -Raw) -replace 'use_mingw <- FALSE','use_mingw <- TRUE') | Set-Content -Path $install_libs
70+
((Get-Content -Path $install_libs -Raw) -Replace 'use_mingw <- FALSE','use_mingw <- TRUE') | Set-Content -Path $install_libs
71+
} elseif ($env:TOOLCHAIN -eq "MSYS") {
72+
Write-Output "Telling R to use MSYS"
73+
$install_libs = "$env:BUILD_SOURCESDIRECTORY/R-package/src/install.libs.R"
74+
((Get-Content -Path $install_libs -Raw) -Replace 'use_msys2 <- FALSE','use_msys2 <- TRUE') | Set-Content -Path $install_libs
75+
} elseif ($env:TOOLCHAIN -eq "MSVC") {
76+
# no customization for MSVC
77+
} else {
78+
Write-Output "[ERROR] Unrecognized compiler: $env:TOOLCHAIN"
79+
Check-Output $false
5480
}
5581

5682
# download R and RTools
5783
Write-Output "Downloading R and Rtools"
5884
Download-File-With-Retries -url "https://cloud.r-project.org/bin/windows/base/old/$env:R_WINDOWS_VERSION/R-$env:R_WINDOWS_VERSION-win.exe" -destfile "R-win.exe"
59-
Download-File-With-Retries -url "https://cloud.r-project.org/bin/windows/Rtools/Rtools35.exe" -destfile "Rtools.exe"
85+
Download-File-With-Retries -url "https://cloud.r-project.org/bin/windows/Rtools/$env:RTOOLS_EXE_FILE" -destfile "Rtools.exe"
6086

6187
# Install R
6288
Write-Output "Installing R"
@@ -67,9 +93,9 @@ Write-Output "Installing Rtools"
6793
Start-Process -FilePath Rtools.exe -NoNewWindow -Wait -ArgumentList "/VERYSILENT /DIR=$env:R_LIB_PATH/Rtools" ; Check-Output $?
6894
Write-Output "Done installing Rtools"
6995

70-
# MiKTeX and pandoc can be skipped on non-MINGW builds, since we don't
96+
# MiKTeX and pandoc can be skipped on MSVC builds, since we don't
7197
# build the package documentation for those
72-
if ($env:COMPILER -eq "MINGW") {
98+
if ($env:COMPILER -ne "MSVC") {
7399
Download-Miktex-Setup "$env:CTAN_MIKTEX_ARCHIVE" "miktexsetup-x64.zip"
74100
Add-Type -AssemblyName System.IO.Compression.FileSystem
75101
[System.IO.Compression.ZipFile]::ExtractToDirectory("miktexsetup-x64.zip", "miktex")
@@ -115,7 +141,7 @@ if ($env:COMPILER -ne "MSVC") {
115141
$note_str = Get-Content "${LOG_FILE_NAME}" | Select-String -Pattern ' NOTE' | Out-String ; Check-Output $?
116142
$relevant_line = $note_str -match '.*Status: (\d+) NOTE.*'
117143
$NUM_CHECK_NOTES = $matches[1]
118-
$ALLOWED_CHECK_NOTES = 3
144+
$ALLOWED_CHECK_NOTES = 4
119145
if ([int]$NUM_CHECK_NOTES -gt $ALLOWED_CHECK_NOTES) {
120146
Write-Output "Found ${NUM_CHECK_NOTES} NOTEs from R CMD check. Only ${ALLOWED_CHECK_NOTES} are allowed"
121147
Check-Output $False
@@ -139,6 +165,16 @@ if ($checks.Matches.length -eq 0) {
139165
Check-Output $False
140166
}
141167

168+
# Checking that we got the right toolchain for MinGW. If using MinGW, both
169+
# MinGW and MSYS toolchains are supported
170+
if ($env:COMPILER -eq "MINGW") {
171+
$checks = Select-String -Path "${INSTALL_LOG_FILE_NAME}" -Pattern "Trying to build with.*$env:TOOLCHAIN"
172+
if ($checks.Matches.length -eq 0) {
173+
Write-Output "The wrong toolchain was used. Check the build logs."
174+
Check-Output $False
175+
}
176+
}
177+
142178
if ($env:COMPILER -eq "MSVC") {
143179
Write-Output "Running tests with testthat.R"
144180
cd R-package/tests

.github/workflows/main.yml

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ on:
1010

1111
jobs:
1212
test:
13-
name: ${{ matrix.task }} (${{ matrix.os }}, ${{ matrix.compiler }})
13+
name: ${{ matrix.task }} (${{ matrix.os }}, ${{ matrix.compiler }}, R ${{ matrix.r_version }})
1414
runs-on: ${{ matrix.os }}
1515
strategy:
1616
fail-fast: false
@@ -19,15 +19,35 @@ jobs:
1919
- os: ubuntu-latest
2020
task: r-package
2121
compiler: gcc
22+
r_version: 3.6
23+
- os: ubuntu-latest
24+
task: r-package
25+
compiler: gcc
26+
r_version: 4.0
27+
- os: ubuntu-latest
28+
task: r-package
29+
compiler: clang
30+
r_version: 3.6
2231
- os: ubuntu-latest
2332
task: r-package
2433
compiler: clang
34+
r_version: 4.0
2535
- os: macOS-latest
2636
task: r-package
2737
compiler: gcc
38+
r_version: 3.6
39+
- os: macOS-latest
40+
task: r-package
41+
compiler: gcc
42+
r_version: 4.0
43+
- os: macOS-latest
44+
task: r-package
45+
compiler: clang
46+
r_version: 3.6
2847
- os: macOS-latest
2948
task: r-package
3049
compiler: clang
50+
r_version: 4.0
3151
steps:
3252
- name: Checkout repository
3353
uses: actions/checkout@v1
@@ -53,5 +73,6 @@ jobs:
5373
export CONDA="$HOME/miniconda"
5474
export PATH="$CONDA/bin:${HOME}/.local/bin:$PATH"
5575
export LGB_VER=$(head -n 1 VERSION.txt)
76+
export R_VERSION="${{ matrix.r_version }}"
5677
$GITHUB_WORKSPACE/.ci/setup.sh
5778
$GITHUB_WORKSPACE/.ci/test.sh

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@ R-package/src-i386
406406
lightgbm_r/*
407407
lightgbm*.tar.gz
408408
lightgbm.Rcheck/
409+
*.def
409410

410411
# Files created by R examples and tests
411412
**/lgb-Dataset.data

.vsts-ci.yml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,18 @@ jobs:
109109
pool:
110110
vmImage: 'vs2017-win2016'
111111
strategy:
112-
maxParallel: 4
112+
maxParallel: 5
113113
matrix:
114-
r_package:
114+
r_package_msvc:
115115
TASK: r-package
116116
COMPILER: MSVC
117+
R_VERSION: 3.6
118+
TOOLCHAIN: MSVC
119+
r_package_msys:
120+
TASK: r-package
121+
COMPILER: MINGW
122+
R_VERSION: 4.0
123+
TOOLCHAIN: MSYS
117124
regular:
118125
TASK: regular
119126
PYTHON_VERSION: 3.6

R-package/README.md

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,53 @@ Note: 32-bit (i386) R/Rtools is currently not supported.
2020

2121
#### Windows Preparation
2222

23-
Installing [Rtools](https://cran.r-project.org/bin/windows/Rtools/) is mandatory, and only support the 64-bit version. It requires to add to PATH the Rtools MinGW64 folder, if it was not done automatically during installation.
23+
Installing a 64-bit version of [Rtools](https://cran.r-project.org/bin/windows/Rtools/) is mandatory.
2424

25-
The default compiler is Visual Studio (or [VS Build Tools](https://visualstudio.microsoft.com/downloads/)) in Windows, with an automatic fallback to Rtools or any [MinGW64](https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/) (x86_64-posix-seh) available (this means if you have only Rtools and CMake, it will compile fine).
25+
After installing `Rtools` and `CMake`, be sure the following paths are added to the environment variable `PATH`. These may have been automatically added when installing other software.
2626

27-
To force the usage of Rtools / MinGW, you can set `use_mingw` to `TRUE` in `R-package/src/install.libs.R`.
27+
* `Rtools`
28+
- If you have `Rtools` 3.x, example:
29+
- `C:\Rtools\mingw_64\bin`
30+
- If you have `Rtools` 4.0, example:
31+
- `C:\rtools40\mingw64\bin`
32+
- `C:\rtools40\usr\bin`
33+
* `CMake`
34+
- example: `C:\Program Files\CMake\bin`
35+
* `R`
36+
- example: `C:\Program Files\R\R-3.6.1\bin`
37+
38+
NOTE: Two `Rtools` paths are required from `Rtools` 4.0 onwards because paths and the list of included software was changed in `Rtools` 4.0.
39+
40+
#### Windows Toolchain Options
41+
42+
A "toolchain" refers to the collection of software used to build the library. The R package can be built with three different toolchains.
2843

2944
**Warning for Windows users**: it is recommended to use *Visual Studio* for its better multi-threading efficiency in Windows for many core systems. For very simple systems (dual core computers or worse), MinGW64 is recommended for maximum performance. If you do not know what to choose, it is recommended to use [Visual Studio](https://visualstudio.microsoft.com/downloads/), the default compiler. **Do not try using MinGW in Windows on many core systems. It may result in 10x slower results than Visual Studio.**
3045

46+
**Visual Studio (default)**
47+
48+
By default, the package will be built with [Visual Studio Build Tools](https://visualstudio.microsoft.com/downloads/).
49+
50+
**MinGW (R 3.x)**
51+
52+
If you are using R 3.x and installation fails with Visual Studio, `LightGBM` will fall back to using [MinGW](http://mingw-w64.org/doku.php) bundled with `Rtools`.
53+
54+
If you want to force `LightGBM` to use MinGW (for any R version), open `R-package/src/install.libs.R` and change `use_mingw`:
55+
56+
```r
57+
use_mingw <- TRUE
58+
```
59+
60+
**MSYS2 (R 4.x)**
61+
62+
If you are using R 4.x and installation fails with Visual Studio, `LightGBM` will fall back to using [MSYS2](https://www.msys2.org/). This should work with the tools already bundled in `Rtools` 4.0.
63+
64+
If you want to force `LightGBM` to use MSYS2 (for any R version), open `R-package/src/install.libs.R` and change `use_msys2`:
65+
66+
```r
67+
use_msys2 <- TRUE
68+
```
69+
3170
#### Mac OS Preparation
3271

3372
You can perform installation either with **Apple Clang** or **gcc**. In case you prefer **Apple Clang**, you should install **OpenMP** (details for installation can be found in [Installation Guide](https://github.com/microsoft/LightGBM/blob/master/docs/Installation-Guide.rst#apple-clang)) first and **CMake** version 3.16 or higher is required. In case you prefer **gcc**, you need to install it (details for installation can be found in [Installation Guide](https://github.com/microsoft/LightGBM/blob/master/docs/Installation-Guide.rst#gcc)) and set some environment variables to tell R to use `gcc` and `g++`. If you install these from Homebrew, your versions of `g++` and `gcc` are most likely in `/usr/local/bin`, as shown below.

R-package/inst/make-r-def.R

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# [description]
2+
# Create a definition file (.def) from a .dll file, using objdump.
3+
#
4+
# [usage]
5+
#
6+
# Rscript make-r-def.R something.dll something.def
7+
#
8+
# [references]
9+
# * https://www.cs.colorado.edu/~main/cs1300/doc/mingwfaq.html
10+
11+
args <- commandArgs(trailingOnly = TRUE)
12+
13+
IN_DLL_FILE <- args[[1L]]
14+
OUT_DEF_FILE <- args[[2L]]
15+
DLL_BASE_NAME <- basename(IN_DLL_FILE)
16+
17+
message(sprintf("Creating '%s' from '%s'", OUT_DEF_FILE, IN_DLL_FILE))
18+
19+
# system() will not raise an R exception if the process called
20+
# fails. Wrapping it here to get that behavior.
21+
#
22+
# system() introduces a lot of overhead, at least on Windows,
23+
# so trying processx if it is available
24+
.pipe_shell_command_to_stdout <- function(command, args, out_file) {
25+
has_processx <- suppressMessages({
26+
suppressWarnings({
27+
require("processx") # nolint
28+
})
29+
})
30+
if (has_processx) {
31+
p <- processx::process$new(
32+
command = command
33+
, args = args
34+
, stdout = out_file
35+
, windows_verbatim_args = FALSE
36+
)
37+
invisible(p$wait())
38+
} else {
39+
message(paste0(
40+
"Using system2() to run shell commands. Installing "
41+
, "'processx' with install.packages('processx') might "
42+
, "make this faster."
43+
))
44+
# shQuote() is necessary here since one of the arguments
45+
# is a file-path to R.dll, which may have spaces. processx
46+
# does such quoting but system2() does not
47+
exit_code <- system2(
48+
command = command
49+
, args = shoQuote(args)
50+
, stdout = out_file
51+
)
52+
if (exit_code != 0L) {
53+
stop(paste0("Command failed with exit code: ", exit_code))
54+
}
55+
}
56+
return(invisible(NULL))
57+
}
58+
59+
# use objdump to dump all the symbols
60+
OBJDUMP_FILE <- "objdump-out.txt"
61+
.pipe_shell_command_to_stdout(
62+
command = "objdump"
63+
, args = c("-p", IN_DLL_FILE)
64+
, out_file = OBJDUMP_FILE
65+
)
66+
67+
objdump_results <- readLines(OBJDUMP_FILE)
68+
invisible(file.remove(OBJDUMP_FILE))
69+
70+
# Only one table in the objdump results matters for our purposes,
71+
# see https://www.cs.colorado.edu/~main/cs1300/doc/mingwfaq.html
72+
start_index <- which(
73+
grepl(
74+
pattern = "[Ordinal/Name Pointer] Table"
75+
, x = objdump_results
76+
, fixed = TRUE
77+
)
78+
)
79+
empty_lines <- which(objdump_results == "")
80+
end_of_table <- empty_lines[empty_lines > start_index][1L]
81+
82+
# Read the contents of the table
83+
exported_symbols <- objdump_results[(start_index + 1L):end_of_table]
84+
exported_symbols <- gsub("\t", "", exported_symbols)
85+
exported_symbols <- gsub(".*\\] ", "", exported_symbols)
86+
exported_symbols <- gsub(" ", "", exported_symbols)
87+
88+
# Write R.def file
89+
writeLines(
90+
text = c(
91+
paste0("LIBRARY \"", DLL_BASE_NAME, "\"")
92+
, "EXPORTS"
93+
, exported_symbols
94+
)
95+
, con = OUT_DEF_FILE
96+
, sep = "\n"
97+
)
98+
message(sprintf("Successfully created '%s'", OUT_DEF_FILE))

0 commit comments

Comments
 (0)