CMakeLists.txt 28.6 KB
Newer Older
1
2
3
4
5
option(USE_MPI "Enable MPI-based distributed learning" OFF)
option(USE_OPENMP "Enable OpenMP" ON)
option(USE_GPU "Enable GPU-accelerated training" OFF)
option(USE_SWIG "Enable SWIG to generate Java API" OFF)
option(USE_TIMETAG "Set to ON to output time costs" OFF)
6
option(USE_CUDA "Enable CUDA-accelerated training " OFF)
7
option(USE_ROCM "Enable ROCm-accelerated training " OFF)
8
option(USE_DEBUG "Set to ON for Debug mode" OFF)
9
option(USE_SANITIZER "Use sanitizer flags" OFF)
10
set(
11
12
13
14
15
16
17
  ENABLED_SANITIZERS
  "address" "leak" "undefined"
  CACHE
  STRING
  "Semicolon separated list of sanitizer names, e.g., 'address;leak'. \
Supported sanitizers are address, leak, undefined and thread."
)
18
19
option(USE_HOMEBREW_FALLBACK "(macOS-only) also look in 'brew --prefix' for libraries (e.g. OpenMP)" ON)
option(BUILD_CLI "Build the 'lightgbm' command-line interface in addition to lib_lightgbm" ON)
20
21
option(BUILD_CPP_TEST "Build C++ tests with Google Test" OFF)
option(BUILD_STATIC_LIB "Build static library" OFF)
22
option(INSTALL_HEADERS "Install headers to CMAKE_INSTALL_PREFIX (e.g. '/usr/local/include')" ON)
23
24
option(__BUILD_FOR_PYTHON "Set to ON if building lib_lightgbm for use with the Python-package" OFF)
option(__BUILD_FOR_R "Set to ON if building lib_lightgbm for use with the R-package" OFF)
25
option(__INTEGRATE_OPENCL "Set to ON if building LightGBM with the OpenCL ICD Loader and its dependencies included" OFF)
26

27
cmake_minimum_required(VERSION 3.28)
Guolin Ke's avatar
Guolin Ke committed
28

29
30
31
32
33
34
35
36
# If using Visual Studio generators, always target v10.x of the Windows SDK.
# Doing this avoids lookups that could fall back to very old versions, e.g. by finding
# outdated registry entries.
# ref: https://cmake.org/cmake/help/latest/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION.html
if(CMAKE_GENERATOR MATCHES "Visual Studio")
    set(CMAKE_SYSTEM_VERSION 10.0 CACHE INTERNAL "target Windows SDK version" FORCE)
endif()

37
project(lightgbm LANGUAGES C CXX)
Guolin Ke's avatar
Guolin Ke committed
38

39
40
41
42
43
44
45
if(BUILD_CPP_TEST)
  set(CMAKE_CXX_STANDARD 14)
else()
  set(CMAKE_CXX_STANDARD 11)
endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON)

46
47
48
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules")

#-- Sanitizer
49
if(USE_SANITIZER)
50
51
  if(MSVC)
    message(FATAL_ERROR "Sanitizers are not supported with MSVC.")
52
  endif()
53
54
  include(cmake/Sanitizer.cmake)
  enable_sanitizers("${ENABLED_SANITIZERS}")
55
endif()
56

57
58
59
60
61
62
if(__INTEGRATE_OPENCL)
  set(__INTEGRATE_OPENCL ON CACHE BOOL "" FORCE)
  set(USE_GPU OFF CACHE BOOL "" FORCE)
  message(STATUS "Building library with integrated OpenCL components")
endif()

63
if(__BUILD_FOR_PYTHON OR __BUILD_FOR_R OR USE_SWIG)
64
    # the SWIG wrapper, the Python and R packages don't require the CLI
65
    set(BUILD_CLI OFF)
66
    # installing the SWIG wrapper, the R and Python packages shouldn't place LightGBM's headers
67
68
69
70
    # outside of where the package is installed
    set(INSTALL_HEADERS OFF)
endif()

Qiwei Ye's avatar
Qiwei Ye committed
71
72
73
74
75
76
77
78
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
  if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.8.2")
    message(FATAL_ERROR "Insufficient gcc version")
  endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
  if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "3.8")
    message(FATAL_ERROR "Insufficient Clang version")
  endif()
79
80
81
82
83
84
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
  if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "8.1.0")
    message(FATAL_ERROR "Insufficient AppleClang version")
  endif()
elseif(MSVC)
  if(MSVC_VERSION LESS 1900)
85
86
87
88
    message(
      FATAL_ERROR
      "The compiler ${CMAKE_CXX_COMPILER} doesn't support required C++11 features. Please use a newer MSVC."
    )
89
  endif()
90
endif()
Guolin Ke's avatar
Guolin Ke committed
91

92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
if(USE_SWIG)
  find_package(SWIG REQUIRED)
  find_package(Java REQUIRED)
  find_package(JNI REQUIRED)
  include(UseJava)
  include(UseSWIG)
  set(SWIG_CXX_EXTENSION "cxx")
  set(SWIG_EXTRA_LIBRARIES "")
  set(SWIG_JAVA_EXTRA_FILE_EXTENSIONS ".java" "JNI.java")
  set(SWIG_MODULE_JAVA_LANGUAGE "JAVA")
  set(SWIG_MODULE_JAVA_SWIG_LANGUAGE_FLAG "java")
  set(CMAKE_SWIG_OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/java")
  include_directories(Java_INCLUDE_DIRS)
  include_directories(JNI_INCLUDE_DIRS)
  include_directories($ENV{JAVA_HOME}/include)
107
  if(WIN32)
108
      set(LGBM_SWIG_DESTINATION_DIR "${CMAKE_CURRENT_BINARY_DIR}/com/microsoft/ml/lightgbm/windows/x86_64")
109
      include_directories($ENV{JAVA_HOME}/include/win32)
110
  elseif(APPLE)
111
      set(LGBM_SWIG_DESTINATION_DIR "${CMAKE_CURRENT_BINARY_DIR}/com/microsoft/ml/lightgbm/osx/x86_64")
112
      include_directories($ENV{JAVA_HOME}/include/darwin)
113
  else()
114
      set(LGBM_SWIG_DESTINATION_DIR "${CMAKE_CURRENT_BINARY_DIR}/com/microsoft/ml/lightgbm/linux/x86_64")
115
116
      include_directories($ENV{JAVA_HOME}/include/linux)
  endif()
117
  file(MAKE_DIRECTORY "${LGBM_SWIG_DESTINATION_DIR}")
118
endif()
119

120
set(EIGEN_DIR "${PROJECT_SOURCE_DIR}/external_libs/eigen")
121
122
include_directories(${EIGEN_DIR})

123
# See https://gitlab.com/libeigen/eigen/-/blob/master/COPYING.README
124
add_definitions(-DEIGEN_MPL2_ONLY)
125
add_definitions(-DEIGEN_DONT_PARALLELIZE)
126

127
128
129
130
131
132
set(FAST_DOUBLE_PARSER_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/external_libs/fast_double_parser/include")
include_directories(${FAST_DOUBLE_PARSER_INCLUDE_DIR})

set(FMT_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/external_libs/fmt/include")
include_directories(${FMT_INCLUDE_DIR})

133
if(__BUILD_FOR_R)
134
135
136
    find_package(LibR REQUIRED)
    message(STATUS "LIBR_EXECUTABLE: ${LIBR_EXECUTABLE}")
    message(STATUS "LIBR_INCLUDE_DIRS: ${LIBR_INCLUDE_DIRS}")
137
    message(STATUS "LIBR_LIBS_DIR: ${LIBR_LIBS_DIR}")
138
139
    message(STATUS "LIBR_CORE_LIBRARY: ${LIBR_CORE_LIBRARY}")
    include_directories(${LIBR_INCLUDE_DIRS})
140
    add_definitions(-DLGB_R_BUILD)
141
endif()
142

143
if(USE_TIMETAG)
144
    add_definitions(-DTIMETAG)
145
endif()
146

Guolin Ke's avatar
Guolin Ke committed
147
if(USE_DEBUG)
148
    add_definitions(-DDEBUG)
149
endif()
Guolin Ke's avatar
Guolin Ke committed
150

Guolin Ke's avatar
Guolin Ke committed
151
if(USE_MPI)
Guolin Ke's avatar
Guolin Ke committed
152
    find_package(MPI REQUIRED)
153
    add_definitions(-DUSE_MPI)
Guolin Ke's avatar
Guolin Ke committed
154
else()
155
    add_definitions(-DUSE_SOCKET)
156
endif()
Guolin Ke's avatar
Guolin Ke committed
157

158
if(USE_CUDA)
159
    set(CMAKE_CUDA_HOST_COMPILER "${CMAKE_CXX_COMPILER}")
160
    enable_language(CUDA)
161
    set(USE_OPENMP ON CACHE BOOL "CUDA requires OpenMP" FORCE)
162
endif()
163

164
165
166
167
168
if(USE_ROCM)
    enable_language(HIP)
    set(USE_OPENMP ON CACHE BOOL "ROCm requires OpenMP" FORCE)
endif()

169
if(USE_OPENMP)
170
171
172
    if(APPLE)
        find_package(OpenMP)
        if(NOT OpenMP_FOUND)
173
174
175
176
            if(USE_HOMEBREW_FALLBACK)
                # libomp 15.0+ from brew is keg-only, so have to search in other locations.
                # See https://github.com/Homebrew/homebrew-core/issues/112107#issuecomment-1278042927.
                execute_process(COMMAND brew --prefix libomp
177
178
                            OUTPUT_VARIABLE HOMEBREW_LIBOMP_PREFIX
                            OUTPUT_STRIP_TRAILING_WHITESPACE)
179
180
181
182
183
184
                set(OpenMP_C_FLAGS "-Xpreprocessor -fopenmp -I${HOMEBREW_LIBOMP_PREFIX}/include")
                set(OpenMP_CXX_FLAGS "-Xpreprocessor -fopenmp -I${HOMEBREW_LIBOMP_PREFIX}/include")
                set(OpenMP_C_LIB_NAMES omp)
                set(OpenMP_CXX_LIB_NAMES omp)
                set(OpenMP_omp_LIBRARY ${HOMEBREW_LIBOMP_PREFIX}/lib/libomp.dylib)
            endif()
185
186
187
188
189
            find_package(OpenMP REQUIRED)
        endif()
    else()
        find_package(OpenMP REQUIRED)
    endif()
190
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
191
endif()
Guolin Ke's avatar
Guolin Ke committed
192

193
if(USE_GPU)
194
    set(BOOST_COMPUTE_HEADER_DIR ${PROJECT_SOURCE_DIR}/external_libs/compute/include)
195
    include_directories(${BOOST_COMPUTE_HEADER_DIR})
196
197
    find_package(OpenCL REQUIRED)
    include_directories(${OpenCL_INCLUDE_DIRS})
198
    message(STATUS "OpenCL include directory: " ${OpenCL_INCLUDE_DIRS})
199
    if(WIN32)
Guolin Ke's avatar
Guolin Ke committed
200
201
        set(Boost_USE_STATIC_LIBS ON)
    endif()
202
    find_package(Boost 1.56.0 COMPONENTS filesystem system REQUIRED)
203
    if(WIN32)
204
        # disable autolinking in boost
205
        add_definitions(-DBOOST_ALL_NO_LIB)
206
    endif()
Guolin Ke's avatar
Guolin Ke committed
207
    include_directories(${Boost_INCLUDE_DIRS})
208
    add_definitions(-DUSE_GPU)
209
endif()
210

211
if(__INTEGRATE_OPENCL)
212
213
214
    if(APPLE)
        message(FATAL_ERROR "Integrated OpenCL build is not available on macOS")
    else()
215
        include(cmake/IntegratedOpenCL.cmake)
216
        add_definitions(-DUSE_GPU)
217
218
    endif()
endif()
219

220
221
222
223
224
if(BUILD_CPP_TEST AND MSVC)
  # Use /MT flag to statically link the C runtime
  set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()

225
if(USE_CUDA)
226
227
    find_package(CUDAToolkit 11.0 REQUIRED)
    include_directories(${CUDAToolkit_INCLUDE_DIRS})
228
    set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcompiler=${OpenMP_CXX_FLAGS} -Xcompiler=-fPIC -Xcompiler=-Wall")
229

230
231
    # reference for mapping of CUDA toolkit component versions to supported architectures ("compute capabilities"):
    # https://en.wikipedia.org/wiki/CUDA#GPUs_supported
232
    set(CUDA_ARCHS "60" "61" "62" "70" "75")
233
    if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL "11.0")
234
        list(APPEND CUDA_ARCHS "80")
235
    endif()
236
    if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL "11.1")
237
        list(APPEND CUDA_ARCHS "86")
238
    endif()
239
    if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL "11.5")
