CMakeLists.txt 29.2 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
if(USE_CUDA OR USE_ROCM)
40
41
  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
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}")

297
298
299
    # Building for ROCm almost always means USE_CUDA.
    # Exceptions to this will be guarded by USE_ROCM.
    add_definitions(-DUSE_CUDA)
300
301
302
    add_definitions(-DUSE_ROCM)
endif()

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

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

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

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

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

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

403
404
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR})
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR})
Guolin Ke's avatar
Guolin Ke committed
405

406
include_directories(${LightGBM_HEADER_DIR})
Guolin Ke's avatar
Guolin Ke committed
407
408
409

if(USE_MPI)
  include_directories(${MPI_CXX_INCLUDE_PATH})
410
endif()
Guolin Ke's avatar
Guolin Ke committed
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
445
446
447
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
448
)
449
set(
450
    LGBM_CUDA_SOURCES
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
481
482
483
      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
484
485
)

486
if(USE_CUDA OR USE_ROCM)
487
  list(APPEND LGBM_SOURCES ${LGBM_CUDA_SOURCES})
488
489
endif()

490
491
492
493
494
495
496
497
498
499
500
if(USE_ROCM)
  set(CU_FILES "")
  foreach(file IN LISTS LGBM_CUDA_SOURCES)
      string(REGEX MATCH "\\.cu$" is_cu_file "${file}")
      if(is_cu_file)
          list(APPEND CU_FILES "${file}")
      endif()
  endforeach()
  set_source_files_properties(${CU_FILES} PROPERTIES LANGUAGE HIP)
endif()

501
add_library(lightgbm_objs OBJECT ${LGBM_SOURCES})
502

503
504
505
506
if(BUILD_CLI)
    add_executable(lightgbm src/main.cpp src/application/application.cpp)
    target_link_libraries(lightgbm PRIVATE lightgbm_objs)
endif()
507

508
set(API_SOURCES "src/c_api.cpp")
509
# Only build the R part of the library if building for
510
# use with the R-package
511
if(__BUILD_FOR_R)
512
  list(APPEND API_SOURCES "src/lightgbm_R.cpp")
513
endif()
514

515
516
add_library(lightgbm_capi_objs OBJECT ${API_SOURCES})

517
if(BUILD_STATIC_LIB)
518
  add_library(_lightgbm STATIC)
519
else()
520
  add_library(_lightgbm SHARED)
521
endif()
522
523
524
525
526
527
528
529
530
531
532

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

533
# LightGBM headers include openmp, cuda, R etc. headers,
534
535
# thus PUBLIC is required for building _lightgbm_swig target.
target_link_libraries(_lightgbm PUBLIC lightgbm_capi_objs lightgbm_objs)
536

537
if(MSVC AND NOT __BUILD_FOR_R)
538
  set_target_properties(_lightgbm PROPERTIES OUTPUT_NAME "lib_lightgbm")
539
endif()
540

541
542
if(USE_SWIG)
  set_property(SOURCE swig/lightgbmlib.i PROPERTY CPLUSPLUS ON)
543
  list(APPEND swig_options -package com.microsoft.ml.lightgbm)
544
  set_property(SOURCE swig/lightgbmlib.i PROPERTY SWIG_FLAGS "${swig_options}")
545
  swig_add_library(_lightgbm_swig LANGUAGE java SOURCES swig/lightgbmlib.i)
546
  swig_link_libraries(_lightgbm_swig _lightgbm)
547
548
549
550
551
552
553
554
  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"
  )
555
  if(WIN32)
556
    set(LGBM_SWIG_LIB_DESTINATION_PATH "${LGBM_SWIG_DESTINATION_DIR}/lib_lightgbm_swig.dll")
557
    if(MINGW OR CYGWIN)
558
559
        set(LGBM_LIB_SOURCE_PATH "${PROJECT_SOURCE_DIR}/lib_lightgbm.dll")
        set(LGBM_SWIG_LIB_SOURCE_PATH "${PROJECT_SOURCE_DIR}/lib_lightgbm_swig.dll")
560
    else()
561
562
        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")
563
    endif()
564
  elseif(APPLE)
565
566
567
    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")
568
  else()
569
570
571
    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")
572
  endif()
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
  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
    )
591
endif()
Guolin Ke's avatar
Guolin Ke committed
592
593

if(USE_MPI)
594
  target_link_libraries(lightgbm_objs PUBLIC ${MPI_CXX_LIBRARIES})
595
endif()
Guolin Ke's avatar
Guolin Ke committed
596

fanliwen's avatar
fanliwen committed
597
if(USE_OPENMP)
598
  if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
599
600
601
602
603
604
605
606
607
    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()
608
endif()
fanliwen's avatar
fanliwen committed
609

610
if(USE_GPU)
611
  target_link_libraries(lightgbm_objs PUBLIC ${OpenCL_LIBRARY} ${Boost_LIBRARIES})
612
endif()
613

614
if(__INTEGRATE_OPENCL)
615
  # targets OpenCL and Boost are added in IntegratedOpenCL.cmake
616
  add_dependencies(lightgbm_objs OpenCL Boost)
