Repository: machines-in-motion/mim_solvers (language: python)

Issue:
Add CI
This also means we need to add unit tests in C++ and Python


Discussion / hints:
C++ and python unit tests were added in the [last release](https://github.com/machines-in-motion/mim_solvers/releases/tag/v0.0.3)

We will add the CI as soon as possible 

Fix patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..4dcbf62
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,31 @@
+name: Test
+
+on: [push, pull_request]
+
+env:
+  CTEST_OUTPUT_ON_FAILURE: ON
+  CTEST_PARALLEL_LEVEL: 4
+  CTEST_PROGRESS_OUTPUT: ON
+
+jobs:
+  test:
+    name: "Test python ${{ matrix.python-version }}"
+    runs-on: "ubuntu-latest"
+    strategy:
+      matrix:
+        python-version: ["3.8", "3.9", "3.10", "3.11"]
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          submodules: 'true'
+      - uses: actions/setup-python@v5
+        with:
+          python-version: ${{ matrix.python-version }}
+      - run: python -m pip install crocoddyl[build] proxsuite[build]
+      - run: echo "CMAKE_PREFIX_PATH=$(cmeel cmake)" >> $GITHUB_ENV
+      - run: cmake -B build -S .
+      - run: cmake --build build -j 4
+
+      - run: python -m pip install osqp
+      - run: echo "LD_LIBRARY_PATH=$(cmeel lib)" >> $GITHUB_ENV
+      - run: cmake --build build -t test
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..29f8457
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,5 @@
+repos:
+- repo: https://github.com/cheshirekow/cmake-format-precommit
+  rev: v0.6.13
+  hooks:
+  - id: cmake-format
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 932e8ce..4db1e82 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,14 +3,15 @@ cmake_minimum_required(VERSION 3.10)
 # Project properties
 set(PROJECT_NAMESPACE machines-in-motion)
 set(PROJECT_NAME mim_solvers)
-set(PROJECT_DESCRIPTION "Numerical solvers of the Machines in Motion Laboratory.")
+set(PROJECT_DESCRIPTION
+    "Numerical solvers of the Machines in Motion Laboratory.")
 set(PROJECT_URL https://github.com/${PROJECT_NAMESPACE}/${PROJECT_NAME})
-SET(PROJECT_CUSTOM_HEADER_EXTENSION "hpp")
-SET(PROJECT_USE_CMAKE_EXPORT TRUE)
-SET(PROJECT_USE_KEYWORD_LINK_LIBRARIES TRUE)
+set(PROJECT_CUSTOM_HEADER_EXTENSION "hpp")
+set(PROJECT_USE_CMAKE_EXPORT TRUE)
+set(PROJECT_USE_KEYWORD_LINK_LIBRARIES TRUE)
 
-SET(CXX_DISABLE_WERROR True)
-SET(CMAKE_VERBOSE_MAKEFILE True)
+set(CXX_DISABLE_WERROR True)
+set(CMAKE_VERBOSE_MAKEFILE True)
 
 add_compile_options(--warn-no-conversion)
 
@@ -26,30 +27,55 @@ set(CUSTOM_HEADER_DIR ${PROJECT_NAME})
 
 # Check if the submodule cmake have been initialized
 set(JRL_CMAKE_MODULES "${CMAKE_CURRENT_LIST_DIR}/cmake")
-IF(NOT EXISTS "${CMAKE_SOURCE_DIR}/cmake/base.cmake")
-  MESSAGE(FATAL_ERROR "\ncmake/base.cmake not found. Please run the following command first:\ngit submodule update --init\n")
-ENDIF()
+if(EXISTS "${JRL_CMAKE_MODULES}/base.cmake")
+  message(STATUS "JRL cmakemodules found in 'cmake/' git submodule")
+else()
+  find_package(jrl-cmakemodules QUIET CONFIG)
+  if(jrl-cmakemodules_FOUND)
+    get_property(
+      JRL_CMAKE_MODULES
+      TARGET jrl-cmakemodules::jrl-cmakemodules
+      PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
+    message(STATUS "JRL cmakemodules found on system at ${JRL_CMAKE_MODULES}")
+  elseif(${CMAKE_VERSION} VERSION_LESS "3.14.0")
+    message(
+      FATAL_ERROR
+        "\nCan't find jrl-cmakemodules. Please either:\n"
+        "  - use git submodule: 'git submodule update --init'\n"
+        "  - or install https://github.com/jrl-umi3218/jrl-cmakemodules\n"
+        "  - or upgrade your CMake version to >= 3.14 to allow automatic fetching\n"
+    )
+  else()
+    message(STATUS "JRL cmakemodules not found. Let's fetch it.")
+    include(FetchContent)
+    FetchContent_Declare(
+      "jrl-cmakemodules"
+      GIT_REPOSITORY "https://github.com/jrl-umi3218/jrl-cmakemodules.git")
+    FetchContent_MakeAvailable("jrl-cmakemodules")
+    FetchContent_GetProperties("jrl-cmakemodules" SOURCE_DIR JRL_CMAKE_MODULES)
+  endif()
+endif()
 
 # JRL-cmakemodule setup
-include(${CMAKE_CURRENT_LIST_DIR}/cmake/base.cmake)
+include(${JRL_CMAKE_MODULES}/base.cmake)
 # Project definition
-COMPUTE_PROJECT_ARGS(PROJECT_ARGS LANGUAGES CXX)
+compute_project_args(PROJECT_ARGS LANGUAGES CXX)
 project(${PROJECT_NAME} ${PROJECT_ARGS})
 
-include(${CMAKE_CURRENT_LIST_DIR}/cmake/boost.cmake)
-include(${CMAKE_CURRENT_LIST_DIR}/cmake/apple.cmake)
-include(${CMAKE_CURRENT_LIST_DIR}/cmake/ide.cmake)
+include(${JRL_CMAKE_MODULES}/boost.cmake)
+include(${JRL_CMAKE_MODULES}/apple.cmake)
+include(${JRL_CMAKE_MODULES}/ide.cmake)
 include(CMakeDependentOption)
 
 # If needed, fix CMake policy for APPLE systems
-APPLY_DEFAULT_APPLE_CONFIGURATION()
+apply_default_apple_configuration()
 
 # Project dependencies
 find_package(Boost REQUIRED COMPONENTS unit_test_framework)
 add_project_dependency(crocoddyl 2.0.0 REQUIRED)
 add_project_dependency(eigenpy 2.7.10 REQUIRED)
 add_project_dependency(example-robot-data 4.0.7 REQUIRED PKG_CONFIG_REQUIRES
-"example-robot-data >= 4.0.7")
+                       "example-robot-data >= 4.0.7")
 
 if(BUILD_WITH_PROXSUITE)
   add_project_dependency(proxsuite REQUIRED)
@@ -59,45 +85,44 @@ endif()
 find_package(OpenMP REQUIRED COMPONENTS CXX)
 
 if(BUILD_PYTHON_INTERFACE)
-  string(REGEX REPLACE "-" "_" PY_NAME ${PROJECT_NAME})
+  set(PY_NAME ${PROJECT_NAME}_pywrap)
   set(${PY_NAME}_INSTALL_DIR ${PYTHON_SITELIB}/${PY_NAME})
 endif()
 
 # Main Library
 set(${PROJECT_NAME}_HEADERS
-  include/${PROJECT_NAME}/kkt.hpp
-  include/${PROJECT_NAME}/ddp.hpp
-  include/${PROJECT_NAME}/fddp.hpp
-  include/${PROJECT_NAME}/sqp.hpp
-  include/${PROJECT_NAME}/csqp.hpp
-  )
-
-set(${PROJECT_NAME}_SOURCES
-  src/kkt.cpp
-  src/ddp.cpp
-  src/fddp.cpp
-  src/sqp.cpp
-  src/csqp.cpp
-  )
+    include/${PROJECT_NAME}/kkt.hpp include/${PROJECT_NAME}/ddp.hpp
+    include/${PROJECT_NAME}/fddp.hpp include/${PROJECT_NAME}/sqp.hpp
+    include/${PROJECT_NAME}/csqp.hpp)
+
+set(${PROJECT_NAME}_SOURCES src/kkt.cpp src/ddp.cpp src/fddp.cpp src/sqp.cpp
+                            src/csqp.cpp)
 
 if(BUILD_WITH_PROXSUITE)
   list(APPEND ${PROJECT_NAME}_HEADERS include/${PROJECT_NAME}/csqp_proxqp.hpp)
   list(APPEND ${PROJECT_NAME}_SOURCES src/csqp_proxqp.cpp)
 endif(BUILD_WITH_PROXSUITE)
 
-add_library(${PROJECT_NAME} SHARED ${${PROJECT_NAME}_SOURCES} ${${PROJECT_NAME}_HEADERS})
-target_include_directories(${PROJECT_NAME} PUBLIC $<INSTALL_INTERFACE:include>)
+add_library(${PROJECT_NAME} SHARED ${${PROJECT_NAME}_SOURCES}
+                                   ${${PROJECT_NAME}_HEADERS})
+target_include_directories(
+  ${PROJECT_NAME} PUBLIC $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
 
 if(SUFFIX_SO_VERSION)
   set_target_properties(${PROJECT_NAME} PROPERTIES SOVERSION ${PROJECT_VERSION})
 endif()
 
 # Main Executable
-TARGET_LINK_LIBRARIES(${PROJECT_NAME} crocoddyl::crocoddyl)
+target_link_libraries(${PROJECT_NAME} crocoddyl::crocoddyl)
 if(BUILD_WITH_PROXSUITE)
-  TARGET_LINK_LIBRARIES(${PROJECT_NAME} proxsuite::proxsuite)
+  target_link_libraries(${PROJECT_NAME} proxsuite::proxsuite)
+endif()
+
+if(UNIX)
+  get_relative_rpath(${CMAKE_INSTALL_LIBDIR} ${PROJECT_NAME}_INSTALL_RPATH)
+  set_target_properties(
+    ${PROJECT_NAME} PROPERTIES INSTALL_RPATH "${${PROJECT_NAME}_INSTALL_RPATH}")
 endif()
-set_target_properties(${PROJECT_NAME} PROPERTIES INSTALL_RPATH "\$ORIGIN/../lib")
 
 # Python Bindings
 if(BUILD_PYTHON_INTERFACE)
@@ -113,5 +138,8 @@ if(BUILD_BENCHMARKS)
 endif()
 
 # Installation
-install(TARGETS ${PROJECT_NAME} EXPORT ${TARGETS_EXPORT_NAME} DESTINATION lib)
+install(
+  TARGETS ${PROJECT_NAME}
+  EXPORT ${TARGETS_EXPORT_NAME}
+  DESTINATION ${CMAKE_INSTALL_LIBDIR})
 install(FILES package.xml DESTINATION share/${PROJECT_NAME})
diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt
index 23fd6b7..1ba739c 100644
--- a/benchmarks/CMakeLists.txt
+++ b/benchmarks/CMakeLists.txt
@@ -1,21 +1,15 @@
-set(${PROJECT_NAME}_BENCHMARK
-  ur5
-  solo12
-  )
+set(${PROJECT_NAME}_BENCHMARK ur5 solo12)
 
+set(${PROJECT_NAME}_BENCHMARK_TOOLS timings.hpp timings.cpp)
 
-set(${PROJECT_NAME}_BENCHMARK_TOOLS
-  timings.hpp
-  timings.cpp
-  )
-
-add_library(${PROJECT_NAME}_BENCHMARK_TOOLS SHARED ${${PROJECT_NAME}_BENCHMARK_TOOLS})
+add_library(${PROJECT_NAME}_BENCHMARK_TOOLS SHARED
+            ${${PROJECT_NAME}_BENCHMARK_TOOLS})
 
 foreach(BENCHMARK_NAME ${${PROJECT_NAME}_BENCHMARK})
   add_executable(${BENCHMARK_NAME} ${BENCHMARK_NAME}.cpp)
-  target_link_libraries(${BENCHMARK_NAME} ${PROJECT_NAME}
-                        example-robot-data::example-robot-data
-                        ${PROJECT_NAME}_BENCHMARK_TOOLS)
+  target_link_libraries(
+    ${BENCHMARK_NAME} ${PROJECT_NAME} example-robot-data::example-robot-data
+    ${PROJECT_NAME}_BENCHMARK_TOOLS)
   add_custom_target("benchmarks-cpp-${BENCHMARK_NAME}" ${BENCHMARK_NAME}
                                                        \${INPUT})
-endforeach(BENCHMARK_NAME ${${PROJECT_NAME}_BENCHMARK})
\ No newline at end of file
+endforeach(BENCHMARK_NAME ${${PROJECT_NAME}_BENCHMARK})
diff --git a/bindings/CMakeLists.txt b/bindings/CMakeLists.txt
index 759b0fa..7ebf79c 100644
--- a/bindings/CMakeLists.txt
+++ b/bindings/CMakeLists.txt
@@ -1,39 +1,37 @@
 if(GENERATE_PYTHON_STUBS)
-  include(${PROJECT_SOURCE_DIR}/cmake/stubs.cmake)
+  include(${JRL_CMAKE_MODULES}/stubs.cmake)
 endif(GENERATE_PYTHON_STUBS)
 
-set(${PY_NAME}_SOURCES
-  ddp.cpp
-  fddp.cpp
-  sqp.cpp
-  csqp.cpp
-  main.cpp
-  )
+set(${PY_NAME}_SOURCES ddp.cpp fddp.cpp sqp.cpp csqp.cpp main.cpp)
 
 if(BUILD_WITH_PROXSUITE)
   list(APPEND ${PY_NAME}_SOURCES csqp_proxqp.cpp)
 endif(BUILD_WITH_PROXSUITE)
 
-add_library(${PY_NAME}_pywrap MODULE ${${PY_NAME}_SOURCES})
-target_link_libraries(${PY_NAME}_pywrap PUBLIC ${PROJECT_NAME} eigenpy::eigenpy)
+add_library(${PY_NAME} MODULE ${${PY_NAME}_SOURCES})
+target_link_libraries(${PY_NAME} PUBLIC ${PROJECT_NAME} eigenpy::eigenpy)
 
-set_target_properties(${PY_NAME}_pywrap PROPERTIES
-  LIBRARY_OUTPUT_DIRECTORY ${PY_NAME}_pywrap
-  PREFIX ""
-  SUFFIX ${PYTHON_EXT_SUFFIX})
+set_target_properties(
+  ${PY_NAME}
+  PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_NAME}
+             PREFIX ""
+             SUFFIX ${PYTHON_EXT_SUFFIX})
 
 if(GENERATE_PYTHON_STUBS)
   load_stubgen()
-  generate_stubs(${CMAKE_CURRENT_BINARY_DIR} ${PY_NAME}_pywrap ${PYTHON_SITELIB} ${PY_NAME}_pywrap)
+  generate_stubs(${CMAKE_CURRENT_BINARY_DIR} ${PY_NAME} ${PYTHON_SITELIB}
+                 ${PY_NAME})
 endif(GENERATE_PYTHON_STUBS)
 
-set(${PY_NAME}_PYTHON mim_solvers/__init__.py)
+set(${PY_NAME}_PYTHON __init__.py)
 
-SET(PYLIB_INSTALL_DIR ${PYTHON_SITELIB}/mim_solvers)
-IF(UNIX)
-  GET_RELATIVE_RPATH(${PYLIB_INSTALL_DIR} PYLIB_RPATH)
-  SET_TARGET_PROPERTIES(${PY_NAME}_pywrap PROPERTIES INSTALL_RPATH ${PYLIB_RPATH})
-ENDIF()
+set(PYLIB_INSTALL_DIR ${PYTHON_SITELIB}/${PROJECT_NAME})
+if(UNIX)
+  get_relative_rpath(${PYLIB_INSTALL_DIR} PYLIB_RPATH)
+  set_target_properties(${PY_NAME} PROPERTIES INSTALL_RPATH ${PYLIB_RPATH})
+endif()
 
-install(TARGETS ${PY_NAME}_pywrap LIBRARY DESTINATION ${PYLIB_INSTALL_DIR})
-install(FILES "${${PY_NAME}_PYTHON}" DESTINATION ${PYLIB_INSTALL_DIR})
+install(TARGETS ${PY_NAME} LIBRARY DESTINATION ${PYLIB_INSTALL_DIR})
+foreach(source ${${PY_NAME}_PYTHON})
+  python_install_on_site(${PROJECT_NAME} ${source})
+endforeach()


Test patch:
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index e3fb303..3c49052 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,15 +1,14 @@
 set(${PROJECT_NAME}_FACTORY_TEST
-  factory/constraints.hpp
-  factory/constraints.cpp
-  factory/point-mass.hpp
-  factory/point-mass.cpp
-  factory/model.hpp
-  factory/model.cpp
-  factory/problem.hpp
-  factory/problem.cpp
-  factory/solver.hpp
-  factory/solver.cpp
-  )
+    factory/constraints.hpp
+    factory/constraints.cpp
+    factory/point-mass.hpp
+    factory/point-mass.cpp
+    factory/model.hpp
+    factory/model.cpp
+    factory/problem.hpp
+    factory/problem.cpp
+    factory/solver.hpp
+    factory/solver.cpp)
 
 add_library(${PROJECT_NAME}_unittest SHARED ${${PROJECT_NAME}_FACTORY_TEST})
 target_link_libraries(
@@ -19,14 +18,20 @@ target_link_libraries(
 add_unit_test(test_solvers test_solvers.cpp)
 target_link_libraries(test_solvers PUBLIC ${PROJECT_NAME}_unittest)
 
-
 if(BUILD_PYTHON_INTERFACE)
-  add_python_unit_test("py-test-clqr-convergence-all" "tests/python/test_clqr_convergence_all.py" src tests)
-  add_python_unit_test("py-test-clqr-osqp" "tests/python/test_clqr_osqp.py" src tests)
-  add_python_unit_test("py-test-clqr-stagewise-admm" "tests/python/test_clqr_stagewise_admm.py" src tests)
-  add_python_unit_test("py-test-lqr-sqp" "tests/python/test_lqr_problem.py" src tests)
-  add_python_unit_test("py-test-sqp-no-reg" "tests/python/test_sqp.py" src tests)
-  add_python_unit_test("py-test-sqp-taichi-convergence" "tests/python/test_sqp_taichi_convergence.py" src tests)
-  add_python_unit_test("py-test-sqp-ur5-convergence" "tests/python/test_sqp_ur5_convergence.py" src tests)
-  add_python_unit_test("py-test-csqp-ur5" "tests/python/test_ur5_csqp.py" src tests)
-endif()
\ No newline at end of file
+  add_python_unit_test("py-test-clqr-convergence-all"
+                       "tests/python/test_clqr_convergence_all.py" bindings)
+  add_python_unit_test("py-test-clqr-osqp" "tests/python/test_clqr_osqp.py"
+                       bindings)
+  add_python_unit_test("py-test-clqr-stagewise-admm"
+                       "tests/python/test_clqr_stagewise_admm.py" bindings)
+  add_python_unit_test("py-test-lqr-sqp" "tests/python/test_lqr_problem.py"
+                       bindings)
+  add_python_unit_test("py-test-sqp-no-reg" "tests/python/test_sqp.py" bindings)
+  add_python_unit_test("py-test-sqp-taichi-convergence"
+                       "tests/python/test_sqp_taichi_convergence.py" bindings)
+  add_python_unit_test("py-test-sqp-ur5-convergence"
+                       "tests/python/test_sqp_ur5_convergence.py" bindings)
+  add_python_unit_test("py-test-csqp-ur5" "tests/python/test_ur5_csqp.py"
+                       bindings)
+endif()