Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
69ed843
bindings: Setup nanobind + sphinx
arntanguy Apr 25, 2025
ca18545
bindings: setup MotionVecd/ForceVecd
arntanguy Apr 28, 2025
4ec82ef
build(bindings): Setup incremental and isolated build targets
arntanguy Jul 23, 2025
afc43db
chore: .gitignore
arntanguy Jul 23, 2025
d738293
build(bindings): do not build nanobind targets by default as they nee…
arntanguy Nov 18, 2025
ce0fe02
build(bindings): Use venv python version
arntanguy Nov 19, 2025
c2c164a
build(nix): Add nix flake (based on gepetto setup)
arntanguy Jan 5, 2026
d4d3e15
build(cmake-v2): add as submodule
arntanguy Jan 5, 2026
0c7fb43
build(cmake-v2): export SpaceVecAlg, build testing
arntanguy Jan 6, 2026
2586d04
build(cmake-v2): adapt nanobind bindings
arntanguy Jan 6, 2026
70f7254
build: disable cython bindings
arntanguy Jan 6, 2026
abdf020
build(cmake-v2): fix rebase
arntanguy Jan 6, 2026
9614bb5
build(cmake-v2): cleanup
arntanguy Jan 6, 2026
cfc9153
build(nanobind): cleanup, add sphinx to nix
arntanguy Jan 6, 2026
0b97611
feat(bindings): use sphinx-autodoc
arntanguy Jan 7, 2026
00ea437
feat(bindings)!: Re-export all public symbols from sva.sva_pywrap to sva
arntanguy Jan 7, 2026
fa9872f
build(cmake-v2): Fix use of add_subdirectory
arntanguy Jan 7, 2026
4d99283
feat(nix): setup direnv
arntanguy Jan 7, 2026
609bff9
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 7, 2026
e590621
feat(nix/cmake-v2): Use jrl-cmakemodule flake for the PR branch
arntanguy Jan 7, 2026
79a89b0
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 7, 2026
d5ef7b5
feat(bindings): partially bind PTransformd
arntanguy Jan 8, 2026
74f0ca9
build(cmake-v2): fix install of headers
arntanguy Jan 8, 2026
84b4984
feat(bindings): Bind everything needed to implement test_sva_ptransfo…
arntanguy Jan 9, 2026
8a5f7b8
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 9, 2026
08d02c3
feat(nix): follow Guilhem suggestions
arntanguy Jan 12, 2026
5e44bcd
feat(bindings): Add missing operators, port test_sva_inertia
arntanguy Feb 2, 2026
c38d4d5
feat(bindings): add pikle serialization
arntanguy Feb 2, 2026
e77b8b6
feat(bindings): bind AdmittanceVecd/ImpedanceVecd/EigenUtils and test…
arntanguy Feb 2, 2026
795c1b4
feat(bindings): adapt test_sva_containers.py
arntanguy Feb 2, 2026
3b14c12
ci(nix): setup nix ci
arntanguy Feb 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export NIX_CONFIG="allow-import-from-derivation = true"

if command -v nix >/dev/null; then
use flake
fi
40 changes: 40 additions & 0 deletions .github/workflows/nix.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Nix CI

on:
push:
branches:
- '**'
pull_request:
branches:
- '**'

jobs:
nix-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install Nix
uses: cachix/install-nix-action@v27

- name: Show Nix environment info
run: nix --version

- name: Build with CMake in Nix shell
run: |
nix develop --command cmake -S . -B build
nix develop --command cmake --build build

- name: Run tests (pytest)
run: |
nix develop --command pytest binding/nanobind/tests

# Optionally, run C++ tests if you have them
- name: Run C++ tests (ctest)
run: |
nix develop --command ctest --test-dir build || true

# Optionally, build documentation
- name: Build documentation
run: |
nix develop --command make -C binding/nanobind/docs html || true
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@ binding/python/build
*.swp
CMakeLists.txt.user*
*.autosave
build/
build
.cache/
compile_commands.json
result
.direnv/
6 changes: 3 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[submodule "cmake"]
path = cmake
url = https://github.com/jrl-umi3218/jrl-cmakemodules
[submodule "cmake-v2"]
path = cmake-v2
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use this script to handle the new jrl-cmakemodules.

This allows to remove submodules completely.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I didn't want to bother with figuring out nix setup (we cannot use FetchContent in that case), so I went the easy route of submodule for now. Turns out it was simple to handle in nix, so this is fixed in e590621

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good. Once the JRL V2 PR is merged, don't forget to update the link in the fetchcontent part

url = https://github.com/ahoarau/jrl-cmakemodules.git
76 changes: 47 additions & 29 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,39 @@
# Copyright 2012-2019 CNRS-UM LIRMM, CNRS-AIST JRL
#

cmake_minimum_required(VERSION 3.5)
cmake_minimum_required(VERSION 3.22)

