build_r.R 6.78 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
187
188
189
# 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)
}

190
191
192
193
194
195
196
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)

197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# 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
222
223
224
# 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
225
.run_shell_command("R", c("CMD", "build", TEMP_R_DIR, "--keep-empty-dirs"))
James Lamb's avatar
James Lamb committed
226
227
228

# Install the package
version <- gsub(
229
230
231
  "Version: ",
  "",
  grep(
232
    "Version: "
233
    , readLines(con = file.path(TEMP_R_DIR, "DESCRIPTION"))
234
    , value = TRUE
235
  )
James Lamb's avatar
James Lamb committed
236
237
238
)
tarball <- file.path(getwd(), sprintf("lightgbm_%s.tar.gz", version))

239
240
install_cmd <- "R"
install_args <- c("CMD", "INSTALL", "--no-multiarch", "--with-keep.source", tarball)
241
if (INSTALL_AFTER_BUILD) {
242
  .run_shell_command(install_cmd, install_args)
243
} else {
244
  cmd <- paste0(install_cmd, " ", paste0(install_args, collapse = " "))
245
246
  print(sprintf("Skipping installation. Install the package with command '%s'", cmd))
}