build_r.R 6.98 KB
Newer Older
1
# For macOS users who have decided to use gcc
2
# (replace 8 with version of gcc installed on your machine)
James Lamb's avatar
James Lamb committed
3
4
5
6
7
# NOTE: your gcc / g++ from Homebrew is probably in /usr/local/bin
#export CXX=/usr/local/bin/g++-8 CC=/usr/local/bin/gcc-8
# Sys.setenv("CXX" = "/usr/local/bin/g++-8")
# Sys.setenv("CC" = "/usr/local/bin/gcc-8")

8
9
args <- commandArgs(trailingOnly = TRUE)
INSTALL_AFTER_BUILD <- !("--skip-install" %in% args)
10
11
TEMP_R_DIR <- file.path(getwd(), "lightgbm_r")
TEMP_SOURCE_DIR <- file.path(TEMP_R_DIR, "src")
12

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
USING_GPU <- "--use-gpu" %in% args
USING_MINGW <- "--use-mingw" %in% args
USING_MSYS2 <- "--use-msys2" %in% args

recognized_args <- c(
  "--skip-install"
  , "--use-gpu"
  , "--use-mingw"
  , "--use-msys2"
)
unrecognized_args <- setdiff(args, recognized_args)
if (length(unrecognized_args) > 0L) {
  msg <- paste0(
    "Unrecognized arguments: "
    , paste0(unrecognized_args, collapse = ", ")
  )
  stop(msg)
}

# [description] Replace statements in install.libs.R code based on
#               command-line flags
.replace_flag <- function(variable_name, value, content) {
  out <- gsub(
    pattern = paste0(variable_name, " <-.*")
    , replacement = paste0(variable_name, " <- ", as.character(value))
    , x = content
  )
  return(out)
}

43
44
45
install_libs_content <- readLines(
  file.path("R-package", "src", "install.libs.R")
)
46
47
48
install_libs_content <- .replace_flag("use_gpu", USING_GPU, install_libs_content)
install_libs_content <- .replace_flag("use_mingw", USING_MINGW, install_libs_content)
install_libs_content <- .replace_flag("use_msys2", USING_MSYS2, install_libs_content)
49

James Lamb's avatar
James Lamb committed
50
51
# R returns FALSE (not a non-zero exit code) if a file copy operation
# breaks. Let's fix that
52
.handle_result <- function(res) {
53
  if (!all(res)) {
54
55
    stop("Copying files failed!")
  }
James Lamb's avatar
James Lamb committed
56
57
}

58
# system() will not raise an R exception if the process called
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# fails. Wrapping it here to get that behavior.
#
# system() introduces a lot of overhead, at least on Windows,
# so trying processx if it is available
.run_shell_command <- function(cmd, args, strict = TRUE) {
    on_windows <- .Platform$OS.type == "windows"
    has_processx <- suppressMessages({
      suppressWarnings({
        require("processx")  # nolint
      })
    })
    if (has_processx && on_windows) {
      result <- processx::run(
        command = cmd
        , args = args
        , windows_verbatim_args = TRUE
        , error_on_status = FALSE
        , echo = TRUE
      )
      exit_code <- result$status
    } else {
      if (on_windows) {
        message(paste0(
          "Using system() to run shell commands. Installing "
          , "'processx' with install.packages('processx') might "
          , "make this faster."
        ))
      }
      cmd <- paste0(cmd, " ", paste0(args, collapse = " "))
      exit_code <- system(cmd)
    }

    if (exit_code != 0L && isTRUE(strict)) {
92
93
        stop(paste0("Command failed with exit code: ", exit_code))
    }
94
    return(invisible(exit_code))
95
96
}

James Lamb's avatar
James Lamb committed
97
# Make a new temporary folder to work in
98
99
unlink(x = TEMP_R_DIR, recursive = TRUE)
dir.create(TEMP_R_DIR)
James Lamb's avatar
James Lamb committed
100
101

# copy in the relevant files
102
103
result <- file.copy(
  from = "R-package/./"
104
  , to = sprintf("%s/", TEMP_R_DIR)
105
106
107
  , recursive = TRUE
  , overwrite = TRUE
)
James Lamb's avatar
James Lamb committed
108
109
.handle_result(result)

110
111
112
113
114
115
# overwrite src/install.libs.R with new content based on command-line flags
writeLines(
  text = install_libs_content
  , con = file.path(TEMP_SOURCE_DIR, "install.libs.R")
)

116
117
118
119
120
121
122
123
124
125
126
127
128
129
# Add blank Makevars files
result <- file.copy(
  from = file.path(TEMP_R_DIR, "inst", "Makevars")
  , to = file.path(TEMP_SOURCE_DIR, "Makevars")
  , overwrite = TRUE
)
.handle_result(result)
result <- file.copy(
  from = file.path(TEMP_R_DIR, "inst", "Makevars.win")
  , to = file.path(TEMP_SOURCE_DIR, "Makevars.win")
  , overwrite = TRUE
)
.handle_result(result)

