build_r.R 4.7 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

James Lamb's avatar
James Lamb committed
13
14
# R returns FALSE (not a non-zero exit code) if a file copy operation
# breaks. Let's fix that
15
.handle_result <- function(res) {
16
  if (!all(res)) {
17
18
    stop("Copying files failed!")
  }
James Lamb's avatar
James Lamb committed
19
20
}

21
# system() will not raise an R exception if the process called
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# 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)) {
55
56
        stop(paste0("Command failed with exit code: ", exit_code))
    }
57
    return(invisible(exit_code))
58
59
}

James Lamb's avatar
James Lamb committed
60
# Make a new temporary folder to work in
61
62
unlink(x = TEMP_R_DIR, recursive = TRUE)
dir.create(TEMP_R_DIR)
James Lamb's avatar
James Lamb committed
63
64

# copy in the relevant files
65
66
result <- file.copy(
  from = "R-package/./"
67
  , to = sprintf("%s/", TEMP_R_DIR)
68
69
70
  , recursive = TRUE
  , overwrite = TRUE
)
James Lamb's avatar
James Lamb committed
71
72
.handle_result(result)

73
74
75
76
77
78
79
80
81
82
83
84
85
86
# 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)

87
88
result <- file.copy(
  from = "include/"
89
  , to =  sprintf("%s/", TEMP_SOURCE_DIR)
90
91
92
  , recursive = TRUE
  , overwrite = TRUE
)
James Lamb's avatar
James Lamb committed
93
94
.handle_result(result)

95
96
result <- file.copy(
  from = "src/"
97
  , to = sprintf("%s/", TEMP_SOURCE_DIR)
98
99
100
  , recursive = TRUE
  , overwrite = TRUE
)
Gao Tao's avatar
Gao Tao committed
101
102
.handle_result(result)

103
104
result <- file.copy(
  from = "compute/"
105
  , to = sprintf("%s/", TEMP_SOURCE_DIR)
106
107
108
  , recursive = TRUE
  , overwrite = TRUE
)
James Lamb's avatar
James Lamb committed
109
110
.handle_result(result)

111
112
result <- file.copy(
  from = "CMakeLists.txt"
113
  , to = file.path(TEMP_R_DIR, "inst", "bin/")
114
115
  , overwrite = TRUE
)
James Lamb's avatar
James Lamb committed
116
117
.handle_result(result)

118
119
120
121
122
123
124
125
126
127
# remove CRAN-specific files
result <- file.remove(
  file.path(TEMP_R_DIR, "configure")
  , 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)

128
129
130
131
132
133
134
135
136
137
138
139
140
141
# 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)
}

142
143
144
145
146
147
148
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)

James Lamb's avatar
James Lamb committed
149
150
151
# 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
152
.run_shell_command("R", c("CMD", "build", TEMP_R_DIR, "--keep-empty-dirs"))
James Lamb's avatar
James Lamb committed
153
154
155

# Install the package
version <- gsub(
156
157
158
  "Version: ",
  "",
  grep(
159
    "Version: "
160
    , readLines(con = file.path(TEMP_R_DIR, "DESCRIPTION"))
161
    , value = TRUE
162
  )
James Lamb's avatar
James Lamb committed
163
164
165
)
tarball <- file.path(getwd(), sprintf("lightgbm_%s.tar.gz", version))

166
167
install_cmd <- "R"
install_args <- c("CMD", "INSTALL", "--no-multiarch", "--with-keep.source", tarball)
168
if (INSTALL_AFTER_BUILD) {
169
  .run_shell_command(install_cmd, install_args)
170
} else {
171
  cmd <- paste0(install_cmd, " ", paste0(install_args, collapse = " "))
172
173
  print(sprintf("Skipping installation. Install the package with command '%s'", cmd))
}