Merge pull request #2925 from njzjz-bot/master

ci: add cibuildwheel builds
This commit is contained in:
Geoff Hutchison authored and GitHub committed 2026-05-20 08:48:41 -04:00
commit 05e0e429c9
8 files changed
+404 -53

No files matched your search

+4 -3
View File
@@ -9,9 +9,10 @@ jobs:
with:
python-version: '3.10'
- run: pip install --upgrade pip wheel
- run: pip install --upgrade setuptools
- run: pip install bandit black codespell flake8 flake8-bugbear
flake8-comprehensions isort mypy pytest pyupgrade safety
- run: pip install --upgrade "setuptools<81"
- run: pip install bandit "black>=26.3.1" codespell flake8 flake8-bugbear
flake8-comprehensions isort mypy "pytest>=9.0.3" pyupgrade "safety<3"
- run: safety generate policy_file --path .
- run: bandit --recursive --skip B101,B314,B404,B405,B602,B603,B607 .
- run: black --check . || true
- run: codespell --ignore-words-list="anumber,ba,nd,ser,te,tring" || true # --skip="*.css,*.js,*.lock"
+142
View File
@@ -0,0 +1,142 @@
name: Build wheels
on:
pull_request:
push:
branches:
- master
tags:
- 'v*'
workflow_dispatch:
jobs:
build_wheels:
name: Build wheels for cp${{ matrix.python }}-${{ matrix.platform_id }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
python: '310'
platform_id: manylinux_x86_64
- os: ubuntu-latest
python: '311'
platform_id: manylinux_x86_64
- os: ubuntu-latest
python: '312'
platform_id: manylinux_x86_64
- os: ubuntu-latest
python: '313'
platform_id: manylinux_x86_64
- os: ubuntu-latest
python: '314'
platform_id: manylinux_x86_64
- os: ubuntu-24.04-arm
python: '310'
platform_id: manylinux_aarch64
- os: ubuntu-24.04-arm
python: '311'
platform_id: manylinux_aarch64
- os: ubuntu-24.04-arm
python: '312'
platform_id: manylinux_aarch64
- os: ubuntu-24.04-arm
python: '313'
platform_id: manylinux_aarch64
- os: ubuntu-24.04-arm
python: '314'
platform_id: manylinux_aarch64
- os: macos-15-intel
python: '310'
platform_id: macosx_x86_64
- os: macos-15-intel
python: '311'
platform_id: macosx_x86_64
- os: macos-15-intel
python: '312'
platform_id: macosx_x86_64
- os: macos-15-intel
python: '313'
platform_id: macosx_x86_64
- os: macos-15-intel
python: '314'
platform_id: macosx_x86_64
- os: macos-14
python: '310'
platform_id: macosx_arm64
- os: macos-14
python: '311'
platform_id: macosx_arm64
- os: macos-14
python: '312'
platform_id: macosx_arm64
- os: macos-14
python: '313'
platform_id: macosx_arm64
- os: macos-14
python: '314'
platform_id: macosx_arm64
- os: windows-2025
python: '310'
platform_id: win_amd64
- os: windows-2025
python: '311'
platform_id: win_amd64
- os: windows-2025
python: '312'
platform_id: win_amd64
- os: windows-2025
python: '313'
platform_id: win_amd64
- os: windows-2025
python: '314'
platform_id: win_amd64
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: lukka/get-cmake@latest
- name: Build wheels
uses: pypa/cibuildwheel@v3.4
env:
CIBW_BUILD_VERBOSITY: 1
CIBW_ARCHS: all
CIBW_BUILD: cp${{ matrix.python }}-${{ matrix.platform_id }}
- uses: actions/upload-artifact@v6
with:
name: cibw-wheels-cp${{ matrix.python }}-${{ matrix.platform_id }}-${{ strategy.job-index }}
path: ./wheelhouse/*.whl
build_sdist:
name: Build source distribution
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: actions/setup-python@v6
with:
python-version: '3.14'
- run: python -m pip install build
- name: Build sdist
run: python -m build --sdist
- uses: actions/upload-artifact@v6
with:
name: cibw-sdist
path: dist/*.tar.gz
upload_pypi:
needs: [build_wheels, build_sdist]
runs-on: ubuntu-latest
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v7
with:
pattern: cibw-*
path: dist
merge-multiple: true
- uses: pypa/gh-action-pypi-publish@release/v1
+47 -2
View File
@@ -390,7 +390,25 @@ endif()
# Some rpath handling for Linux and Mac
if(UNIX AND BUILD_SHARED)
if(APPLE)
if(BUILD_BY_PIP)
# Wheels are installed under a relocatable package directory
# (openbabel/). Avoid embedding the temporary scikit-build install
# prefix, otherwise auditwheel/delocate cannot find libraries after the
# wheel is unpacked for repair. The different depths cover the Python
# extension (openbabel/), command-line tools (openbabel/bin), libraries
# (openbabel/lib), and plugins (openbabel/lib/openbabel/<version>).
if(APPLE)
set(CMAKE_INSTALL_NAME_DIR "@rpath")
set(CMAKE_INSTALL_RPATH
"@loader_path;@loader_path/lib;@loader_path/../lib;@loader_path/..;@loader_path/../..")
else()
set(CMAKE_INSTALL_RPATH
"$ORIGIN;$ORIGIN/lib;$ORIGIN/../lib;$ORIGIN/..;$ORIGIN/../..")
endif()
set(CMAKE_SKIP_BUILD_RPATH FALSE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
elseif(APPLE)
set(CMAKE_INSTALL_NAME_DIR ${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR})
else()
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}")
@@ -415,6 +433,22 @@ endif()
file(GLOB_RECURSE headers include/openbabel/*.h)
if(BUILD_BY_PIP)
find_package(Python COMPONENTS Interpreter)
if(Python_Interpreter_FOUND)
execute_process(
COMMAND ${Python_EXECUTABLE} -m cmeel cmake
OUTPUT_VARIABLE CMEEL_PREFIX_PATH
RESULT_VARIABLE CMEEL_RESULT
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
if(CMEEL_RESULT EQUAL 0 AND NOT "${CMEEL_PREFIX_PATH}" STREQUAL "")
list(PREPEND CMAKE_PREFIX_PATH "${CMEEL_PREFIX_PATH}")
endif()
endif()
endif()
# Detect Eigen3 before configure_file so HAVE_EIGEN3 lands in babelconfig.h
find_package(Eigen3 CONFIG)
if(TARGET Eigen3::Eigen)
@@ -606,7 +640,12 @@ if(WITH_JSON)
endif()
endif()
find_package(Cairo)
if(NOT BUILD_BY_PIP OR BUILD_WHEEL_CAIRO)
find_package(Cairo)
else()
set(CAIRO_FOUND FALSE)
message(STATUS "Skipping Cairo for wheel builds. Set BUILD_WHEEL_CAIRO=ON to enable PNG output support.")
endif()
if(CAIRO_FOUND)
message(STATUS "Cairo found. PNG output will be supported.")
else(CAIRO_FOUND)
@@ -685,6 +724,12 @@ option(PHP_BINDINGS "Build PHP bindings" OFF)
# Should Python bindings be built?
option(PYTHON_BINDINGS "Build Python bindings" OFF)
# Install Python bindings into the scikit-build wheel package directory when
# building through pip/cibuildwheel. Normal CMake installs keep using the
# interpreter platlib path.
option(BUILD_BY_PIP "Install Python bindings relative to CMAKE_INSTALL_PREFIX for wheel builds" OFF)
option(BUILD_WHEEL_CAIRO "Enable Cairo-backed PNG depiction support in wheel builds" OFF)
# Should Ruby bindings be built?
option(RUBY_BINDINGS "Build Ruby bindings" OFF)
+88
View File
@@ -0,0 +1,88 @@
[build-system]
requires = [
"scikit-build-core>=0.11",
"setuptools-scm",
"swig",
"cmeel-eigen",
]
build-backend = "scikit_build_core.build"
[project]
name = "openbabel"
dynamic = ["version"]
description = "Python interface to the Open Babel chemistry library"
readme = "scripts/python/README.rst"
requires-python = ">=3.10"
authors = [
{name = "Noel O'Boyle", email = "openbabel-discuss@lists.sourceforge.net"},
]
license = {text = "GPL-2.0-only"}
classifiers = [
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Environment :: Other Environment",
"Intended Audience :: Education",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: GNU General Public License (GPL)",
"Natural Language :: English",
"Operating System :: MacOS :: MacOS X",
"Operating System :: Microsoft :: Windows",
"Operating System :: POSIX",
"Operating System :: Unix",
"Programming Language :: C++",
"Programming Language :: Python",
"Topic :: Scientific/Engineering :: Bio-Informatics",
"Topic :: Scientific/Engineering :: Chemistry",
"Topic :: Software Development :: Libraries",
]
[project.urls]
Homepage = "https://openbabel.org/"
Repository = "https://github.com/openbabel/openbabel"
Documentation = "https://openbabel.org/docs/"
[project.scripts]
obabel = "openbabel.executable:obabel"
[tool.scikit-build]
minimum-version = "0.11"
cmake.version = ">=3.12"
sdist.include = ["scripts/openbabel-python.i", "scripts/stereo.i"]
wheel.install-dir = "openbabel"
metadata.version.provider = "scikit_build_core.metadata.setuptools_scm"
[tool.scikit-build.cmake.define]
CMAKE_BUILD_TYPE = "Release"
WITH_INCHI = "ON"
PYTHON_BINDINGS = "ON"
RUN_SWIG = "ON"
BUILD_BY_PIP = "ON"
ENABLE_TESTS = "OFF"
BUILD_GUI = "OFF"
CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY = "OFF"
BUILD_WHEEL_CAIRO = "OFF"
[tool.setuptools_scm]
[tool.cibuildwheel]
test-command = [
"python -c \"from openbabel import openbabel; conv = openbabel.OBConversion(); assert conv.SetInAndOutFormats('smi', 'can')\"",
"obabel -:C -ocan",
]
build = ["cp310-*", "cp311-*", "cp312-*", "cp313-*", "cp314-*"]
skip = ["*-win32", "*-manylinux_i686", "*-musllinux*", "cp314t-*"]
manylinux-x86_64-image = "manylinux2014"
manylinux-aarch64-image = "manylinux2014"
[tool.cibuildwheel.linux]
before-all = [
"yum install -y epel-release",
"yum install -y eigen3-devel libxml2-devel zlib-devel",
]
[tool.cibuildwheel.macos]
before-all = "brew install eigen"
[tool.cibuildwheel.windows]
before-build = "pip install delvewheel"
repair-wheel-command = "delvewheel repair -w {dest_dir} {wheel} --ignore-in-wheel"
+78 -33
View File
@@ -32,19 +32,34 @@ endif()
if (PYTHON_BINDINGS)
if (EXISTS ${openbabel_SOURCE_DIR}/scripts/python/openbabel/openbabel-python.cpp OR RUN_SWIG)
find_package(Python COMPONENTS Interpreter Development)
if (BUILD_BY_PIP)
find_package(Python COMPONENTS Interpreter Development.Module)
else()
find_package(Python COMPONENTS Interpreter Development)
endif()
if (NOT Python_Interpreter_FOUND)
message(STATUS "Python interpreter NOT found")
endif (NOT Python_Interpreter_FOUND)
if (NOT Python_Development_FOUND)
message(STATUS "Python libraries NOT found")
endif (NOT Python_Development_FOUND)
if (BUILD_BY_PIP)
if (NOT Python_Development.Module_FOUND)
message(STATUS "Python development module support NOT found")
endif (NOT Python_Development.Module_FOUND)
if(Python_Development_FOUND AND Python_Interpreter_FOUND)
set(DO_PYTHON_BINDINGS ON BOOL)
message(STATUS "Python bindings will be compiled")
endif(Python_Development_FOUND AND Python_Interpreter_FOUND)
if(Python_Development.Module_FOUND AND Python_Interpreter_FOUND)
set(DO_PYTHON_BINDINGS ON BOOL)
message(STATUS "Python bindings will be compiled")
endif(Python_Development.Module_FOUND AND Python_Interpreter_FOUND)
else()
if (NOT Python_Development_FOUND)
message(STATUS "Python libraries NOT found")
endif (NOT Python_Development_FOUND)
if(Python_Development_FOUND AND Python_Interpreter_FOUND)
set(DO_PYTHON_BINDINGS ON BOOL)
message(STATUS "Python bindings will be compiled")
endif(Python_Development_FOUND AND Python_Interpreter_FOUND)
endif()
else ()
message(STATUS "Warning: Python bindings NOT found. Generate using -DRUN_SWIG=ON.")
@@ -53,7 +68,11 @@ if (PYTHON_BINDINGS)
endif (PYTHON_BINDINGS)
if (DO_PYTHON_BINDINGS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations")
if(MSVC)
add_compile_options(/wd4996)
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations")
endif()
include_directories(
${Python_INCLUDE_DIRS}
)
@@ -78,8 +97,20 @@ if (DO_PYTHON_BINDINGS)
else()
target_link_libraries(bindings_python ${BABEL_LIBRARY})
endif()
elseif(BUILD_BY_PIP AND NOT WIN32)
# Python extension modules for Unix-like wheels should resolve Python
# symbols from the running interpreter. Linking libpython can leave a
# non-portable NEEDED entry for the build interpreter's libpython.
# Open Babel enables --no-undefined globally on Linux, so explicitly
# allow unresolved Python API symbols for this extension module.
set(PYTHON_EXTENSION_EXTRA_LINK_FLAGS "-Wl,--unresolved-symbols=ignore-all")
if(BINDINGS_ONLY)
target_link_libraries(bindings_python ${BABEL_SYSTEM_LIBRARY} ${PYTHON_EXTENSION_EXTRA_LINK_FLAGS})
else()
target_link_libraries(bindings_python ${BABEL_LIBRARY} ${PYTHON_EXTENSION_EXTRA_LINK_FLAGS})
endif()
else()
# Link against Python library on Linux and Windows
# Link against Python library on non-wheel Linux and Windows
if(BINDINGS_ONLY)
target_link_libraries(bindings_python ${Python_LIBRARIES} ${BABEL_SYSTEM_LIBRARY})
else()
@@ -93,58 +124,72 @@ if (DO_PYTHON_BINDINGS)
PREFIX ""
LIBRARY_OUTPUT_DIRECTORY "${openbabel_SOURCE_DIR}/scripts/python/openbabel/"
SUFFIX .so )
if( "${PYTHON_INSTDIR}" STREQUAL "" )
execute_process(
COMMAND
${Python_EXECUTABLE} -c "import sysconfig
print(sysconfig.get_path('platlib', vars={'base': '${CMAKE_INSTALL_PREFIX}', 'platbase': '${CMAKE_INSTALL_PREFIX}'}))"
OUTPUT_VARIABLE PYTHON_INSTDIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(BUILD_BY_PIP)
set(PYOBABEL_INSTDIR ".")
else()
if( "${PYTHON_INSTDIR}" STREQUAL "" )
execute_process(
COMMAND
${Python_EXECUTABLE} -c "import sysconfig; print(sysconfig.get_path('platlib', vars={'base': '${CMAKE_INSTALL_PREFIX}'}))"
OUTPUT_VARIABLE PYTHON_INSTDIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
endif()
set(PYOBABEL_INSTDIR ${PYTHON_INSTDIR}/openbabel)
endif()
if(NOT BINDINGS_ONLY)
add_dependencies(bindings_python openbabel)
endif()
install(TARGETS bindings_python
LIBRARY DESTINATION ${PYTHON_INSTDIR}/openbabel
LIBRARY DESTINATION ${PYOBABEL_INSTDIR}
COMPONENT bindings_python)
install(FILES ${openbabel_SOURCE_DIR}/scripts/python/openbabel/__init__.py
DESTINATION ${PYTHON_INSTDIR}/openbabel
DESTINATION ${PYOBABEL_INSTDIR}
COMPONENT bindings_python)
install(FILES ${openbabel_SOURCE_DIR}/scripts/python/openbabel/openbabel.py
DESTINATION ${PYTHON_INSTDIR}/openbabel
DESTINATION ${PYOBABEL_INSTDIR}
COMPONENT bindings_python)
install(FILES ${openbabel_SOURCE_DIR}/scripts/python/openbabel/pybel.py
DESTINATION ${PYTHON_INSTDIR}/openbabel
DESTINATION ${PYOBABEL_INSTDIR}
COMPONENT bindings_python)
install(FILES ${openbabel_SOURCE_DIR}/scripts/python/openbabel/executable.py
DESTINATION ${PYOBABEL_INSTDIR}
COMPONENT bindings_python)
else(NOT WIN32)
set_target_properties(bindings_python PROPERTIES
OUTPUT_NAME _openbabel
PREFIX ""
SUFFIX .pyd )
execute_process(
COMMAND
${Python_EXECUTABLE} -c "import sysconfig
print(sysconfig.get_path('platlib', vars={'base': '${CMAKE_INSTALL_PREFIX}', 'platbase': '${CMAKE_INSTALL_PREFIX}'}))"
OUTPUT_VARIABLE PYTHON_INSTDIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(BUILD_BY_PIP)
set(PYOBABEL_INSTDIR ".")
else()
execute_process(
COMMAND
${Python_EXECUTABLE} -c "import sysconfig; print(sysconfig.get_path('platlib', vars={'base': '${CMAKE_INSTALL_PREFIX}'}))"
OUTPUT_VARIABLE PYTHON_INSTDIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
set(PYOBABEL_INSTDIR ${PYTHON_INSTDIR}/openbabel)
endif()
if(NOT BINDINGS_ONLY)
add_dependencies(bindings_python openbabel)
endif()
install(TARGETS bindings_python
LIBRARY DESTINATION ${PYTHON_INSTDIR}/openbabel
LIBRARY DESTINATION ${PYOBABEL_INSTDIR}
COMPONENT bindings_python)
install(FILES ${openbabel_SOURCE_DIR}/scripts/python/openbabel/__init__.py
DESTINATION ${PYTHON_INSTDIR}/openbabel
DESTINATION ${PYOBABEL_INSTDIR}
COMPONENT bindings_python)
install(FILES ${openbabel_SOURCE_DIR}/scripts/python/openbabel/openbabel.py
DESTINATION ${PYTHON_INSTDIR}/openbabel
DESTINATION ${PYOBABEL_INSTDIR}
COMPONENT bindings_python)
install(FILES ${openbabel_SOURCE_DIR}/scripts/python/openbabel/pybel.py
DESTINATION ${PYTHON_INSTDIR}/openbabel
DESTINATION ${PYOBABEL_INSTDIR}
COMPONENT bindings_python)
install(FILES ${openbabel_SOURCE_DIR}/scripts/python/openbabel/executable.py
DESTINATION ${PYOBABEL_INSTDIR}
COMPONENT bindings_python)
add_custom_command(TARGET bindings_python POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${openbabel_SOURCE_DIR}/scripts/python/openbabel/openbabel.py ${openbabel_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}
+13 -1
View File
@@ -1,8 +1,20 @@
import os
import sys
import warnings
from . import openbabel
__version__ = "${BABEL_VERSION}"
base_dir = os.path.abspath(os.path.dirname(__file__))
if os.name == "nt":
dll_dir = os.path.join(base_dir, "bin")
if os.path.isdir(dll_dir):
os.add_dll_directory(dll_dir)
if "${BUILD_BY_PIP}" == "ON":
os.environ["BABEL_LIBDIR"] = os.path.join(base_dir, "lib", "openbabel", __version__)
os.environ["BABEL_DATADIR"] = os.path.join(base_dir, "share", "openbabel", __version__)
from . import openbabel
_thismodule = sys.modules[__name__]
+16
View File
@@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
import os
import subprocess
import sys
ROOT_DIR = os.path.dirname(__file__)
def _program(name, args):
return subprocess.call([os.path.join(ROOT_DIR, "bin", name)] + args, close_fds=False)
def obabel():
suffix = '.exe' if os.name == 'nt' else ''
raise SystemExit(_program('obabel' + suffix, sys.argv[1:]))
+16 -14
View File
@@ -52,20 +52,22 @@ endif(MSVC)
if(NOT MINIMAL_BUILD)
if(MSVC)
set(xmlsources "")
foreach(format ${formats_xml})
set(xmlsources ${xmlsources} xml/${format}.cpp)
endforeach(format)
add_library(formats_xml ${PLUGIN_TYPE} ${xmlsources} xml/xml.cpp)
target_link_libraries(formats_xml ${libs} ${LIBXML2_LIBRARIES} openbabel)
install(TARGETS formats_xml
RUNTIME DESTINATION ${BIN_INSTALL_DIR}
LIBRARY DESTINATION ${OB_PLUGIN_INSTALL_DIR}
ARCHIVE DESTINATION ${OB_PLUGIN_INSTALL_DIR})
set_target_properties(formats_xml PROPERTIES
OUTPUT_NAME formats_xml
PREFIX ""
SUFFIX ${MODULE_EXTENSION})
if(formats_xml)
set(xmlsources "")
foreach(format ${formats_xml})
set(xmlsources ${xmlsources} xml/${format}.cpp)
endforeach(format)
add_library(formats_xml ${PLUGIN_TYPE} ${xmlsources} xml/xml.cpp)
target_link_libraries(formats_xml ${libs} ${LIBXML2_LIBRARIES} openbabel)
install(TARGETS formats_xml
RUNTIME DESTINATION ${BIN_INSTALL_DIR}
LIBRARY DESTINATION ${OB_PLUGIN_INSTALL_DIR}
ARCHIVE DESTINATION ${OB_PLUGIN_INSTALL_DIR})
set_target_properties(formats_xml PROPERTIES
OUTPUT_NAME formats_xml
PREFIX ""
SUFFIX ${MODULE_EXTENSION})
endif()
if(WITH_JSON)
set(jsonsources "")