CMakeLists.txt 28.7 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
if(USE_CUDA)
  set(CMAKE_CXX_STANDARD 17)
elseif(BUILD_CPP_TEST)
42
43
44
45
46
47
  set(CMAKE_CXX_STANDARD 14)
else()
  set(CMAKE_CXX_STANDARD 11)
endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON)

48
49
50
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules")

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

59
60
61
62
63
64
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()

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

Qiwei Ye's avatar
Qiwei Ye committed
73
74
75
76
77
78
79
80
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()
81
82
83
84
85
86
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)
87
88
89
90
    message(
      FATAL_ERROR
      "The compiler ${CMAKE_CXX_COMPILER} doesn't support required C++11 features. Please use a newer MSVC."
    )
91
  endif()
92
endif()
Guolin Ke's avatar
Guolin Ke committed
93

94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
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)
109
  if(WIN32)
110
      set(LGBM_SWIG_DESTINATION_DIR "${CMAKE_CURRENT_BINARY_DIR}/com/microsoft/ml/lightgbm/windows/x86_64")
111
      include_directories($ENV{JAVA_HOME}/include/win32)
112
  elseif(APPLE)
113
      set(LGBM_SWIG_DESTINATION_DIR "${CMAKE_CURRENT_BINARY_DIR}/com/microsoft/ml/lightgbm/osx/x86_64")
114
      include_directories($ENV{JAVA_HOME}/include/darwin)
115
  else()
116
      set(LGBM_SWIG_DESTINATION_DIR "${CMAKE_CURRENT_BINARY_DIR}/com/microsoft/ml/lightgbm/linux/x86_64")
117
118
      include_directories($ENV{JAVA_HOME}/include/linux)
  endif()
119
  file(MAKE_DIRECTORY "${LGBM_SWIG_DESTINATION_DIR}")
120
endif()
121

122
set(EIGEN_DIR "${PROJECT_SOURCE_DIR}/external_libs/eigen")
123
124
include_directories(${EIGEN_DIR})

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

129
130
131
132
133
134
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})

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

145
if(USE_TIMETAG)
146
    add_definitions(-DTIMETAG)
147
endif()
148

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

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

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

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

171
if(USE_OPENMP)
172
173
174
    if(APPLE)
        find_package(OpenMP)
        if(NOT OpenMP_FOUND)
175
176
177
178
            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
179
180
                            OUTPUT_VARIABLE HOMEBREW_LIBOMP_PREFIX
                            OUTPUT_STRIP_TRAILING_WHITESPACE)
181
182
183
184
185
186
                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()
187
188
189
190
191
            find_package(OpenMP REQUIRED)
        endif()
    else()
        find_package(OpenMP REQUIRED)
    endif()
192
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
193
endif()
Guolin Ke's avatar
Guolin Ke committed
194

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

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

222
223
224
225
226
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()

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

232
233
    # reference for mapping of CUDA toolkit component versions to supported architectures ("compute capabilities"):
    # https://en.wikipedia.org/wiki/CUDA#GPUs_supported
234
    set(CUDA_ARCHS "60" "61" "62" "70" "75")
235
    if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL "11.0")
236
        list(APPEND CUDA_ARCHS "80")
237
    endif()
238
    if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL "11.1")
239
        list(APPEND CUDA_ARCHS "86")
240
    endif()
241
    if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL "11.5")
242
        list(APPEND CUDA_ARCHS "87")
243
    endif()
244
    if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL "11.8")
245
246
        list(APPEND CUDA_ARCHS "89")
        list(APPEND CUDA_ARCHS "90")
247
    endif()
248
249
250
251
252
    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
253
    list(POP_BACK CUDA_ARCHS CUDA_LAST_SUPPORTED_ARCH)
254
255
256
    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}")
257
    if(USE_DEBUG)
258
      set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -g")
259
    else()
260
      set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -O3 -lineinfo")
261
262
263
    endif()
    message(STATUS "CMAKE_CUDA_FLAGS: ${CMAKE_CUDA_FLAGS}")

