Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@ if(USE_SANITIZER)
enable_sanitizers("${ENABLED_SANITIZERS}")
endif()

include(cmake/FindOpenMPMacOS.cmake)
if(USE_OPENMP)
if(APPLE)
find_openmp_macos()
else()
find_package(OpenMP REQUIRED)
endif()
else()
message(STATUS "Disabling OpenMP")
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.")
Expand Down Expand Up @@ -119,6 +130,10 @@ foreach(lib ${TREELITE_TARGETS})
endif()
endforeach()

if(USE_OPENMP AND APPLE AND NOT Treelite_BUILD_STATIC_LIBS)
patch_openmp_path_macos(treelite)
endif()

# Export install targets
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
# Include CPack only if the current project is top level.
Expand Down
99 changes: 99 additions & 0 deletions cmake/FindOpenMPMacOS.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# 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 an XGBoost shared library 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/lightgbm-org/LightGBM/pull/6391 for detailed
# explanation. Adapted from https://github.com/lightgbm-org/LightGBM/pull/6391
# by James Lamb.
# MacOS only.
function(patch_openmp_path_macos target)
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
)
# 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.
add_custom_command(
TARGET ${target}
POST_BUILD
COMMAND
install_name_tool
-change
${__OpenMP_LIBRARY_LOCATION}
"@rpath/${__OpenMP_LIBRARY_NAME}"
"$<TARGET_FILE:${target}>"
VERBATIM
)
message(STATUS
"${target}: "
"Replacing hard-coded OpenMP install_name with '@rpath/${__OpenMP_LIBRARY_NAME}'..."
)
# Add RPATH entries to ensure the loader looks in the following locations:
#
# - R builds: wherever the active R toolchain's OpenMP discovery found libomp.
# - Other builds: Homebrew's libomp followed by the discovered OpenMP library directory.
#
# 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
if(R_LIB)
set(__OPENMP_RPATH "${__OpenMP_LIBRARY_DIR}")
else()
execute_process(COMMAND brew --prefix libomp
OUTPUT_VARIABLE HOMEBREW_LIBOMP_PREFIX
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(__OPENMP_RPATH "${HOMEBREW_LIBOMP_PREFIX}/lib;${__OpenMP_LIBRARY_DIR}")
endif()
set_target_properties(
${target}
PROPERTIES
BUILD_WITH_INSTALL_RPATH TRUE
INSTALL_RPATH "${__OPENMP_RPATH}"
INSTALL_RPATH_USE_LINK_PATH FALSE
)
endfunction()
4 changes: 2 additions & 2 deletions python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,11 @@ repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest

[[tool.cibuildwheel.overrides]]
select = "*-macosx_x86_64"
environment = { MACOSX_DEPLOYMENT_TARGET = "10.15" }
environment = { MACOSX_DEPLOYMENT_TARGET = "10.15", LDFLAGS = "-Wl,-rpath,/opt/homebrew/opt/libomp/lib" }

[[tool.cibuildwheel.overrides]]
select = "*-macosx_arm64"
environment = { MACOSX_DEPLOYMENT_TARGET = "12.0" }
environment = { MACOSX_DEPLOYMENT_TARGET = "12.0", LDFLAGS = "-Wl,-rpath,/usr/local/opt/libomp/lib" }

[tool.ruff]
line-length = 120
Expand Down
25 changes: 0 additions & 25 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,6 @@
add_library(objtreelite OBJECT)
target_link_libraries(objtreelite PRIVATE RapidJSON::rapidjson nlohmann_json::nlohmann_json std::mdspan)

if(USE_OPENMP)
if(APPLE)
find_package(OpenMP)
if (NOT OpenMP_FOUND)
# Try again with extra path info; required for libomp 15+ from Homebrew
message(STATUS "OpenMP not found; attempting to locate libomp from Homebrew...")
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()
else()
find_package(OpenMP REQUIRED)
endif()
else()
message(STATUS "Disabling OpenMP")
endif()

if(ENABLE_ALL_WARNINGS)
target_compile_options(objtreelite PRIVATE -Wall -Wextra)
endif()
Expand Down
Loading