130
131
result <- file.copy(
  from = "include/"
132
  , to =  sprintf("%s/", TEMP_SOURCE_DIR)
133
134
135
  , recursive = TRUE
  , overwrite = TRUE
)
James Lamb's avatar
James Lamb committed
136
137
.handle_result(result)

138
139
result <- file.copy(
  from = "src/"
140
  , to = sprintf("%s/", TEMP_SOURCE_DIR)
141
142
143
  , recursive = TRUE
  , overwrite = TRUE
)
Gao Tao's avatar
Gao Tao committed
144
145
.handle_result(result)

146
147
148
149
150
151
152
153
154
155
156
# compute/ is a submodule with boost, only needed if
# building the R package with GPU support
if (USING_GPU) {
  result <- file.copy(
    from = "compute/"
    , to = sprintf("%s/", TEMP_SOURCE_DIR)
    , recursive = TRUE
    , overwrite = TRUE
  )
  .handle_result(result)
}
James Lamb's avatar
James Lamb committed
157

158
159
result <- file.copy(
  from = "CMakeLists.txt"
160
  , to = file.path(TEMP_R_DIR, "inst", "bin/")
161
162
  , overwrite = TRUE
)
James Lamb's avatar
James Lamb committed
163
164
.handle_result(result)

165
166
# remove CRAN-specific files
result <- file.remove(
167
168
  file.path(TEMP_R_DIR, "cleanup")
  , file.path(TEMP_R_DIR, "configure")
169
170
171
172
173
174
175
  , file.path(TEMP_R_DIR, "configure.ac")
  , file.path(TEMP_R_DIR, "configure.win")
  , file.path(TEMP_SOURCE_DIR, "Makevars.in")
  , file.path(TEMP_SOURCE_DIR, "Makevars.win.in")
)
.handle_result(result)

176
177
178
179
180
181
182
183
184
185
186
#------------#
# submodules #
#------------#
result <- file.copy(
  from = "external_libs/"
  , to = sprintf("%s/", TEMP_SOURCE_DIR)
  , recursive = TRUE
  , overwrite = TRUE
)
.handle_result(result)

187
188
189
190
191
192
193
194
195
196
197
198
199
200
# copy files into the place CMake expects
for (src_file in c("lightgbm_R.cpp", "lightgbm_R.h", "R_object_helper.h")) {
  result <- file.copy(
    from = file.path(TEMP_SOURCE_DIR, src_file)
    , to = file.path(TEMP_SOURCE_DIR, "src", src_file)
    , overwrite = TRUE
  )
  .handle_result(result)
  result <- file.remove(
    file.path(TEMP_SOURCE_DIR, src_file)
  )
  .handle_result(result)
}

201
202
203
204
205
206
207
result <- file.copy(
  from = file.path("R-package", "inst", "make-r-def.R")
  , to = file.path(TEMP_R_DIR, "inst", "bin/")
  , overwrite = TRUE
)
.handle_result(result)

208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# R packages cannot have versions like 3.0.0rc1, but
# 3.0.0-1 is acceptable
LGB_VERSION <- readLines("VERSION.txt")[1L]
LGB_VERSION <- gsub(
  pattern = "rc"
  , replacement = "-"
  , x = LGB_VERSION
)

# DESCRIPTION has placeholders for version
# and date so it doesn't have to be updated manually
DESCRIPTION_FILE <- file.path(TEMP_R_DIR, "DESCRIPTION")
description_contents <- readLines(DESCRIPTION_FILE)
description_contents <- gsub(
  pattern = "~~VERSION~~"
  , replacement = LGB_VERSION
  , x = description_contents
)
description_contents <- gsub(
  pattern = "~~DATE~~"
  , replacement = as.character(Sys.Date())
  , x = description_contents
)
writeLines(description_contents, DESCRIPTION_FILE)

James Lamb's avatar
James Lamb committed
233
234
235
# NOTE: --keep-empty-dirs is necessary to keep the deep paths expected
#       by CMake while also meeting the CRAN req to create object files
#       on demand
236
.run_shell_command("R", c("CMD", "build", TEMP_R_DIR, "--keep-empty-dirs"))
James Lamb's avatar
James Lamb committed
237
238
239

# Install the package
version <- gsub(
240
241
242
  "Version: ",
  "",
  grep(
243
    "Version: "
244
    , readLines(con = file.path(TEMP_R_DIR, "DESCRIPTION"))
245
    , value = TRUE
246
  )
James Lamb's avatar
James Lamb committed
247
248
249
)
tarball <- file.path(getwd(), sprintf("lightgbm_%s.tar.gz", version))

250
251
install_cmd <- "R"
install_args <- c("CMD", "INSTALL", "--no-multiarch", "--with-keep.source", tarball)
252
if (INSTALL_AFTER_BUILD) {
253
  .run_shell_command(install_cmd, install_args)
254
} else {
255
  cmd <- paste0(install_cmd, " ", paste0(install_args, collapse = " "))
256
257
  print(sprintf("Skipping installation. Install the package with command '%s'", cmd))
}