diff --git a/.github/workflows/nonvendored_wheels.yml b/.github/workflows/nonvendored_wheels.yml index bfee453..57f0272 100644 --- a/.github/workflows/nonvendored_wheels.yml +++ b/.github/workflows/nonvendored_wheels.yml @@ -49,10 +49,13 @@ jobs: python-version: "3.10" - name: Install cibuildwheel - run: python -m pip install cibuildwheel==2.18.0 + run: python -m pip install cibuildwheel==2.21.3 - name: Build wheels run: python -m cibuildwheel --output-dir wheelhouse + env: + CIBW_REPAIR_WHEEL_COMMAND_MACOS: "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}" + CIBW_ENVIRONMENT_MACOS: MACOSX_DEPLOYMENT_TARGET=12.0 CMAKE_ARGS="-DSDTN_ENABLE_ARCH_FLAGS=OFF -DSDTN_DISABLE_OPENMP=ON" - name: Verify clean directory run: git diff --exit-code diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 92b6dc9..96731e3 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -59,7 +59,7 @@ jobs: - name: Clone OpenMP repo if: matrix.os == 'macos-latest' && steps.clone-openmp.outputs.cache-hit != 'true' run: | - git clone --depth 1 --branch llvmorg-17.0.6 https://github.com/llvm/llvm-project + git clone --depth 1 --branch llvmorg-19.1.1 https://github.com/llvm/llvm-project - name: Build OpenMP @@ -81,7 +81,7 @@ jobs: cmake --build openmp_build --target install --config Release - name: Install cibuildwheel - run: python -m pip install cibuildwheel==2.21.2 + run: python -m pip install cibuildwheel==2.21.3 - name: Build wheels run: python -m cibuildwheel --output-dir wheelhouse @@ -90,7 +90,7 @@ jobs: CIBW_TEST_COMMAND: pytest {project}/tests && python -c "from sparse_dot_topn import _has_openmp_support;assert _has_openmp_support" # only build for arm; x86_64 wheels are build seperately CIBW_ARCHS_MACOS: "arm64" - CIBW_ENVIRONMENT_MACOS: PATH="$HOME/.local/:$PATH" MACOSX_DEPLOYMENT_TARGET="12.0" DYLD_LIBRARY_PATH="$HOME/.local/libomp/lib" CMAKE_ARGS="-DSDTN_ENABLE_ARCH_FLAGS=OFF -DSDTN_ENABLE_OPENMP=ON -DSDTN_DISABLE_OPENMP=OFF -DOpenMP_ROOT=$HOME/.local/libomp" + CIBW_ENVIRONMENT_MACOS: PATH="$HOME/.local/:$PATH" MACOSX_DEPLOYMENT_TARGET=12.0 DYLD_LIBRARY_PATH="$HOME/.local/libomp/lib" CMAKE_ARGS="-DSDTN_ENABLE_ARCH_FLAGS=OFF -DSDTN_VENDOR_OPENMP=ON -DSDTN_ENABLE_OPENMP=ON -DSDTN_DISABLE_OPENMP=OFF -DOpenMP_ROOT=$HOME/.local/libomp" - name: Verify clean directory run: git diff --exit-code @@ -123,7 +123,7 @@ jobs: - name: Clone OpenMP repo if: steps.clone-openmp.outputs.cache-hit != 'true' run: | - git clone --depth 1 --branch llvmorg-17.0.6 https://github.com/llvm/llvm-project + git clone --depth 1 --branch llvmorg-19.1.1 https://github.com/llvm/llvm-project - name: Build OpenMP shell: bash @@ -141,11 +141,11 @@ jobs: -DCMAKE_INSTALL_PREFIX="/usr/local" cmake --build openmp_build --target install --config Release - - uses: pypa/cibuildwheel@v2.21.2 + - uses: pypa/cibuildwheel@v2.21.3 env: # only build for x86_64, arm wheels are build seperately CIBW_ARCHS: "x86_64" - CIBW_ENVIRONMENT_MACOS: MACOSX_DEPLOYMENT_TARGET="12.0" CMAKE_ARGS="-DSDTN_ENABLE_ARCH_FLAGS=OFF -DSDTN_ENABLE_OPENMP=ON -DSDTN_DISABLE_OPENMP=OFF -DOpenMP_ROOT=/usr/local" + CIBW_ENVIRONMENT_MACOS: MACOSX_DEPLOYMENT_TARGET=12.0 CMAKE_ARGS="-DSDTN_ENABLE_ARCH_FLAGS=OFF -DSDTN_VENDOR_OPENMP=ON -DSDTN_ENABLE_OPENMP=ON -DSDTN_DISABLE_OPENMP=OFF -DOpenMP_ROOT=/usr/local" - name: Verify clean directory run: git diff --exit-code diff --git a/.gitignore b/.gitignore index ae27ac4..ad1e893 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ mprofile_*.dat .python-version .pymon .cache +*.dylib diff --git a/CHANGES.md b/CHANGES.md index f5b9a21..0ee982d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Release history: +## v1.1.5 + +### Changes + +BLD: Set rpath for libomp on MacOS to fix compatibility with latest XGBoost version + ## v1.1.4 ### Changes diff --git a/CMakeLists.txt b/CMakeLists.txt index 49fe7ee..4f55c9a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,10 @@ if (NOT SKBUILD) after editing C++ files.") endif() +if(SDTN_MBUILD) + set(CMAKE_INSTALL_PREFIX "${PROJECT_SOURCE_DIR}/src") +endif() + set(SDTN_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/src/sparse_dot_topn_core/include") set(SDTN_SRC_PREF "${PROJECT_SOURCE_DIR}/src/sparse_dot_topn_core/src/") set(SDTN_SRC_FILES @@ -49,6 +53,7 @@ include(FindDependencies) include(ConfigureBuildType) # -- target nanobind_add_module(_sparse_dot_topn_core STABLE_ABI NB_STATIC LTO NOMINSIZE ${SDTN_SRC_FILES}) + include(ConfigureTarget) -include(InstallTarget) +install(TARGETS _sparse_dot_topn_core LIBRARY DESTINATION "${PROJECT_NAME}/lib") include(CleanUp) diff --git a/pyproject.toml b/pyproject.toml index f269425..ce79190 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build" [project] name = "sparse_dot_topn" -version = "1.1.4" +version = "1.1.5" description = "This package boosts a sparse matrix multiplication followed by selecting the top-n multiplication" readme = "README.md" requires-python = ">=3.8" @@ -57,7 +57,7 @@ SDTN_ENABLE_ARCH_FLAGS = true [tool.cibuildwheel] archs = ["auto64"] skip = ["*-win32", "*-manylinux_i686", "*-musllinux_x86_64"] -build = ["cp38-*", "cp39-*", "cp310-*", "cp311-*", "cp312-*"] +build = ["cp38-*", "cp39-*", "cp310-*", "cp311-*", "cp312-*", "cp313-*"] # make sure to build generic wheels environment = {CMAKE_ARGS="-DSDTN_ENABLE_ARCH_FLAGS=OFF -DSDTN_DISABLE_OPENMP=ON"} @@ -77,8 +77,7 @@ repair-wheel-command = "auditwheel repair -w {dest_dir} {wheel}" [tool.cibuildwheel.macos] archs = ["x86_64", "arm64"] # Needed for full C++17 support -environment = { MACOSX_DEPLOYMENT_TARGET = "12.0", CMAKE_ARGS="-DSDTN_ENABLE_ARCH_FLAGS=OFF -DSDTN_DISABLE_OPENMP=ON" } -repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}" +repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel} --exclude libomp.dylib" [tool.cibuildwheel.windows] before-build = "pip install delvewheel" diff --git a/src/sparse_dot_topn_core/cmake/ConfigureTarget.cmake b/src/sparse_dot_topn_core/cmake/ConfigureTarget.cmake index 1e3f21f..5422ab8 100644 --- a/src/sparse_dot_topn_core/cmake/ConfigureTarget.cmake +++ b/src/sparse_dot_topn_core/cmake/ConfigureTarget.cmake @@ -1,14 +1,86 @@ target_include_directories(_sparse_dot_topn_core PUBLIC ${SDTN_INCLUDE_DIR}) if(OpenMP_CXX_FOUND) - target_link_libraries(_sparse_dot_topn_core PUBLIC OpenMP::OpenMP_CXX) - target_compile_definitions(_sparse_dot_topn_core PRIVATE SDTN_OMP_ENABLED=TRUE) + target_link_libraries(_sparse_dot_topn_core PUBLIC OpenMP::OpenMP_CXX) + target_compile_definitions(_sparse_dot_topn_core PRIVATE SDTN_OMP_ENABLED=TRUE) + if(APPLE) + # Modified implementation from LightGBM written by JamesLamb + # see https://github.com/microsoft/LightGBM/pull/6391 + # store path to libomp found at build time in a variable + get_target_property( + OpenMP_LIBRARY_LOCATION + OpenMP::OpenMP_CXX + INTERFACE_LINK_LIBRARIES + ) + # get just the filename of that path + # (to deal with the possibility that it might be 'libomp.dylib' or 'libgomp.dylib' or 'libiomp.dylib') + get_filename_component( + OpenMP_LIBRARY_NAME + ${OpenMP_LIBRARY_LOCATION} + NAME + ) + # get directory of that path + 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 _sparse_dot_topn_core + POST_BUILD + COMMAND + install_name_tool + -change + ${OpenMP_LIBRARY_LOCATION} + "@rpath/${OpenMP_LIBRARY_NAME}" + $ + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Replacing hard-coded OpenMP install_name with '@rpath/${OpenMP_LIBRARY_NAME}'..." + ) + if (SDTN_VENDOR_OPENMP) + set(OpenMP_target_location ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/.dylibs/${OpenMP_LIBRARY_NAME}) + + message(STATUS "Copying ${OpenMP_LIBRARY_NAME} to ${OpenMP_target_location}") + add_custom_command( + TARGET _sparse_dot_topn_core + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${OpenMP_LIBRARY_LOCATION} + ${OpenMP_target_location} + ) + # add RPATH entries to ensure the loader looks in the following, in the following order: + # - @loader_path/../.dylibs/ the dylib we're shipping alongside + set_target_properties( + _sparse_dot_topn_core + PROPERTIES + BUILD_WITH_INSTALL_RPATH TRUE + INSTALL_RPATH "@loader_path/../.dylibs/" + INSTALL_RPATH_USE_LINK_PATH FALSE + ) + else() + # add RPATH entries to ensure the loader looks in the following, in the following order: + # - ${OpenMP_LIBRARY_DIR} (wherever find_package(OpenMP) found OpenMP at build time) + # - /opt/homebrew/opt/libomp/lib (where 'brew install' / 'brew link' puts libomp.dylib) + set_target_properties( + _sparse_dot_topn_core + PROPERTIES + BUILD_WITH_INSTALL_RPATH TRUE + INSTALL_RPATH "${OpenMP_LIBRARY_DIR};/opt/homebrew/opt/libomp/lib" + INSTALL_RPATH_USE_LINK_PATH FALSE + ) + + endif() + endif() endif() target_compile_definitions(_sparse_dot_topn_core PRIVATE VERSION_INFO=${SKBUILD_PROJECT_VERSION}) # -- Optional if(SDTN_ENABLE_DEVMODE) - target_compile_options(_sparse_dot_topn_core PRIVATE ${SDTN_DEVMODE_OPTIONS}) + target_compile_options(_sparse_dot_topn_core PRIVATE ${SDTN_DEVMODE_OPTIONS}) endif() # -- Options & Properties @@ -19,41 +91,41 @@ set_property(TARGET _sparse_dot_topn_core PROPERTY POSITION_INDEPENDENT_CODE ON) include(CheckCXXCompilerFlag) function(check_cxx_support FLAG DEST) - string(SUBSTRING ${FLAG} 1 -1 STRIPPED_FLAG) - string(REGEX REPLACE "=" "_" STRIPPED_FLAG ${STRIPPED_FLAG}) - string(TOUPPER ${STRIPPED_FLAG} STRIPPED_FLAG) - set(RES_VAR "${STRIPPED_FLAG}_SUPPORTED") - check_cxx_compiler_flag("${FLAG}" ${RES_VAR}) - if(${RES_VAR}) - set(${DEST} "${${DEST}} ${FLAG}" PARENT_SCOPE) - endif() + string(SUBSTRING ${FLAG} 1 -1 STRIPPED_FLAG) + string(REGEX REPLACE "=" "_" STRIPPED_FLAG ${STRIPPED_FLAG}) + string(TOUPPER ${STRIPPED_FLAG} STRIPPED_FLAG) + set(RES_VAR "${STRIPPED_FLAG}_SUPPORTED") + check_cxx_compiler_flag("${FLAG}" ${RES_VAR}) + if(${RES_VAR}) + set(${DEST} "${${DEST}} ${FLAG}" PARENT_SCOPE) + endif() endfunction() # -- Compiler Flags if (SDTN_ENABLE_ARCH_FLAGS AND "${CMAKE_CXX_FLAGS}" STREQUAL "${CMAKE_CXX_FLAGS_DEFAULT}") - set(SDTN_ARCHITECTURE_FLAGS "") - if (APPLE AND (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "arm64")) - # see https://github.com/google/highway/issues/745 - check_cxx_support("-march=native" SDTN_ARCHITECTURE_FLAGS) - else() - include(FindSse) - include(FindAvx) - SDTN_CHECK_FOR_SSE() - add_definitions(${SSE_DEFINITIONS}) - SDTN_CHECK_FOR_AVX() - string(APPEND SDTN_ARCHITECTURE_FLAGS "${SSE_FLAGS} ${AVX_FLAGS}") - endif() + set(SDTN_ARCHITECTURE_FLAGS "") + if (APPLE AND (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "arm64")) + # see https://github.com/google/highway/issues/745 + check_cxx_support("-march=native" SDTN_ARCHITECTURE_FLAGS) + else() + include(FindSse) + include(FindAvx) + SDTN_CHECK_FOR_SSE() + add_definitions(${SSE_DEFINITIONS}) + SDTN_CHECK_FOR_AVX() + string(APPEND SDTN_ARCHITECTURE_FLAGS "${SSE_FLAGS} ${AVX_FLAGS}") + endif() - if (NOT ${SDTN_ARCHITECTURE_FLAGS} STREQUAL "") - string(STRIP ${SDTN_ARCHITECTURE_FLAGS} SDTN_ARCHITECTURE_FLAGS) - message(STATUS "sparse-dot-topn | Enabled arch flags: ${SDTN_ARCHITECTURE_FLAGS}") - if (MSVC) - separate_arguments(SDTN_ARCHITECTURE_FLAGS WINDOWS_COMMAND "${SDTN_ARCHITECTURE_FLAGS}") - else() - separate_arguments(SDTN_ARCHITECTURE_FLAGS UNIX_COMMAND "${SDTN_ARCHITECTURE_FLAGS}") - endif() - target_compile_options(_sparse_dot_topn_core PRIVATE $<$:${SDTN_ARCHITECTURE_FLAGS}>) + if (NOT ${SDTN_ARCHITECTURE_FLAGS} STREQUAL "") + string(STRIP ${SDTN_ARCHITECTURE_FLAGS} SDTN_ARCHITECTURE_FLAGS) + message(STATUS "sparse-dot-topn | Enabled arch flags: ${SDTN_ARCHITECTURE_FLAGS}") + if (MSVC) + separate_arguments(SDTN_ARCHITECTURE_FLAGS WINDOWS_COMMAND "${SDTN_ARCHITECTURE_FLAGS}") else() - message(STATUS "sparse-dot-topn | Architecture flags enabled but no valid flags were found") + separate_arguments(SDTN_ARCHITECTURE_FLAGS UNIX_COMMAND "${SDTN_ARCHITECTURE_FLAGS}") endif() + target_compile_options(_sparse_dot_topn_core PRIVATE $<$:${SDTN_ARCHITECTURE_FLAGS}>) + else() + message(STATUS "sparse-dot-topn | Architecture flags enabled but no valid flags were found") + endif() endif() diff --git a/src/sparse_dot_topn_core/cmake/FindAvx.cmake b/src/sparse_dot_topn_core/cmake/FindAvx.cmake index 6cebabb..c122d83 100644 --- a/src/sparse_dot_topn_core/cmake/FindAvx.cmake +++ b/src/sparse_dot_topn_core/cmake/FindAvx.cmake @@ -1,4 +1,4 @@ -############################################################################### +# ############################################################################## # Check for the presence of AVX and figure out the flags to use for it. # This routine is a modified version of `PCL_CHECK_FOR_AVX` # Reference: https://github.com/PointCloudLibrary/pcl/blob/master/cmake/pcl_find_avx.cmake diff --git a/src/sparse_dot_topn_core/cmake/FindSse.cmake b/src/sparse_dot_topn_core/cmake/FindSse.cmake index 0832aa4..2ebed97 100644 --- a/src/sparse_dot_topn_core/cmake/FindSse.cmake +++ b/src/sparse_dot_topn_core/cmake/FindSse.cmake @@ -1,4 +1,4 @@ -############################################################################### +# ############################################################################## # Check for the presence of SSE and figure out the flags to use for it. # This routine is a modified version of `PCL_CHECK_FOR_SSE` # https://github.com/PointCloudLibrary/pcl/blob/master/cmake/pcl_find_sse.cmake diff --git a/src/sparse_dot_topn_core/cmake/InstallTarget.cmake b/src/sparse_dot_topn_core/cmake/InstallTarget.cmake deleted file mode 100644 index 1282560..0000000 --- a/src/sparse_dot_topn_core/cmake/InstallTarget.cmake +++ /dev/null @@ -1,5 +0,0 @@ -if(SDTN_MBUILD) - set(CMAKE_INSTALL_PREFIX "${PROJECT_SOURCE_DIR}/src") -endif() - -install(TARGETS _sparse_dot_topn_core LIBRARY DESTINATION "${PROJECT_NAME}/lib")