240
        list(APPEND CUDA_ARCHS "87")
241
    endif()
242
    if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL "11.8")
243
244
        list(APPEND CUDA_ARCHS "89")
        list(APPEND CUDA_ARCHS "90")
245
    endif()
246
247
248
249
250
    if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL "12.8")
        list(APPEND CUDA_ARCHS "100")
        list(APPEND CUDA_ARCHS "120")
    endif()
    # Generate PTX for the most recent architecture for forwards compatibility
251
    list(POP_BACK CUDA_ARCHS CUDA_LAST_SUPPORTED_ARCH)
252
253
254
    list(TRANSFORM CUDA_ARCHS APPEND "-real")
    list(APPEND CUDA_ARCHS "${CUDA_LAST_SUPPORTED_ARCH}-real" "${CUDA_LAST_SUPPORTED_ARCH}-virtual")
    message(STATUS "CUDA_ARCHITECTURES: ${CUDA_ARCHS}")
255
    if(USE_DEBUG)
256
      set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -g")
257
    else()
258
      set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -O3 -lineinfo")
259
260
261
    endif()
    message(STATUS "CMAKE_CUDA_FLAGS: ${CMAKE_CUDA_FLAGS}")

262
    add_definitions(-DUSE_CUDA)
263

264
    if(NOT DEFINED CMAKE_CUDA_STANDARD)
265
266
267
      set(CMAKE_CUDA_STANDARD 11)
      set(CMAKE_CUDA_STANDARD_REQUIRED ON)
    endif()
268
endif()
269

270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
if(USE_ROCM)
    find_package(HIP)
    include_directories(${HIP_INCLUDE_DIRS})
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__HIP_PLATFORM_AMD__")
    set(CMAKE_HIP_FLAGS "${CMAKE_HIP_FLAGS} ${OpenMP_CXX_FLAGS} -fPIC -Wall")

    # avoid warning: unused variable 'mask' due to __shfl_down_sync work-around
    set(DISABLED_WARNINGS "${DISABLED_WARNINGS} -Wno-unused-variable")
    # avoid warning: 'hipHostAlloc' is deprecated: use hipHostMalloc instead
    set(DISABLED_WARNINGS "${DISABLED_WARNINGS} -Wno-deprecated-declarations")
    # avoid many warnings about missing overrides
    set(DISABLED_WARNINGS "${DISABLED_WARNINGS} -Wno-inconsistent-missing-override")
    # avoid warning: shift count >= width of type in feature_histogram.hpp
    set(DISABLED_WARNINGS "${DISABLED_WARNINGS} -Wno-shift-count-overflow")

    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${DISABLED_WARNINGS}")
    set(CMAKE_HIP_FLAGS "${CMAKE_HIP_FLAGS} ${DISABLED_WARNINGS}")

    if(USE_DEBUG)
      set(CMAKE_HIP_FLAGS "${CMAKE_HIP_FLAGS} -g -O0")
    else()
      set(CMAKE_HIP_FLAGS "${CMAKE_HIP_FLAGS} -O3")
    endif()
    message(STATUS "CMAKE_HIP_FLAGS: ${CMAKE_HIP_FLAGS}")

    add_definitions(-DUSE_ROCM)
endif()

298
299
300
301
302
303
304
305
306
307
308
include(CheckCXXSourceCompiles)
check_cxx_source_compiles("
#include <xmmintrin.h>
int main() {
  int a = 0;
  _mm_prefetch(&a, _MM_HINT_NTA);
  return 0;
}
" MM_PREFETCH)

if(${MM_PREFETCH})
309
  message(STATUS "Using _mm_prefetch")
310
  add_definitions(-DMM_PREFETCH)
311
312
endif()

313
314
315
316
317
318
319
320
321
322
323
include(CheckCXXSourceCompiles)
check_cxx_source_compiles("
#include <mm_malloc.h>
int main() {
  char *a = (char*)_mm_malloc(8, 16);
  _mm_free(a);
  return 0;
}
" MM_MALLOC)

if(${MM_MALLOC})
324
  message(STATUS "Using _mm_malloc")
325
  add_definitions(-DMM_MALLOC)
326
327
endif()

Guolin Ke's avatar
Guolin Ke committed
328
if(UNIX OR MINGW OR CYGWIN)
329
  set(
330
    CMAKE_CXX_FLAGS
331
    "${CMAKE_CXX_FLAGS} -pthread -Wextra -Wall -Wno-ignored-attributes -Wno-unknown-pragmas -Wno-return-type"
332
  )
333
334
335
336
337
338
339
  if(MINGW)
    # ignore this warning: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95353
    set(
      CMAKE_CXX_FLAGS
      "${CMAKE_CXX_FLAGS} -Wno-stringop-overflow"
    )
  endif()
340
  if(USE_DEBUG)
341
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0")
342
  else()
343
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
344
345
  endif()
  if(USE_SWIG)
346
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing")
347
348
  endif()
  if(NOT USE_OPENMP)
349
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas -Wno-unused-private-field")
350
351
  endif()
  if(__BUILD_FOR_R AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
352
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-cast-function-type")
353
  endif()
Guolin Ke's avatar
Guolin Ke committed
354
355
endif()

Nikita Titov's avatar
Nikita Titov committed
356
if(WIN32 AND MINGW)
357
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libstdc++")
Nikita Titov's avatar
Nikita Titov committed
358
359
endif()

360
361
362
363
364
365
366
367
368
369
370
371
# Check if inet_pton is already available, to avoid conflicts with the implementation in LightGBM.
# As of 2022, MinGW started including a definition of inet_pton.
if(WIN32)
  include(CheckSymbolExists)
  list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32")
  check_symbol_exists(inet_pton "ws2tcpip.h" WIN_INET_PTON_FOUND)
  if(WIN_INET_PTON_FOUND)
    add_definitions(-DWIN_HAS_INET_PTON)
  endif()
  list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES "ws2_32")
endif()

Guolin Ke's avatar
Guolin Ke committed
372
if(MSVC)
373
374
    # compiling 'fmt' on MSVC: "Unicode support requires compiling with /utf-8"
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /MP /utf-8")
375
376
377
378
379
380
381
    if(__BUILD_FOR_R)
        # MSVC does not like this commit:
        # https://github.com/wch/r-source/commit/fb52ac1a610571fcb8ac92d886b9fefcffaa7d48
        #
        # and raises "error C3646: 'private_data_c': unknown override specifier"
        add_definitions(-DR_LEGACY_RCOMPLEX)
    endif()
Guolin Ke's avatar
Guolin Ke committed
382
    if(USE_DEBUG)
383
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Od")
Guolin Ke's avatar
Guolin Ke committed
384
    else()
385
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /O2 /Ob2 /Oi /Ot /Oy")
Guolin Ke's avatar
Guolin Ke committed
386
    endif()
Guolin Ke's avatar
Guolin Ke committed
387
else()
388
389
390
    if(NOT BUILD_STATIC_LIB)
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
    endif()
Guolin Ke's avatar
Guolin Ke committed
391
    if(NOT USE_DEBUG)
392
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -funroll-loops")
Guolin Ke's avatar
Guolin Ke committed
393
    endif()
394
endif()
Guolin Ke's avatar
Guolin Ke committed
395

396
set(LightGBM_HEADER_DIR ${PROJECT_SOURCE_DIR}/include)
Guolin Ke's avatar
Guolin Ke committed
397

398
399
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR})
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR})
Guolin Ke's avatar
Guolin Ke committed
400

401
include_directories(${LightGBM_HEADER_DIR})
Guolin Ke's avatar
Guolin Ke committed
402
403
404

if(USE_MPI)
  include_directories(${MPI_CXX_INCLUDE_PATH})
405
endif()
Guolin Ke's avatar
Guolin Ke committed
406