find_package(jrl-cmakemodules 0.2.0 CONFIG QUIET)

include(cmake/get-jrl-cmakemodules.cmake)

project(
SpaceVecAlg
VERSION 1.2.7
DESCRIPTION
"Implementation of spatial vector algebra with the Eigen3 linear algebra library."
HOMEPAGE_URL "https://github.com/jrl-umi3218/SpaceVecAlg"
LANGUAGES CXX)

# Configure default binary and install directories, build type, etc.
jrl_configure_defaults()

set(CMAKE_CXX_STANDARD 17)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)

set(PROJECT_NAME SpaceVecAlg)
set(PROJECT_DESCRIPTION
"Implementation of spatial vector algebra with the Eigen3 linear algebra library."
)
set(PROJECT_URL "https://github.com/jrl-umi3218/SpaceVecAlg")
set(PROJECT_VERSION 1.2.8)
set(PROJECT_USE_CMAKE_EXPORT TRUE)
set(PYTHON_BINDING_USE_PYTEST ON)

include(cmake/base.cmake)
include(cmake/cython/cython.cmake)
include(cmake/msvc-specific.cmake)

# Disable -Werror on Unix for now.
set(CXX_DISABLE_WERROR True)
project(${PROJECT_NAME} CXX)

option(BENCHMARKS "Generate benchmark." OFF)

find_package(Eigen3 QUIET NO_CMAKE_PACKAGE_REGISTRY)
if(Eigen3_FOUND)
add_project_dependency(Eigen3 REQUIRED NO_CMAKE_PACKAGE_REGISTRY)
else()
add_project_dependency(Eigen3 MODULE REQUIRED)
endif()
# -------
# OPTIONS
# -------
jrl_option(BUILD_TESTING "Build testing." ON)
jrl_option(BENCHMARKS "Generate benchmark." OFF)
jrl_option(NANOBIND_BINDINGS "Build nanobind Python bindings" ON)
# Disable old python bindings jrl_option(PYTHON_BINDING "Build cython Python
# bindings" OFF)

# ------------
# DEPENDENCIES
# ------------
jrl_find_package(Eigen3 REQUIRED)

# For MSVC, set local environment variable to enable finding the built dll of
# the main library when launching ctest with RUN_TESTS
Expand All @@ -41,10 +44,25 @@ endif(MSVC)

add_subdirectory(src)

if(${BUILD_TESTING} OR ${BENCHMARKS})
if(BUILD_TESTING OR BENCHMARKS)
enable_testing()
add_subdirectory(tests)
endif()

if(${PYTHON_BINDING})
add_subdirectory(binding/python)
if(NANOBIND_BINDINGS)
message(STATUS "[${PROJECT_NAME}] Building Python bindings")
jrl_find_python(3.8 REQUIRED COMPONENTS Interpreter Development.Module)
jrl_find_nanobind(2.4.0 CONFIG REQUIRED)
jrl_find_package(nanoeigenpy CONFIG REQUIRED)
add_subdirectory(binding/nanobind)
endif()

# Disable cython bindings if(PYTHON_BINDING) add_subdirectory(binding/python)
# endif()

# Export the package files (headers, targets, config files, etc.)
jrl_export_package()

# Print summaries
jrl_print_dependencies_summary()
jrl_print_options_summary()
2 changes: 2 additions & 0 deletions binding/nanobind/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CMakeCache.txt
CMakeFiles/
76 changes: 76 additions & 0 deletions binding/nanobind/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# We are now ready to compile the actual extension module
nanobind_add_module(
# Name of the extension
sva_pywrap
# Target the stable ABI for Python 3.12+, which reduces the number of binary
# wheels that must be built. This does nothing on older Python versions
STABLE_ABI
# Build libnanobind statically and merge it into the extension (which itself
# remains a shared library)
#
# If your project builds multiple extensions, you can replace this flag by
# NB_SHARED to conserve space by reusing a shared libnanobind across libraries
NB_STATIC
LTO
NB_SUPPRESS_WARNINGS
# Source code goes here
src/sva/sva_module.cpp
src/sva/bind_PTransformd.cpp
src/sva/bind_ForceVecd.cpp
src/sva/bind_MotionVecd.cpp
src/sva/bind_ABInertiad.cpp
src/sva/bind_RBInertiad.cpp
src/sva/bind_Operators.cpp
src/sva/bind_AdmittanceVecd.cpp
src/sva/bind_ImpedanceVecd.cpp
src/sva/bind_EigenUtility.cpp)
jrl_check_python_module_name(sva_pywrap)
target_link_libraries(sva_pywrap PRIVATE SpaceVecAlg)
target_link_libraries(sva_pywrap PRIVATE nanoeigenpy::nanoeigenpy_headers)
jrl_target_set_default_compile_options(sva_pywrap PRIVATE)
jrl_target_set_output_directory(sva_pywrap OUTPUT_DIRECTORY
${CMAKE_BINARY_DIR}/lib/site-packages/sva)