264
    add_definitions(-DUSE_CUDA)
265

266
    if(NOT DEFINED CMAKE_CUDA_STANDARD)
267
      set(CMAKE_CUDA_STANDARD 17)
268
269
      set(CMAKE_CUDA_STANDARD_REQUIRED ON)
    endif()
270
endif()
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
298
299
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()

300
301
302
303
304
305
306
307
308
309
310
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})
311
  message(STATUS "Using _mm_prefetch")
312
  add_definitions(-DMM_PREFETCH)
313
314
endif()

315
316
317
318
319
320
321
322
323
324
325
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})
326
  message(STATUS "Using _mm_malloc")
327
  add_definitions(-DMM_MALLOC)
328
329
endif()

Guolin Ke's avatar
Guolin Ke committed
330
if(UNIX OR MINGW OR CYGWIN)
331
  set(
332
    CMAKE_CXX_FLAGS
333
    "${CMAKE_CXX_FLAGS} -pthread -Wextra -Wall -Wno-ignored-attributes -Wno-unknown-pragmas -Wno-return-type"
334
  )
335
336
337
338
339
340
341
  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()
342
  if(USE_DEBUG)
343
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0")
344
  else()
345
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
346
347
  endif()
  if(USE_SWIG)
348
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing")
349
350
  endif()
  if(NOT USE_OPENMP)
351
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas -Wno-unused-private-field")
352
353
  endif()
  if(__BUILD_FOR_R AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
354
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-cast-function-type")
355
  endif()
Guolin Ke's avatar
Guolin Ke committed
356
357
endif()

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

362
363
364
365
366
367
368
369
370
371
372
373
# 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
374
if(MSVC)
375
376
    # compiling 'fmt' on MSVC: "Unicode support requires compiling with /utf-8"
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /MP /utf-8")
377
378
379
380
381
382
383
    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
384
    if(USE_DEBUG)
385
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Od")
Guolin Ke's avatar
Guolin Ke committed
386
    else()
387
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /O2 /Ob2 /Oi /Ot /Oy")
Guolin Ke's avatar
Guolin Ke committed
388
    endif()
Guolin Ke's avatar
Guolin Ke committed
389
else()
390
391
392
    if(NOT BUILD_STATIC_LIB)
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
    endif()
Guolin Ke's avatar
Guolin Ke committed
393
    if(NOT USE_DEBUG)
394
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -funroll-loops")
Guolin Ke's avatar
Guolin Ke committed
395
    endif()
396
endif()
Guolin Ke's avatar
Guolin Ke committed
397

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

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

403
include_directories(${LightGBM_HEADER_DIR})
Guolin Ke's avatar
Guolin Ke committed
404
405
406

if(USE_MPI)
  include_directories(${MPI_CXX_INCLUDE_PATH})
407
endif()
Guolin Ke's avatar
Guolin Ke committed
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
443
444
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
445
)
446
set(
447
    LGBM_CUDA_SOURCES
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
479
480
      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
481
482
)

483
if(USE_CUDA)
484
  list(APPEND LGBM_SOURCES ${LGBM_CUDA_SOURCES})
485
486
endif()

487
add_library(lightgbm_objs OBJECT ${LGBM_SOURCES})
488

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

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

501
502
add_library(lightgbm_capi_objs OBJECT ${API_SOURCES})

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

# 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()

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

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

527
528
if(USE_SWIG)
  set_property(SOURCE swig/lightgbmlib.i PROPERTY CPLUSPLUS ON)
529
  list(APPEND swig_options -package com.microsoft.ml.lightgbm)
530
  set_property(SOURCE swig/lightgbmlib.i PROPERTY SWIG_FLAGS "${swig_options}")
531
  swig_add_library(_lightgbm_swig LANGUAGE java SOURCES swig/lightgbmlib.i)
532
  swig_link_libraries(_lightgbm_swig _lightgbm)
533
534
535
536
537
538
539
540
  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"
  )
541
  if(WIN32)
