From 368d71fd0109c21a93fea21aa49f0aad7209bf22 Mon Sep 17 00:00:00 2001 From: Jiaming Yuan Date: Tue, 3 Feb 2026 15:50:55 +0800 Subject: [PATCH 1/6] Add optional support to use static omp on macos suppress warning. Disable for other platforms. cleanup. Fix. cleanups. Upload the wheel. build config. script. msg early exit. --- CMakeLists.txt | 6 +- cmake/FindOpenMPMacOS.cmake | 67 +++++++++++++++++++---- ops/pipeline/build-python-wheels-macos.sh | 6 +- python-package/packager/build_config.py | 2 + src/common/io.cc | 1 + 5 files changed, 67 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b91de35f22f..772146ae8c03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,7 @@ 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_WITH_SHARED_OPENMP "Build with shared OpenMP library. Only macos build supports OFF." ON) option(BUILD_STATIC_LIB "Build static library" OFF) option(BUILD_DEPRECATED_CLI "Build the deprecated command line interface" OFF) option(FORCE_SHARED_CRT "Build with dynamic CRT on Windows (/MD)" OFF) @@ -260,6 +261,9 @@ endif() # -- OpenMP include(cmake/FindOpenMPMacOS.cmake) if(USE_OPENMP) + if(NOT APPLE AND NOT BUILD_WITH_SHARED_OPENMP) + message(FATAL_ERROR "Static OpenMP (BUILD_WITH_SHARED_OPENMP=OFF) is only supported on macOS.") + endif() if(APPLE) find_openmp_macos() else() @@ -397,7 +401,7 @@ if(JVM_BINDINGS) xgboost_target_defs(xgboost4j) endif() -if(USE_OPENMP AND APPLE AND NOT BUILD_STATIC_LIB) +if(USE_OPENMP AND APPLE AND NOT BUILD_STATIC_LIB AND BUILD_WITH_SHARED_OPENMP) patch_openmp_path_macos(xgboost libxgboost) endif() diff --git a/cmake/FindOpenMPMacOS.cmake b/cmake/FindOpenMPMacOS.cmake index 416a9b8cf1bc..672822727373 100644 --- a/cmake/FindOpenMPMacOS.cmake +++ b/cmake/FindOpenMPMacOS.cmake @@ -3,26 +3,69 @@ # lint_cmake: -package/consistency +function(_is_clang result_out) + if (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") + set(${result_out} TRUE PARENT_SCOPE) + else() + set(${result_out} FALSE PARENT_SCOPE) + endif() +endfunction() + 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. + + _is_clang(_using_clang) + if(NOT _using_clang) + find_package(OpenMP REQUIRED C CXX) + return() + endif() + + if(NOT BUILD_WITH_SHARED_OPENMP) 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") + OUTPUT_VARIABLE HOMEBREW_LIBOMP_PREFIX + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE BREW_RESULT) + # Static linking with libomp.a eliminates runtime dependency on libomp.dylib. + if(BREW_RESULT EQUAL 0 AND EXISTS "${HOMEBREW_LIBOMP_PREFIX}/lib/libomp.a") + set(_omp_static_lib "${HOMEBREW_LIBOMP_PREFIX}/lib/libomp.a") + set(_omp_include_dir "${HOMEBREW_LIBOMP_PREFIX}/include") + elseif(EXISTS "/opt/local/lib/libomp/libomp.a") + # MacPorts + set(_omp_static_lib "/opt/local/lib/libomp/libomp.a") + set(_omp_include_dir "/opt/local/include/libomp") + else() + message(FATAL_ERROR "USE_OPENMP_STATIC=ON but libomp.a not found. " + "Install via: brew install libomp") + endif() + + message(STATUS "Found static OpenMP: ${_omp_static_lib}") + set(OpenMP_C_FLAGS "-Xpreprocessor -fopenmp -I${_omp_include_dir}") + set(OpenMP_CXX_FLAGS "-Xpreprocessor -fopenmp -I${_omp_include_dir}") set(OpenMP_C_LIB_NAMES omp) set(OpenMP_CXX_LIB_NAMES omp) - set(OpenMP_omp_LIBRARY ${HOMEBREW_LIBOMP_PREFIX}/lib/libomp.dylib) + set(OpenMP_omp_LIBRARY "${_omp_static_lib}") find_package(OpenMP REQUIRED) + else() + # Dynamic linking (default) + 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() endif() endmacro() diff --git a/ops/pipeline/build-python-wheels-macos.sh b/ops/pipeline/build-python-wheels-macos.sh index 9c04032d8587..c15ff9db20fe 100755 --- a/ops/pipeline/build-python-wheels-macos.sh +++ b/ops/pipeline/build-python-wheels-macos.sh @@ -31,13 +31,15 @@ if [[ "$platform_id" == macosx_* ]]; then export CIBW_ARCHS=${cibw_archs} export CIBW_TEST_SKIP='*-macosx_arm64' export CIBW_BUILD_VERBOSITY=3 + # Use static libomp.a to eliminate runtime dependency on libomp.dylib + export CIBW_CONFIG_SETTINGS="build_with_shared_openmp=false" else echo "Platform not supported: $platform_id" exit 2 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" +export CIBW_REPAIR_WHEEL_COMMAND_MACOS="delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}" + python -m pip install cibuildwheel python -m cibuildwheel python-package --output-dir wheelhouse diff --git a/python-package/packager/build_config.py b/python-package/packager/build_config.py index 8a4347a16ace..c147a6291552 100644 --- a/python-package/packager/build_config.py +++ b/python-package/packager/build_config.py @@ -22,6 +22,8 @@ class BuildConfiguration: # pylint: disable=R0902 plugin_federated: bool = False # Whether to enable rmm support plugin_rmm: bool = False + # Whether to use shared OpenMP runtime on macOS (False = static libomp.a) + build_with_shared_openmp: bool = True # Special option: See explanation below use_system_libxgboost: bool = False diff --git a/src/common/io.cc b/src/common/io.cc index 0b2f34b44a30..d6726e013453 100644 --- a/src/common/io.cc +++ b/src/common/io.cc @@ -421,6 +421,7 @@ AlignedMemWriteStream::~AlignedMemWriteStream() = default; return static_cast(status.ullTotalPhys); #else LOG(FATAL) << "Not implemented"; + return 0; #endif // defined(__linux__) } } // namespace xgboost::common From 7d3af9dea2496b43e558d4939d02bb9683121a5c Mon Sep 17 00:00:00 2001 From: Jiaming Yuan Date: Wed, 4 Feb 2026 17:47:30 +0800 Subject: [PATCH 2/6] retention. --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 502a58d33a81..1f062c46dd96 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -562,7 +562,7 @@ jobs: with: name: python-wheel-${{ matrix.platform_id }} path: wheelhouse/*.whl - retention-days: 1 + retention-days: 2 - name: Upload Python wheel to S3 if: github.ref == 'refs/heads/master' || contains(github.ref, 'refs/heads/release_') run: | From 071f0a94f7fe256344ca1bb53ec5153d21bba444 Mon Sep 17 00:00:00 2001 From: Jiaming Yuan Date: Wed, 4 Feb 2026 17:50:02 +0800 Subject: [PATCH 3/6] pre-commit. --- cmake/FindOpenMPMacOS.cmake | 6 +++--- src/common/io.cc | 30 +++++++++++++++--------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/cmake/FindOpenMPMacOS.cmake b/cmake/FindOpenMPMacOS.cmake index 672822727373..fba47fbe61db 100644 --- a/cmake/FindOpenMPMacOS.cmake +++ b/cmake/FindOpenMPMacOS.cmake @@ -4,7 +4,7 @@ # lint_cmake: -package/consistency function(_is_clang result_out) - if (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") + if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") set(${result_out} TRUE PARENT_SCOPE) else() set(${result_out} FALSE PARENT_SCOPE) @@ -55,8 +55,8 @@ macro(find_openmp_macos) # 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) + OUTPUT_VARIABLE HOMEBREW_LIBOMP_PREFIX + OUTPUT_STRIP_TRAILING_WHITESPACE) set(OpenMP_C_FLAGS "-Xpreprocessor -fopenmp -I${HOMEBREW_LIBOMP_PREFIX}/include") set(OpenMP_CXX_FLAGS diff --git a/src/common/io.cc b/src/common/io.cc index d6726e013453..d75120fcb18b 100644 --- a/src/common/io.cc +++ b/src/common/io.cc @@ -20,23 +20,23 @@ #endif // defined(__unix__) || defined(__APPLE__) -#include // for copy, transform -#include // for tolower -#include // for size_t -#include // for int32_t, uint32_t -#include // for fread, fseek -#include // for memcpy -#include // for filesystem, weakly_canonical -#include // for ifstream -#include // for distance -#include // for unique_ptr, make_unique -#include // for string -#include // for move -#include // for vector +#include // for copy, transform +#include // for tolower +#include // for size_t +#include // for int32_t, uint32_t +#include // for fread, fseek +#include // for memcpy +#include // for filesystem, weakly_canonical +#include // for ifstream +#include // for distance +#include // for unique_ptr, make_unique +#include // for string +#include // for move +#include // for vector #include "io.h" -#include "xgboost/logging.h" // for CHECK_LE -#include "xgboost/string_view.h" // for StringView +#include "xgboost/logging.h" // for CHECK_LE +#include "xgboost/string_view.h" // for StringView #if !defined(__linux__) && !defined(__GLIBC__) && !defined(xgboost_IS_WIN) #include // for numeric_limits From 34c21a844c9257d665217a14637468eb31d6b35d Mon Sep 17 00:00:00 2001 From: Jiaming Yuan Date: Wed, 4 Feb 2026 22:35:54 +0800 Subject: [PATCH 4/6] bump deployment target. --- ops/pipeline/build-python-wheels-macos.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ops/pipeline/build-python-wheels-macos.sh b/ops/pipeline/build-python-wheels-macos.sh index c15ff9db20fe..203dc9f5197a 100755 --- a/ops/pipeline/build-python-wheels-macos.sh +++ b/ops/pipeline/build-python-wheels-macos.sh @@ -16,12 +16,12 @@ if [[ "$platform_id" == macosx_* ]]; then # MacOS, Apple Silicon cpython_ver=310 cibw_archs=arm64 - export MACOSX_DEPLOYMENT_TARGET=12.0 + export MACOSX_DEPLOYMENT_TARGET=14.0 elif [[ "$platform_id" == macosx_x86_64 ]]; then # MacOS, Intel cpython_ver=310 cibw_archs=x86_64 - export MACOSX_DEPLOYMENT_TARGET=10.15 + export MACOSX_DEPLOYMENT_TARGET=14.0 else echo "Platform not supported: $platform_id" exit 3 From 54c4efb051214164b48fe9e5a7fdcb34b718e5f2 Mon Sep 17 00:00:00 2001 From: Jiaming Yuan Date: Thu, 5 Feb 2026 06:29:10 +0800 Subject: [PATCH 5/6] Update cmake/FindOpenMPMacOS.cmake Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- cmake/FindOpenMPMacOS.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/FindOpenMPMacOS.cmake b/cmake/FindOpenMPMacOS.cmake index fba47fbe61db..19b5da9edd0c 100644 --- a/cmake/FindOpenMPMacOS.cmake +++ b/cmake/FindOpenMPMacOS.cmake @@ -36,7 +36,7 @@ macro(find_openmp_macos) set(_omp_static_lib "/opt/local/lib/libomp/libomp.a") set(_omp_include_dir "/opt/local/include/libomp") else() - message(FATAL_ERROR "USE_OPENMP_STATIC=ON but libomp.a not found. " + message(FATAL_ERROR "BUILD_WITH_SHARED_OPENMP=OFF (static OpenMP) but libomp.a not found. " "Install via: brew install libomp") endif() From 8517443fc4a99f15899c0561a1b9a5618069f70b Mon Sep 17 00:00:00 2001 From: Jiaming Yuan Date: Thu, 5 Feb 2026 06:40:24 +0800 Subject: [PATCH 6/6] notation. --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1f062c46dd96..973f146ce263 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -name: XGBoost CI +name: Main on: [push, pull_request] @@ -575,7 +575,7 @@ jobs: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY_IAM_S3_UPLOADER }} test-cross-platform-inference: - name: Cross-platform inference test (macOS Apple Silicon) + name: Cross-platform inference test needs: [test-python-wheel-gpu, python-wheels-macos] runs-on: ${{ matrix.os }} strategy: