diff --git a/.github/workflows/build-cibw.yml b/.github/workflows/build-cibw.yml index dac64d19de..bae27470fa 100644 --- a/.github/workflows/build-cibw.yml +++ b/.github/workflows/build-cibw.yml @@ -1,3 +1,6 @@ +# This workflow builds the Python wheels using cibuildwheel and uploads them to TestPyPI. +# It can be triggered on push to the develop branch or manually via Github Actions. + name: Build Wheels (cibuildwheel) on: @@ -7,6 +10,9 @@ on: workflow_dispatch: jobs: + # Get the system time and store it in an output. This is used to tag the wheels. + # This needs to be done in a separate job so that each matrix job in build_wheels can + # access the same timestamp. get_system_time: name: Get System Time runs-on: ubuntu-latest @@ -96,6 +102,8 @@ jobs: with: python-version: ${{ matrix.python_version }} + # Set the DEVELOP flag and the TIMESTAMP environment variables. This is used in the + # top-level CMakeLists.txt to generate the GTSAM_VERSION_STRING. - name: Set Develop Flag run: | echo "DEVELOP=1" >> $GITHUB_ENV @@ -113,18 +121,26 @@ jobs: exit 1 fi + # We first build the Python wrapper module on the host machine. This is done because cibuildwheel + # expects a setup.py file to be present in the project directory. + # + # The Python wrapper module is then rebuilt within the cibuildwheel container before building + # the wheels to ensure platform compatibility. - name: Run CMake run: | cmake . -B build -DGTSAM_BUILD_PYTHON=1 -DGTSAM_PYTHON_VERSION=${{ matrix.python_version }} - name: Build and test wheels env: + # Generate the platform identifier. See https://cibuildwheel.pypa.io/en/stable/options/#build-skip. CIBW_BUILD: cp${{ matrix.cibw_python_version }}-${{ matrix.platform_id }} CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux_image }} CIBW_MANYLINUX_AARCH64_IMAGE: ${{ matrix.manylinux_image }} CIBW_ARCHS: all CIBW_ENVIRONMENT_PASS_LINUX: DEVELOP TIMESTAMP + # Use build instead of pip wheel to build the wheels. This is recommended by PyPA. + # See https://cibuildwheel.pypa.io/en/stable/options/#build-frontend. CIBW_BUILD_FRONTEND: "build" CIBW_BEFORE_ALL: bash {project}/build_tools/wheels/cibw_before_all.sh ${{ matrix.python_version }} {project} diff --git a/CMakeLists.txt b/CMakeLists.txt index 826785becc..cabde7653d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,14 @@ set (GTSAM_VERSION_PATCH 0) set (GTSAM_PRERELEASE_VERSION "a0") math (EXPR GTSAM_VERSION_NUMERIC "10000 * ${GTSAM_VERSION_MAJOR} + 100 * ${GTSAM_VERSION_MINOR} + ${GTSAM_VERSION_PATCH}") +# Set the version string for the library. +# +# If the environment variable DEVELOP is set, then the version string will be +# "MAJOR.MINORprerelease.devTIMESTAMP". TIMESTAMP is another environment variable that should be set to the current +# datetime. See build-cibw.yaml for example usage. +# +# If the prerelease version is empty, then the version string will be "MAJOR.MINOR.PATCH". Otherwise, the version +# string will be "MAJOR.MINORprerelease". if (DEFINED ENV{DEVELOP}) set (GTSAM_VERSION_STRING "${GTSAM_VERSION_MAJOR}.${GTSAM_VERSION_MINOR}${GTSAM_PRERELEASE_VERSION}.dev$ENV{TIMESTAMP}") set (SETUP_NAME "gtsam-develop") diff --git a/build_tools/wheels/build_wheels.sh b/build_tools/wheels/build_wheels.sh index a8cbb93de6..29247953d9 100644 --- a/build_tools/wheels/build_wheels.sh +++ b/build_tools/wheels/build_wheels.sh @@ -1,5 +1,8 @@ #!/bin/bash +# This script calls cibuildwheel to build the wheels for the project. It is used in the build-cibw.yml workflow in .github/workflows. +# Note that the build/python directory contains the wrapper module built for the specified Python version. + set -e set -x diff --git a/build_tools/wheels/cibw_before_all.sh b/build_tools/wheels/cibw_before_all.sh index 4f0f33f16b..2398877a89 100644 --- a/build_tools/wheels/cibw_before_all.sh +++ b/build_tools/wheels/cibw_before_all.sh @@ -13,6 +13,7 @@ ARCH=$(uname -m) export PYTHON="python${PYTHON_VERSION}" if [ "$(uname)" == "Linux" ]; then + # manylinux2014 is based on CentOS 7, so use yum to install dependencies yum install -y wget # Install Boost from source @@ -32,6 +33,7 @@ $(which $PYTHON) -m pip install -r $PROJECT_DIR/python/dev_requirements.txt rm -rf $PROJECT_DIR/build rm -rf CMakeCache.txt CMakeFiles +# Build the Python wrapper module cmake $PROJECT_DIR \ -B build \ -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ @@ -50,6 +52,7 @@ cmake $PROJECT_DIR \ cd $PROJECT_DIR/build/python +# Install the Python wrapper module and generate Python stubs if [ "$(uname)" == "Linux" ]; then make -j $(nproc) install make -j $(nproc) python-stubs diff --git a/python/setup.py.in b/python/setup.py.in index 96c21a55ed..cd9177ab88 100644 --- a/python/setup.py.in +++ b/python/setup.py.in @@ -20,6 +20,8 @@ package_data = { # Cleaner to read in the contents rather than copy them over. readme_contents = open("${GTSAM_SOURCE_DIR}/README.md").read() +# The cibuildwheel tool won't recognize a wheel as platform-dependent unless the ext_modules option is defined in setup.py. This is used to define C/C++ source files that need to be built for the wheel. +# However, we pre-build our C++ files. Thus, we force cibuildwheel to think that there are ext_modules defined by overwriting the has_ext_modules() function. class BinaryDistribution(Distribution): def has_ext_modules(foo): return True