From 58d3a84e94ffca7d90bd75ce661490cca46d08c7 Mon Sep 17 00:00:00 2001 From: DiegoP-G Date: Thu, 25 Jun 2026 16:05:05 +0200 Subject: [PATCH 1/5] add automatic Python stub generation with pybind11-stubgen --- src/CMakeLists.txt | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7664b04..803c4f0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,6 +21,23 @@ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ------------------------------------------------------------------------------ +# Python stubs generation (optional) +# ------------------------------------------------------------------------------ +option(GENERATE_PYTHON_STUBS "Generate Python stubs (.pyi files)" ON) + +if(GENERATE_PYTHON_STUBS) + find_program(PYBIND11_STUBGEN_EXECUTABLE pybind11-stubgen) + if(NOT PYBIND11_STUBGEN_EXECUTABLE) + message( + WARNING + "pybind11-stubgen not found. Stubs will not be generated." + ) + set(GENERATE_PYTHON_STUBS OFF CACHE BOOL "Generate Python stubs" FORCE) + else() + message(STATUS "pybind11-stubgen found: ${PYBIND11_STUBGEN_EXECUTABLE}") + endif() +endif() # ADD_PYTHON_LIBRARY ( module FILES file1.cc file2.cc ... LINK_LIBRARIES # hpp-core::hpp-core ...) @@ -82,6 +99,45 @@ macro(ADD_PYTHON_LIBRARY MODULE) ) endmacro() +macro(ADD_PYTHON_STUBS MODULE) + set(options) + set(oneValueArgs) + set(multiValueArgs FILES PY_FILES LINK_LIBRARIES) + cmake_parse_arguments( + ADD_PYTHON_STUBS + "${options}" + "${oneValueArgs}" + "${multiValueArgs}" + ${ARGN} + ) + string(REPLACE "/" "." MODULE_DOT_PATH "${MODULE}") + set(STUB_OUTPUT_DIR ${CMAKE_BINARY_DIR}/stubs) + string(REGEX REPLACE "[/-]" "_" TARGET_NAME "${MODULE}") + + add_custom_command( + OUTPUT ${STUB_OUTPUT_DIR}/${MODULE}/${LIBNAME}.pyi + COMMAND + ${CMAKE_COMMAND} -E env + "PYTHONPATH=${CMAKE_BINARY_DIR}/src:$ENV{PYTHONPATH}" + ${PYBIND11_STUBGEN_EXECUTABLE} ${MODULE_DOT_PATH}.${LIBNAME} + --output-dir ${STUB_OUTPUT_DIR} + DEPENDS ${TARGET_NAME} + COMMENT "Generating Python stubs for ${MODULE_DOT_PATH}.${LIBNAME}" + VERBATIM + ) + + add_custom_target( + stubs_${TARGET_NAME} + ALL + DEPENDS ${STUB_OUTPUT_DIR}/${MODULE}/${LIBNAME}.pyi + ) + + install( + FILES ${STUB_OUTPUT_DIR}/${MODULE}/${LIBNAME}.pyi + DESTINATION ${PYTHON_SITELIB}/${MODULE} + ) +endmacro() + python_install_on_site(pyhpp __init__.py) python_install_on_site(pyhpp/pinocchio utils.py) @@ -199,6 +255,22 @@ add_python_library( hpp-manipulation-urdf::hpp-manipulation-urdf ) +if(GENERATE_PYTHON_STUBS AND PYBIND11_STUBGEN_EXECUTABLE) + add_python_stubs(pyhpp/pinocchio) + add_python_stubs(pyhpp/pinocchio/urdf) + + add_python_stubs(pyhpp/core) + add_python_stubs(pyhpp/core/problem_target) + add_python_stubs(pyhpp/core/path) + add_python_stubs(pyhpp/core/path_optimization) + + add_python_stubs(pyhpp/constraints) + + add_python_stubs(pyhpp/manipulation) + add_python_stubs(pyhpp/manipulation/steering_method) + add_python_stubs(pyhpp/manipulation/urdf) +endif() + # Install tool submodule python_install_on_site(pyhpp/tools __init__.py) python_install_on_site(pyhpp/tools xacro.py) From 2bcd679ec2927ed215bb3d934a037492cdf22187 Mon Sep 17 00:00:00 2001 From: DiegoP-G Date: Fri, 26 Jun 2026 12:29:26 +0200 Subject: [PATCH 2/5] add the stub generation option on the main CMakelist.txt --- CMakeLists.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ccd722..85f13d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,6 +125,20 @@ endif() add_library(${PROJECT_NAME} INTERFACE) install(TARGETS ${PROJECT_NAME} EXPORT ${TARGETS_EXPORT_NAME} DESTINATION lib) +option(GENERATE_PYTHON_STUBS "Generate Python stubs (.pyi files)" ON) +if(GENERATE_PYTHON_STUBS) + find_program(PYBIND11_STUBGEN_EXECUTABLE pybind11-stubgen) + if(NOT PYBIND11_STUBGEN_EXECUTABLE) + message( + WARNING + "pybind11-stubgen not found. Stubs will not be generated." + ) + set(GENERATE_PYTHON_STUBS OFF CACHE BOOL "Generate Python stubs" FORCE) + else() + message(STATUS "pybind11-stubgen found: ${PYBIND11_STUBGEN_EXECUTABLE}") + endif() +endif() + add_subdirectory(src) if(BUILD_TESTING) add_subdirectory(tests) From 516c07e3b540f5894742d65fc53d83c1550e9cad Mon Sep 17 00:00:00 2001 From: DiegoP-G Date: Fri, 26 Jun 2026 12:29:35 +0200 Subject: [PATCH 3/5] add the stub generation option on the main CMakelist.txt --- src/CMakeLists.txt | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 803c4f0..d3c9c60 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,23 +21,6 @@ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# ------------------------------------------------------------------------------ -# Python stubs generation (optional) -# ------------------------------------------------------------------------------ -option(GENERATE_PYTHON_STUBS "Generate Python stubs (.pyi files)" ON) - -if(GENERATE_PYTHON_STUBS) - find_program(PYBIND11_STUBGEN_EXECUTABLE pybind11-stubgen) - if(NOT PYBIND11_STUBGEN_EXECUTABLE) - message( - WARNING - "pybind11-stubgen not found. Stubs will not be generated." - ) - set(GENERATE_PYTHON_STUBS OFF CACHE BOOL "Generate Python stubs" FORCE) - else() - message(STATUS "pybind11-stubgen found: ${PYBIND11_STUBGEN_EXECUTABLE}") - endif() -endif() # ADD_PYTHON_LIBRARY ( module FILES file1.cc file2.cc ... LINK_LIBRARIES # hpp-core::hpp-core ...) From f5c6a39d3bef72e625e4cc566b9b795a1e884b9d Mon Sep 17 00:00:00 2001 From: DiegoP-G Date: Fri, 26 Jun 2026 13:51:17 +0200 Subject: [PATCH 4/5] cmake: fix race condition in stub generation with parallel builds Add a global synchronization target `all_pyhpp_bindings` that regroup all binding targets. Stub generation now depends on this target instead of individual bindings, ensuring all .so files are compiled before any pybind11-stubgen invocation when using `make -j`. --- src/CMakeLists.txt | 80 ++++++++++++++++++---------------------------- 1 file changed, 31 insertions(+), 49 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d3c9c60..a8a59d0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -80,45 +80,40 @@ macro(ADD_PYTHON_LIBRARY MODULE) FILES ${CMAKE_CURRENT_BINARY_DIR}/${MODULE}/__init__.py DESTINATION ${PYTHON_SITELIB}/${MODULE} ) -endmacro() -macro(ADD_PYTHON_STUBS MODULE) - set(options) - set(oneValueArgs) - set(multiValueArgs FILES PY_FILES LINK_LIBRARIES) - cmake_parse_arguments( - ADD_PYTHON_STUBS - "${options}" - "${oneValueArgs}" - "${multiValueArgs}" - ${ARGN} + set_property( + GLOBAL + APPEND + PROPERTY PYHPP_ALL_BINDING_TARGETS ${TARGET_NAME} ) - string(REPLACE "/" "." MODULE_DOT_PATH "${MODULE}") - set(STUB_OUTPUT_DIR ${CMAKE_BINARY_DIR}/stubs) - string(REGEX REPLACE "[/-]" "_" TARGET_NAME "${MODULE}") - add_custom_command( - OUTPUT ${STUB_OUTPUT_DIR}/${MODULE}/${LIBNAME}.pyi - COMMAND - ${CMAKE_COMMAND} -E env - "PYTHONPATH=${CMAKE_BINARY_DIR}/src:$ENV{PYTHONPATH}" - ${PYBIND11_STUBGEN_EXECUTABLE} ${MODULE_DOT_PATH}.${LIBNAME} - --output-dir ${STUB_OUTPUT_DIR} - DEPENDS ${TARGET_NAME} - COMMENT "Generating Python stubs for ${MODULE_DOT_PATH}.${LIBNAME}" - VERBATIM - ) + if(GENERATE_PYTHON_STUBS AND PYBIND11_STUBGEN_EXECUTABLE) + string(REPLACE "/" "." MODULE_DOT_PATH "${MODULE}") + set(STUB_OUTPUT_DIR ${CMAKE_BINARY_DIR}/stubs) - add_custom_target( - stubs_${TARGET_NAME} - ALL - DEPENDS ${STUB_OUTPUT_DIR}/${MODULE}/${LIBNAME}.pyi - ) + add_custom_command( + OUTPUT ${STUB_OUTPUT_DIR}/${MODULE}/${LIBNAME}.pyi + COMMAND + ${CMAKE_COMMAND} -E env + "PYTHONPATH=${CMAKE_BINARY_DIR}/src:$ENV{PYTHONPATH}" + ${PYBIND11_STUBGEN_EXECUTABLE} ${MODULE_DOT_PATH}.${LIBNAME} + --output-dir ${STUB_OUTPUT_DIR} + DEPENDS all_pyhpp_bindings + COMMENT "Generating Python stubs for ${MODULE_DOT_PATH}.${LIBNAME}" + VERBATIM + ) - install( - FILES ${STUB_OUTPUT_DIR}/${MODULE}/${LIBNAME}.pyi - DESTINATION ${PYTHON_SITELIB}/${MODULE} - ) + add_custom_target( + stubs_${TARGET_NAME} + ALL + DEPENDS ${STUB_OUTPUT_DIR}/${MODULE}/${LIBNAME}.pyi + ) + + install( + FILES ${STUB_OUTPUT_DIR}/${MODULE}/${LIBNAME}.pyi + DESTINATION ${PYTHON_SITELIB}/${MODULE} + ) + endif() endmacro() python_install_on_site(pyhpp __init__.py) @@ -238,21 +233,8 @@ add_python_library( hpp-manipulation-urdf::hpp-manipulation-urdf ) -if(GENERATE_PYTHON_STUBS AND PYBIND11_STUBGEN_EXECUTABLE) - add_python_stubs(pyhpp/pinocchio) - add_python_stubs(pyhpp/pinocchio/urdf) - - add_python_stubs(pyhpp/core) - add_python_stubs(pyhpp/core/problem_target) - add_python_stubs(pyhpp/core/path) - add_python_stubs(pyhpp/core/path_optimization) - - add_python_stubs(pyhpp/constraints) - - add_python_stubs(pyhpp/manipulation) - add_python_stubs(pyhpp/manipulation/steering_method) - add_python_stubs(pyhpp/manipulation/urdf) -endif() +get_property(PYHPP_ALL_BINDINGS GLOBAL PROPERTY PYHPP_ALL_BINDING_TARGETS) +add_custom_target(all_pyhpp_bindings DEPENDS ${PYHPP_ALL_BINDINGS}) # Install tool submodule python_install_on_site(pyhpp/tools __init__.py) From af774e4cfc40aee65e5d2cb8e5cd86f59ee47211 Mon Sep 17 00:00:00 2001 From: Guilhem Saurel Date: Fri, 26 Jun 2026 14:21:55 +0200 Subject: [PATCH 5/5] nix: add pybind11-stubgen --- flake.nix | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/flake.nix b/flake.nix index 91de00e..861e72b 100644 --- a/flake.nix +++ b/flake.nix @@ -27,19 +27,22 @@ inputs.hpp-core.overlays.flakoboros inputs.hpp-manipulation.overlays.flakoboros ]; - pyOverrideAttrs.hpp-python = { - src = lib.fileset.toSource { - root = ./.; - fileset = lib.fileset.unions [ - ./CMakeLists.txt - ./doc - ./include - ./package.xml - ./src - ./tests - ]; + pyOverrideAttrs.hpp-python = + { drv-prev, python-final, ... }: + { + nativeBuildInputs = drv-prev.nativeBuildInputs ++ [ python-final.pybind11-stubgen ]; + src = lib.fileset.toSource { + root = ./.; + fileset = lib.fileset.unions [ + ./CMakeLists.txt + ./doc + ./include + ./package.xml + ./src + ./tests + ]; + }; }; - }; } ); }