407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
set(
    LGBM_SOURCES
      src/boosting/boosting.cpp
      src/boosting/gbdt_model_text.cpp
      src/boosting/gbdt_prediction.cpp
      src/boosting/gbdt.cpp
      src/boosting/prediction_early_stop.cpp
      src/boosting/sample_strategy.cpp
      src/io/bin.cpp
      src/io/config_auto.cpp
      src/io/config.cpp
      src/io/dataset_loader.cpp
      src/io/dataset.cpp
      src/io/file_io.cpp
      src/io/json11.cpp
      src/io/metadata.cpp
      src/io/parser.cpp
      src/io/train_share_states.cpp
      src/io/tree.cpp
      src/metric/dcg_calculator.cpp
      src/metric/metric.cpp
      src/network/linker_topo.cpp
      src/network/linkers_mpi.cpp
      src/network/linkers_socket.cpp
      src/network/network.cpp
      src/objective/objective_function.cpp
      src/treelearner/data_parallel_tree_learner.cpp
      src/treelearner/feature_histogram.cpp
      src/treelearner/feature_parallel_tree_learner.cpp
      src/treelearner/gpu_tree_learner.cpp
      src/treelearner/gradient_discretizer.cpp
      src/treelearner/linear_tree_learner.cpp
      src/treelearner/serial_tree_learner.cpp
      src/treelearner/tree_learner.cpp
      src/treelearner/voting_parallel_tree_learner.cpp
      src/utils/openmp_wrapper.cpp
443
)
444
set(
445
    LGBM_CUDA_SOURCES
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
      src/boosting/cuda/cuda_score_updater.cpp
      src/boosting/cuda/cuda_score_updater.cu
      src/metric/cuda/cuda_binary_metric.cpp
      src/metric/cuda/cuda_pointwise_metric.cpp
      src/metric/cuda/cuda_regression_metric.cpp
      src/metric/cuda/cuda_pointwise_metric.cu
      src/objective/cuda/cuda_binary_objective.cpp
      src/objective/cuda/cuda_multiclass_objective.cpp
      src/objective/cuda/cuda_rank_objective.cpp
      src/objective/cuda/cuda_regression_objective.cpp
      src/objective/cuda/cuda_binary_objective.cu
      src/objective/cuda/cuda_multiclass_objective.cu
      src/objective/cuda/cuda_rank_objective.cu
      src/objective/cuda/cuda_regression_objective.cu
      src/treelearner/cuda/cuda_best_split_finder.cpp
      src/treelearner/cuda/cuda_data_partition.cpp
      src/treelearner/cuda/cuda_histogram_constructor.cpp
      src/treelearner/cuda/cuda_leaf_splits.cpp
      src/treelearner/cuda/cuda_single_gpu_tree_learner.cpp
      src/treelearner/cuda/cuda_best_split_finder.cu
      src/treelearner/cuda/cuda_data_partition.cu
      src/treelearner/cuda/cuda_gradient_discretizer.cu
      src/treelearner/cuda/cuda_histogram_constructor.cu
      src/treelearner/cuda/cuda_leaf_splits.cu
      src/treelearner/cuda/cuda_single_gpu_tree_learner.cu
      src/io/cuda/cuda_column_data.cu
      src/io/cuda/cuda_tree.cu
      src/io/cuda/cuda_column_data.cpp
      src/io/cuda/cuda_metadata.cpp
      src/io/cuda/cuda_row_data.cpp
      src/io/cuda/cuda_tree.cpp
      src/cuda/cuda_utils.cpp
      src/cuda/cuda_algorithms.cu
Guolin Ke's avatar
Guolin Ke committed
479
480
)

481
if(USE_CUDA)
482
  list(APPEND LGBM_SOURCES ${LGBM_CUDA_SOURCES})
483
484
endif()

485
add_library(lightgbm_objs OBJECT ${LGBM_SOURCES})
486

487
488
489
490
if(BUILD_CLI)
    add_executable(lightgbm src/main.cpp src/application/application.cpp)
    target_link_libraries(lightgbm PRIVATE lightgbm_objs)
endif()
491

492
set(API_SOURCES "src/c_api.cpp")
493
# Only build the R part of the library if building for
494
# use with the R-package
495
if(__BUILD_FOR_R)
496
  list(APPEND API_SOURCES "src/lightgbm_R.cpp")
497
endif()
498

499
500
add_library(lightgbm_capi_objs OBJECT ${API_SOURCES})

501
if(BUILD_STATIC_LIB)
502
  add_library(_lightgbm STATIC)
503
else()
504
  add_library(_lightgbm SHARED)
505
endif()
506
507
508
509
510
511
512
513
514
515
516

# R expects libraries of the form <project>.{dll,dylib,so}, not lib_<project>.{dll,dylib,so}
if(__BUILD_FOR_R)
  set_target_properties(
    _lightgbm
    PROPERTIES
      PREFIX ""
      OUTPUT_NAME "lightgbm"
  )
endif()

517
# LightGBM headers include openmp, cuda, R etc. headers,
518
519
# thus PUBLIC is required for building _lightgbm_swig target.
target_link_libraries(_lightgbm PUBLIC lightgbm_capi_objs lightgbm_objs)
520

521
if(MSVC AND NOT __BUILD_FOR_R)
522
  set_target_properties(_lightgbm PROPERTIES OUTPUT_NAME "lib_lightgbm")
523
endif()
524

525
526
if(USE_SWIG)
  set_property(SOURCE swig/lightgbmlib.i PROPERTY CPLUSPLUS ON)
527
  list(APPEND swig_options -package com.microsoft.ml.lightgbm)
528
  set_property(SOURCE swig/lightgbmlib.i PROPERTY SWIG_FLAGS "${swig_options}")
529
  swig_add_library(_lightgbm_swig LANGUAGE java SOURCES swig/lightgbmlib.i)
530
  swig_link_libraries(_lightgbm_swig _lightgbm)
531
532
533
534
535
536
537
538
  set_target_properties(
    _lightgbm_swig
    PROPERTIES
      # needed to ensure Linux build does not have lib prefix specified twice, e.g. liblib_lightgbm_swig
      PREFIX ""
      # needed in some versions of CMake for VS and MinGW builds to ensure output dll has lib prefix
      OUTPUT_NAME "lib_lightgbm_swig"
  )
539
  if(WIN32)
540
    set(LGBM_SWIG_LIB_DESTINATION_PATH "${LGBM_SWIG_DESTINATION_DIR}/lib_lightgbm_swig.dll")
541
    if(MINGW OR CYGWIN)
542
543
        set(LGBM_LIB_SOURCE_PATH "${PROJECT_SOURCE_DIR}/lib_lightgbm.dll")
        set(LGBM_SWIG_LIB_SOURCE_PATH "${PROJECT_SOURCE_DIR}/lib_lightgbm_swig.dll")
544
    else()
545
546
        set(LGBM_LIB_SOURCE_PATH "${PROJECT_SOURCE_DIR}/Release/lib_lightgbm.dll")
        set(LGBM_SWIG_LIB_SOURCE_PATH "${PROJECT_SOURCE_DIR}/Release/lib_lightgbm_swig.dll")
547
    endif()
548
  elseif(APPLE)
549
550
551
    set(LGBM_LIB_SOURCE_PATH "${PROJECT_SOURCE_DIR}/lib_lightgbm.dylib")
    set(LGBM_SWIG_LIB_SOURCE_PATH "${PROJECT_SOURCE_DIR}/lib_lightgbm_swig.jnilib")
    set(LGBM_SWIG_LIB_DESTINATION_PATH "${LGBM_SWIG_DESTINATION_DIR}/lib_lightgbm_swig.dylib")
552
  else()
553
554
555
    set(LGBM_LIB_SOURCE_PATH "${PROJECT_SOURCE_DIR}/lib_lightgbm.so")
    set(LGBM_SWIG_LIB_SOURCE_PATH "${PROJECT_SOURCE_DIR}/lib_lightgbm_swig.so")
    set(LGBM_SWIG_LIB_DESTINATION_PATH "${LGBM_SWIG_DESTINATION_DIR}/lib_lightgbm_swig.so")