# XXX: as-is the default generated __init__.py includes the package as
# sva.sva_pywrap.<classname>. I prefer all symbols to be in the public namespace
# sva.<classname> thus we install our own jrl_python_generate_init_py(
# sva_pywrap OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib/site-packages/sva/__init__.py)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/__init__.conf.py
${CMAKE_BINARY_DIR}/lib/site-packages/sva/__init__.py COPYONLY)

# This is needed for LSP to recognise sva.<symbol> instead of
# sva.sva_pywrap.<symbol>
file(WRITE ${CMAKE_BINARY_DIR}/lib/site-packages/sva/__init__.pyi
"from .sva_pywrap import *\n")

# Display output of stub generation
set(CMAKE_EXECUTE_PROCESS_COMMAND_ECHO STDOUT)

nanobind_add_stub(
sva_pywrap_stub
MODULE
sva.sva_pywrap
OUTPUT
${CMAKE_BINARY_DIR}/lib/site-packages/sva/sva_pywrap.pyi
PYTHON_PATH
${CMAKE_BINARY_DIR}/lib/site-packages
MARKER_FILE
${CMAKE_BINARY_DIR}/lib/site-packages/sva/py.typed
DEPENDS
sva_pywrap
VERBOSE)

jrl_python_compile_all(DIRECTORY ${CMAKE_BINARY_DIR}/lib/site-packages/sva
VERBOSE)

jrl_python_compute_install_dir(python_install_dir)
install(
DIRECTORY ${CMAKE_BINARY_DIR}/lib/site-packages/sva
DESTINATION ${python_install_dir}
FILES_MATCHING
PATTERN "*.py"
PATTERN "*.pyc"
PATTERN "*.pyi"
PATTERN "*.typed"
PATTERN "*.so"
PATTERN "*.pyd")
5 changes: 5 additions & 0 deletions binding/nanobind/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# nanobind python bindings for mc_rtc

## Build

See https://nanobind.readthedocs.io/en/latest/packaging.html for details
53 changes: 53 additions & 0 deletions binding/nanobind/__init__.conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Copyright 2025-2026 Inria

import os
import platform
import contextlib

class DllDirectoryManager(contextlib.AbstractContextManager):
"""Context manager for managing DLL directories on Windows.
On Windows with Python 3.8+, Python doesn't search DLL in PATH anymore
We must specify DLL search path manually with `os.add_dll_directory`
On Linux and MacOS, we can use DYLD_LIBRARY_PATH and LD_LIBRARY_PATH
environment variables, or simply the rpath search for shared libraries.
ref: https://github.com/python/cpython/issues/87339#issuecomment-1093902060
https://docs.python.org/3/library/os.html#os.add_dll_directory
"""

def safe_add_dll_directory(self, dll_dir: str):
"""Add a DLL directory to the search path, if path exists."""
if os.path.isdir(dll_dir):
self.dll_dirs.append(os.add_dll_directory(dll_dir))

def __enter__(self):
self.dll_dirs = []
return self

def __exit__(self, *exc_details):
for d in self.dll_dirs:
d.close()

if platform.system() == 'Windows':
with DllDirectoryManager() as mgr:
module_path = os.path.dirname(__file__)
mgr.safe_add_dll_directory(os.path.join(module_path, '..', '..', '..', 'bin'))
dll_dirs = []
for dll_dir in dll_dirs:
mgr.safe_add_dll_directory(os.path.join(module_path, dll_dir))

# from .sva_pywrap import * # noqa
# if hasattr(sva_pywrap, "__version__"):
# __version__ = sva_pywrap.__version__ # noqa

# Re-export all public names from sva_pywrap
# This is done to get a flat package structure where types appear as
# sva.<classname> instead of sva.sva_pywrap.<symbol>
#
# Consider symbols prefixed with an underscore _<symbol> as private.
# They remain accessible as sva.sva_pywrap._<symbol>
from . import sva_pywrap
for name in dir(sva_pywrap):
if not name.startswith("_"):
globals()[name] = getattr(sva_pywrap, name)

__all__ = [name for name in dir(sva_pywrap) if not name.startswith("_")]
20 changes: 20 additions & 0 deletions binding/nanobind/docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
35 changes: 35 additions & 0 deletions binding/nanobind/docs/make.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@ECHO OFF

pushd %~dp0

REM Command file for Sphinx documentation

if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build

%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)

if "%1" == "" goto help

%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end

:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%

:end
popd
12 changes: 12 additions & 0 deletions binding/nanobind/docs/source/api.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
API Reference
=============

.. automodule:: sva
:members:
:undoc-members:
:show-inheritance:

.. automodule:: sva.sva_pywrap
:members:
:undoc-members:
:show-inheritance:
Loading
Loading