diff --git a/.github/workflows/common-ci.yml b/.github/workflows/common-ci.yml deleted file mode 100644 index 96f23fe37..000000000 --- a/.github/workflows/common-ci.yml +++ /dev/null @@ -1,231 +0,0 @@ -name: Common CI - -on: [pull_request] - -# Cancels any in-progress workflow runs for the same PR when a new push is made, -# allowing the runner to become available more quickly for the latest changes. -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - build: - name: ${{ matrix.name }} ${{ matrix.build_type }} C++/Python ${{ matrix.python_version }} - runs-on: ${{ matrix.os }} - - env: - CTEST_OUTPUT_ON_FAILURE: ON - CTEST_PARALLEL_LEVEL: 2 - CMAKE_BUILD_TYPE: ${{ matrix.build_type }} - PYTHON_VERSION: ${{ matrix.python_version }} - GTSAM_INSTALL_DIR_MACOS: ${{ github.workspace }}/gtsam_install_prefix - - strategy: - fail-fast: false - matrix: - name: [ubuntu-24.04-gcc-11, ubuntu-24.04-clang-16, macOS-14-xcode-15.4] - build_type: [Debug, Release] - python_version: [3] - include: - - name: ubuntu-24.04-gcc-11 - os: ubuntu-24.04 - compiler: gcc - version: "11" - - name: ubuntu-24.04-clang-16 - os: ubuntu-24.04 - compiler: clang - version: "16" - - name: macOS-14-xcode-15.4 - os: macOS-14 - compiler: xcode - version: "15.4" - - steps: - - name: Checkout GTDynamics (Your Project) - uses: actions/checkout@v4 - - - name: Install System Dependencies (Linux) - if: runner.os == 'Linux' - run: | - set -e - sudo apt-get -y update - if [ "${{ matrix.compiler }}" = "gcc" ]; then - sudo apt-get install -y g++-${{ matrix.version }} g++-${{ matrix.version }}-multilib - echo "CC=gcc-${{ matrix.version }}" >> $GITHUB_ENV - echo "CXX=g++-${{ matrix.version }}" >> $GITHUB_ENV - else - sudo apt-get install -y clang-${{ matrix.version }} g++-multilib - echo "CC=clang" >> $GITHUB_ENV - echo "CXX=clang++" >> $GITHUB_ENV - fi - sudo sh -c 'echo "deb http://packages.osrfoundation.org/gazebo/ubuntu-stable `lsb_release -cs` main" > /etc/apt/sources.list.d/gazebo-stable.list' - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys D2486D2DD83DB69272AFE98867170598AF249743 - sudo apt-get -y update - sudo apt-get -y install libtbb-dev libboost-all-dev libsdformat15-dev - - # GTDynamics C++ tests use CppUnitLite (not provided by default on the runner). - git clone https://github.com/borglab/CppUnitLite.git /tmp/CppUnitLite - cd /tmp/CppUnitLite - mkdir build && cd build - # CppUnitLite's CMakeLists uses an old minimum CMake version; newer CMake - # requires explicitly opting into older policy compatibility. - # Force a modern C++ standard for newer Boost headers (Homebrew Boost requires C++11+). - cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_STANDARD_REQUIRED=ON .. - sudo make -j$(nproc) install - cd ${{ github.workspace }} - sudo rm -rf /tmp/CppUnitLite - - - name: Install System Dependencies (macOS) - if: runner.os == 'macOS' - run: | - set -e - if [ "${{ matrix.compiler }}" = "gcc" ]; then - brew install gcc@${{ matrix.version }} - echo "CC=gcc-${{ matrix.version }}" >> $GITHUB_ENV - echo "CXX=g++-${{ matrix.version }}" >> $GITHUB_ENV - else - sudo xcode-select -switch /Applications/Xcode_${{ matrix.version }}.app - echo "CC=clang" >> $GITHUB_ENV - echo "CXX=clang++" >> $GITHUB_ENV - fi - brew install boost - brew tap osrf/simulation - brew install sdformat15 - - # GTDynamics C++ tests use CppUnitLite (install it once per job). - git clone https://github.com/borglab/CppUnitLite.git /tmp/CppUnitLite - cd /tmp/CppUnitLite - mkdir build && cd build - # CppUnitLite's CMakeLists uses an old minimum CMake version; newer CMake - # requires explicitly opting into older policy compatibility. - # Force a modern C++ standard for newer Boost headers (Homebrew Boost requires C++11+). - cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_STANDARD_REQUIRED=ON .. - sudo make -j$(sysctl -n hw.physicalcpu) install - cd ${{ github.workspace }} - rm -rf /tmp/CppUnitLite - - - name: Python Dependencies (Linux) - if: runner.os == 'Linux' - run: | - pip3 install -U "setuptools<70" wheel numpy pyparsing pyyaml "pybind11-stubgen>=2.5.1" - - - name: Python Dependencies and venv Setup (macOS) - if: runner.os == 'macOS' - shell: bash - run: | - set -e - python${{ env.PYTHON_VERSION }} -m venv venv - source venv/bin/activate - echo "VENV_PYTHON_EXECUTABLE=${{ github.workspace }}/venv/bin/python" >> $GITHUB_ENV - echo "${{ github.workspace }}/venv/bin" >> $GITHUB_PATH - python -m pip install --upgrade pip - python -m pip install --break-system-packages -U "setuptools<70" wheel numpy pyparsing pyyaml "pybind11-stubgen>=2.5.1" - - - name: GTSAM (Linux) - if: runner.os == 'Linux' - run: | - set -e - git clone https://github.com/borglab/gtsam.git /tmp/gtsam_source_linux - cd /tmp/gtsam_source_linux - mkdir build && cd $_ - cmake -D GTSAM_BUILD_EXAMPLES_ALWAYS=OFF -DGTSAM_BUILD_PYTHON=ON .. - sudo make -j$(nproc) install - make python-install - sudo ldconfig - cd ${{ github.workspace }} - sudo rm -rf /tmp/gtsam_source_linux - - - name: GTSAM (macOS) - if: runner.os == 'macOS' - shell: bash - run: | - set -e - git clone https://github.com/borglab/gtsam.git /tmp/gtsam_source_macos - cd /tmp/gtsam_source_macos - mkdir build && cd $_ - cmake -D GTSAM_BUILD_EXAMPLES_ALWAYS=OFF \ - -D GTSAM_BUILD_PYTHON=ON \ - -D GTSAM_WITH_TBB=OFF \ - -D PYTHON_EXECUTABLE=${{ env.VENV_PYTHON_EXECUTABLE }} \ - -D CMAKE_INSTALL_PREFIX=${{ env.GTSAM_INSTALL_DIR_MACOS }} \ - .. - make -j$(sysctl -n hw.physicalcpu) install - make -j$(sysctl -n hw.physicalcpu) python-install - cd ${{ github.workspace }} - rm -rf /tmp/gtsam_source_macos - - - name: Build Directory for GTDynamics - run: mkdir build - - - name: Configure GTDynamics (Linux) - if: runner.os == 'Linux' - run: | - set -e - cmake -DGTDYNAMICS_BUILD_PYTHON=ON .. - working-directory: ./build - - - name: Configure GTDynamics (macOS) - if: runner.os == 'macOS' - shell: bash - run: | - set -e - GTSAM_CONFIG_DIR_PATH="${{ env.GTSAM_INSTALL_DIR_MACOS }}/lib/cmake/GTSAM" - cmake -DGTDYNAMICS_BUILD_PYTHON=ON \ - -DPYTHON_EXECUTABLE=${{ env.VENV_PYTHON_EXECUTABLE }} \ - -DGTSAM_DIR="${GTSAM_CONFIG_DIR_PATH}" \ - -DCMAKE_PREFIX_PATH="${{ env.GTSAM_INSTALL_DIR_MACOS }};$(brew --prefix)" \ - .. - working-directory: ./build - - - name: Build GTDynamics - run: make -j$(if [[ "$(uname)" == "Darwin" ]]; then sysctl -n hw.physicalcpu; else nproc; fi) - working-directory: ./build - - - name: Test GTDynamics C++ (macOS) - # Mirror the python-test linker settings: GTSAM is installed to a local prefix. - if: runner.os == 'macOS' - shell: bash - run: | - set -e - export DYLD_LIBRARY_PATH="${{ env.GTSAM_INSTALL_DIR_MACOS }}/lib${DYLD_LIBRARY_PATH:+:$DYLD_LIBRARY_PATH}" - make -j$(sysctl -n hw.physicalcpu) check - working-directory: ./build - - - name: Test GTDynamics C++ (Linux) - if: runner.os == 'Linux' - run: | - set -e - make -j$(nproc) check - working-directory: ./build - - - name: Install GTDynamics Python Wrappers - shell: bash - run: | - set -e - if [[ "$RUNNER_OS" == "macOS" ]]; then - make -j$(sysctl -n hw.physicalcpu) python-install - else - sudo make -j$(nproc) python-install - fi - working-directory: ./build - - - name: Test GTDynamics Python - # For macOS, set DYLD_LIBRARY_PATH so the dynamic linker can find the installed GTSAM libraries. - # Also, ensure the venv is active for the context of running the tests. - if: runner.os == 'macOS' - shell: bash - run: | - set -e - source ${{ github.workspace }}/venv/bin/activate # Ensure venv Python and its site-packages are primary - export DYLD_LIBRARY_PATH="${{ env.GTSAM_INSTALL_DIR_MACOS }}/lib${DYLD_LIBRARY_PATH:+:$DYLD_LIBRARY_PATH}" - echo "DYLD_LIBRARY_PATH is set to: $DYLD_LIBRARY_PATH" - make -j$(sysctl -n hw.physicalcpu) python-test - working-directory: ./build - - name: Test GTDynamics Python (Linux) - if: runner.os == 'Linux' - run: | - set -e - # On Linux, sudo ldconfig during GTSAM install should handle library paths for system-wide installs. - # If GTSAM was installed to a custom prefix not in ldconfig paths, LD_LIBRARY_PATH would be needed. - make -j$(nproc) python-test - working-directory: ./build diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml new file mode 100644 index 000000000..500363a24 --- /dev/null +++ b/.github/workflows/linux-ci.yml @@ -0,0 +1,131 @@ +name: Linux CI + +on: [pull_request] + +# Cancels any in-progress workflow runs for the same PR when a new push is made, +# allowing the runner to become available more quickly for the latest changes. +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + build: + name: ${{ matrix.name }} ${{ matrix.build_type }} C++/Python ${{ matrix.python_version }} + runs-on: ${{ matrix.os }} + + env: + CTEST_OUTPUT_ON_FAILURE: ON + CTEST_PARALLEL_LEVEL: 2 + CMAKE_BUILD_TYPE: ${{ matrix.build_type }} + PYTHON_VERSION: ${{ matrix.python_version }} + GTSAM_INSTALL_DIR: ${{ github.workspace }}/gtsam_install + + strategy: + fail-fast: false + matrix: + name: [ubuntu-24.04-gcc-14, ubuntu-24.04-clang-16] + build_type: [Debug, Release] + python_version: [3] + include: + - name: ubuntu-24.04-gcc-14 + os: ubuntu-24.04 + compiler: gcc + version: "14" + - name: ubuntu-24.04-clang-16 + os: ubuntu-24.04 + compiler: clang + version: "16" + + steps: + - name: Checkout GTDynamics Repository + uses: actions/checkout@v4 + + - name: Install System Dependencies + run: | + set -e + sudo apt-get -y update + if [ "${{ matrix.compiler }}" = "gcc" ]; then + sudo apt-get install -y g++-${{ matrix.version }} g++-${{ matrix.version }}-multilib + echo "CC=gcc-${{ matrix.version }}" >> $GITHUB_ENV + echo "CXX=g++-${{ matrix.version }}" >> $GITHUB_ENV + else + sudo apt-get install -y clang-${{ matrix.version }} g++-multilib + echo "CC=clang" >> $GITHUB_ENV + echo "CXX=clang++" >> $GITHUB_ENV + fi + sudo sh -c 'echo "deb http://packages.osrfoundation.org/gazebo/ubuntu-stable `lsb_release -cs` main" > /etc/apt/sources.list.d/gazebo-stable.list' + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys D2486D2DD83DB69272AFE98867170598AF249743 + sudo apt-get -y update + sudo apt-get -y install libtbb-dev libboost-all-dev libsdformat15-dev + + # GTDynamics C++ tests use CppUnitLite (not provided by default on the runner). + git clone https://github.com/borglab/CppUnitLite.git /tmp/CppUnitLite + cd /tmp/CppUnitLite + mkdir build && cd build + # CppUnitLite's CMakeLists uses an old minimum CMake version; newer CMake + # requires explicitly opting into older policy compatibility. + # Force a modern C++ standard for newer Boost headers (Homebrew Boost requires C++11+). + cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_STANDARD_REQUIRED=ON .. + sudo make -j$(nproc) install + cd ${{ github.workspace }} + sudo rm -rf /tmp/CppUnitLite + + - name: Python Dependencies + run: | + pip3 install -U "setuptools<70" wheel numpy pyparsing pyyaml "pybind11-stubgen>=2.5.1" + + - name: Build & Install GTSAM + shell: bash + run: | + set -e + git clone https://github.com/borglab/gtsam.git /tmp/gtsam_source_linux + cd /tmp/gtsam_source_linux + mkdir build && cd $_ + cmake -D GTSAM_BUILD_EXAMPLES_ALWAYS=OFF \ + -D GTSAM_BUILD_PYTHON=ON \ + -D GTSAM_WITH_TBB=OFF \ + -D CMAKE_CXX_FLAGS="-faligned-new" \ + -D CMAKE_INSTALL_PREFIX=${{ env.GTSAM_INSTALL_DIR }} \ + .. + sudo make -j$(nproc) install + make python-install + sudo ldconfig + cd ${{ github.workspace }} + sudo rm -rf /tmp/gtsam_source_linux + + - name: Create build directory + run: mkdir build + + - name: Configure GTDynamics + run: | + set -e + cmake -DGTDYNAMICS_BUILD_PYTHON=ON \ + -DGTSAM_DIR="${{ env.GTSAM_INSTALL_DIR }}/lib/cmake/GTSAM" \ + -DCMAKE_PREFIX_PATH="${{ env.GTSAM_INSTALL_DIR }}" \ + .. + working-directory: ./build + + - name: Build + run: make -j$(nproc) + working-directory: ./build + + - name: Test C++ + run: | + set -e + make -j$(nproc) check + working-directory: ./build + + - name: Install Python Wrappers + shell: bash + run: | + set -e + sudo make -j$(nproc) python-install + working-directory: ./build + + - name: Test Python + run: | + set -e + # On Linux, `sudo ldconfig` during GTSAM install should handle library paths for system-wide installs. + # If GTSAM was installed to a custom prefix not in ldconfig paths, LD_LIBRARY_PATH would be needed. + make -j$(nproc) python-test + working-directory: ./build diff --git a/.github/workflows/macos-ci.yml b/.github/workflows/macos-ci.yml new file mode 100644 index 000000000..d53a758c3 --- /dev/null +++ b/.github/workflows/macos-ci.yml @@ -0,0 +1,145 @@ +name: macOS CI + +on: [pull_request] + +# Cancels any in-progress workflow runs for the same PR when a new push is made, +# allowing the runner to become available more quickly for the latest changes. +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + build: + name: ${{ matrix.name }} ${{ matrix.build_type }} C++/Python ${{ matrix.python_version }} + runs-on: ${{ matrix.os }} + + env: + CTEST_OUTPUT_ON_FAILURE: ON + CTEST_PARALLEL_LEVEL: 2 + CMAKE_BUILD_TYPE: ${{ matrix.build_type }} + PYTHON_VERSION: ${{ matrix.python_version }} + GTSAM_INSTALL_DIR: ${{ github.workspace }}/gtsam_install + + strategy: + fail-fast: false + matrix: + name: [macos-15-xcode-16.4] + build_type: [Debug, Release] + python_version: [3] + include: + - name: macos-15-xcode-16.4 + os: macos-15 + compiler: xcode + version: "16.4" + + steps: + - name: Checkout GTDynamics Repository + uses: actions/checkout@v4 + + - name: Install System Dependencies + if: runner.os == 'macOS' + run: | + set -e + if [ "${{ matrix.compiler }}" = "gcc" ]; then + brew install gcc@${{ matrix.version }} + echo "CC=gcc-${{ matrix.version }}" >> $GITHUB_ENV + echo "CXX=g++-${{ matrix.version }}" >> $GITHUB_ENV + else + sudo xcode-select -switch /Applications/Xcode_${{ matrix.version }}.app + echo "CC=clang" >> $GITHUB_ENV + echo "CXX=clang++" >> $GITHUB_ENV + fi + brew install boost + brew tap osrf/simulation + brew install sdformat15 + + # GTDynamics C++ tests use CppUnitLite (install it once per job). + git clone https://github.com/borglab/CppUnitLite.git /tmp/CppUnitLite + cd /tmp/CppUnitLite + mkdir build && cd build + # CppUnitLite's CMakeLists uses an old minimum CMake version; newer CMake + # requires explicitly opting into older policy compatibility. + # Force a modern C++ standard for newer Boost headers (Homebrew Boost requires C++11+). + cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_STANDARD_REQUIRED=ON .. + sudo make -j$(sysctl -n hw.physicalcpu) install + cd ${{ github.workspace }} + rm -rf /tmp/CppUnitLite + + - name: Python Dependencies + + shell: bash + run: | + set -e + python${{ env.PYTHON_VERSION }} -m venv venv + source venv/bin/activate + echo "VENV_PYTHON_EXECUTABLE=${{ github.workspace }}/venv/bin/python" >> $GITHUB_ENV + echo "${{ github.workspace }}/venv/bin" >> $GITHUB_PATH + python -m pip install --upgrade pip + python -m pip install --break-system-packages -U "setuptools<70" wheel numpy pyparsing pyyaml "pybind11-stubgen>=2.5.1" + + - name: Build & Install GTSAM + shell: bash + run: | + set -e + git clone https://github.com/borglab/gtsam.git /tmp/gtsam_source_macos + cd /tmp/gtsam_source_macos + mkdir build && cd $_ + cmake -D GTSAM_BUILD_EXAMPLES_ALWAYS=OFF \ + -D GTSAM_BUILD_PYTHON=ON \ + -D GTSAM_WITH_TBB=OFF \ + -D CMAKE_CXX_FLAGS="-faligned-new" \ + -D PYTHON_EXECUTABLE=${{ env.VENV_PYTHON_EXECUTABLE }} \ + -D CMAKE_INSTALL_PREFIX=${{ env.GTSAM_INSTALL_DIR }} \ + .. + make -j$(sysctl -n hw.physicalcpu) install + make -j$(sysctl -n hw.physicalcpu) python-install + cd ${{ github.workspace }} + rm -rf /tmp/gtsam_source_macos + + - name: Create build directory + run: mkdir build + + - name: Configure GTDynamics + if: runner.os == 'macOS' + shell: bash + run: | + set -e + GTSAM_CONFIG_DIR_PATH="${{ env.GTSAM_INSTALL_DIR }}/lib/cmake/GTSAM" + cmake -DGTDYNAMICS_BUILD_PYTHON=ON \ + -DPYTHON_EXECUTABLE=${{ env.VENV_PYTHON_EXECUTABLE }} \ + -DGTSAM_DIR="${GTSAM_CONFIG_DIR_PATH}" \ + -DCMAKE_PREFIX_PATH="${{ env.GTSAM_INSTALL_DIR }};$(brew --prefix)" \ + .. + working-directory: ./build + + - name: Build + run: make -j$(sysctl -n hw.physicalcpu) + working-directory: ./build + + - name: Test C++ + shell: bash + run: | + # Mirror the python-test linker settings: GTSAM is installed to a local prefix. + set -e + export DYLD_LIBRARY_PATH="${{ env.GTSAM_INSTALL_DIR }}/lib${DYLD_LIBRARY_PATH:+:$DYLD_LIBRARY_PATH}" + make -j$(sysctl -n hw.physicalcpu) check + working-directory: ./build + + - name: Install Python Wrappers + shell: bash + run: | + set -e + make -j$(sysctl -n hw.physicalcpu) python-install + working-directory: ./build + + - name: Test Python + # For macOS, set DYLD_LIBRARY_PATH so the dynamic linker can find the installed GTSAM libraries. + # Also, ensure the venv is active for the context of running the tests. + shell: bash + run: | + set -e + source ${{ github.workspace }}/venv/bin/activate # Ensure venv Python and its site-packages are primary + export DYLD_LIBRARY_PATH="${{ env.GTSAM_INSTALL_DIR }}/lib${DYLD_LIBRARY_PATH:+:$DYLD_LIBRARY_PATH}" + echo "DYLD_LIBRARY_PATH is set to: $DYLD_LIBRARY_PATH" + make -j$(sysctl -n hw.physicalcpu) python-test + working-directory: ./build diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 67dca1d12..7a9b8d772 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -13,10 +13,8 @@ set(GTDYNAMICS_PYTHON_PATH ${PROJECT_SOURCE_DIR}/python/) file(TO_NATIVE_PATH "${PROJECT_BINARY_DIR}/python" GTD_PYTHON_BINARY_DIR) -configure_file(${GTDYNAMICS_PYTHON_PATH}/templates/setup.py.in - ${GTD_PYTHON_BINARY_DIR}/setup.py) -configure_file(${GTDYNAMICS_PYTHON_PATH}/requirements.txt - ${GTD_PYTHON_BINARY_DIR}/requirements.txt COPYONLY) +configure_file(${GTDYNAMICS_PYTHON_PATH}/templates/pyproject.toml.in + ${GTD_PYTHON_BINARY_DIR}/pyproject.toml) configure_file(${GTDYNAMICS_PYTHON_PATH}/templates/${PROJECT_NAME}.tpl ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.tpl) file(COPY ${GTDYNAMICS_PYTHON_PATH}/${PROJECT_NAME} diff --git a/python/README.md b/python/README.md index 60e134ceb..45cfe4ed8 100644 --- a/python/README.md +++ b/python/README.md @@ -3,16 +3,19 @@ This directory is where the Pybind11-generated GTDynamics package lives and where the CI jobs assemble and verify the Python bindings. ## Layout + - `gtdynamics/` contains the Python package that mirrors the C++ API and exposes convenience helpers such as the `sim.py` sensor models. -- `templates/` and `Setup.py.in` drive the `pip install .` step that packages the compiled `.so`/`.pyd` into a wheel. +- `templates/` and `pyproject.toml` drive the `pip install .` step that packages the compiled `.so`/`.pyd` into a wheel. - `tests/` is the unit-test tree run by `make python-test.*` targets defined in `python/CMakeLists.txt`. ## Build prerequisites + 1. **GTSAM** must be built with Python support (i.e., `-DGTSAM_BUILD_PYTHON=ON`) and installed to a prefix that GTDynamics can see via `GTSAM_DIR` or `CMAKE_PREFIX_PATH`. 2. **Python tooling**: the CI job installs `setuptools<70`, `wheel`, `numpy`, `pyparsing`, `pyyaml`, and `pybind11-stubgen` before configuring the project; matching this list locally avoids the same runtime issues. 3. On macOS, the workflow creates and activates a virtual environment (`pythonX -m venv venv`) so that `pip install` and the tests run in the same interpreter that baked the bindings. ## Building and installing locally + 1. Create a build directory and configure with the wrapper flag enabled: ```sh mkdir -p build && cd build @@ -30,11 +33,13 @@ This directory is where the Pybind11-generated GTDynamics package lives and wher This runs `${PYTHON_EXECUTABLE} -m pip install .` in `build/python`, which produces a wheel in `pip`'s cache before installing it. ## Running Python tests + - `make python-test` runs every suite captured by the `PYTHON_UNIT_TEST_SUITE` macro (base plus optional `cablerobot`/`jumpingrobot`). - Individual suites can be executed via `make python-test.base`, `make python-test.cablerobot`, etc., and they rely on the package being built and discoverable via `PYTHONPATH`. - The CI job runs the tests on macOS with `venv` activation and on Linux after `sudo make python-install` so you can copy those steps when reproducing test failures. ## Packaging tips + - `python/templates/setup.py.in` reads the CMake-generated `requirements.txt` and packages the shared library blobs (`.so` / `.pyd`) from `python/gtdynamics` so running `pip wheel .` in `build/python` yields a complete asset. - Keep `python/requirements.txt` in sync with the requirements file copied to `build/python/requirements.txt` so that CI and a local `pip install` use the same dependency list. - If you need to publish a wheel manually, the packaged wheel that `pip install .` writes to `~/.cache/pip` already encodes the GTDynamics version reported by `CMakeLists.txt`. diff --git a/python/requirements.txt b/python/requirements.txt deleted file mode 100644 index f39221cad..000000000 --- a/python/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -numpy -gtsam \ No newline at end of file diff --git a/python/templates/pyproject.toml.in b/python/templates/pyproject.toml.in new file mode 100644 index 000000000..bf6077e0f --- /dev/null +++ b/python/templates/pyproject.toml.in @@ -0,0 +1,49 @@ +[project] +name = "${PROJECT_NAME}" +version = "${PROJECT_VERSION}" +description = "${PROJECT_DESCRIPTION}" +authors = [ + { name = "Varun Agrawal", email = "varunagrawal@gatech.edu" }, + { name="Frank Dellaert", email="dellaert@gatech.edu" } +] +readme = "${PROJECT_SOURCE_DIR}/README.md" +license = "BSD-3-Clause" +keywords=["robotics", "kinematics", "dynamics", "factor graphs"] + +classifiers = [ + "Intended Audience :: Education", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "Operating System :: MacOS", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python", +] + +requires-python = ">=3.10" +dependencies = [ + "numpy>=2.0.0", + "gtsam>=4.2", +] + +[dependency-groups] +dev = [ + "pytest>=9.0.1", + "pytest-cov>=7.0.0", +] + +[project.urls] +Homepage = "https://github.com/borglab/GTDynamics" +Documentation = "https://github.com/borglab/GTDynamics" +Repository = "https://github.com/borglab/GTDynamics" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.pytest.ini_options] +addopts = "--cov=gtdynamics --cov-report html --cov-report term" +norecursedirs = [] + +[tool.uv.build-backend] +module-name = "gtdynamics" diff --git a/python/templates/setup.py.in b/python/templates/setup.py.in deleted file mode 100644 index 905300bd8..000000000 --- a/python/templates/setup.py.in +++ /dev/null @@ -1,56 +0,0 @@ -import os - -try: - from setuptools import setup, find_packages -except ImportError: - from distutils.core import setup, find_packages - -packages = find_packages() - -package_data = { - package: [ - f - for f in os.listdir(package.replace(".", os.path.sep)) - if os.path.splitext(f)[1] in (".so", ".pyd") - ] - for package in packages -} - -dependency_list = open("${PYTHON_REQUIREMENTS_PATH}").read().split('\n') -dependencies = [x for x in dependency_list if x[0] != '#'] - -setup( - name='${PROJECT_NAME}', - description='${PROJECT_DESCRIPTION}', - url='${PROJECT_HOMEPAGE_URL}', - version='${PROJECT_VERSION}', - author="${PROJECT_AUTHOR}", - author_email="${PROJECT_AUTHOR_EMAIL}", - license='Simplified BSD license', - keywords="", - long_description=open("${PROJECT_SOURCE_DIR}/README.md").read(), - long_description_content_type="text/markdown", - python_requires=">=3.6", - # https://pypi.org/pypi?%3Aaction=list_classifiers - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Education', - 'Intended Audience :: Developers', - 'Intended Audience :: Science/Research', - 'Operating System :: MacOS', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX', - 'License :: OSI Approved :: BSD License', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 3', - ], - packages=packages, - # Load the built shared object files - package_data=package_data, - include_package_data=True, - test_suite="${PYTHON_TESTS}", - # Ensure that the compiled .so file is properly packaged - zip_safe=False, - platforms="any", - install_requires=dependencies, -)