556
  endif()
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
  add_custom_command(
      TARGET _lightgbm_swig
      POST_BUILD
      COMMAND "${Java_JAVAC_EXECUTABLE}" -d . java/*.java
      COMMAND
        "${CMAKE_COMMAND}"
        -E
        copy_if_different
        "${LGBM_LIB_SOURCE_PATH}"
        "${LGBM_SWIG_DESTINATION_DIR}"
      COMMAND
        "${CMAKE_COMMAND}"
        -E
        copy_if_different
        "${LGBM_SWIG_LIB_SOURCE_PATH}"
        "${LGBM_SWIG_LIB_DESTINATION_PATH}"
      COMMAND "${Java_JAR_EXECUTABLE}" -cf lightgbmlib.jar com
    )
575
endif()
Guolin Ke's avatar
Guolin Ke committed
576
577

if(USE_MPI)
578
  target_link_libraries(lightgbm_objs PUBLIC ${MPI_CXX_LIBRARIES})
579
endif()
Guolin Ke's avatar
Guolin Ke committed
580

fanliwen's avatar
fanliwen committed
581
if(USE_OPENMP)
582
  if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
583
584
585
586
587
588
589
590
591
    target_link_libraries(lightgbm_objs PUBLIC OpenMP::OpenMP_CXX)
    # c_api headers also includes OpenMP headers, thus compiling
    # lightgbm_capi_objs needs include directory for OpenMP.
    # Specifying OpenMP in target_link_libraries will get include directory
    # requirements for compilation.
    # This uses CMake's Transitive Usage Requirements. Refer to CMake doc:
    # https://cmake.org/cmake/help/v3.16/manual/cmake-buildsystem.7.html#transitive-usage-requirements
    target_link_libraries(lightgbm_capi_objs PUBLIC OpenMP::OpenMP_CXX)
  endif()
592
endif()
fanliwen's avatar
fanliwen committed
593

594
if(USE_GPU)
595
  target_link_libraries(lightgbm_objs PUBLIC ${OpenCL_LIBRARY} ${Boost_LIBRARIES})
596
endif()
597

598
if(__INTEGRATE_OPENCL)
599
  # targets OpenCL and Boost are added in IntegratedOpenCL.cmake
600
  add_dependencies(lightgbm_objs OpenCL Boost)
601
  # variables INTEGRATED_OPENCL_* are set in IntegratedOpenCL.cmake
602
603
  target_include_directories(lightgbm_objs PRIVATE ${INTEGRATED_OPENCL_INCLUDES})
  target_compile_definitions(lightgbm_objs PRIVATE ${INTEGRATED_OPENCL_DEFINITIONS})
604
  target_link_libraries(lightgbm_objs PUBLIC ${INTEGRATED_OPENCL_LIBRARIES} ${CMAKE_DL_LIBS})
605
606
endif()

607
if(USE_CUDA)
608

609
610
611
  set_target_properties(
    lightgbm_objs
    PROPERTIES
612
      CUDA_ARCHITECTURES "${CUDA_ARCHS}"
613
614
615
616
617
618
      CUDA_SEPARABLE_COMPILATION ON
  )

  set_target_properties(
    _lightgbm
    PROPERTIES
619
      CUDA_ARCHITECTURES "${CUDA_ARCHS}"
620
621
      CUDA_RESOLVE_DEVICE_SYMBOLS ON
  )
622

623
  if(BUILD_CLI)
624
625
626
    set_target_properties(
      lightgbm
      PROPERTIES
627
        CUDA_ARCHITECTURES "${CUDA_ARCHS}"
628
629
        CUDA_RESOLVE_DEVICE_SYMBOLS ON
    )
630
  endif()
631
endif()
632

633
634
if(WIN32)
    if(MINGW OR CYGWIN)
635
      target_link_libraries(lightgbm_objs PUBLIC ws2_32 iphlpapi)
636
637
    endif()
endif()
Guolin Ke's avatar
Guolin Ke committed
638

639
if(__BUILD_FOR_R)
640
641
  # utils/log.h and capi uses R headers, thus both object libraries need to link
  # with R lib.
642
  if(MSVC)
643
    set(R_LIB ${LIBR_MSVC_CORE_LIBRARY})
644
  else()
645
    set(R_LIB ${LIBR_CORE_LIBRARY})
646
  endif()
647
648
  target_link_libraries(lightgbm_objs PUBLIC ${R_LIB})
  target_link_libraries(lightgbm_capi_objs PUBLIC ${R_LIB})
649
endif()
650

651
652
653
654
655
656
657
658
659
#-- Google C++ tests
if(BUILD_CPP_TEST)
  find_package(GTest CONFIG)
  if(NOT GTEST_FOUND)
    message(STATUS "Did not find Google Test in the system root. Fetching Google Test now...")
    include(FetchContent)
    FetchContent_Declare(
      googletest
      GIT_REPOSITORY https://github.com/google/googletest.git
660
      GIT_TAG        v1.14.0
661
662
663
664
    )
    FetchContent_MakeAvailable(googletest)
    add_library(GTest::GTest ALIAS gtest)
  endif()
665
666
667
668

  set(LightGBM_TEST_HEADER_DIR ${PROJECT_SOURCE_DIR}/tests/cpp_tests)
  include_directories(${LightGBM_TEST_HEADER_DIR})

669
670
671
672
673
674
675
676
677
678
679
680
681
  set(
    CPP_TEST_SOURCES
      tests/cpp_tests/test_array_args.cpp
      tests/cpp_tests/test_arrow.cpp
      tests/cpp_tests/test_byte_buffer.cpp
      tests/cpp_tests/test_chunked_array.cpp
      tests/cpp_tests/test_common.cpp
      tests/cpp_tests/test_main.cpp
      tests/cpp_tests/test_serialize.cpp
      tests/cpp_tests/test_single_row.cpp
      tests/cpp_tests/test_stream.cpp
      tests/cpp_tests/testutils.cpp
    )
682
  if(MSVC)
June Liu's avatar
June Liu committed
683
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /permissive-")
684
  endif()
685
  add_executable(testlightgbm ${CPP_TEST_SOURCES})
686
  target_link_libraries(testlightgbm PRIVATE lightgbm_objs lightgbm_capi_objs GTest::GTest)
687
688
endif()

689
690
691
692
693
694
695
if(BUILD_CLI)
    install(
      TARGETS lightgbm
      RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
    )
endif()

696
697
698
699
if(__BUILD_FOR_PYTHON)
    set(CMAKE_INSTALL_PREFIX "lightgbm")
endif()

700
# The macOS linker puts an absolute path to linked libraries in lib_lightgbm.dylib.
701
702
703
# This block overrides that information for LightGBM's OpenMP dependency, to allow
# finding that library in more places.
#
704
# This reduces the risk of runtime issues resulting from multiple {libgomp,libiomp,libomp}.dylib being loaded.
705
#
706
if(APPLE AND USE_OPENMP AND NOT BUILD_STATIC_LIB)
707
  # store path to {libgomp,libiomp,libomp}.dylib found at build time in a variable
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
  get_target_property(
    OpenMP_LIBRARY_LOCATION
    OpenMP::OpenMP_CXX
    INTERFACE_LINK_LIBRARIES
  )
  # get just the filename of that path
  # (to deal with the possibility that it might be 'libomp.dylib' or 'libgomp.dylib' or 'libiomp.dylib')
  get_filename_component(
    OpenMP_LIBRARY_NAME
    ${OpenMP_LIBRARY_LOCATION}
    NAME
  )
  # get directory of that path
  get_filename_component(
    OpenMP_LIBRARY_DIR
    ${OpenMP_LIBRARY_LOCATION}
    DIRECTORY
  )
  # get exact name of the library in a variable
  get_target_property(
    __LIB_LIGHTGBM_OUTPUT_NAME
    _lightgbm
    OUTPUT_NAME
  )
  if(NOT __LIB_LIGHTGBM_OUTPUT_NAME)
    set(__LIB_LIGHTGBM_OUTPUT_NAME "lib_lightgbm")
  endif()

  if(CMAKE_SHARED_LIBRARY_SUFFIX_CXX)
    set(
      __LIB_LIGHTGBM_FILENAME "${__LIB_LIGHTGBM_OUTPUT_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX_CXX}"
      CACHE INTERNAL "lightgbm shared library filename"
    )
  else()
    set(
      __LIB_LIGHTGBM_FILENAME "${__LIB_LIGHTGBM_OUTPUT_NAME}.dylib"
      CACHE INTERNAL "lightgbm shared library filename"
    )
  endif()

  # Override the absolute path to OpenMP with a relative one using @rpath.
  #
750
  # This also ensures that if a {libgomp,libiomp,libomp}.dylib has already been loaded, it'll just use that.
751
752
753
754
755
756
757
758
759
760
761
762
763
764
  add_custom_command(
    TARGET _lightgbm
    POST_BUILD
      COMMAND
        install_name_tool
        -change
        ${OpenMP_LIBRARY_LOCATION}
        "@rpath/${OpenMP_LIBRARY_NAME}"
        "${__LIB_LIGHTGBM_FILENAME}"
      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
      COMMENT "Replacing hard-coded OpenMP install_name with '@rpath/${OpenMP_LIBRARY_NAME}'..."
  )
  # add RPATH entries to ensure the loader looks in the following, in the following order:
  #
765
  #   - (R-only) ${LIBR_LIBS_DIR}    (wherever R for macOS stores vendored third-party libraries)
766
  #   - ${OpenMP_LIBRARY_DIR}        (wherever find_package(OpenMP) found OpenMP at build time)
767
768
  #   - /opt/homebrew/opt/libomp/lib (where 'brew install' / 'brew link' puts libomp.dylib)
  #   - /opt/local/lib/libomp        (where 'port install' puts libomp.dylib)
769
  #
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808

  # with some compilers, OpenMP ships with the compiler (e.g. libgomp with gcc)
  list(APPEND __omp_install_rpaths "${OpenMP_LIBRARY_DIR}")

  # with clang, libomp doesn't ship with the compiler and might be supplied separately
  if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
      list(
        APPEND __omp_install_rpaths
          "/opt/homebrew/opt/libomp/lib"
          "/opt/local/lib/libomp"
      )
      # It appears that CRAN's macOS binaries compiled with -fopenmp have install names
      # of the form:
      #
      #   /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libomp.dylib
      #
      # That corresponds to the libomp.dylib that ships with the R framework for macOS, available
      # from https://cran.r-project.org/bin/macosx/.
      #
      # That absolute-path install name leads to that library being loaded unconditionally.
      #
      # That can result in e.g. 'library(data.table)' loading R's libomp.dylib and 'library(lightgbm)' loading
      # Homebrew's. Having 2 loaded in the same process can lead to segfaults and unpredictable behavior.
      #
      # This can't be easily avoided by forcing R-package builds in LightGBM to use R's libomp.dylib
      # at build time... LightGBM's CMake uses find_package(OpenMP), and R for macOS only provides the
      # library, not CMake config files for it.
      #
      # Best we can do, to allow CMake-based builds of the R-package here to continue to work
      # alongside CRAN-prepared binaries of other packages with OpenMP dependencies, is to
      # ensure that R's library directory is the first place the loader searches for
      # libomp.dylib when clang is used.
      #
      # ref: https://github.com/microsoft/LightGBM/issues/6628
      #
      if(__BUILD_FOR_R)
        list(PREPEND __omp_install_rpaths "${LIBR_LIBS_DIR}")
      endif()
  endif()
809
810
811
812
  set_target_properties(
    _lightgbm
    PROPERTIES
      BUILD_WITH_INSTALL_RPATH TRUE
813
      INSTALL_RPATH "${__omp_install_rpaths}"
814
815
816
817
      INSTALL_RPATH_USE_LINK_PATH FALSE
  )
endif()

818
install(
819
  TARGETS _lightgbm
820
821
822
823
  RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
  LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib
  ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib
)
824

825
if(INSTALL_HEADERS)
826
827
828
829
830
831
832
833
834
835
836
837
838
    install(
      DIRECTORY ${LightGBM_HEADER_DIR}/LightGBM
      DESTINATION ${CMAKE_INSTALL_PREFIX}/include
    )
    install(
      FILES ${FAST_DOUBLE_PARSER_INCLUDE_DIR}/fast_double_parser.h
      DESTINATION ${CMAKE_INSTALL_PREFIX}/include/LightGBM/utils
    )
    install(
      DIRECTORY ${FMT_INCLUDE_DIR}/
      DESTINATION ${CMAKE_INSTALL_PREFIX}/include/LightGBM/utils
      FILES_MATCHING PATTERN "*.h"
    )
839
endif()