From cef4a7f519ab0b2f4f267e434a4d128217f73033 Mon Sep 17 00:00:00 2001 From: Jade Turner Date: Sun, 28 Jul 2024 21:13:33 +0800 Subject: [PATCH] [trajoptlib] Add Python bindings using nanobind Signed-off-by: Jade Turner --- .github/workflows/repair_wheel.py | 19 +++ .github/workflows/trajoptlib-py.yml | 152 +++++++++++++++++++ .gitignore | 3 + trajoptlib/CMakeLists.txt | 89 +++++++++++ trajoptlib/cmake/modules/Pybind11Mkdoc.cmake | 31 ++++ trajoptlib/py/__init__.py | 5 + trajoptlib/py/cpp/BindSwerve.cpp | 62 ++++++++ trajoptlib/py/cpp/Binders.hpp | 36 +++++ trajoptlib/py/cpp/Main.cpp | 138 +++++++++++++++++ trajoptlib/pyproject.toml | 44 ++++++ 10 files changed, 579 insertions(+) create mode 100755 .github/workflows/repair_wheel.py create mode 100644 .github/workflows/trajoptlib-py.yml create mode 100644 trajoptlib/cmake/modules/Pybind11Mkdoc.cmake create mode 100644 trajoptlib/py/__init__.py create mode 100644 trajoptlib/py/cpp/BindSwerve.cpp create mode 100644 trajoptlib/py/cpp/Binders.hpp create mode 100644 trajoptlib/py/cpp/Main.cpp create mode 100644 trajoptlib/pyproject.toml diff --git a/.github/workflows/repair_wheel.py b/.github/workflows/repair_wheel.py new file mode 100755 index 000000000..e02b53c1b --- /dev/null +++ b/.github/workflows/repair_wheel.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 + +import os +import sys + + +def main(): + for filename in sys.argv[1:]: + src = filename + dest = src.replace("linux_x86_64", "manylinux_2_35_x86_64").replace( + "macosx_14_arm64", "macosx_14_0_arm64" + ) + if src != dest: + print(f"{src} -> {dest}") + os.rename(src, dest) + + +if __name__ == "__main__": + main() diff --git a/.github/workflows/trajoptlib-py.yml b/.github/workflows/trajoptlib-py.yml new file mode 100644 index 000000000..af3ed5667 --- /dev/null +++ b/.github/workflows/trajoptlib-py.yml @@ -0,0 +1,152 @@ +name: Build Python + +on: [pull_request, push] + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true + +jobs: + build-wheel: + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + include: + - name: Windows x86_64 + os: windows-2022 + version: "3.9" + cmake-env: + - name: Windows x86_64 + os: windows-2022 + version: "3.10" + cmake-env: + - name: Windows x86_64 + os: windows-2022 + version: "3.11" + cmake-env: + - name: Windows x86_64 + os: windows-2022 + version: "3.12" + cmake-env: + - name: Linux x86_64 + os: ubuntu-24.04 + version: "3.9" + cmake-env: + - name: Linux x86_64 + os: ubuntu-24.04 + version: "3.10" + cmake-env: + - name: Linux x86_64 + os: ubuntu-24.04 + version: "3.11" + cmake-env: + - name: Linux x86_64 + os: ubuntu-24.04 + version: "3.12" + cmake-env: + - name: macOS universal + os: macOS-14 + version: "3.10" + cmake-env: CMAKE_OSX_ARCHITECTURES="x86_64;arm64" + - name: macOS universal + os: macOS-14 + version: "3.11" + cmake-env: CMAKE_OSX_ARCHITECTURES="x86_64;arm64" + - name: macOS universal + os: macOS-14 + version: "3.12" + cmake-env: CMAKE_OSX_ARCHITECTURES="x86_64;arm64" + + name: ${{ matrix.version }} ${{ matrix.name }} wheel + runs-on: ${{ matrix.os }} + defaults: + run: + working-directory: ./trajoptlib + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Make GCC 14 the default toolchain (Linux) + if: startsWith(matrix.os, 'ubuntu') + run: | + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 200 + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-14 200 + + - run: sudo xcode-select -switch /Applications/Xcode_15.3.app + if: startsWith(matrix.os, 'macOS') + + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.version }} + + - run: pip install typing_extensions + if: matrix.version == '3.9' || matrix.version == '3.10' + + - run: pip3 install build pytest + - run: ${{ matrix.cmake-env }} python3 -m build --wheel + - run: python3 ../.github/workflows/repair_wheel.py trajoptlib-*.whl + if: startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macOS') + working-directory: dist + - run: pip3 install dist/trajoptlib-*.whl + shell: bash + - run: pytest + shell: bash + + - uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.version }} ${{ matrix.name }} wheel + path: dist + + build-sdist: + timeout-minutes: 10 + + name: sdist + runs-on: ubuntu-24.04 + defaults: + run: + working-directory: ./trajoptlib + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-python@v5 + with: + python-version: 3.12 + + - run: pip3 install build + - run: python3 -m build --sdist + + - uses: actions/upload-artifact@v4 + with: + name: sdist + path: dist + + pypi-publish: + name: Upload release to PyPI + runs-on: ubuntu-24.04 + defaults: + run: + working-directory: ./trajoptlib + needs: [build-wheel, build-sdist] + if: github.repository_owner == 'SleipnirGroup' && github.ref == 'refs/heads/main' + environment: + name: pypi + url: https://pypi.org/p/trajoptlib + permissions: + id-token: write + steps: + - uses: actions/download-artifact@v4 + with: + path: dist + pattern: '* wheel' + merge-multiple: true + - uses: actions/download-artifact@v4 + with: + path: dist + pattern: 'sdist' + merge-multiple: true + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.gitignore b/.gitignore index a6f441895..a297e8fd9 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,6 @@ CMakeUserPresets.json build/ cli/ + +# python +trajoptlib/.py-build-cmake_cache/ diff --git a/trajoptlib/CMakeLists.txt b/trajoptlib/CMakeLists.txt index b2dd6f73b..fce0798b7 100644 --- a/trajoptlib/CMakeLists.txt +++ b/trajoptlib/CMakeLists.txt @@ -63,6 +63,7 @@ option(BUILD_SHARED_LIBS "Build using shared libraries" ON) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS FALSE) option(BUILD_EXAMPLES "Build examples" OFF) +option(BUILD_PYTHON "Build python" OFF) include(CompilerFlags) @@ -83,6 +84,8 @@ target_compile_definitions(TrajoptLib PRIVATE TRAJOPT_EXPORTS) include(CTest) include(FetchContent) +option(USE_SYSTEM_NANOBIND "Use system nanobind" OFF) + if(BUILD_TESTING) # Catch2 dependency fetchcontent_declare( @@ -242,3 +245,89 @@ if(BUILD_EXAMPLES) endif() endforeach() endif() + +if(BUILD_PYTHON) + find_package(Python REQUIRED COMPONENTS Interpreter Development) + if(DEFINED PY_BUILD_CMAKE_MODULE_NAME) + set(PY_DEST ${PY_BUILD_CMAKE_MODULE_NAME}) + else() + set(PY_DEST lib/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}) + endif() + + # nanobind dependency + if(NOT USE_SYSTEM_NANOBIND) + fetchcontent_declare( + nanobind + GIT_REPOSITORY https://github.com/wjakob/nanobind.git + GIT_TAG v2.1.0 + ) + fetchcontent_makeavailable(nanobind) + else() + find_package(nanobind CONFIG REQUIRED) + endif() + + file(GLOB_RECURSE trajoptlibpy_src py/cpp/*.cpp) + + # Build Trajoptlib dependency directly into the wheel to avoid having to + # configure RPATHs + nanobind_add_module(_trajoptlib ${trajoptlibpy_src} ${TrajoptLib_src}) + compiler_flags(_trajoptlib) + target_compile_definitions(_trajoptlib PRIVATE TRAJOPTLIB=1) + target_include_directories( + _trajoptlib + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/py/cpp + ) + target_link_libraries(_trajoptlib PUBLIC Sleipnir) + + # Suppress compiler warnings in nanobind + if(${CMAKE_CXX_COMPILER_ID} STREQUAL "AppleClang") + target_compile_options(nanobind-static PRIVATE "-Wno-array-bounds") + endif() + + install( + TARGETS _trajoptlib + COMPONENT python_modules + LIBRARY + DESTINATION ${PY_DEST} + ) + + nanobind_add_stub( + _trajoptlib_stub + INSTALL_TIME + MARKER_FILE py/py.typed + MODULE _trajoptlib + OUTPUT trajoptlib/py/__init__.pyi + PYTHON_PATH $ + DEPENDS _trajoptlib + COMPONENT python_modules + ) + + # # pybind11_mkdoc doesn't support Windows + # if(NOT WIN32 AND NOT CMAKE_CROSSCOMPILING) + # # pybind11_mkdoc dependency + # fetchcontent_declare( + # pybind11_mkdoc + # GIT_REPOSITORY https://github.com/pybind/pybind11_mkdoc.git + # GIT_TAG master + # GIT_SUBMODULES "" + # ) + # fetchcontent_makeavailable(pybind11_mkdoc) + # + # file( + # GLOB_RECURSE trajoptlib_headers + # include/trajopt/*.hpp + # include/trajopt/path/*.hpp + # include/trajopt/geometry/*.hpp + # include/trajopt/constraint/*.hpp + # include/trajopt/util/*.hpp + # ) + # + # # Generate docs for the Python module + # include(cmake/modules/Pybind11Mkdoc.cmake) + # pybind11_mkdoc(_trajoptlib "${trajoptlib_headers}") + # add_dependencies(_trajoptlib _trajoptlib_docstrings) + # endif() +endif() diff --git a/trajoptlib/cmake/modules/Pybind11Mkdoc.cmake b/trajoptlib/cmake/modules/Pybind11Mkdoc.cmake new file mode 100644 index 000000000..c153f9732 --- /dev/null +++ b/trajoptlib/cmake/modules/Pybind11Mkdoc.cmake @@ -0,0 +1,31 @@ +function(pybind11_mkdoc target headers) + find_package(Python3 REQUIRED COMPONENTS Interpreter) + if(UNIX AND NOT APPLE) + set(env_vars LLVM_DIR_PATH=/usr/lib LIBCLANG_PATH=/usr/lib/libclang.so) + endif() + + get_target_property(target_dirs ${target} INCLUDE_DIRECTORIES) + list(TRANSFORM target_dirs PREPEND "-I") + + get_target_property(eigen_dirs Eigen3::Eigen INTERFACE_INCLUDE_DIRECTORIES) + list(FILTER eigen_dirs INCLUDE REGEX "\\$") + list(TRANSFORM eigen_dirs PREPEND "-I") + + add_custom_command( + OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/jormungandr/cpp/Docstrings.hpp + COMMAND + ${env_vars} ${Python3_EXECUTABLE} -m pybind11_mkdoc ${headers} -o + ${CMAKE_CURRENT_SOURCE_DIR}/jormungandr/cpp/Docstrings.hpp + -I/usr/lib/clang/18/include ${target_dirs} ${eigen_dirs} -std=c++23 + COMMAND + ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/fix_docstrings.py + ${CMAKE_CURRENT_SOURCE_DIR}/jormungandr/cpp/Docstrings.hpp + DEPENDS ${headers} + USES_TERMINAL + ) + add_custom_target( + ${target}_docstrings + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/jormungandr/cpp/Docstrings.hpp + ) +endfunction() diff --git a/trajoptlib/py/__init__.py b/trajoptlib/py/__init__.py new file mode 100644 index 000000000..8cbaae764 --- /dev/null +++ b/trajoptlib/py/__init__.py @@ -0,0 +1,5 @@ +""" +A library to generate time optimal trajectories for FRC robots. +""" + +from ._trajoptlib import * diff --git a/trajoptlib/py/cpp/BindSwerve.cpp b/trajoptlib/py/cpp/BindSwerve.cpp new file mode 100644 index 000000000..4f3056281 --- /dev/null +++ b/trajoptlib/py/cpp/BindSwerve.cpp @@ -0,0 +1,62 @@ +// Copyright (c) TrajoptLib contributors + +#include +#include + +#include + +#include +#include +#include + +namespace nb = nanobind; + +namespace trajopt { +/* +void BindSwerveDrivetrain(nb::class_ cls); +void BindSwerveSolution(nb::class_ cls); +void BindSwerveTrajectorySample(nb::class_ cls); +void BindSwerveTrajectory(nb::class_ cls); +void BindSwervePath(nb::class_ cls); +void BindSwervePathBuilder(nb::class_ cls); +void BindSwerveTrajectoryGenerator(nb::class_ cls); + */ + +void BindSwerveDrivetrain(nb::class_& cls) { + using namespace nb::literals; + + cls.def(nb::init>(), + "mass"_a, "moi"_a, "wheelRadius"_a, "wheelMaxAngularVelocity"_a, + "wheelMaxTorque"_a, "modules"_a); + cls.def_ro("mass", double, DOC(_trajoptlib, SwerveDrivetrain, double)); + cls.def_ro("moi", double, DOC(_trajoptlib, SwerveDrivetrain, double)); + cls.def_ro("wheelRadius", double, DOC(_trajoptlib, SwerveDrivetrain, double)); + cls.def_ro("wheelMaxAngularVelocity", double, DOC(_trajoptlib, SwerveDrivetrain, double)); + cls.def_ro("wheelMaxTorque", double, DOC(_trajoptlib, SwerveDrivetrain, double)); + cls.def_ro("modules", std::vector, DOC(_trajoptlib, SwerveDrivetrain, std::vector)); +} + +// void BindSwerveSolution(nb::class_& cls) { +// using namespace nb::literals; +// +// cls.def(nb::init< +// std::vector, std::vector, std::vector, std::vector, std::vector, std::vector, std::vector, std::vector, std::vector, std::vector, std::vector, std::vector, std::vector, >(), +// "dt"_a, "x"_a, "y"_a, "thetacos"_a, "thetasin"_a, "vx"_a, "vy"_a, +// "omega"_a, "ax"_a, "ay"_a, "alpha"_a, "moduleFX"_a, "moduleFY"_a); +// cls.def_ro("dt", std::vector); +// cls.def_ro("x", std::vector); +// cls.def_ro("y", std::vector); +// cls.def_ro("thetacos", std::vector); +// cls.def_ro("thetasin", std::vector); +// cls.def_ro("vx", std::vector); +// cls.def_ro("vy", std::vector); +// cls.def_ro("omega", std::vector); +// cls.def_ro("ax", std::vector); +// cls.def_ro("ay", std::vector); +// cls.def_ro("alpha", std::vector); +// cls.def_ro("moduleFX", std::vector); +// cls.def_ro("moduleFY", std::vector); +// } + +} // namespace trajopt diff --git a/trajoptlib/py/cpp/Binders.hpp b/trajoptlib/py/cpp/Binders.hpp new file mode 100644 index 000000000..89e7519b3 --- /dev/null +++ b/trajoptlib/py/cpp/Binders.hpp @@ -0,0 +1,36 @@ +// Copyright (c) TrajoptLib contributors + +#pragma once + +#include & + +namespace nb = nanobind; + +namespace trajopt { + +void BindSwerveDrivetrain(nb::class_& cls); +void BindSwerveSolution(nb::class_& cls); +void BindSwerveTrajectorySample(nb::class_& cls); +void BindSwerveTrajectory(nb::class_& cls); +void BindSwervePath(nb::class_& cls); +void BindSwervePathBuilder(nb::class_& cls); +void BindSwerveTrajectoryGenerator(nb::class_& cls); + +void BindDifferentialDrivetrain(nb::class_& cls); +void BindDifferentialSolution(nb::class_& cls); +void BindDifferentialTrajectorySample( + nb::class_& cls); +void BindDifferentialTrajectory(nb::class_& cls); +void BindDifferentialPath(nb::class_& cls); +void BindDifferentialPathBuilder(nb::class_& cls); +void BindDifferentialTrajectoryGenerator( + nb::class_& cls); + +// void BindExpressionType(nb::enum_&& e); +// void BindVariable(nb::module_& autodiff, nb::class__&& cls); +// void BindOCPSolver(nb::enum_&& transcription_method, +// nb::enum_&& dynamics_type, +// nb::enum_&& timestep_method, +// nb::class__&& cls); + +} // namespace trajopt diff --git a/trajoptlib/py/cpp/Main.cpp b/trajoptlib/py/cpp/Main.cpp new file mode 100644 index 000000000..e46952396 --- /dev/null +++ b/trajoptlib/py/cpp/Main.cpp @@ -0,0 +1,138 @@ +// Copyright (c) TrajoptLib contributors + +#include + +#include +#include + +#include "Binders.hpp" +// #include "Docstrings.hpp" // TODO docs + +namespace nb = nanobind; + +namespace trajoptlib { + +NB_MODULE(_trajoptlib, m) { + m.doc() = + "A library for generating time optimal trajectories for FRC robots."; + + nb::module_ geometry = m.def_submodule("constraint"); + nb::module_ geometry = m.def_submodule("geometry"); + nb::module_ geometry = m.def_submodule("obstacle"); + nb::module_ util = m.def_submodule("path"); + nb::module_ util = m.def_submodule("util"); + + nb::class_ swerve_drivetrain{ + _trajoptlib, "SwerveDrivetrain", DOC(trajoptlib, SwerveDrivetrain)}; + nb::class_ swerve_solution{_trajoptlib, "SwerveSolution", + DOC(trajoptlib, SwerveSolution)}; + nb::class_ swerve_trajectory_sample{ + _trajoptlib, "SwerveTrajectorySample", + DOC(trajoptlib, SwerveTrajectorySample)}; + nb::class_ swerve_trajectory{ + _trajoptlib, "SwerveTrajectory", DOC(trajoptlib, SwerveTrajectory)}; + nb::class_ swerve_path{_trajoptlib, "SwervePath", + DOC(trajoptlib, SwervePath)}; + nb::class_ swerve_path_builder{ + _trajoptlib, "SwervePathBuilder", DOC(trajoptlib, SwervePathBuilder)}; + nb::class_ swerve_trajectory_generator{ + _trajoptlib, "SwerveTrajectoryGenerator", + DOC(trajoptlib, SwerveTrajectoryGenerator)}; + + BindSwerveDrivetrain(swerve_drivetrain); + BindSwerveSolution(swerve_solution); + BindSwerveTrajectorySample(swerve_trajectory_sample); + BindSwerveTrajectory(swerve_trajectory); + BindSwervePath(swerve_path); + BindSwervePathBuilder(swerve_path_builder); + BindSwerveTrajectoryGenerator(swerve_trajectory_generator); + + nb::class_ differential_drivetrain{ + _trajoptlib, "DifferentialDrivetrain", + DOC(trajoptlib, DifferentialDrivetrain)}; + nb::class_ differential_solution{ + _trajoptlib, "DifferentialSolution", + DOC(trajoptlib, DifferentialSolution)}; + nb::class_ differential_trajectory_sample{ + _trajoptlib, "DifferentialTrajectorySample", + DOC(trajoptlib, DifferentialTrajectorySample)}; + nb::class_ differential_trajectory{ + _trajoptlib, "DifferentialTrajectory", + DOC(trajoptlib, DifferentialTrajectory)}; + nb::class_ differential_path{ + _trajoptlib, "DifferentialPath", DOC(trajoptlib, DifferentialPath)}; + nb::class_ differential_path_builder{ + _trajoptlib, "DifferentialPathBuilder", + DOC(trajoptlib, DifferentialPathBuilder)}; + nb::class_ differential_trajectory_generator{ + _trajoptlib, "DifferentialTrajectoryGenerator", + DOC(trajoptlib, DifferentialTrajectoryGenerator)}; + + BindDifferentialDrivetrain(differential_drivetrain); + BindDifferentialSolution(differential_solution); + BindDifferentialTrajectorySample(differential_trajectory_sample); + BindDifferentialTrajectory(differential_trajectory); + BindDifferentialPath(differential_path); + BindDifferentialPathBuilder(differential_path_builder); + BindDifferentialTrajectoryGenerator(differential_trajectory_generator); + + // nb::class_ variable{autodiff, "Variable", DOC(sleipnir, + // Variable)}; nb::class_ variable_matrix{autodiff, + // "VariableMatrix", + // DOC(sleipnir, VariableMatrix)}; + // nb::class_> variable_block{ + // autodiff, "VariableBlock", DOC(sleipnir, VariableBlock)}; + // + // nb::class_ gradient{autodiff, "Gradient", DOC(sleipnir, + // Gradient)}; nb::class_ hessian{autodiff, "Hessian", DOC(sleipnir, + // Hessian)}; nb::class_ jacobian{autodiff, "Jacobian", + // DOC(sleipnir, Jacobian)}; + // + // nb::class_ equality_constraints{ + // optimization, "EqualityConstraints", DOC(sleipnir, + // EqualityConstraints)}; + // nb::class_ inequality_constraints{ + // optimization, "InequalityConstraints", + // DOC(sleipnir_InequalityConstraints)}; + // + // nb::class_ solver_iteration_info{ + // optimization, "SolverIterationInfo", DOC(sleipnir, + // SolverIterationInfo)}; + // nb::class_ solver_status{optimization, "SolverStatus", + // DOC(sleipnir, SolverStatus)}; + // + // nb::class_ optimization_problem{ + // optimization, "OptimizationProblem", DOC(sleipnir, + // OptimizationProblem)}; + // + // nb::class_ ocp_solver{ + // control, "OCPSolver", DOC(sleipnir, OCPSolver)}; + // + // BindExpressionType(expression_type); + // + // BindVariable(autodiff, variable); + // BindVariableMatrix(autodiff, variable_matrix); + // BindVariableBlock(variable_block); + // + // // Implicit conversions + // variable.def(nb::init_implicit()); + // variable_matrix.def(nb::init_implicit>()); + // + // BindGradient(gradient); + // BindHessian(hessian); + // BindJacobian(jacobian); + // + // BindEqualityConstraints(equality_constraints); + // BindInequalityConstraints(inequality_constraints); + // + // BindSolverExitCondition(solver_exit_condition); + // BindSolverIterationInfo(solver_iteration_info); + // BindSolverStatus(solver_status); + // + // BindOptimizationProblem(optimization_problem); + // + // BindOCPSolver(transcription_method, dynamics_type, timestep_method, + // ocp_solver); +} + +} // namespace trajoptlib diff --git a/trajoptlib/pyproject.toml b/trajoptlib/pyproject.toml new file mode 100644 index 000000000..71416ce3c --- /dev/null +++ b/trajoptlib/pyproject.toml @@ -0,0 +1,44 @@ +[project] +name = "trajoptlib" +description = "A library for generating time optimal trajectories for FRC robots." +version = "0.0.1.dev124" +readme = "README.md" +requires-python = ">=3.9" +dependencies = [ "matplotlib", "numpy", "scipy" ] + + [project.license] + file = "LICENSE.txt" + + [project.urls] + Documentation = "https://sleipnirgroup.github.io/Choreo/trajoptlib/" + +[build-system] +requires = [ "py-build-cmake~=0.1.8", "nanobind", "pybind11-mkdoc" ] +build-backend = "py_build_cmake.build" + +[tool.py-build-cmake.module] +name = "py" +directory = "." + +[tool.py-build-cmake.sdist] +include = [ + "CMakeLists.txt", + "TrajoptlibConfig.cmake.in", + "cmake/modules/*.cmake", + "cmake/*.py", + "include/*", + "src/*", + "py/*" +] +exclude = [ ] + +[tool.py-build-cmake.cmake] +build_type = "Release" +source_path = "." +build_args = [ "--target", "_trajoptlib" ] +install_components = [ "python_modules" ] +install_args = [ ] + + [tool.py-build-cmake.cmake.options] + BUILD_TESTING = "OFF" + BUILD_PYTHON = "ON"