win11下c++/msvc使用动态运行时库编译机器学习库xgboost
win11下c++/msvc使用动态运行时库编译的操作见我写的文章https://blog.csdn.net/hyl20012/article/details/153516758?spm=1011.2124.3001.6209
操作过程大致相同。
首先到官网https://github.com/dmlc/xgboost/releases下载源代码 Source code(zip)。解压到硬盘,我的路径 G:\software\xgboost310 。然后到网站 https://github.com/dmlc/dmlc-core 下载dmlc-core,解压后全部复制到 G:\software\xgboost310\dmlc-core 下面。
G:\software\xgboost310 下面的CMakeLists内容替换为
cmake_minimum_required(VERSION 3.18 FATAL_ERROR)
if(PLUGIN_SYCL)
string(REPLACE " -isystem ${CONDA_PREFIX}/include" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
endif()
project(xgboost LANGUAGES CXX C VERSION 3.1.0)
include(cmake/Utils.cmake)
list(APPEND CMAKE_MODULE_PATH "${xgboost_SOURCE_DIR}/cmake/modules")
# 设置动态运行时库
if(MSVC)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDLL")
set(CMAKE_CXX_FLAGS_RELEASE "/MD")
set(CMAKE_C_FLAGS_RELEASE "/MD")
set(CMAKE_CXX_FLAGS_DEBUG "/MDd")
set(CMAKE_C_FLAGS_DEBUG "/MDd")
endif()
# These policies are already set from 3.18 but we still need to set the policy
# default variables here for lower minimum versions in the submodules
set(CMAKE_POLICY_DEFAULT_CMP0063 NEW)
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)
set(CMAKE_POLICY_DEFAULT_CMP0076 NEW)
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
set(CMAKE_POLICY_DEFAULT_CMP0079 NEW)
message(STATUS "CMake version ${CMAKE_VERSION}")
# Check compiler versions
# Use recent compilers to ensure that std::filesystem is available
if(MSVC)
if(MSVC_VERSION LESS 1920)
message(FATAL_ERROR "Need Visual Studio 2019 or newer to build XGBoost")
endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "8.1")
message(FATAL_ERROR "Need GCC 8.1 or newer to build XGBoost")
endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "11.0")
message(FATAL_ERROR "Need Xcode 11.0 (AppleClang 11.0) or newer to build XGBoost")
endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.0")
message(FATAL_ERROR "Need Clang 9.0 or newer to build XGBoost")
endif()
endif()
include(${xgboost_SOURCE_DIR}/cmake/PrefetchIntrinsics.cmake)
find_prefetch_intrinsics()
include(${xgboost_SOURCE_DIR}/cmake/Version.cmake)
write_version()
set_default_configuration_release()
#-- Options
include(CMakeDependentOption)
## User options
option(BUILD_C_DOC "Build documentation for C APIs using Doxygen." OFF)
option(USE_OPENMP "Build with OpenMP support." ON)
option(BUILD_SHARED_LIBS "Build shared library" ON) # 修改:使用 BUILD_SHARED_LIBS
option(BUILD_DEPRECATED_CLI "Build the deprecated command line interface" OFF)
option(FORCE_SHARED_CRT "Build with dynamic CRT on Windows (/MD)" OFF)
option(BUILD_WITH_GIT_HASH "Add a short git hash to the build info." OFF)
## Bindings
option(JVM_BINDINGS "Build JVM bindings" OFF)
option(R_LIB "Build shared library for R package" OFF)
## Dev
option(USE_DEBUG_OUTPUT "Dump internal training results like gradients and predictions to stdout.
Should only be used for debugging." OFF)
option(FORCE_COLORED_OUTPUT "Force colored output from compilers, useful when ninja is used instead of make." OFF)
option(ENABLE_ALL_WARNINGS "Enable all compiler warnings. Only effective for GCC/Clang" OFF)
option(LOG_CAPI_INVOCATION "Log all C API invocations for debugging" OFF)
option(GOOGLE_TEST "Build google tests" OFF)
option(USE_DMLC_GTEST "Use google tests bundled with dmlc-core submodule" OFF)
option(USE_DEVICE_DEBUG "Generate CUDA device debug info." OFF)
option(USE_NVTX "Build with cuda profiling annotations. Developers only." OFF)
set(NVTX_HEADER_DIR "" CACHE PATH "Path to the stand-alone nvtx header")
option(HIDE_CXX_SYMBOLS "Build shared library and hide all C++ symbols" OFF)
option(KEEP_BUILD_ARTIFACTS_IN_BINARY_DIR "Output build artifacts in CMake binary dir" OFF)
## CUDA
option(USE_CUDA "Build with GPU acceleration" OFF)
option(USE_NCCL "Build with NCCL to enable distributed GPU support." OFF)
option(USE_NVCOMP "Build with nvcomp to enable sparse data compression. (experimental)" OFF)
# This is specifically designed for PyPI binary release and should be disabled for most of the cases.
option(USE_DLOPEN_NCCL "Whether to load nccl dynamically." OFF)
option(BUILD_WITH_SHARED_NCCL "Build with shared NCCL library." OFF)
if(USE_CUDA)
if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES AND NOT DEFINED ENV{CUDAARCHS})
set(GPU_COMPUTE_VER "" CACHE STRING
"Semicolon separated list of compute versions to be built against, e.g. '35;61'")
else()
# Clear any cached values from previous runs
unset(GPU_COMPUTE_VER)
unset(GPU_COMPUTE_VER CACHE)
endif()
endif()
# CUDA device LTO was introduced in CMake v3.25 and requires host LTO to also be enabled but can still
# be explicitly disabled allowing for LTO on host only, host and device, or neither, but device-only LTO
# is not a supproted configuration
cmake_dependent_option(USE_CUDA_LTO
"Enable link-time optimization for CUDA device code"
"${CMAKE_INTERPROCEDURAL_OPTIMIZATION}"
"CMAKE_VERSION VERSION_GREATER_EQUAL 3.25;USE_CUDA;CMAKE_INTERPROCEDURAL_OPTIMIZATION"
OFF)
## Sanitizers
option(USE_SANITIZER "Use santizer flags" OFF)
option(SANITIZER_PATH "Path to sanitizes.")
set(ENABLED_SANITIZERS "address" "leak" CACHE STRING
"Semicolon separated list of sanitizer names. E.g 'address;leak'. Supported sanitizers are
address, leak, undefined and thread.")
## Plugins
option(PLUGIN_RMM "Build with RAPIDS Memory Manager (RMM)" OFF)
option(PLUGIN_FEDERATED "Build with Federated Learning" OFF)
## TODO: 1. Add check if DPC++ compiler is used for building
option(PLUGIN_SYCL "SYCL plugin" OFF)
option(ADD_PKGCONFIG "Add xgboost.pc into system." ON)
#-- Checks for building XGBoost
if(USE_DEBUG_OUTPUT AND (NOT (CMAKE_BUILD_TYPE MATCHES Debug)))
message(SEND_ERROR "Do not enable `USE_DEBUG_OUTPUT' with release build.")
endif()
if(USE_NVTX AND (NOT USE_CUDA))
message(SEND_ERROR "`USE_NVTX` must be enabled with `USE_CUDA` flag.")
endif()
if(USE_NVTX)
if(CMAKE_VERSION VERSION_LESS "3.25.0")
# CUDA:nvtx3 target is added in 3.25
message("cmake >= 3.25 is required for NVTX.")
endif()
endif()
if(USE_NCCL AND (NOT USE_CUDA))
message(SEND_ERROR "`USE_NCCL` must be enabled with `USE_CUDA` flag.")
endif()
if(USE_NVCOMP AND (NOT USE_CUDA))
message(SEND_ERROR "`USE_NVCOMP` must be enabled with `USE_CUDA` flag.")
endif()
if(USE_DEVICE_DEBUG AND (NOT USE_CUDA))
message(SEND_ERROR "`USE_DEVICE_DEBUG` must be enabled with `USE_CUDA` flag.")
endif()
if(BUILD_WITH_SHARED_NCCL AND (NOT USE_NCCL))
message(SEND_ERROR "Build XGBoost with -DUSE_NCCL=ON to enable BUILD_WITH_SHARED_NCCL.")
endif()
if(USE_DLOPEN_NCCL AND (NOT USE_NCCL))
message(SEND_ERROR "Build XGBoost with -DUSE_NCCL=ON to enable USE_DLOPEN_NCCL.")
endif()
if(USE_DLOPEN_NCCL AND (NOT (CMAKE_SYSTEM_NAME STREQUAL "Linux")))
message(SEND_ERROR "`USE_DLOPEN_NCCL` supports only Linux at the moment.")
endif()
if(JVM_BINDINGS AND R_LIB)
message(SEND_ERROR "`R_LIB' is not compatible with `JVM_BINDINGS' as they both have customized configurations.")
endif()
if(R_LIB AND GOOGLE_TEST)
message(
WARNING
"Some C++ tests will fail with `R_LIB` enabled, as R package redirects some functions to R runtime implementation."
)
endif()
if(R_LIB AND USE_NCCL)
message(SEND_ERROR "`R_LIB` doesn't support distributed computing with NCCL yet.")
endif()
if(PLUGIN_RMM AND NOT (USE_CUDA))
message(SEND_ERROR "`PLUGIN_RMM` must be enabled with `USE_CUDA` flag.")
endif()
if(PLUGIN_RMM AND NOT ((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")))
message(SEND_ERROR "`PLUGIN_RMM` must be used with GCC or Clang compiler.")
endif()
if(PLUGIN_RMM AND NOT (CMAKE_SYSTEM_NAME STREQUAL "Linux"))
message(SEND_ERROR "`PLUGIN_RMM` must be used with Linux.")
endif()
if(ENABLE_ALL_WARNINGS)
if((NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") AND (NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU"))
message(SEND_ERROR "ENABLE_ALL_WARNINGS is only available for Clang and GCC.")
endif()
endif()
if(NOT BUILD_SHARED_LIBS AND (R_LIB OR JVM_BINDINGS)) # 修改:使用 BUILD_SHARED_LIBS
message(SEND_ERROR "Cannot build a static library libxgboost.a when R or JVM packages are enabled.")
endif()
if(PLUGIN_FEDERATED)
if(CMAKE_CROSSCOMPILING)
message(SEND_ERROR "Cannot cross compile with federated learning support")
endif()
if(NOT BUILD_SHARED_LIBS) # 修改:使用 BUILD_SHARED_LIBS
message(SEND_ERROR "Cannot build static lib with federated learning support")
endif()
if(R_LIB OR JVM_BINDINGS)
message(SEND_ERROR "Cannot enable federated learning support when R or JVM packages are enabled.")
endif()
if(WIN32)
message(SEND_ERROR "Federated learning not supported for Windows platform")
endif()
endif()
#-- Removed options
if(USE_AVX)
message(SEND_ERROR "The option `USE_AVX` is deprecated as experimental AVX features have been removed from XGBoost.")
endif()
if(PLUGIN_LZ4)
message(SEND_ERROR "The option `PLUGIN_LZ4` is removed from XGBoost.")
endif()
if(RABIT_BUILD_MPI)
message(SEND_ERROR "The option `RABIT_BUILD_MPI` has been removed from XGBoost.")
endif()
if(USE_S3)
message(SEND_ERROR "The option `USE_S3` has been removed from XGBoost")
endif()
if(USE_AZURE)
message(SEND_ERROR "The option `USE_AZURE` has been removed from XGBoost")
endif()
if(USE_HDFS)
message(SEND_ERROR "The option `USE_HDFS` has been removed from XGBoost")
endif()
if(PLUGIN_DENSE_PARSER)
message(SEND_ERROR "The option `PLUGIN_DENSE_PARSER` has been removed from XGBoost.")
endif()
#-- Sanitizer
if(USE_SANITIZER)
include(cmake/Sanitizer.cmake)
enable_sanitizers("${ENABLED_SANITIZERS}")
endif()
if(USE_CUDA)
set(USE_OPENMP ON CACHE BOOL "CUDA requires OpenMP" FORCE)
# `export CXX=' is ignored by CMake CUDA.
if(NOT DEFINED CMAKE_CUDA_HOST_COMPILER AND NOT DEFINED ENV{CUDAHOSTCXX})
set(CMAKE_CUDA_HOST_COMPILER ${CMAKE_CXX_COMPILER} CACHE FILEPATH
"The compiler executable to use when compiling host code for CUDA or HIP language files.")
mark_as_advanced(CMAKE_CUDA_HOST_COMPILER)
message(STATUS "Configured CUDA host compiler: ${CMAKE_CUDA_HOST_COMPILER}")
endif()
if(NOT DEFINED CMAKE_CUDA_RUNTIME_LIBRARY)
set(CMAKE_CUDA_RUNTIME_LIBRARY Static)
endif()
enable_language(CUDA)
if(${CMAKE_CUDA_COMPILER_VERSION} VERSION_LESS 11.0)
message(FATAL_ERROR "CUDA version must be at least 11.0!")
endif()
if(DEFINED GPU_COMPUTE_VER)
compute_cmake_cuda_archs("${GPU_COMPUTE_VER}")
endif()
find_package(CUDAToolkit REQUIRED)
endif()
if(USE_NVCOMP)
find_package(nvcomp REQUIRED)
if(${CMAKE_CUDA_COMPILER_VERSION} VERSION_LESS 12.8)
message(SEND_ERROR "NVComp support requires CUDA >= 12.8")
endif()
endif()
if(FORCE_COLORED_OUTPUT AND (CMAKE_GENERATOR STREQUAL "Ninja") AND
((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR
(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")))
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always")
endif()
if(NOT (CMAKE_SYSTEM_NAME STREQUAL "Emscripten"))
find_package(Threads REQUIRED)
endif()
# -- OpenMP
include(cmake/FindOpenMPMacOS.cmake)
if(USE_OPENMP)
if(APPLE)
find_openmp_macos()
else()
find_package(OpenMP REQUIRED)
endif()
endif()
# Add for IBM i
if(${CMAKE_SYSTEM_NAME} MATCHES "OS400")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
set(CMAKE_CXX_ARCHIVE_CREATE "<CMAKE_AR> -X64 qc <TARGET> <OBJECTS>")
endif()
if(USE_NCCL)
find_package(Nccl REQUIRED)
endif()
if(MSVC)
if(FORCE_SHARED_CRT)
message(STATUS "XGBoost: Using dynamically linked MSVC runtime...")
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
else()
message(STATUS "XGBoost: Using statically linked MSVC runtime...")
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
endif()
# dmlc-core
set(DMLC_FORCE_SHARED_CRT ${FORCE_SHARED_CRT})
add_subdirectory(${xgboost_SOURCE_DIR}/dmlc-core)
if(MSVC)
if(TARGET dmlc_unit_tests)
target_compile_options(
dmlc_unit_tests PRIVATE
-D_CRT_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE
)
endif()
endif()
# core xgboost
add_subdirectory(${xgboost_SOURCE_DIR}/src)
target_link_libraries(objxgboost PUBLIC dmlc)
# Link -lstdc++fs for GCC 8.x
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.0")
target_link_libraries(objxgboost PUBLIC stdc++fs)
endif()
# Exports some R specific definitions and objects
if(R_LIB)
add_subdirectory(${xgboost_SOURCE_DIR}/R-package)
endif()
# This creates its own shared library `xgboost4j'.
if(JVM_BINDINGS)
add_subdirectory(${xgboost_SOURCE_DIR}/jvm-packages)
endif()
# Plugin
add_subdirectory(${xgboost_SOURCE_DIR}/plugin)
if(PLUGIN_RMM)
find_package(rmm REQUIRED)
# Patch the rmm targets so they reference the static cudart
# Remove this patch once RMM stops specifying cudart requirement
# (since RMM is a header-only library, it should not specify cudart in its CMake config)
get_target_property(rmm_link_libs rmm::rmm INTERFACE_LINK_LIBRARIES)
list(REMOVE_ITEM rmm_link_libs CUDA::cudart)
list(APPEND rmm_link_libs CUDA::cudart_static)
set_target_properties(rmm::rmm PROPERTIES INTERFACE_LINK_LIBRARIES "${rmm_link_libs}")
# Pick up patched CCCL from RMM
elseif(USE_CUDA)
# If using CUDA and not RMM, search for CCCL.
find_package(CCCL CONFIG)
if(CCCL_FOUND)
message(STATUS "Standalone CCCL found.")
else()
message(STATUS "Standalone CCCL not found. Attempting to use CCCL from CUDA Toolkit...")
find_package(CCCL CONFIG
HINTS ${CUDAToolkit_LIBRARY_DIR}/cmake)
if(NOT CCCL_FOUND)
message(STATUS "Could not locate CCCL from CUDA Toolkit. Using Thrust and CUB from CUDA Toolkit...")
find_package(libcudacxx CONFIG REQUIRED
HINTS ${CUDAToolkit_LIBRARY_DIR}/cmake)
find_package(CUB CONFIG REQUIRED
HINTS ${CUDAToolkit_LIBRARY_DIR}/cmake)
find_package(Thrust CONFIG REQUIRED
HINTS ${CUDAToolkit_LIBRARY_DIR}/cmake)
thrust_create_target(Thrust HOST CPP DEVICE CUDA)
add_library(CCCL::CCCL INTERFACE IMPORTED GLOBAL)
target_link_libraries(CCCL::CCCL INTERFACE libcudacxx::libcudacxx CUB::CUB Thrust)
endif()
endif()
# Define guard macros to prevent windows.h from conflicting with winsock2.h
if(WIN32)
target_compile_definitions(CCCL::CCCL INTERFACE NOMINMAX WIN32_LEAN_AND_MEAN _WINSOCKAPI_)
endif()
endif()
if(PLUGIN_SYCL)
set(CMAKE_CXX_LINK_EXECUTABLE
"icpx <FLAGS> <CMAKE_CXX_LINK_FLAGS> -qopenmp <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
set(CMAKE_CXX_CREATE_SHARED_LIBRARY
"icpx -shared <CMAKE_SHARED_LIBRARY_CXX_FLAGS> -qopenmp <LANGUAGE_COMPILE_FLAGS> \
<CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <SONAME_FLAG>,<TARGET_SONAME> \
-o <TARGET> <OBJECTS> <LINK_LIBRARIES>")
endif()
#-- library
# 创建库 -
if(BUILD_SHARED_LIBS)
# 构建共享库
add_library(xgboost SHARED)
message(STATUS "构建动态链接库 (DLL)")
# Windows特定设置
if(WIN32)
# 确保生成导入库
set_target_properties(xgboost PROPERTIES
WINDOWS_EXPORT_ALL_SYMBOLS ON
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN ON
)
endif()
else()
# 构建静态库
add_library(xgboost STATIC)
message(STATUS "构建静态链接库 (LIB)")
endif()
target_link_libraries(xgboost PRIVATE objxgboost)
target_include_directories(xgboost
INTERFACE
$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>)
#-- End shared library
#-- CLI for xgboost
if(BUILD_DEPRECATED_CLI)
add_executable(runxgboost ${xgboost_SOURCE_DIR}/src/cli_main.cc)
target_link_libraries(runxgboost PRIVATE objxgboost)
target_include_directories(runxgboost
PRIVATE
${xgboost_SOURCE_DIR}/include
${xgboost_SOURCE_DIR}/dmlc-core/include
)
set_target_properties(runxgboost PROPERTIES OUTPUT_NAME xgboost)
xgboost_target_properties(runxgboost)
xgboost_target_link_libraries(runxgboost)
xgboost_target_defs(runxgboost)
if(KEEP_BUILD_ARTIFACTS_IN_BINARY_DIR)
set_output_directory(runxgboost ${xgboost_BINARY_DIR})
else()
set_output_directory(runxgboost ${xgboost_SOURCE_DIR})
endif()
endif()
#-- End CLI for xgboost
# Common setup for all targets
foreach(target xgboost objxgboost dmlc)
xgboost_target_properties(${target})
xgboost_target_link_libraries(${target})
xgboost_target_defs(${target})
endforeach()
if(JVM_BINDINGS)
xgboost_target_properties(xgboost4j)
xgboost_target_link_libraries(xgboost4j)
xgboost_target_defs(xgboost4j)
endif()
if(USE_OPENMP AND APPLE AND BUILD_SHARED_LIBS) # 修改:使用 BUILD_SHARED_LIBS
patch_openmp_path_macos(xgboost libxgboost)
endif()
if(KEEP_BUILD_ARTIFACTS_IN_BINARY_DIR)
set_output_directory(xgboost ${xgboost_BINARY_DIR}/lib)
else()
set_output_directory(xgboost ${xgboost_SOURCE_DIR}/lib)
endif()
# Ensure these two targets do not build simultaneously, as they produce outputs with conflicting names
if(BUILD_DEPRECATED_CLI)
add_dependencies(xgboost runxgboost)
endif()
#-- Installing XGBoost
if(R_LIB)
include(cmake/RPackageInstallTargetSetup.cmake)
set_target_properties(xgboost PROPERTIES PREFIX "")
if(APPLE)
set_target_properties(xgboost PROPERTIES SUFFIX ".so")
endif()
setup_rpackage_install_target(xgboost "${CMAKE_CURRENT_BINARY_DIR}/R-package-install")
set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/dummy_inst")
endif()
if(MINGW)
set_target_properties(xgboost PROPERTIES PREFIX "")
endif()
if(BUILD_C_DOC)
include(cmake/Doc.cmake)
run_doxygen()
endif()
include(CPack)
include(GNUInstallDirs)
# Install all headers. Please note that currently the C++ headers does not form an "API".
install(DIRECTORY ${xgboost_SOURCE_DIR}/include/xgboost
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
# Install libraries. If `xgboost` is a static lib, specify `objxgboost` also, to avoid the
# following error:
#
# > install(EXPORT ...) includes target "xgboost" which requires target "objxgboost" that is not
# > in any export set.
#
# https://github.com/dmlc/xgboost/issues/6085
if(NOT BUILD_SHARED_LIBS) # 修改:使用 BUILD_SHARED_LIBS
if(BUILD_DEPRECATED_CLI)
set(INSTALL_TARGETS xgboost runxgboost objxgboost dmlc)
else()
set(INSTALL_TARGETS xgboost objxgboost dmlc)
endif()
else()
if(BUILD_DEPRECATED_CLI)
set(INSTALL_TARGETS xgboost runxgboost)
else()
set(INSTALL_TARGETS xgboost)
endif()
endif()
install(TARGETS ${INSTALL_TARGETS}
EXPORT XGBoostTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
INCLUDES DESTINATION ${LIBLEGACY_INCLUDE_DIRS})
install(EXPORT XGBoostTargets
FILE XGBoostTargets.cmake
NAMESPACE xgboost::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/xgboost)
include(CMakePackageConfigHelpers)
configure_package_config_file(
${CMAKE_CURRENT_LIST_DIR}/cmake/xgboost-config.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/cmake/xgboost-config.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/xgboost)
write_basic_package_version_file(
${CMAKE_BINARY_DIR}/cmake/xgboost-config-version.cmake
VERSION ${XGBOOST_VERSION}
COMPATIBILITY AnyNewerVersion)
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/cmake/xgboost-config.cmake
${CMAKE_BINARY_DIR}/cmake/xgboost-config-version.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/xgboost)
#-- Test
if(GOOGLE_TEST)
enable_testing()
# Unittests.
add_executable(testxgboost)
target_link_libraries(testxgboost PRIVATE objxgboost)
xgboost_target_properties(testxgboost)
xgboost_target_link_libraries(testxgboost)
xgboost_target_defs(testxgboost)
add_subdirectory(${xgboost_SOURCE_DIR}/tests/cpp)
add_test(
NAME TestXGBoostLib
COMMAND testxgboost
WORKING_DIRECTORY ${xgboost_BINARY_DIR})
# CLI tests
configure_file(
${xgboost_SOURCE_DIR}/tests/cli/machine.conf.in
${xgboost_BINARY_DIR}/tests/cli/machine.conf
@ONLY
NEWLINE_STYLE UNIX)
if(BUILD_DEPRECATED_CLI)
add_test(
NAME TestXGBoostCLI
COMMAND runxgboost ${xgboost_BINARY_DIR}/tests/cli/machine.conf
WORKING_DIRECTORY ${xgboost_BINARY_DIR})
set_tests_properties(TestXGBoostCLI
PROPERTIES
PASS_REGULAR_EXPRESSION ".*test-rmse:0.087.*")
endif()
endif()
# Add xgboost.pc
if(ADD_PKGCONFIG)
configure_file(${xgboost_SOURCE_DIR}/cmake/xgboost.pc.in ${xgboost_BINARY_DIR}/xgboost.pc @ONLY)
install(
FILES ${xgboost_BINARY_DIR}/xgboost.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
endif()
而 G:\software\xgboost310\dmlc-core下面的CMakeLists内容替换为
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
cmake_policy(SET CMP0091 NEW)
project(dmlc VERSION 0.6 LANGUAGES C CXX)
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
cmake_policy(SET CMP0135 NEW)
endif()
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/build/private/local_config.cmake)
include(${CMAKE_CURRENT_SOURCE_DIR}/build/private/local_config.cmake)
endif()
set(CMAKE_LOCAL "${PROJECT_SOURCE_DIR}/cmake")
list(APPEND CMAKE_MODULE_PATH ${CMAKE_LOCAL}/Modules)
include(CheckCXXSymbolExists)
include(cmake/Utils.cmake)
# 设置动态运行时库 - 新增部分
if(MSVC)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDLL")
set(CMAKE_CXX_FLAGS_RELEASE "/MD")
set(CMAKE_C_FLAGS_RELEASE "/MD")
set(CMAKE_CXX_FLAGS_DEBUG "/MDd")
set(CMAKE_C_FLAGS_DEBUG "/MDd")
endif()
# Options
dmlccore_option(USE_HDFS "Build with HDFS support" OFF)
dmlccore_option(DMLC_HDFS_SHARED "Build with dynamic HDFS library" OFF)
dmlccore_option(USE_AZURE "Build with AZURE support" OFF)
dmlccore_option(USE_S3 "Build with S3 support" OFF)
dmlccore_option(USE_PARQUET "Build with Arrow Parquet" OFF)
dmlccore_option(USE_OPENMP "Build with OpenMP" ON)
dmlccore_option(GOOGLE_TEST "Build google tests" OFF)
dmlccore_option(INSTALL_DOCUMENTATION "Install documentation" OFF)
dmlccore_option(DMLC_FORCE_SHARED_CRT "Build with dynamic CRT on Windows (/MD)" OFF)
dmlccore_option(DMLC_USE_SANITIZER "Use santizer flags; to specify a custom path for sanitizers, set this variable a value that's not ON or OFF" OFF)
set(DMLC_ENABLED_SANITIZERS "address" "leak" CACHE STRING
"Semicolon separated list of sanitizer names. E.g 'address;leak'. Supported sanitizers are
address, leak and thread.")
include(CheckCXXCompilerFlag)
FILE(GLOB SOURCE "src/*.cc")
FILE(GLOB_RECURSE SOURCE_INCLUDE "include/*")
list(APPEND SOURCE ${SOURCE_INCLUDE})
list(APPEND SOURCE "src/io/line_split.cc")
list(APPEND SOURCE "src/io/recordio_split.cc")
list(APPEND SOURCE "src/io/indexed_recordio_split.cc")
list(APPEND SOURCE "src/io/input_split_base.cc")
list(APPEND SOURCE "src/io/filesys.cc")
list(APPEND SOURCE "src/io/local_filesys.cc")
if(USE_HDFS)
list(APPEND SOURCE "src/io/hdfs_filesys.cc")
endif()
if(USE_S3)
list(APPEND SOURCE "src/io/s3_filesys.cc")
endif()
if(USE_AZURE)
list(APPEND SOURCE "src/io/azure_filesys.cc")
endif()
# 修改:使用 BUILD_SHARED_LIBS 控制库类型
if(BUILD_SHARED_LIBS)
add_library(dmlc SHARED ${SOURCE})
message(STATUS "构建动态链接库 (DLL)")
else()
add_library(dmlc STATIC ${SOURCE})
message(STATUS "构建静态链接库 (LIB)")
endif()
set_target_properties(dmlc PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR})
target_compile_features(dmlc PUBLIC cxx_std_14)
# Windows特定设置 - 新增部分
if(WIN32 AND BUILD_SHARED_LIBS)
# 确保生成导入库
set_target_properties(dmlc PROPERTIES
WINDOWS_EXPORT_ALL_SYMBOLS ON
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN ON
)
endif()
# Sanitizer
if (DMLC_USE_SANITIZER)
# Older CMake versions have had troubles with Sanitizer
cmake_minimum_required(VERSION 3.12)
include(cmake/Sanitizer.cmake)
enable_sanitizers("${DMLC_ENABLED_SANITIZERS}")
endif (DMLC_USE_SANITIZER)
if(USE_PARQUET)
find_package(Arrow REQUIRED)
find_package(Parquet REQUIRED)
if(BUILD_SHARED_LIBS)
target_link_libraries(dmlc PRIVATE arrow_shared parquet_shared)
else()
# No need to use arrow_static / parquet_static here, since
# we don't have a compelling need to include Arrow and Parquet
# in the generaed static lib libdmlc.a. This is similar to
# how we don't statically link with OpenSSL and cURL.
target_link_libraries(dmlc PUBLIC arrow_shared parquet_shared)
endif()
set(DMLC_USE_PARQUET 1)
endif()
# HDFS configurations
if(USE_HDFS)
find_package(HDFS REQUIRED)
find_package(JNI REQUIRED)
target_include_directories(dmlc PRIVATE ${HDFS_INCLUDE_DIR})
if (DMLC_HDFS_SHARED)
target_link_libraries(dmlc PRIVATE ${HDFS_LIBRARIES} ${JAVA_JVM_LIBRARY})
else()
target_link_libraries(dmlc PRIVATE ${HDFS_STATIC_LIB} ${JAVA_JVM_LIBRARY})
endif()
target_compile_definitions(dmlc PRIVATE -DDMLC_USE_HDFS=1)
else()
target_compile_definitions(dmlc PRIVATE -DDMLC_USE_HDFS=0)
endif()
# S3 configurations
if(USE_S3)
find_package(CURL REQUIRED)
target_include_directories(dmlc SYSTEM PRIVATE ${CURL_INCLUDE_DIR})
target_link_libraries(dmlc PRIVATE ${CURL_LIBRARY})
find_package(OpenSSL REQUIRED)
target_include_directories(dmlc SYSTEM PRIVATE ${OPENSSL_INCLUDE_DIR})
target_link_libraries(dmlc PRIVATE ${OPENSSL_LIBRARY} ${OPENSSL_LIBRARIES} ${OPENSSL_CRYPTO_LIBRARY})
target_compile_definitions(dmlc PRIVATE -DDMLC_USE_S3=1)
else()
target_compile_definitions(dmlc PRIVATE -DDMLC_USE_S3=0)
endif()
# Azure configurations
if(USE_AZURE)
target_compile_definitions(dmlc PRIVATE -DDMLC_USE_AZURE=1)
else()
target_compile_definitions(dmlc PRIVATE -DDMLC_USE_AZURE=0)
endif()
# OpenMP
include(cmake/FindOpenMPMacOS.cmake)
if(USE_OPENMP)
if(APPLE)
find_openmp_macos()
if(BUILD_SHARED_LIBS)
patch_openmp_path_macos(dmlc libdmlc)
endif()
else()
find_package(OpenMP REQUIRED)
endif()
target_link_libraries(dmlc PRIVATE OpenMP::OpenMP_CXX)
endif()
if(WIN32 AND (NOT MSVC)) # On Windows, link Shlwapi.lib for non-MSVC compilers
target_link_libraries(dmlc PRIVATE shlwapi)
endif()
# Check location of clock_gettime; if it's in librt, link it
include(CheckLibraryExists)
CHECK_LIBRARY_EXISTS(rt clock_gettime "time.h" HAVE_CLOCK_GETTIME_IN_LIBRT)
if(HAVE_CLOCK_GETTIME_IN_LIBRT)
target_link_libraries(dmlc PRIVATE rt)
endif()
# Check headers and symbols
include(CheckSymbolExists)
include(CheckIncludeFile)
include(CheckIncludeFileCXX)
check_symbol_exists(fopen64 stdio.h DMLC_FOPEN_64_PRESENT)
check_include_file_cxx(cxxabi.h DMLC_CXXABI_H_PRESENT)
check_symbol_exists(nanosleep time.h DMLC_NANOSLEEP_PRESENT)
# Check existence of backtrace(3)
find_package(Backtrace)
if(Backtrace_FOUND)
set(DMLC_EXECINFO_H_PRESENT 1)
set(DMLC_EXECINFO_H ${Backtrace_HEADER})
target_include_directories(dmlc SYSTEM PRIVATE ${Backtrace_INCLUDE_DIRS})
target_link_libraries(dmlc PRIVATE ${Backtrace_LIBRARIES})
else()
set(DMLC_EXECINFO_H_PRESENT 0)
endif()
# Check endianness
include(TestBigEndian)
test_big_endian(BIG_ENDIAN)
if(BIG_ENDIAN)
set(DMLC_CMAKE_LITTLE_ENDIAN 0)
else()
set(DMLC_CMAKE_LITTLE_ENDIAN 1)
endif()
message(STATUS "${CMAKE_LOCAL}/build_config.h.in -> include/dmlc/build_config.h")
configure_file("cmake/build_config.h.in" "include/dmlc/build_config.h")
target_include_directories(dmlc PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>
$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include>)
target_compile_definitions(dmlc PRIVATE -D_XOPEN_SOURCE=700
-D_POSIX_SOURCE -D_POSIX_C_SOURCE=200809L -D_DARWIN_C_SOURCE)
# Older stdc++ enable c++11 items
target_compile_definitions(dmlc PUBLIC -D__USE_XOPEN2K8)
# DMLC_CORE_USE_CMAKE macro constant indicates the use of CMake
target_compile_definitions(dmlc PUBLIC -DDMLC_CORE_USE_CMAKE)
# compiler flags
if(MSVC)
target_compile_definitions(dmlc PUBLIC -DDMLC_USE_CXX11=1)
if(DMLC_FORCE_SHARED_CRT)
message(STATUS "DMLC-Core: Using dynamically linked MSVC runtime...")
set(DMLC_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
else()
message(STATUS "DMLC-Core: Using statically linked MSVC runtime...")
set(DMLC_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
set(CMAKE_MSVC_RUNTIME_LIBRARY "${DMLC_MSVC_RUNTIME_LIBRARY}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc")
set_target_properties(dmlc PROPERTIES
MSVC_RUNTIME_LIBRARY "${DMLC_MSVC_RUNTIME_LIBRARY}")
else()
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
check_cxx_compiler_flag("-msse2" SUPPORT_MSSE2)
if(SUPPORT_MSSE2)
target_compile_options(dmlc PRIVATE -msse2)
endif()
target_compile_options(dmlc PRIVATE -Wall -Wno-unknown-pragmas -fPIC)
if(CMAKE_BUILD_TYPE STREQUAL "DEBUG" OR CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_options(dmlc PRIVATE -g -O0)
else()
target_compile_options(dmlc PRIVATE -O3)
endif()
target_compile_definitions(dmlc PUBLIC -DDMLC_USE_CXX11=1 -DDMLC_USE_CXX14=1)
endif()
include(GNUInstallDirs)
# ---[ Install Includes
install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/dmlc
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/include/dmlc/build_config.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dmlc)
# ---[ Install the archive static lib and header files
install(TARGETS dmlc
EXPORT DMLCTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(EXPORT DMLCTargets
FILE DMLCTargets.cmake
NAMESPACE dmlc::
EXPORT_LINK_INTERFACE_LIBRARIES
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/dmlc)
# ---[ Install documentation
if(INSTALL_DOCUMENTATION)
install(DIRECTORY doc DESTINATION ${CMAKE_INSTALL_DATADIR})
endif()
# ---[ Package configurations
include(CMakePackageConfigHelpers)
configure_package_config_file(
${CMAKE_LOCAL}/dmlc-config.cmake.in
${CMAKE_BINARY_DIR}/cmake/dmlc-config.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/dmlc)
write_basic_package_version_file(
${CMAKE_BINARY_DIR}/cmake/dmlc-config-version.cmake
VERSION ${DMLC_VERSION}
COMPATIBILITY AnyNewerVersion)
install(
FILES
${CMAKE_BINARY_DIR}/cmake/dmlc-config.cmake
${CMAKE_BINARY_DIR}/cmake/dmlc-config-version.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/dmlc)
# ---[ Linter target
if(MSVC)
find_package(PythonInterp)
set(PYTHON_EXECUTABLE ${PYTHON_EXECUTABLE} CACHE FILEPATH "Path to the python 2.x executable")
endif()
set(LINT_DIRS include src scripts)
add_custom_target(dmlc_lint COMMAND ${CMAKE_COMMAND} -DMSVC=${MSVC} -DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE} -DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR} -DLINT_DIRS=${LINT_DIRS} -DPROJECT_NAME=dmlc -P ${PROJECT_SOURCE_DIR}/cmake/lint.cmake)
# Setup testing
if(GOOGLE_TEST)
include(CTest)
add_subdirectory(test)
endif()
要上面这两个文件内容要保持一致。
接下来使用命令行进行动态运行时库编译:
打开VS2022开发者命令提示符 Developer Command Prompt for VS
确保MSVC工具链在PATH中
先定位到上面那个解压后的目录下面(使用cd +绝对路径)
然后依次操作下面命令
mkdir build
cd build
# 配置
cmake -G "Visual Studio 17 2022" -A x64 -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDLL -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../install ..
# 编译
cmake --build . --config Release --parallel
# 安装
cmake --build . --config Release --target INSTALL
编译好后看到G:/ 下面有3个文件夹bin、lib、include。这就是我们要的东西,当然你可以建一个文件夹来存放他们。
接下来我们就可以用代码来测试。CMakeLists设置如下:
set(XGBoost_INCLUDE_DIR "G:/include")
set(XGBoost_LIB_DIR "G:/lib")
# 包含头文件
include_directories(${XGBoost_INCLUDE_DIR})
# 添加可执行文件
add_executable(XGBoosttest ../XGBoost/test.cpp)
target_link_libraries(XGBoosttest "${XGBoost_LIB_DIR}/dmlc.lib" "${XGBoost_LIB_DIR}/xgboost.lib")
if(WIN32)
# 将 DLL 复制到输出目录
add_custom_command(TARGET XGBoosttest POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"G:/bin/dmlc.dll"
$<TARGET_FILE_DIR:XGBoosttest>
)
# 将 DLL 复制到输出目录
add_custom_command(TARGET XGBoosttest POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"G:/bin/xgboost.dll"
$<TARGET_FILE_DIR:XGBoosttest>
)
endif()
测试代码:
#include <iostream>
#include <vector>
#include <cassert>
#include "xgboost/c_api.h" // 包含XGBoost C API头文件
int main() {
std::cout << "XGBoost C API 简单测试" << std::endl;
// 1. 检查XGBoost版本
int major, minor, patch;
XGBoostVersion(&major, &minor, &patch);
std::cout << "XGBoost版本: " << major << "." << minor << "." << patch << std::endl;
// 2. 创建简单的训练数据
// 特征数据: 4个样本,每个样本2个特征
std::vector<float> data = {
1.0f, 2.0f, // 样本1
2.0f, 3.0f, // 样本2
3.0f, 4.0f, // 样本3
4.0f, 5.0f // 样本4
};
// 标签数据: 二分类标签
std::vector<float> labels = {0.0f, 1.0f, 0.0f, 1.0f};
// 3. 创建DMatrix
DMatrixHandle train_matrix;
int result = XGDMatrixCreateFromMat(
data.data(), // 数据指针
4, // 样本数 (行数)
2, // 特征数 (列数)
-1.0f, // 缺失值
&train_matrix // 输出的DMatrix句柄
);
if (result != 0) {
std::cerr << "创建DMatrix失败: " << XGBGetLastError() << std::endl;
return -1;
}
// 4. 设置标签
result = XGDMatrixSetFloatInfo(train_matrix, "label", labels.data(), labels.size());
if (result != 0) {
std::cerr << "设置标签失败: " << XGBGetLastError() << std::endl;
XGDMatrixFree(train_matrix);
return -1;
}
// 5. 创建Booster
BoosterHandle booster;
DMatrixHandle dmats[] = {train_matrix};
result = XGBoosterCreate(dmats, 1, &booster);
if (result != 0) {
std::cerr << "创建Booster失败: " << XGBGetLastError() << std::endl;
XGDMatrixFree(train_matrix);
return -1;
}
// 6. 设置参数
XGBoosterSetParam(booster, "objective", "binary:logistic");
XGBoosterSetParam(booster, "max_depth", "3");
XGBoosterSetParam(booster, "eta", "0.1");
XGBoosterSetParam(booster, "seed", "42");
// 7. 训练模型 (1轮迭代)
for (int iter = 0; iter < 10; ++iter) {
result = XGBoosterUpdateOneIter(booster, iter, train_matrix);
if (result != 0) {
std::cerr << "训练失败,迭代 " << iter << ": " << XGBGetLastError() << std::endl;
break;
}
// 评估训练误差
const char* eval_names[] = {"train"};
DMatrixHandle eval_dmats[] = {train_matrix};
const char* eval_result;
result = XGBoosterEvalOneIter(booster, iter, eval_dmats, eval_names, 1, &eval_result);
if (result == 0) {
std::cout << "迭代 " << iter << ": " << eval_result << std::endl;
}
}
// 8. 创建测试数据并进行预测
std::vector<float> test_data = {
1.5f, 2.5f, // 测试样本1
3.5f, 4.5f // 测试样本2
};
DMatrixHandle test_matrix;
result = XGDMatrixCreateFromMat(
test_data.data(),
2, // 2个测试样本
2, // 2个特征
-1.0f, // 缺失值
&test_matrix
);
if (result == 0) {
// 进行预测
bst_ulong out_len;
const float* out_result;
result = XGBoosterPredict(
booster,
test_matrix,
0, // 选项掩码: 正常预测
0, // 树的数量限制: 使用所有树
0, // training: false
&out_len,
&out_result
);
if (result == 0) {
std::cout << "\n预测结果:" << std::endl;
for (bst_ulong i = 0; i < out_len; ++i) {
std::cout << "样本 " << i << ": " << out_result[i] << std::endl;
}
} else {
std::cerr << "预测失败: " << XGBGetLastError() << std::endl;
}
XGDMatrixFree(test_matrix);
}
// 9. 保存模型
result = XGBoosterSaveModel(booster, "simple_model.model");
if (result == 0) {
std::cout << "\n模型已保存到: simple_model.model" << std::endl;
} else {
std::cerr << "保存模型失败: " << XGBGetLastError() << std::endl;
}
// 10. 清理资源
XGBoosterFree(booster);
XGDMatrixFree(train_matrix);
std::cout << "测试完成!" << std::endl;
return 0;
}
运行结果:
XGBoost C API 简单测试
XGBoost版本: 3.1.0
迭代 0: [0] train-logloss:0.69314718246459961
迭代 1: [1] train-logloss:0.69314718246459961
迭代 2: [2] train-logloss:0.69314718246459961
迭代 3: [3] train-logloss:0.69314718246459961
迭代 4: [4] train-logloss:0.69314718246459961
迭代 5: [5] train-logloss:0.69314718246459961
迭代 6: [6] train-logloss:0.69314718246459961
迭代 7: [7] train-logloss:0.69314718246459961
迭代 8: [8] train-logloss:0.69314718246459961
迭代 9: [9] train-logloss:0.69314718246459961
预测结果:
样本 0: 0.5
样本 1: 0.5
模型已保存到: simple_model.model
测试完成!
[13:45:47] WARNING: G:\software\xgboost310\src\c_api\c_api.cc:1570: Saving model in the UBJSON format as default. You can use a file extension: `json` or `ubj` to choose between formats.
Process finished with exit code 0
更多推荐
所有评论(0)