617
  # variables INTEGRATED_OPENCL_* are set in IntegratedOpenCL.cmake
618
619
  target_include_directories(lightgbm_objs PRIVATE ${INTEGRATED_OPENCL_INCLUDES})
  target_compile_definitions(lightgbm_objs PRIVATE ${INTEGRATED_OPENCL_DEFINITIONS})
620
  target_link_libraries(lightgbm_objs PUBLIC ${INTEGRATED_OPENCL_LIBRARIES} ${CMAKE_DL_LIBS})
621
622
endif()

623
if(USE_CUDA)
624

625
626
627
  set_target_properties(
    lightgbm_objs
    PROPERTIES
628
      CUDA_ARCHITECTURES "${CUDA_ARCHS}"
629
630
631
632
633
634
      CUDA_SEPARABLE_COMPILATION ON
  )

  set_target_properties(
    _lightgbm
    PROPERTIES
635
      CUDA_ARCHITECTURES "${CUDA_ARCHS}"
636
637
      CUDA_RESOLVE_DEVICE_SYMBOLS ON
  )
638

639
  if(BUILD_CLI)
640
641
642
    set_target_properties(
      lightgbm
      PROPERTIES
643
        CUDA_ARCHITECTURES "${CUDA_ARCHS}"
644
645
        CUDA_RESOLVE_DEVICE_SYMBOLS ON
    )
646
  endif()
647
endif()
648

649
650
651
652
if(USE_ROCM)
  target_link_libraries(lightgbm_objs PUBLIC hip::host)
endif()

653
654
if(WIN32)
    if(MINGW OR CYGWIN)
655
      target_link_libraries(lightgbm_objs PUBLIC ws2_32 iphlpapi)
656
657
    endif()
endif()
Guolin Ke's avatar
Guolin Ke committed
658

659
if(__BUILD_FOR_R)
660
661
  # utils/log.h and capi uses R headers, thus both object libraries need to link
  # with R lib.
662
  if(MSVC)
663
    set(R_LIB ${LIBR_MSVC_CORE_LIBRARY})
664
  else()
665
    set(R_LIB ${LIBR_CORE_LIBRARY})
666
  endif()
667
668
  target_link_libraries(lightgbm_objs PUBLIC ${R_LIB})
  target_link_libraries(lightgbm_capi_objs PUBLIC ${R_LIB})
669
endif()
670

671
672
673
674
675
676
677
678
679
#-- 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
680
      GIT_TAG        v1.14.0
681
682
683
684
    )
    FetchContent_MakeAvailable(googletest)
    add_library(GTest::GTest ALIAS gtest)
  endif()
685
686
687
688

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

689
690
691
692
693
694
695
696
697
698
699
700
701
  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
    )
702
  if(MSVC)
June Liu's avatar
June Liu committed
703
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /permissive-")
704
  endif()
705
  add_executable(testlightgbm ${CPP_TEST_SOURCES})
706
  target_link_libraries(testlightgbm PRIVATE lightgbm_objs lightgbm_capi_objs GTest::GTest)
707
708
endif()

709
710
711
712
713
714
715
if(BUILD_CLI)
    install(
      TARGETS lightgbm
      RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
    )
endif()

716
717
718
719
if(__BUILD_FOR_PYTHON)
    set(CMAKE_INSTALL_PREFIX "lightgbm")
endif()

720
# The macOS linker puts an absolute path to linked libraries in lib_lightgbm.dylib.
721
722
723
# This block overrides that information for LightGBM's OpenMP dependency, to allow
# finding that library in more places.
#
724
# This reduces the risk of runtime issues resulting from multiple {libgomp,libiomp,libomp}.dylib being loaded.
725
#
726
if(APPLE AND USE_OPENMP AND NOT BUILD_STATIC_LIB)
727
  # store path to {libgomp,libiomp,libomp}.dylib found at build time in a variable
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
  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.
  #
770
  # This also ensures that if a {libgomp,libiomp,libomp}.dylib has already been loaded, it'll just use that.
771
772
773
774
775
776
777
778
779
780
781
782
783
784
  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:
  #
785
  #   - (R-only) ${LIBR_LIBS_DIR}    (wherever R for macOS stores vendored third-party libraries)
786
  #   - ${OpenMP_LIBRARY_DIR}        (wherever find_package(OpenMP) found OpenMP at build time)
787
788
  #   - /opt/homebrew/opt/libomp/lib (where 'brew install' / 'brew link' puts libomp.dylib)
  #   - /opt/local/lib/libomp        (where 'port install' puts libomp.dylib)
789
  #
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828

  # 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()
829
830
831
832
  set_target_properties(
    _lightgbm
    PROPERTIES
      BUILD_WITH_INSTALL_RPATH TRUE
833
      INSTALL_RPATH "${__omp_install_rpaths}"
834
835
836
837
      INSTALL_RPATH_USE_LINK_PATH FALSE
  )
endif()

838
install(
839
  TARGETS _lightgbm
840
841
842
843
  RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
  LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib
  ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib
)
844

845
if(INSTALL_HEADERS)
846
847
848
849
850
851
852
853
854
855
856
857
858
    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"
    )
859
endif()