[CI] Stop vendoring libomp.dylib in MacOS Python wheels (#10440)
This commit is contained in:
parent
b9e5229ff2
commit
8689f0b562
4
.github/workflows/python_wheels.yml
vendored
4
.github/workflows/python_wheels.yml
vendored
@ -28,6 +28,10 @@ jobs:
|
|||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||||
with:
|
with:
|
||||||
submodules: 'true'
|
submodules: 'true'
|
||||||
|
- name: Set up homebrew
|
||||||
|
uses: Homebrew/actions/setup-homebrew@68fa6aeb1ccb0596d311f2b34ec74ec21ee68e54
|
||||||
|
- name: Install libomp
|
||||||
|
run: brew install libomp
|
||||||
- uses: conda-incubator/setup-miniconda@a4260408e20b96e80095f42ff7f1a15b27dd94ca # v3.0.4
|
- uses: conda-incubator/setup-miniconda@a4260408e20b96e80095f42ff7f1a15b27dd94ca # v3.0.4
|
||||||
with:
|
with:
|
||||||
miniforge-variant: Mambaforge
|
miniforge-variant: Mambaforge
|
||||||
|
|||||||
@ -234,27 +234,16 @@ endif()
|
|||||||
|
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
|
# -- OpenMP
|
||||||
|
include(cmake/FindOpenMPMacOS.cmake)
|
||||||
if(USE_OPENMP)
|
if(USE_OPENMP)
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
find_package(OpenMP)
|
find_openmp_macos()
|
||||||
if(NOT OpenMP_FOUND)
|
|
||||||
# Try again with extra path info; required for libomp 15+ 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()
|
else()
|
||||||
find_package(OpenMP REQUIRED)
|
find_package(OpenMP REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Add for IBM i
|
# Add for IBM i
|
||||||
if(${CMAKE_SYSTEM_NAME} MATCHES "OS400")
|
if(${CMAKE_SYSTEM_NAME} MATCHES "OS400")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
|
||||||
@ -380,6 +369,10 @@ if(JVM_BINDINGS)
|
|||||||
xgboost_target_defs(xgboost4j)
|
xgboost_target_defs(xgboost4j)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(USE_OPENMP AND APPLE)
|
||||||
|
patch_openmp_path_macos(xgboost libxgboost)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(KEEP_BUILD_ARTIFACTS_IN_BINARY_DIR)
|
if(KEEP_BUILD_ARTIFACTS_IN_BINARY_DIR)
|
||||||
set_output_directory(xgboost ${xgboost_BINARY_DIR}/lib)
|
set_output_directory(xgboost ${xgboost_BINARY_DIR}/lib)
|
||||||
else()
|
else()
|
||||||
|
|||||||
124
cmake/FindOpenMPMacOS.cmake
Normal file
124
cmake/FindOpenMPMacOS.cmake
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
# 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()
|
||||||
@ -24,3 +24,8 @@ target_include_directories(xgboost4j
|
|||||||
${PROJECT_SOURCE_DIR}/dmlc-core/include)
|
${PROJECT_SOURCE_DIR}/dmlc-core/include)
|
||||||
|
|
||||||
set_output_directory(xgboost4j ${PROJECT_SOURCE_DIR}/lib)
|
set_output_directory(xgboost4j ${PROJECT_SOURCE_DIR}/lib)
|
||||||
|
|
||||||
|
# MacOS: Patch libxgboost4j.dylib to use @rpath/libomp.dylib
|
||||||
|
if(USE_OPENMP AND APPLE)
|
||||||
|
patch_openmp_path_macos(xgboost4j libxgboost4j)
|
||||||
|
endif()
|
||||||
|
|||||||
@ -11,33 +11,19 @@ fi
|
|||||||
platform_id=$1
|
platform_id=$1
|
||||||
commit_id=$2
|
commit_id=$2
|
||||||
|
|
||||||
# Bundle libomp 11.1.0 when targeting MacOS.
|
|
||||||
# This is a workaround in order to prevent segfaults when running inside a Conda environment.
|
|
||||||
# See https://github.com/dmlc/xgboost/issues/7039#issuecomment-1025125003 for more context.
|
|
||||||
# The workaround is also used by the scikit-learn project.
|
|
||||||
if [[ "$platform_id" == macosx_* ]]; then
|
if [[ "$platform_id" == macosx_* ]]; then
|
||||||
# Make sure to use a libomp version binary compatible with the oldest
|
|
||||||
# supported version of the macos SDK as libomp will be vendored into the
|
|
||||||
# XGBoost wheels for MacOS.
|
|
||||||
|
|
||||||
if [[ "$platform_id" == macosx_arm64 ]]; then
|
if [[ "$platform_id" == macosx_arm64 ]]; then
|
||||||
# MacOS, Apple Silicon
|
# MacOS, Apple Silicon
|
||||||
# arm64 builds must cross compile because CI is on x64
|
|
||||||
# cibuildwheel will take care of cross-compilation.
|
|
||||||
wheel_tag=macosx_12_0_arm64
|
wheel_tag=macosx_12_0_arm64
|
||||||
cpython_ver=39
|
cpython_ver=39
|
||||||
cibw_archs=arm64
|
cibw_archs=arm64
|
||||||
export MACOSX_DEPLOYMENT_TARGET=12.0
|
export MACOSX_DEPLOYMENT_TARGET=12.0
|
||||||
#OPENMP_URL="https://anaconda.org/conda-forge/llvm-openmp/11.1.0/download/osx-arm64/llvm-openmp-11.1.0-hf3c4609_1.tar.bz2"
|
|
||||||
OPENMP_URL="https://xgboost-ci-jenkins-artifacts.s3.us-west-2.amazonaws.com/llvm-openmp-11.1.0-hf3c4609_1-osx-arm64.tar.bz2"
|
|
||||||
elif [[ "$platform_id" == macosx_x86_64 ]]; then
|
elif [[ "$platform_id" == macosx_x86_64 ]]; then
|
||||||
# MacOS, Intel
|
# MacOS, Intel
|
||||||
wheel_tag=macosx_10_15_x86_64.macosx_11_0_x86_64.macosx_12_0_x86_64
|
wheel_tag=macosx_10_15_x86_64.macosx_11_0_x86_64.macosx_12_0_x86_64
|
||||||
cpython_ver=39
|
cpython_ver=39
|
||||||
cibw_archs=x86_64
|
cibw_archs=x86_64
|
||||||
export MACOSX_DEPLOYMENT_TARGET=10.15
|
export MACOSX_DEPLOYMENT_TARGET=10.15
|
||||||
#OPENMP_URL="https://anaconda.org/conda-forge/llvm-openmp/11.1.0/download/osx-64/llvm-openmp-11.1.0-hda6cdc1_1.tar.bz2"
|
|
||||||
OPENMP_URL="https://xgboost-ci-jenkins-artifacts.s3.us-west-2.amazonaws.com/llvm-openmp-11.1.0-hda6cdc1_1-osx-64.tar.bz2"
|
|
||||||
else
|
else
|
||||||
echo "Platform not supported: $platform_id"
|
echo "Platform not supported: $platform_id"
|
||||||
exit 3
|
exit 3
|
||||||
@ -48,26 +34,23 @@ if [[ "$platform_id" == macosx_* ]]; then
|
|||||||
export CIBW_ENVIRONMENT=${setup_env_var}
|
export CIBW_ENVIRONMENT=${setup_env_var}
|
||||||
export CIBW_TEST_SKIP='*-macosx_arm64'
|
export CIBW_TEST_SKIP='*-macosx_arm64'
|
||||||
export CIBW_BUILD_VERBOSITY=3
|
export CIBW_BUILD_VERBOSITY=3
|
||||||
|
|
||||||
mamba create -n build $OPENMP_URL
|
|
||||||
PREFIX="$HOME/miniconda3/envs/build"
|
|
||||||
|
|
||||||
# Set up build flags for cibuildwheel
|
|
||||||
# This is needed to bundle libomp lib we downloaded earlier
|
|
||||||
export CC=/usr/bin/clang
|
|
||||||
export CXX=/usr/bin/clang++
|
|
||||||
export CPPFLAGS="$CPPFLAGS -Xpreprocessor -fopenmp"
|
|
||||||
export CFLAGS="$CFLAGS -I$PREFIX/include"
|
|
||||||
export CXXFLAGS="$CXXFLAGS -I$PREFIX/include"
|
|
||||||
export LDFLAGS="$LDFLAGS -Wl,-rpath,$PREFIX/lib -L$PREFIX/lib -lomp"
|
|
||||||
else
|
else
|
||||||
echo "Platform not supported: $platform_id"
|
echo "Platform not supported: $platform_id"
|
||||||
exit 2
|
exit 2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Tell delocate-wheel to not vendor libomp.dylib into the wheel"
|
||||||
|
export CIBW_REPAIR_WHEEL_COMMAND_MACOS="delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel} --exclude libomp.dylib"
|
||||||
|
|
||||||
python -m pip install cibuildwheel
|
python -m pip install cibuildwheel
|
||||||
python -m cibuildwheel python-package --output-dir wheelhouse
|
python -m cibuildwheel python-package --output-dir wheelhouse
|
||||||
python tests/ci_build/rename_whl.py \
|
python tests/ci_build/rename_whl.py \
|
||||||
--wheel-path wheelhouse/*.whl \
|
--wheel-path wheelhouse/*.whl \
|
||||||
--commit-hash ${commit_id} \
|
--commit-hash ${commit_id} \
|
||||||
--platform-tag ${wheel_tag}
|
--platform-tag ${wheel_tag}
|
||||||
|
|
||||||
|
# List dependencies of libxgboost.dylib
|
||||||
|
mkdir tmp
|
||||||
|
unzip -j wheelhouse/xgboost-*.whl xgboost/lib/libxgboost.dylib -d tmp
|
||||||
|
otool -L tmp/libxgboost.dylib
|
||||||
|
rm -rf tmp
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user