542
    set(LGBM_SWIG_LIB_DESTINATION_PATH "${LGBM_SWIG_DESTINATION_DIR}/lib_lightgbm_swig.dll")
543
    if(MINGW OR CYGWIN)
544
545
        set(LGBM_LIB_SOURCE_PATH "${PROJECT_SOURCE_DIR}/lib_lightgbm.dll")
        set(LGBM_SWIG_LIB_SOURCE_PATH "${PROJECT_SOURCE_DIR}/lib_lightgbm_swig.dll")
546
    else()
547
548
        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")
549
    endif()
550
  elseif(APPLE)
551
552
553
    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")
554
  else()
555
556
557
    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")
558
  endif()
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
  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
    )
577
endif()
Guolin Ke's avatar
Guolin Ke committed
578
579

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

fanliwen's avatar
fanliwen committed
583
if(USE_OPENMP)
584
  if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
585
586
587
588
589
590
591
592
593
    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()
594
endif()
fanliwen's avatar
fanliwen committed
595

596
if(USE_GPU)
597
  target_link_libraries(lightgbm_objs PUBLIC ${OpenCL_LIBRARY} ${Boost_LIBRARIES})
598
endif()
599

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

609
if(USE_CUDA)
610

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

  set_target_properties(
    _lightgbm
    PROPERTIES
621
      CUDA_ARCHITECTURES "${CUDA_ARCHS}"
622
623
      CUDA_RESOLVE_DEVICE_SYMBOLS ON
  )
624

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

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

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

653
654
655
656
657
658
659
660
661
#-- 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
662
      GIT_TAG        v1.14.0
663
664
665
666
    )
    FetchContent_MakeAvailable(googletest)
    add_library(GTest::GTest ALIAS gtest)
  endif()
667
668
669
670

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

671
672
673
674
675
676
677
678
679
680
681
682
683
  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
    )
684
  if(MSVC)
June Liu's avatar
June Liu committed
685
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /permissive-")
686
  endif()
687
  add_executable(testlightgbm ${CPP_TEST_SOURCES})
688
  target_link_libraries(testlightgbm PRIVATE lightgbm_objs lightgbm_capi_objs GTest::GTest)
689
690
endif()

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

698
699
700
701
if(__BUILD_FOR_PYTHON)
    set(CMAKE_INSTALL_PREFIX "lightgbm")
endif()

702
# The macOS linker puts an absolute path to linked libraries in lib_lightgbm.dylib.
703
704
705
# This block overrides that information for LightGBM's OpenMP dependency, to allow
# finding that library in more places.
#
706
# This reduces the risk of runtime issues resulting from multiple {libgomp,libiomp,libomp}.dylib being loaded.
707
#
708
if(APPLE AND USE_OPENMP AND NOT BUILD_STATIC_LIB)
709
  # store path to {libgomp,libiomp,libomp}.dylib found at build time in a variable
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
750
751
  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.
  #
752
  # This also ensures that if a {libgomp,libiomp,libomp}.dylib has already been loaded, it'll just use that.
753
754
755
756
757
758
759
760
761
762
763
764
765
766
  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:
  #
767
  #   - (R-only) ${LIBR_LIBS_DIR}    (wherever R for macOS stores vendored third-party libraries)
768
  #   - ${OpenMP_LIBRARY_DIR}        (wherever find_package(OpenMP) found OpenMP at build time)
769
770
  #   - /opt/homebrew/opt/libomp/lib (where 'brew install' / 'brew link' puts libomp.dylib)
  #   - /opt/local/lib/libomp        (where 'port install' puts libomp.dylib)
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
809
810

  # 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()
811
812
813
814
  set_target_properties(
    _lightgbm
    PROPERTIES
      BUILD_WITH_INSTALL_RPATH TRUE
815
      INSTALL_RPATH "${__omp_install_rpaths}"
816
817
818
819
      INSTALL_RPATH_USE_LINK_PATH FALSE
  )
endif()

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

827
if(INSTALL_HEADERS)
828
829
830
831
832
833
834
835
836
837
838
839
840
    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"
    )
841
endif()