125 lines
4.4 KiB
CMake
125 lines
4.4 KiB
CMake
# Find OpenMP library on MacOS
|
|
# Automatically handle locating libomp from the Homebrew package manager
|
|
|
|
# lint_cmake: -package/consistency
|
|
|
|
macro(find_openmp_macos)
|
|
if(NOT APPLE)
|
|
message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}() must only be used on MacOS")
|
|
endif()
|
|
find_package(OpenMP)
|
|
if(NOT OpenMP_FOUND)
|
|
# Try again with extra path info. This step is required for libomp 15+ from Homebrew,
|
|
# as libomp 15.0+ from brew is keg-only
|
|
# See https://github.com/Homebrew/homebrew-core/issues/112107#issuecomment-1278042927.
|
|
execute_process(COMMAND brew --prefix libomp
|
|
OUTPUT_VARIABLE HOMEBREW_LIBOMP_PREFIX
|
|
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
|
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)
|
|
find_package(OpenMP REQUIRED)
|
|
endif()
|
|
endmacro()
|
|
|
|
# Patch libxgboost.dylib so that it depends on @rpath/libomp.dylib instead of
|
|
# /opt/homebrew/opt/libomp/lib/libomp.dylib or other hard-coded paths.
|
|
# Doing so enables XGBoost to interoperate with multiple kinds of OpenMP
|
|
# libraries. See https://github.com/microsoft/LightGBM/pull/6391 for detailed
|
|
# explanation. Adapted from https://github.com/microsoft/LightGBM/pull/6391
|
|
# by James Lamb.
|
|
# MacOS only.
|
|
function(patch_openmp_path_macos target target_default_output_name)
|
|
if(NOT APPLE)
|
|
message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}() must only be used on MacOS")
|
|
endif()
|
|
# Get path to libomp found at build time
|
|
get_target_property(
|
|
__OpenMP_LIBRARY_LOCATION
|
|
OpenMP::OpenMP_CXX
|
|
INTERFACE_LINK_LIBRARIES
|
|
)
|
|
# Get the base name of the OpenMP lib
|
|
# Usually: libomp.dylib, libgomp.dylib, or libiomp.dylib
|
|
get_filename_component(
|
|
__OpenMP_LIBRARY_NAME
|
|
${__OpenMP_LIBRARY_LOCATION}
|
|
NAME
|
|
)
|
|
# Get the directory containing the OpenMP lib
|
|
get_filename_component(
|
|
__OpenMP_LIBRARY_DIR
|
|
${__OpenMP_LIBRARY_LOCATION}
|
|
DIRECTORY
|
|
)
|
|
# Get the name of the XGBoost lib, e.g. libxgboost
|
|
get_target_property(
|
|
__LIBXGBOOST_OUTPUT_NAME
|
|
${target}
|
|
OUTPUT_NAME
|
|
)
|
|
if(NOT __LIBXGBOOST_OUTPUT_NAME)
|
|
set(__LIBXGBOOST_OUTPUT_NAME "${target_default_output_name}")
|
|
endif()
|
|
|
|
# Get the file name of the XGBoost lib, e.g. libxgboost.dylib
|
|
if(CMAKE_SHARED_LIBRARY_SUFFIX_CXX)
|
|
set(
|
|
__LIBXGBOOST_FILENAME_${target} "${__LIBXGBOOST_OUTPUT_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX_CXX}"
|
|
CACHE INTERNAL "Shared library filename ${target}"
|
|
)
|
|
else()
|
|
set(
|
|
__LIBXGBOOST_FILENAME_${target} "${__LIBXGBOOST_OUTPUT_NAME}.dylib"
|
|
CACHE INTERNAL "Shared library filename ${target}"
|
|
)
|
|
endif()
|
|
|
|
message(STATUS "Creating shared lib for target ${target}: ${__LIBXGBOOST_FILENAME_${target}}")
|
|
|
|
# Override the absolute path to OpenMP with a relative one using @rpath.
|
|
#
|
|
# This also ensures that if a libomp.dylib has already been loaded, it'll just use that.
|
|
if(KEEP_BUILD_ARTIFACTS_IN_BINARY_DIR)
|
|
set(__LIB_DIR ${xgboost_BINARY_DIR}/lib)
|
|
else()
|
|
set(__LIB_DIR ${xgboost_SOURCE_DIR}/lib)
|
|
endif()
|
|
add_custom_command(
|
|
TARGET ${target}
|
|
POST_BUILD
|
|
COMMAND
|
|
install_name_tool
|
|
-change
|
|
${__OpenMP_LIBRARY_LOCATION}
|
|
"@rpath/${__OpenMP_LIBRARY_NAME}"
|
|
"${__LIBXGBOOST_FILENAME_${target}}"
|
|
WORKING_DIRECTORY ${__LIB_DIR}
|
|
)
|
|
message(STATUS
|
|
"${__LIBXGBOOST_FILENAME_${target}}: "
|
|
"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:
|
|
#
|
|
# - /opt/homebrew/opt/libomp/lib (where 'brew install' / 'brew link' puts libomp.dylib)
|
|
# - ${__OpenMP_LIBRARY_DIR} (wherever find_package(OpenMP) found OpenMP at build time)
|
|
#
|
|
# Note: This list will only be used if libomp.dylib isn't already loaded into memory.
|
|
# So Conda users will likely use ${CONDA_PREFIX}/libomp.dylib
|
|
execute_process(COMMAND brew --prefix libomp
|
|
OUTPUT_VARIABLE HOMEBREW_LIBOMP_PREFIX
|
|
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
|
set_target_properties(
|
|
${target}
|
|
PROPERTIES
|
|
BUILD_WITH_INSTALL_RPATH TRUE
|
|
INSTALL_RPATH "${HOMEBREW_LIBOMP_PREFIX}/lib;${__OpenMP_LIBRARY_DIR}"
|
|
INSTALL_RPATH_USE_LINK_PATH FALSE
|
|
)
|
|
endfunction()
|