From 0a55e3ab32ccf68d69a89f89e9a8eda2a1b958a8 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Tue, 13 Oct 2026 23:52:35 +0200 Subject: [PATCH 01/16] Update workflows --- .github/workflows/unixmatrix.yml | 2 +- .github/workflows/vcmatrix.yml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 9c5bb18f7..22e606f0e 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -31,7 +31,7 @@ jobs: - { os: windows-11-vs2026-arm, shell: cmd , arch: arm64 , bitness: 64, runtime: vc18 , cmake_params: '-G "Visual Studio 18" -T v145 -A arm64', cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2026 arm64' } - { os: windows-2025-vs2026 , shell: cmd , arch: x64 , bitness: 64, runtime: vc18 , cmake_params: '-G "Visual Studio 18" -T v145 -A x64' , cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2026 x64' } - { os: windows-2025-vs2026 , shell: cmd , arch: x86 , bitness: 32, runtime: vc18 , cmake_params: '-G "Visual Studio 18" -T v145 -A Win32', cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2026 x86' } - - { os: windows-11-arm , shell: cmd , arch: arm64 , bitness: 64, runtime: vc17 , cmake_params: '-G "Visual Studio 18 2026" -T v143 -A arm64', cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2026 arm64 (v143)' } + - { os: windows-11-vs2026-arm, shell: cmd , arch: arm64 , bitness: 64, runtime: vc17 , cmake_params: '-G "Visual Studio 18" -T v143 -A arm64', cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2022 arm64' } - { os: windows-2022 , shell: cmd , arch: x64 , bitness: 64, runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A x64' , cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2022 x64' } - { os: windows-2022 , shell: cmd , arch: x86 , bitness: 32, runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2022 x86' } # Problems related to C++20 for Visual Studio 2019 and earlier? diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index e7997f6d3..353f515eb 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -26,10 +26,10 @@ jobs: - { os: windows-2022 , shell: cmd , arch: x86 , runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 10, desc: 'Windows Visual Studio 2022 x86 Python 3.10' } - { os: windows-2022 , shell: cmd , arch: x86 , runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 9 , desc: 'Windows Visual Studio 2022 x86 Python 3.9' } - { os: windows-2022 , shell: cmd , arch: x86 , runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 8 , desc: 'Windows Visual Studio 2022 x86 Python 3.8' } - - { os: windows-11-arm , shell: cmd , arch: arm64 , runtime: vc17 , cmake_params: '-G "Visual Studio 18 2026" -T v143 -A arm64', cpcfg: '-win_arm64', py_v_maj: 3, py_v_min: 14, desc: 'Windows Visual Studio 2026 arm64 Python 3.14' } - - { os: windows-11-arm , shell: cmd , arch: arm64 , runtime: vc17 , cmake_params: '-G "Visual Studio 18 2026" -T v143 -A arm64', cpcfg: '-win_arm64', py_v_maj: 3, py_v_min: 13, desc: 'Windows Visual Studio 2026 arm64 Python 3.13' } - - { os: windows-11-arm , shell: cmd , arch: arm64 , runtime: vc17 , cmake_params: '-G "Visual Studio 18 2026" -T v143 -A arm64', cpcfg: '-win_arm64', py_v_maj: 3, py_v_min: 12, desc: 'Windows Visual Studio 2026 arm64 Python 3.12' } - - { os: windows-11-arm , shell: cmd , arch: arm64 , runtime: vc17 , cmake_params: '-G "Visual Studio 18 2026" -T v143 -A arm64', cpcfg: '-win_arm64', py_v_maj: 3, py_v_min: 11, desc: 'Windows Visual Studio 2026 arm64 Python 3.11' } + - { os: windows-11-vs2026-arm, shell: cmd , arch: arm64 , runtime: vc17 , cmake_params: '-G "Visual Studio 18" -T v143 -A arm64', cpcfg: '-win_arm64', py_v_maj: 3, py_v_min: 14, desc: 'Windows Visual Studio 2022 arm64 Python 3.14' } + - { os: windows-11-vs2026-arm, shell: cmd , arch: arm64 , runtime: vc17 , cmake_params: '-G "Visual Studio 18" -T v143 -A arm64', cpcfg: '-win_arm64', py_v_maj: 3, py_v_min: 13, desc: 'Windows Visual Studio 2022 arm64 Python 3.13' } + - { os: windows-11-vs2026-arm, shell: cmd , arch: arm64 , runtime: vc17 , cmake_params: '-G "Visual Studio 18" -T v143 -A arm64', cpcfg: '-win_arm64', py_v_maj: 3, py_v_min: 12, desc: 'Windows Visual Studio 2022 arm64 Python 3.12' } + - { os: windows-11-vs2026-arm, shell: cmd , arch: arm64 , runtime: vc17 , cmake_params: '-G "Visual Studio 18" -T v143 -A arm64', cpcfg: '-win_arm64', py_v_maj: 3, py_v_min: 11, desc: 'Windows Visual Studio 2022 arm64 Python 3.11' } - { os: windows-2022 , shell: cmd , arch: x64 , runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A x64' , cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 14, desc: 'Windows Visual Studio 2022 x64 Python 3.14' } - { os: windows-2022 , shell: cmd , arch: x64 , runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A x64' , cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 13, desc: 'Windows Visual Studio 2022 x64 Python 3.13' } - { os: windows-2022 , shell: cmd , arch: x64 , runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A x64' , cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 12, desc: 'Windows Visual Studio 2022 x64 Python 3.12' } From 8e7990af2ba1c55dda72389a6bfeb900115228d9 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Tue, 13 Oct 2026 23:54:08 +0200 Subject: [PATCH 02/16] macOS Sonoma EOL --- .github/workflows/macosmatrix.yml | 8 ++++---- .github/workflows/unixmatrix.yml | 2 -- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 3fe504024..34e1fdb56 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -21,10 +21,10 @@ jobs: fail-fast: false matrix: cfg: - - { os: macos-14 , shell: bash, arch: arm64 , runtime: sonoma , cmake_flags: '-fPIC', trgt: '11.0' , cpcfg: '-macosx_11_0_arm64' , py_v_maj: 3, py_v_min: 14, desc: 'macOS Sonoma Python 3.14 arm64' } - - { os: macos-14 , shell: bash, arch: arm64 , runtime: sonoma , cmake_flags: '-fPIC', trgt: '11.0' , cpcfg: '-macosx_11_0_arm64' , py_v_maj: 3, py_v_min: 13, desc: 'macOS Sonoma Python 3.13 arm64' } - - { os: macos-14 , shell: bash, arch: arm64 , runtime: sonoma , cmake_flags: '-fPIC', trgt: '11.0' , cpcfg: '-macosx_11_0_arm64' , py_v_maj: 3, py_v_min: 12, desc: 'macOS Sonoma Python 3.12 arm64' } - - { os: macos-14 , shell: bash, arch: arm64 , runtime: sonoma , cmake_flags: '-fPIC', trgt: '11.0' , cpcfg: '-macosx_11_0_arm64' , py_v_maj: 3, py_v_min: 11, desc: 'macOS Sonoma Python 3.11 arm64' } + - { os: macos-15 , shell: bash, arch: arm64 , runtime: sonoma , cmake_flags: '-fPIC', trgt: '11.0' , cpcfg: '-macosx_11_0_arm64' , py_v_maj: 3, py_v_min: 14, desc: 'macOS Sequoia Python 3.14 arm64' } + - { os: macos-15 , shell: bash, arch: arm64 , runtime: sonoma , cmake_flags: '-fPIC', trgt: '11.0' , cpcfg: '-macosx_11_0_arm64' , py_v_maj: 3, py_v_min: 13, desc: 'macOS Sequoia Python 3.13 arm64' } + - { os: macos-15 , shell: bash, arch: arm64 , runtime: sonoma , cmake_flags: '-fPIC', trgt: '11.0' , cpcfg: '-macosx_11_0_arm64' , py_v_maj: 3, py_v_min: 12, desc: 'macOS Sequoia Python 3.12 arm64' } + - { os: macos-15 , shell: bash, arch: arm64 , runtime: sonoma , cmake_flags: '-fPIC', trgt: '11.0' , cpcfg: '-macosx_11_0_arm64' , py_v_maj: 3, py_v_min: 11, desc: 'macOS Sequoia Python 3.11 arm64' } - { os: macos-15-intel , shell: bash, arch: x86_64 , runtime: sequoia , cmake_flags: '-fPIC', trgt: '10.16', cpcfg: '-macosx_10_16_x86_64', py_v_maj: 3, py_v_min: 14, desc: 'macOS Sequoia Python 3.14 x86_64' } - { os: macos-15-intel , shell: bash, arch: x86_64 , runtime: sequoia , cmake_flags: '-fPIC', trgt: '10.16', cpcfg: '-macosx_10_16_x86_64', py_v_maj: 3, py_v_min: 13, desc: 'macOS Sequoia Python 3.13 x86_64' } - { os: macos-15-intel , shell: bash, arch: x86_64 , runtime: sequoia , cmake_flags: '-fPIC', trgt: '10.16', cpcfg: '-macosx_10_16_x86_64', py_v_maj: 3, py_v_min: 12, desc: 'macOS Sequoia Python 3.12 x86_64' } diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 22e606f0e..88d33f6c6 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -66,8 +66,6 @@ jobs: - { os: macos-26 , shell: bash, arch: x86_64 , bitness: 64, runtime: tahoe , cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64 -D CMAKE_OSX_DEPLOYMENT_TARGET=26.0', cmake_flags: '-fPIC', cross: true, desc: 'macOS Tahoe x86_64 (cross)' } - { os: macos-15 , shell: bash, arch: arm64 , bitness: 64, runtime: sequoia , cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64' , cmake_flags: '-fPIC', desc: 'macOS Sequoia arm64' } - { os: macos-15-intel , shell: bash, arch: x86_64 , bitness: 64, runtime: sequoia , cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', desc: 'macOS Sequoia x86_64' } - - { os: macos-14 , shell: bash, arch: arm64 , bitness: 64, runtime: sonoma , cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64' , cmake_flags: '-fPIC', desc: 'macOS Sonoma arm64' } - - { os: macos-14 , shell: bash, arch: x86_64 , bitness: 64, runtime: sonoma , cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64 -D CMAKE_OSX_DEPLOYMENT_TARGET=14.0', cmake_flags: '-fPIC', cross: true, desc: 'macOS Sonoma x86_64 (cross)' } name: ${{ matrix.cfg.desc }} steps: - uses: actions/checkout@v7 From e094be150ccc747d0eee5cd06adcfeee9e7fe04a Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Tue, 13 Oct 2026 23:56:14 +0200 Subject: [PATCH 03/16] Add macOS Golden Gate support --- .github/workflows/unixmatrix.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 88d33f6c6..068aa0552 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -62,6 +62,8 @@ jobs: - { os: ubuntu-24.04-arm , shell: bash, arch: arm64 , bitness: 64, runtime: noble , cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 24.04 arm64' } - { os: ubuntu-22.04 , shell: bash, arch: amd64 , bitness: 64, runtime: jammy , cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 22.04 amd64' } - { os: ubuntu-22.04-arm , shell: bash, arch: arm64 , bitness: 64, runtime: jammy , cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 22.04 arm64' } + - { os: xcode-27 , shell: bash, arch: arm64 , bitness: 64, runtime: goldengate , cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64' , cmake_flags: '-fPIC', desc: 'macOS Golden Gate arm64' } + - { os: xcode-27 , shell: bash, arch: x86_64 , bitness: 64, runtime: goldengate , cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64 -D CMAKE_OSX_DEPLOYMENT_TARGET=27.0', cmake_flags: '-fPIC', cross: true, desc: 'macOS Golden Gate x86_64 (cross)' } - { os: macos-26 , shell: bash, arch: arm64 , bitness: 64, runtime: tahoe , cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64' , cmake_flags: '-fPIC', desc: 'macOS Tahoe arm64' } - { os: macos-26 , shell: bash, arch: x86_64 , bitness: 64, runtime: tahoe , cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64 -D CMAKE_OSX_DEPLOYMENT_TARGET=26.0', cmake_flags: '-fPIC', cross: true, desc: 'macOS Tahoe x86_64 (cross)' } - { os: macos-15 , shell: bash, arch: arm64 , bitness: 64, runtime: sequoia , cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64' , cmake_flags: '-fPIC', desc: 'macOS Sequoia arm64' } From 60265863cc7ffa610b45c88e172690d302204824 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Wed, 14 Oct 2026 00:02:00 +0200 Subject: [PATCH 04/16] Update workflows --- .github/workflows/unixmatrix.yml | 6 +++--- .github/workflows/vcmatrix.yml | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 068aa0552..648d545e1 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -28,10 +28,10 @@ jobs: fail-fast: false matrix: cfg: - - { os: windows-11-vs2026-arm, shell: cmd , arch: arm64 , bitness: 64, runtime: vc18 , cmake_params: '-G "Visual Studio 18" -T v145 -A arm64', cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2026 arm64' } + - { os: windows-11-arm , shell: cmd , arch: arm64 , bitness: 64, runtime: vc18 , cmake_params: '-G "Visual Studio 18" -T v145 -A arm64', cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2026 arm64' } - { os: windows-2025-vs2026 , shell: cmd , arch: x64 , bitness: 64, runtime: vc18 , cmake_params: '-G "Visual Studio 18" -T v145 -A x64' , cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2026 x64' } - { os: windows-2025-vs2026 , shell: cmd , arch: x86 , bitness: 32, runtime: vc18 , cmake_params: '-G "Visual Studio 18" -T v145 -A Win32', cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2026 x86' } - - { os: windows-11-vs2026-arm, shell: cmd , arch: arm64 , bitness: 64, runtime: vc17 , cmake_params: '-G "Visual Studio 18" -T v143 -A arm64', cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2022 arm64' } + - { os: windows-11-arm , shell: cmd , arch: arm64 , bitness: 64, runtime: vc17 , cmake_params: '-G "Visual Studio 18" -T v143 -A arm64', cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2022 arm64' } - { os: windows-2022 , shell: cmd , arch: x64 , bitness: 64, runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A x64' , cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2022 x64' } - { os: windows-2022 , shell: cmd , arch: x86 , bitness: 32, runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2022 x86' } # Problems related to C++20 for Visual Studio 2019 and earlier? @@ -65,7 +65,7 @@ jobs: - { os: xcode-27 , shell: bash, arch: arm64 , bitness: 64, runtime: goldengate , cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64' , cmake_flags: '-fPIC', desc: 'macOS Golden Gate arm64' } - { os: xcode-27 , shell: bash, arch: x86_64 , bitness: 64, runtime: goldengate , cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64 -D CMAKE_OSX_DEPLOYMENT_TARGET=27.0', cmake_flags: '-fPIC', cross: true, desc: 'macOS Golden Gate x86_64 (cross)' } - { os: macos-26 , shell: bash, arch: arm64 , bitness: 64, runtime: tahoe , cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64' , cmake_flags: '-fPIC', desc: 'macOS Tahoe arm64' } - - { os: macos-26 , shell: bash, arch: x86_64 , bitness: 64, runtime: tahoe , cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64 -D CMAKE_OSX_DEPLOYMENT_TARGET=26.0', cmake_flags: '-fPIC', cross: true, desc: 'macOS Tahoe x86_64 (cross)' } + - { os: macos-26-intel , shell: bash, arch: x86_64 , bitness: 64, runtime: tahoe , cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', desc: 'macOS Tahoe x86_64' } - { os: macos-15 , shell: bash, arch: arm64 , bitness: 64, runtime: sequoia , cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64' , cmake_flags: '-fPIC', desc: 'macOS Sequoia arm64' } - { os: macos-15-intel , shell: bash, arch: x86_64 , bitness: 64, runtime: sequoia , cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', desc: 'macOS Sequoia x86_64' } name: ${{ matrix.cfg.desc }} diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 353f515eb..faff8b97b 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -26,10 +26,10 @@ jobs: - { os: windows-2022 , shell: cmd , arch: x86 , runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 10, desc: 'Windows Visual Studio 2022 x86 Python 3.10' } - { os: windows-2022 , shell: cmd , arch: x86 , runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 9 , desc: 'Windows Visual Studio 2022 x86 Python 3.9' } - { os: windows-2022 , shell: cmd , arch: x86 , runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 8 , desc: 'Windows Visual Studio 2022 x86 Python 3.8' } - - { os: windows-11-vs2026-arm, shell: cmd , arch: arm64 , runtime: vc17 , cmake_params: '-G "Visual Studio 18" -T v143 -A arm64', cpcfg: '-win_arm64', py_v_maj: 3, py_v_min: 14, desc: 'Windows Visual Studio 2022 arm64 Python 3.14' } - - { os: windows-11-vs2026-arm, shell: cmd , arch: arm64 , runtime: vc17 , cmake_params: '-G "Visual Studio 18" -T v143 -A arm64', cpcfg: '-win_arm64', py_v_maj: 3, py_v_min: 13, desc: 'Windows Visual Studio 2022 arm64 Python 3.13' } - - { os: windows-11-vs2026-arm, shell: cmd , arch: arm64 , runtime: vc17 , cmake_params: '-G "Visual Studio 18" -T v143 -A arm64', cpcfg: '-win_arm64', py_v_maj: 3, py_v_min: 12, desc: 'Windows Visual Studio 2022 arm64 Python 3.12' } - - { os: windows-11-vs2026-arm, shell: cmd , arch: arm64 , runtime: vc17 , cmake_params: '-G "Visual Studio 18" -T v143 -A arm64', cpcfg: '-win_arm64', py_v_maj: 3, py_v_min: 11, desc: 'Windows Visual Studio 2022 arm64 Python 3.11' } + - { os: windows-11-arm , shell: cmd , arch: arm64 , runtime: vc17 , cmake_params: '-G "Visual Studio 18" -T v143 -A arm64', cpcfg: '-win_arm64', py_v_maj: 3, py_v_min: 14, desc: 'Windows Visual Studio 2022 arm64 Python 3.14' } + - { os: windows-11-arm , shell: cmd , arch: arm64 , runtime: vc17 , cmake_params: '-G "Visual Studio 18" -T v143 -A arm64', cpcfg: '-win_arm64', py_v_maj: 3, py_v_min: 13, desc: 'Windows Visual Studio 2022 arm64 Python 3.13' } + - { os: windows-11-arm , shell: cmd , arch: arm64 , runtime: vc17 , cmake_params: '-G "Visual Studio 18" -T v143 -A arm64', cpcfg: '-win_arm64', py_v_maj: 3, py_v_min: 12, desc: 'Windows Visual Studio 2022 arm64 Python 3.12' } + - { os: windows-11-arm , shell: cmd , arch: arm64 , runtime: vc17 , cmake_params: '-G "Visual Studio 18" -T v143 -A arm64', cpcfg: '-win_arm64', py_v_maj: 3, py_v_min: 11, desc: 'Windows Visual Studio 2022 arm64 Python 3.11' } - { os: windows-2022 , shell: cmd , arch: x64 , runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A x64' , cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 14, desc: 'Windows Visual Studio 2022 x64 Python 3.14' } - { os: windows-2022 , shell: cmd , arch: x64 , runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A x64' , cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 13, desc: 'Windows Visual Studio 2022 x64 Python 3.13' } - { os: windows-2022 , shell: cmd , arch: x64 , runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A x64' , cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 12, desc: 'Windows Visual Studio 2022 x64 Python 3.12' } From 8e71bb16344990315c3e0ecf898ed19892ba0c26 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Wed, 14 Oct 2026 00:12:04 +0200 Subject: [PATCH 05/16] Add Python 3.15 support --- .github/workflows/dockercentos.yml | 6 ++++-- .github/workflows/macosmatrix.yml | 2 ++ .github/workflows/vcmatrix.yml | 3 +++ scripts/wheels/gencodacwhl.sh | 7 ++++--- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/workflows/dockercentos.yml b/.github/workflows/dockercentos.yml index 2494213d5..e3d613a26 100644 --- a/.github/workflows/dockercentos.yml +++ b/.github/workflows/dockercentos.yml @@ -18,11 +18,13 @@ jobs: fail-fast: false matrix: cfg: - - { img: 'lebarsfa/manylinux_2_28_x86_64-for-codac' , os: ubuntu-latest , shell: bash, arch: x86_64 , bitness: 64, runtime: manylinux_2_28, min_python: '3.14', max_python: ' ', desc: 'CentOS manylinux_2_28 x86_64 Python >=3.14' } + - { img: 'lebarsfa/manylinux_2_28_x86_64-for-codac' , os: ubuntu-latest , shell: bash, arch: x86_64 , bitness: 64, runtime: manylinux_2_28, min_python: '3.15', max_python: ' ', desc: 'CentOS manylinux_2_28 x86_64 Python >=3.15' } + - { img: 'lebarsfa/manylinux_2_28_x86_64-for-codac' , os: ubuntu-latest , shell: bash, arch: x86_64 , bitness: 64, runtime: manylinux_2_28, min_python: '3.14', max_python: '3.14', desc: 'CentOS manylinux_2_28 x86_64 Python 3.14' } - { img: 'lebarsfa/manylinux_2_28_x86_64-for-codac' , os: ubuntu-latest , shell: bash, arch: x86_64 , bitness: 64, runtime: manylinux_2_28, min_python: '3.13', max_python: '3.13', desc: 'CentOS manylinux_2_28 x86_64 Python 3.13' } - { img: 'lebarsfa/manylinux_2_28_x86_64-for-codac' , os: ubuntu-latest , shell: bash, arch: x86_64 , bitness: 64, runtime: manylinux_2_28, min_python: '3.11', max_python: '3.12', desc: 'CentOS manylinux_2_28 x86_64 Python 3.11-3.12' } - { img: 'lebarsfa/manylinux_2_28_x86_64-for-codac' , os: ubuntu-latest , shell: bash, arch: x86_64 , bitness: 64, runtime: manylinux_2_28, min_python: ' ', max_python: '3.10', desc: 'CentOS manylinux_2_28 x86_64 Python <=3.10' } - - { img: 'lebarsfa/manylinux_2_28_aarch64-for-codac', os: ubuntu-24.04-arm , shell: bash, arch: aarch64, bitness: 64, runtime: manylinux_2_28, min_python: '3.14', max_python: ' ', docker_flags: '--platform linux/arm64', desc: 'CentOS manylinux_2_28 aarch64 Python >=3.14' } + - { img: 'lebarsfa/manylinux_2_28_aarch64-for-codac', os: ubuntu-24.04-arm , shell: bash, arch: aarch64, bitness: 64, runtime: manylinux_2_28, min_python: '3.15', max_python: ' ', docker_flags: '--platform linux/arm64', desc: 'CentOS manylinux_2_28 aarch64 Python >=3.15' } + - { img: 'lebarsfa/manylinux_2_28_aarch64-for-codac', os: ubuntu-24.04-arm , shell: bash, arch: aarch64, bitness: 64, runtime: manylinux_2_28, min_python: '3.14', max_python: '3.14', docker_flags: '--platform linux/arm64', desc: 'CentOS manylinux_2_28 aarch64 Python 3.14' } - { img: 'lebarsfa/manylinux_2_28_aarch64-for-codac', os: ubuntu-24.04-arm , shell: bash, arch: aarch64, bitness: 64, runtime: manylinux_2_28, min_python: '3.13', max_python: '3.13', docker_flags: '--platform linux/arm64', desc: 'CentOS manylinux_2_28 aarch64 Python 3.13' } - { img: 'lebarsfa/manylinux_2_28_aarch64-for-codac', os: ubuntu-24.04-arm , shell: bash, arch: aarch64, bitness: 64, runtime: manylinux_2_28, min_python: '3.11', max_python: '3.12', docker_flags: '--platform linux/arm64', desc: 'CentOS manylinux_2_28 aarch64 Python 3.11-3.12' } - { img: 'lebarsfa/manylinux_2_28_aarch64-for-codac', os: ubuntu-24.04-arm , shell: bash, arch: aarch64, bitness: 64, runtime: manylinux_2_28, min_python: ' ', max_python: '3.10', docker_flags: '--platform linux/arm64', desc: 'CentOS manylinux_2_28 aarch64 Python <=3.10' } diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 34e1fdb56..4b1ab4494 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -21,10 +21,12 @@ jobs: fail-fast: false matrix: cfg: + - { os: macos-15 , shell: bash, arch: arm64 , runtime: sonoma , cmake_flags: '-fPIC', trgt: '11.0' , cpcfg: '-macosx_11_0_arm64' , py_v_maj: 3, py_v_min: 15, desc: 'macOS Sequoia Python 3.15 arm64' } - { os: macos-15 , shell: bash, arch: arm64 , runtime: sonoma , cmake_flags: '-fPIC', trgt: '11.0' , cpcfg: '-macosx_11_0_arm64' , py_v_maj: 3, py_v_min: 14, desc: 'macOS Sequoia Python 3.14 arm64' } - { os: macos-15 , shell: bash, arch: arm64 , runtime: sonoma , cmake_flags: '-fPIC', trgt: '11.0' , cpcfg: '-macosx_11_0_arm64' , py_v_maj: 3, py_v_min: 13, desc: 'macOS Sequoia Python 3.13 arm64' } - { os: macos-15 , shell: bash, arch: arm64 , runtime: sonoma , cmake_flags: '-fPIC', trgt: '11.0' , cpcfg: '-macosx_11_0_arm64' , py_v_maj: 3, py_v_min: 12, desc: 'macOS Sequoia Python 3.12 arm64' } - { os: macos-15 , shell: bash, arch: arm64 , runtime: sonoma , cmake_flags: '-fPIC', trgt: '11.0' , cpcfg: '-macosx_11_0_arm64' , py_v_maj: 3, py_v_min: 11, desc: 'macOS Sequoia Python 3.11 arm64' } + - { os: macos-15-intel , shell: bash, arch: x86_64 , runtime: sequoia , cmake_flags: '-fPIC', trgt: '10.16', cpcfg: '-macosx_10_16_x86_64', py_v_maj: 3, py_v_min: 15, desc: 'macOS Sequoia Python 3.15 x86_64' } - { os: macos-15-intel , shell: bash, arch: x86_64 , runtime: sequoia , cmake_flags: '-fPIC', trgt: '10.16', cpcfg: '-macosx_10_16_x86_64', py_v_maj: 3, py_v_min: 14, desc: 'macOS Sequoia Python 3.14 x86_64' } - { os: macos-15-intel , shell: bash, arch: x86_64 , runtime: sequoia , cmake_flags: '-fPIC', trgt: '10.16', cpcfg: '-macosx_10_16_x86_64', py_v_maj: 3, py_v_min: 13, desc: 'macOS Sequoia Python 3.13 x86_64' } - { os: macos-15-intel , shell: bash, arch: x86_64 , runtime: sequoia , cmake_flags: '-fPIC', trgt: '10.16', cpcfg: '-macosx_10_16_x86_64', py_v_maj: 3, py_v_min: 12, desc: 'macOS Sequoia Python 3.12 x86_64' } diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index faff8b97b..439ddde90 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -19,6 +19,7 @@ jobs: matrix: cfg: # Should be Visual Studio 2015 for Python 3.5-3.7, Visual Studio 2019 for Python 3.8-3.10, but need at least Visual Studio 2022 for C++20 compatibility...? + - { os: windows-2022 , shell: cmd , arch: x86 , runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 15, desc: 'Windows Visual Studio 2022 x86 Python 3.15' } - { os: windows-2022 , shell: cmd , arch: x86 , runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 14, desc: 'Windows Visual Studio 2022 x86 Python 3.14' } - { os: windows-2022 , shell: cmd , arch: x86 , runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 13, desc: 'Windows Visual Studio 2022 x86 Python 3.13' } - { os: windows-2022 , shell: cmd , arch: x86 , runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 12, desc: 'Windows Visual Studio 2022 x86 Python 3.12' } @@ -26,10 +27,12 @@ jobs: - { os: windows-2022 , shell: cmd , arch: x86 , runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 10, desc: 'Windows Visual Studio 2022 x86 Python 3.10' } - { os: windows-2022 , shell: cmd , arch: x86 , runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 9 , desc: 'Windows Visual Studio 2022 x86 Python 3.9' } - { os: windows-2022 , shell: cmd , arch: x86 , runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 8 , desc: 'Windows Visual Studio 2022 x86 Python 3.8' } + - { os: windows-11-arm , shell: cmd , arch: arm64 , runtime: vc17 , cmake_params: '-G "Visual Studio 18" -T v143 -A arm64', cpcfg: '-win_arm64', py_v_maj: 3, py_v_min: 15, desc: 'Windows Visual Studio 2022 arm64 Python 3.15' } - { os: windows-11-arm , shell: cmd , arch: arm64 , runtime: vc17 , cmake_params: '-G "Visual Studio 18" -T v143 -A arm64', cpcfg: '-win_arm64', py_v_maj: 3, py_v_min: 14, desc: 'Windows Visual Studio 2022 arm64 Python 3.14' } - { os: windows-11-arm , shell: cmd , arch: arm64 , runtime: vc17 , cmake_params: '-G "Visual Studio 18" -T v143 -A arm64', cpcfg: '-win_arm64', py_v_maj: 3, py_v_min: 13, desc: 'Windows Visual Studio 2022 arm64 Python 3.13' } - { os: windows-11-arm , shell: cmd , arch: arm64 , runtime: vc17 , cmake_params: '-G "Visual Studio 18" -T v143 -A arm64', cpcfg: '-win_arm64', py_v_maj: 3, py_v_min: 12, desc: 'Windows Visual Studio 2022 arm64 Python 3.12' } - { os: windows-11-arm , shell: cmd , arch: arm64 , runtime: vc17 , cmake_params: '-G "Visual Studio 18" -T v143 -A arm64', cpcfg: '-win_arm64', py_v_maj: 3, py_v_min: 11, desc: 'Windows Visual Studio 2022 arm64 Python 3.11' } + - { os: windows-2022 , shell: cmd , arch: x64 , runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A x64' , cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 15, desc: 'Windows Visual Studio 2022 x64 Python 3.15' } - { os: windows-2022 , shell: cmd , arch: x64 , runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A x64' , cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 14, desc: 'Windows Visual Studio 2022 x64 Python 3.14' } - { os: windows-2022 , shell: cmd , arch: x64 , runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A x64' , cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 13, desc: 'Windows Visual Studio 2022 x64 Python 3.13' } - { os: windows-2022 , shell: cmd , arch: x64 , runtime: vc17 , cmake_params: '-G "Visual Studio 17" -T v143 -A x64' , cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 12, desc: 'Windows Visual Studio 2022 x64 Python 3.12' } diff --git a/scripts/wheels/gencodacwhl.sh b/scripts/wheels/gencodacwhl.sh index b69db0d1b..5bf26a77d 100644 --- a/scripts/wheels/gencodacwhl.sh +++ b/scripts/wheels/gencodacwhl.sh @@ -4,8 +4,9 @@ read -p "Please enter the codac package version (X.Y.Z[.D]) : " PACKAGE_VERSION PY_V_MAJ=3 -for PY_V_MIN in 8 9 10 11 12 13 14; do - for CPCFG in "-macosx_10_16_x86_64" "-macosx_11_0_arm64" "-manylinux_2_17_x86_64.manylinux_2_28_x86_64" "-manylinux_2_17_aarch64.manylinux_2_28_aarch64" "-win32" "-win_amd64" "-win_arm64"; do - rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +for PY_V_MIN in 8 9 10 11 12 13 14 15; do + for CPCFG in "-macosx_10_16_x86_64" "-macosx_11_0_arm64" "-manylinux_2_27_x86_64.manylinux_2_28_x86_64" "-manylinux_2_26_aarch64.manylinux_2_28_aarch64" "-win32" "-win_amd64" "-win_arm64"; do + rm -Rf codac-${PACKAGE_VERSION}-cp${PY_V_MAJ}${PY_V_MIN}-cp${PY_V_MAJ}${PY_V_MIN}${CPCFG}.whl ; wget https://github.com/codac-team/codac/releases/download/v${PACKAGE_VERSION}/codac-${PACKAGE_VERSION}-cp${PY_V_MAJ}${PY_V_MIN}-cp${PY_V_MAJ}${PY_V_MIN}${CPCFG}.whl --no-check-certificate -nv + rm -Rf codac-${PACKAGE_VERSION}-cp${PY_V_MAJ}${PY_V_MIN}-cp${PY_V_MAJ}${PY_V_MIN}t${CPCFG}.whl ; wget https://github.com/codac-team/codac/releases/download/v${PACKAGE_VERSION}/codac-${PACKAGE_VERSION}-cp${PY_V_MAJ}${PY_V_MIN}-cp${PY_V_MAJ}${PY_V_MIN}t${CPCFG}.whl --no-check-certificate -nv done done From 62d90ce469e57dde000318f23acc468cde74b92a Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Wed, 14 Oct 2026 00:45:26 +0200 Subject: [PATCH 06/16] Install specific Doxygen version with brew on macOS. Issue with Doxygen 1.18.0 might be related to https://github.com/doxygen/doxygen/issues/12326 --- .github/workflows/macosmatrix.yml | 53 +++---------------------------- 1 file changed, 5 insertions(+), 48 deletions(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 4b1ab4494..b0460ef7b 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -56,54 +56,11 @@ jobs: # if: runner.os=='macOS' - run: brew install catch2 # Issues with binary packages when cross-compiling... if: (runner.os=='macOS')&&(matrix.cfg.cross!=true) - # Doxygen 1.18.0 crashes when parsing Codac headers on macOS. - # Doxygen 1.16.1 ARM64 binaries require macOS 15: build from source on macOS 14. - - name: Install Doxygen 1.16.1 - shell: bash - env: - GH_TOKEN: ${{ github.token }} - run: | - set -euo pipefail - case "${{ matrix.cfg.arch }}" in - arm64) archive="doxygen-1.16.1.src.tar.gz" ;; - x86_64) archive="doxygen-1.16.1-mac-intel.zip" ;; - *) echo "Unsupported architecture" >&2; exit 1 ;; - esac - tmpdir="$(mktemp -d)" - trap 'rm -rf "$tmpdir"' EXIT - - # Verify the archive using its official GitHub release SHA-256 digest. - expected_sha="$( - curl -fsSL --retry 3 \ - -H "Authorization: Bearer ${GH_TOKEN}" \ - -H "Accept: application/vnd.github+json" \ - https://api.github.com/repos/doxygen/doxygen/releases/tags/Release_1_16_1 \ - | python3 -c 'import json,sys; a=next(x for x in json.load(sys.stdin)["assets"] if x["name"]==sys.argv[1]); d=a["digest"]; assert d and d.startswith("sha256:") and len(d)==71; print(d[7:])' "$archive" - )" - curl -fsSL --retry 3 \ - "https://github.com/doxygen/doxygen/releases/download/Release_1_16_1/${archive}" \ - -o "$tmpdir/$archive" - echo "${expected_sha} ${tmpdir}/${archive}" | shasum -a 256 -c - - if [[ "${{ matrix.cfg.arch }}" == arm64 ]]; then - brew install ninja flex bison - mkdir "$tmpdir/src" - tar -xzf "$tmpdir/$archive" -C "$tmpdir/src" - export PATH="$(brew --prefix flex)/bin:$(brew --prefix bison)/bin:$PATH" - cmake -G Ninja -S "$tmpdir/src/doxygen-1.16.1" -B "$tmpdir/build" \ - -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 \ - -Dbuild_wizard=OFF -Dbuild_doc=OFF -Duse_libclang=OFF - cmake --build "$tmpdir/build" --target doxygen --parallel 4 - doxygen_bin="$tmpdir/build/bin/doxygen" - else - unzip -q "$tmpdir/$archive" -d "$tmpdir/unpacked" - doxygen_bin="$(find "$tmpdir/unpacked" -type f -name doxygen -print -quit)" - fi - - mkdir -p "$RUNNER_TEMP/codac-doxygen-bin" - install -m 755 "$doxygen_bin" "$RUNNER_TEMP/codac-doxygen-bin/doxygen" - echo "$RUNNER_TEMP/codac-doxygen-bin" >> "$GITHUB_PATH" - "$RUNNER_TEMP/codac-doxygen-bin/doxygen" --version + # Doxygen 1.18.0 crashes when parsing Codac headers on macOS. + - run: | + wget https://github.com/lebarsfa/xbrew/releases/latest/download/xbrew.sh --no-check-certificate -nv ; sudo mv -f xbrew.sh /usr/local/bin/xbrew ; sudo chmod +x /usr/local/bin/xbrew + xbrew reinstall https://github.com/Homebrew/homebrew-core/raw/1d8f87380f143b838b0d6a10068e7488db79ba5d/Formula/d/doxygen.rb ; brew pin --formula doxygen + if: runner.os=='macOS' - run: brew install graphviz ; python -m pip install --upgrade pip ; pip install --upgrade wheel setuptools sphinx breathe sphinx_rtd_theme sphinx-tabs sphinx-issues sphinx-reredirects furo sphinx-math-dollar sphinx_togglebutton sympy if: runner.os=='macOS' - run: | From 3cc2ecf28a996c8f3efcc8f06f605adb73f84dea Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Thu, 22 Oct 2026 17:25:13 +0200 Subject: [PATCH 07/16] [doc] minor update in toc --- doc/manual/index.rst | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/doc/manual/index.rst b/doc/manual/index.rst index 642b81072..bf6726f89 100644 --- a/doc/manual/index.rst +++ b/doc/manual/index.rst @@ -215,14 +215,16 @@ User manual * Extension to custom expressions * :ref:`sec-tubes-trajectories` - * :ref:`sec-trajectories-sampledtraj` - * :ref:`sec-trajectories-analytictraj` - * :ref:`sec-trajectories-operations` - * :ref:`sec-trajectories-io` - * :ref:`sec-tubes-tdomain` - * :ref:`sec-tubes-slice` - * :ref:`sec-tubes-slicedtube` - * :ref:`sec-tubes-examples` + * Trajectories + * :ref:`sec-trajectories-sampledtraj` + * :ref:`sec-trajectories-analytictraj` + * :ref:`sec-trajectories-operations` + * :ref:`sec-trajectories-io` + * Tubes + * :ref:`sec-tubes-tdomain` + * :ref:`sec-tubes-slice` + * :ref:`sec-tubes-slicedtube` + * :ref:`sec-tubes-examples` * Contractors * What are contractors? From 03943b6df7005e8e8e333a4b4b09c15482395a16 Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Thu, 22 Oct 2026 19:22:13 +0200 Subject: [PATCH 08/16] [traj] added truncate_to_common_tdomain + tests + py + doc --- doc/manual/manual/tubes/src.cpp | 10 +++++ doc/manual/manual/tubes/src.py | 8 ++++ .../manual/tubes/trajectory_operations.rst | 23 ++++++++++ python/src/core/CMakeLists.txt | 1 + python/src/core/codac2_py_core.cpp | 2 + .../codac2_py_traj_truncate_common.cpp | 42 +++++++++++++++++++ src/core/trajectory/codac2_TrajBase.h | 9 ++++ .../trajectory/codac2_tests_SampledTraj.cpp | 19 +++++++++ .../trajectory/codac2_tests_SampledTraj.py | 19 +++++++++ 9 files changed, 133 insertions(+) create mode 100644 python/src/core/trajectory/codac2_py_traj_truncate_common.cpp diff --git a/doc/manual/manual/tubes/src.cpp b/doc/manual/manual/tubes/src.cpp index c3d690c36..e525583d5 100644 --- a/doc/manual/manual/tubes/src.cpp +++ b/doc/manual/manual/tubes/src.cpp @@ -336,6 +336,16 @@ TEST_CASE("Trajectory classes - manual") CHECK(x.tdomain() == Interval(0,1)); } + { + // [traj-ops-common-tdomain-beg] + SampledTraj x1({0.,4.}, {0,4}); + SampledTraj x2({2.,5}, {Vector({1,2}), Vector({5,6})}); + truncate_to_common_tdomain(x1,x2); // common time domain: [2,4] + // [traj-ops-common-tdomain-end] + CHECK(x1.tdomain() == Interval(2,4)); + CHECK(x2.tdomain() == Interval(2,4)); + } + { // [traj-analytic-create-beg] ScalarVar t; diff --git a/doc/manual/manual/tubes/src.py b/doc/manual/manual/tubes/src.py index 1ab733ca3..9b1e12753 100644 --- a/doc/manual/manual/tubes/src.py +++ b/doc/manual/manual/tubes/src.py @@ -274,6 +274,14 @@ def test_Trajectories_manual(test): shifted.stretch_tdomain([0,1]) # rescale time, not values # [traj-sampled-time-end] + # [traj-ops-common-tdomain-beg] + x1 = SampledTraj({0.:0, 4.:4}) + x2 = SampledTraj({2.:Vector([1,2]), 5.:Vector([5,6])}) + truncate_to_common_tdomain(x1,x2) # common time domain: [2,4] + # [traj-ops-common-tdomain-end] + test.assertEqual(x1.tdomain(), Interval(2,4)) + test.assertEqual(x2.tdomain(), Interval(2,4)) + # [traj-sampled-npz-beg] import numpy as np import io diff --git a/doc/manual/manual/tubes/trajectory_operations.rst b/doc/manual/manual/tubes/trajectory_operations.rst index 72302c2ad..7460cdd28 100644 --- a/doc/manual/manual/tubes/trajectory_operations.rst +++ b/doc/manual/manual/tubes/trajectory_operations.rst @@ -67,6 +67,29 @@ Cartesian products :end-before: [traj-ops-cartprod-end] :dedent: 4 +Common time domain +------------------ + +``truncate_to_common_tdomain(x1,x2,...)`` truncates all trajectories in place to the intersection of their time domains. An empty intersection raises an error. + +.. tabs:: + + .. group-tab:: Python + + .. literalinclude:: src.py + :language: python + :start-after: [traj-ops-common-tdomain-beg] + :end-before: [traj-ops-common-tdomain-end] + :dedent: 4 + + .. group-tab:: C++ + + .. literalinclude:: src.cpp + :language: c++ + :start-after: [traj-ops-common-tdomain-beg] + :end-before: [traj-ops-common-tdomain-end] + :dedent: 4 + Unwrapping periodic signals --------------------------- diff --git a/python/src/core/CMakeLists.txt b/python/src/core/CMakeLists.txt index c8a7c7a43..6adb86409 100644 --- a/python/src/core/CMakeLists.txt +++ b/python/src/core/CMakeLists.txt @@ -132,6 +132,7 @@ trajectory/codac2_py_SampledTraj.cpp trajectory/codac2_py_TrajBase.h trajectory/codac2_py_traj_cart_prod.cpp + trajectory/codac2_py_traj_truncate_common.cpp ) target_include_directories(_core diff --git a/python/src/core/codac2_py_core.cpp b/python/src/core/codac2_py_core.cpp index f0cefd778..46bbd23bb 100644 --- a/python/src/core/codac2_py_core.cpp +++ b/python/src/core/codac2_py_core.cpp @@ -171,6 +171,7 @@ void export_trunc(py::module& m); void export_AnalyticTraj(py::module& m); void export_SampledTraj(py::module& m); void export_traj_cart_prod(py::module& m); +void export_traj_truncate_common(py::module& m); // Extension > sympy void export_sympy(py::module& m); @@ -357,6 +358,7 @@ PYBIND11_MODULE(_core, m) export_AnalyticTraj(m); export_SampledTraj(m); export_traj_cart_prod(m); + export_traj_truncate_common(m); m.def("srand", []() { diff --git a/python/src/core/trajectory/codac2_py_traj_truncate_common.cpp b/python/src/core/trajectory/codac2_py_traj_truncate_common.cpp new file mode 100644 index 000000000..6e17fc3ab --- /dev/null +++ b/python/src/core/trajectory/codac2_py_traj_truncate_common.cpp @@ -0,0 +1,42 @@ +/** + * traj_truncate_common binding + * ---------------------------------------------------------------------------- + * \date 2024 + * \author Simon Rohou + * \copyright Copyright 2024 Codac Team + * \license GNU Lesser General Public License (LGPL) + */ + +#include +#include +#include +#include +#include +#include "codac2_py_TrajBase_docs.h" // Generated file from Doxygen XML (doxygen2docstring.py) +#include "codac2_py_cast.h" + +using namespace std; +using namespace codac2; +namespace py = pybind11; +using namespace pybind11::literals; + +void export_traj_truncate_common(py::module& m) +{ + m.def("truncate_to_common_tdomain", [](py::args trajs) + { + if(trajs.empty()) + throw py::value_error("At least one trajectory is required"); + + Interval t; + + for(const auto& traj : trajs) + t &= traj.attr("tdomain")().cast(); + + if(t.is_empty()) + throw py::value_error("Trajectories have disjoint time domains"); + + for(const auto& traj : trajs) + traj.attr("truncate_tdomain")(t); + }, + VOID_TRUNCATE_TO_COMMON_TDOMAIN_T_REF_VARIADIC); +} \ No newline at end of file diff --git a/src/core/trajectory/codac2_TrajBase.h b/src/core/trajectory/codac2_TrajBase.h index dba040afa..34af41052 100644 --- a/src/core/trajectory/codac2_TrajBase.h +++ b/src/core/trajectory/codac2_TrajBase.h @@ -45,4 +45,13 @@ namespace codac2 SampledTraj sampled_as(const SampledTraj& x) const; SampledTraj primitive(double dt) const; }; + + template + void truncate_to_common_tdomain(T&... x) + { + static_assert(sizeof...(x) > 0); + Interval t = (x.tdomain() & ...); + assert_release(!t.is_empty() && "Trajectories have disjoint time domains"); + (x.truncate_tdomain(t), ...); + } } diff --git a/tests/core/trajectory/codac2_tests_SampledTraj.cpp b/tests/core/trajectory/codac2_tests_SampledTraj.cpp index 4addc7c93..6fa742c72 100644 --- a/tests/core/trajectory/codac2_tests_SampledTraj.cpp +++ b/tests/core/trajectory/codac2_tests_SampledTraj.cpp @@ -149,4 +149,23 @@ TEST_CASE("SampledTraj: derivative") for(double i = 0 ; i < 10 ; i+=1e-1) CHECK(Approx(p(i),1e-2) == x(i)); +} + +TEST_CASE("truncate_to_common_tdomain") +{ + SampledTraj x({0.,4.}, {0,4}); + SampledTraj y({{1.,{1,2}}, {5.,{5,6}}}); + + ScalarVar t; + AnalyticFunction f({t},t); + AnalyticTraj z({2,6},f); + + truncate_to_common_tdomain(x,y,z); + + CHECK(x.tdomain() == Interval(2,4)); + CHECK(y.tdomain() == Interval(2,4)); + CHECK(z.tdomain() == Interval(2,4)); + + CHECK(x(2.) == 2.); // interpolated boundary + CHECK(y(4.) == Vector({4.,5.})); } \ No newline at end of file diff --git a/tests/core/trajectory/codac2_tests_SampledTraj.py b/tests/core/trajectory/codac2_tests_SampledTraj.py index da0072f37..db069dc39 100644 --- a/tests/core/trajectory/codac2_tests_SampledTraj.py +++ b/tests/core/trajectory/codac2_tests_SampledTraj.py @@ -130,6 +130,25 @@ def test_SampledTraj(self): for i in np.arange(0, 10, 1e-1): self.assertTrue(Approx(p(i),1e-2) == x(i)) + + + # truncate_to_common_tdomain + + x = SampledTraj_Scalar({0.:0., 4.:4.}) + y = SampledTraj_Vector({1.:[1.,2.], 5.:[5.,6.]}) + + t = ScalarVar() + z = AnalyticTraj([2,6], AnalyticFunction([t],t)) + + truncate_to_common_tdomain(x,y,z) + + self.assertTrue(x.tdomain() == Interval(2,4)) + self.assertTrue(y.tdomain() == Interval(2,4)) + self.assertTrue(z.tdomain() == Interval(2,4)) + + self.assertTrue(x(2.) == 2.) + self.assertTrue(y(4.) == Vector([4.,5.])) + if __name__ == '__main__': unittest.main() \ No newline at end of file From d16a2358d38fd08eed003fb8695c6660414990e6 Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Fri, 23 Oct 2026 14:11:48 +0200 Subject: [PATCH 09/16] [doc] added manual page about rounding mode conflicts with external C++ libraries --- doc/manual/index.rst | 1 + .../manual/intervals/Interval_class.rst | 2 + doc/manual/manual/intervals/index.rst | 4 ++ doc/manual/manual/intervals/rounding.rst | 37 +++++++++++++++++++ 4 files changed, 44 insertions(+) create mode 100644 doc/manual/manual/intervals/rounding.rst diff --git a/doc/manual/index.rst b/doc/manual/index.rst index bf6726f89..efd0cb49b 100644 --- a/doc/manual/index.rst +++ b/doc/manual/index.rst @@ -193,6 +193,7 @@ User manual * :ref:`sec-intervals-class` * :ref:`sec-intervals-intervalvector-class` * :ref:`sec-intervals-boolinterval-class` + * :ref:`sec-intervals-rounding` * :ref:`sec-linear` * :ref:`sec-linear-vecmat` diff --git a/doc/manual/manual/intervals/Interval_class.rst b/doc/manual/manual/intervals/Interval_class.rst index 11450b9c6..e6141b76a 100644 --- a/doc/manual/manual/intervals/Interval_class.rst +++ b/doc/manual/manual/intervals/Interval_class.rst @@ -213,6 +213,8 @@ Dealing with closed intervals, most of the operations can rely on their bounds. Note that Codac is built upon a low-level interval library, `GAOL `_, which has been built to provide functionalities for computing arithmetic on intervals, involving basic operations as well as non-linear functions. +For compatibility with other C++ numerical libraries (such as OpenCV), see :ref:`Floating-point rounding and library compatibility `. + All standard arithmetic operations are supported in Codac, both element-wise and with real numbers. .. tabs:: diff --git a/doc/manual/manual/intervals/index.rst b/doc/manual/manual/intervals/index.rst index bd3078c8e..f861673ee 100644 --- a/doc/manual/manual/intervals/index.rst +++ b/doc/manual/manual/intervals/index.rst @@ -10,6 +10,9 @@ Codac provides data structures for handling basic interval sets. These structure - ``IntervalMatrix``: represents a matrix where each element is an interval. - :ref:`The BoolInterval class `: represents a Boolean interval. +For C++ applications using other numerical libraries, see +:ref:`Floating-point rounding and library compatibility `. + .. toctree:: :hidden: @@ -17,6 +20,7 @@ Codac provides data structures for handling basic interval sets. These structure Interval_class.rst IntervalVector_class.rst BoolInterval_class.rst + rounding.rst .. What is an interval? The Interval class diff --git a/doc/manual/manual/intervals/rounding.rst b/doc/manual/manual/intervals/rounding.rst new file mode 100644 index 000000000..7ccc95d2c --- /dev/null +++ b/doc/manual/manual/intervals/rounding.rst @@ -0,0 +1,37 @@ +.. _sec-intervals-rounding: + +Floating-point rounding and library compatibility (C++) +======================================================= + +This page concerns the C++ interface of Codac. + +Codac relies on `GAOL `_ for interval arithmetic. +To guarantee interval enclosures, the current implementation uses upward rounding (toward :math:`+\infty`). +The floating-point rounding mode also affects computations performed outside Codac in the same thread. + +Some libraries assume the usual round-to-nearest mode. +For example, using `OpenCV `_ alongside Codac may cause unexpected results or runtime errors, including segmentation faults in functions such as ``cv::resize``. +This is a rounding-mode compatibility issue rather than necessarily a memory-management bug. +Its occurrence depends on the platform, build configuration, and operations used. +See also `IBEX issue #402 `_. + +Workaround +---------- + +Before calling a library that expects round-to-nearest, select this mode using ``gaol::round_nearest()`` or ``std::fesetround(FE_TONEAREST)`` from ````. +Before resuming Codac interval calculations, restore upward rounding using ``gaol::round_upward()``. + +The previous rounding mode can also be saved using ``std::fegetround()`` and restored using ``std::fesetround()``. +If this approach is used, ensure that upward rounding is active before performing further Codac interval calculations. + +.. warning:: + + Do not perform Codac interval arithmetic while round-to-nearest is active, since the current implementation relies on upward rounding for its guarantees. + Rounding modes are generally thread-local, so a change in one thread does not automatically configure other threads. + +Why Codac does not restore the rounding mode automatically +---------------------------------------------------------- + +The current implementation uses GAOL in a configuration that assumes upward rounding to avoid repeated changes of the processor's rounding mode. +GAOL also provides a ``preserve-rounding`` build option, but Codac explicitly selects upward rounding in several places. +Enabling this option alone is therefore not a validated compatibility fix for Codac. From df81f9bd10bedc0c4ef54da8a8c1badcb3525684 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Wed, 14 Oct 2026 23:29:30 +0200 Subject: [PATCH 10/16] Update MATLAB documentation [no ci] --- doc/manual/manual/installation/matlab.rst | 47 +++++++++- scripts/MATLAB/codac4matlab_usage.m | 106 ++++++++++++++++++++++ scripts/MATLAB/codac4matlabonline.m | 50 ++++++++++ 3 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 scripts/MATLAB/codac4matlab_usage.m create mode 100644 scripts/MATLAB/codac4matlabonline.m diff --git a/doc/manual/manual/installation/matlab.rst b/doc/manual/manual/installation/matlab.rst index 02f1f7c78..aa4ae963e 100644 --- a/doc/manual/manual/installation/matlab.rst +++ b/doc/manual/manual/installation/matlab.rst @@ -28,6 +28,50 @@ Once Codac is installed, you can use it in your MATLAB scripts using the followi .. py.importlib.import_module('codac') +Known installation issues and possible workarounds +-------------------------------------------------- + +* On some configurations, you might need to use ``pip3`` instead of ``pip``, and/or ``--break-system-packages`` parameter, *e.g.* + + .. code-block:: bash + + pip3 install --break-system-packages codac4matlab + + or + + .. code-block:: bash + + python3 -m pip install --break-system-packages codac4matlab + +* In some cases, you might have multiple Python installations at different locations on your system (possibly the same Python version can be installed separately at different locations, also some versions of Python might be installed but incompatible with your version of MATLAB). In MATLAB, try to examine the output of the ``pyenv`` command to try to determine where is the Python installation that it uses and possibly run the pip install command from inside MATLAB command window with the ``!`` prefix *e.g.* ``!pip install codac4matlab``, or from a Windows Administrator command prompt with the full path to the python executable *e.g* ``"C:\Python312\python.exe" -m pip install codac4matlab``. + +* Python virtual environments compatibility has not been tested. + +* MATLAB Online: you will need to run `codac4matlabonline.m `_. Tweak it if it does not work for you. It might not be possible to view the `VIBes viewer <../visualization/vibes.html>`_ windows, possibly use the VIBes functions to save the desired pictures as a workaround. + + +MATLAB specificities +-------------------- + +* You might need to add as prefix ``py.codac4matlab.`` to class or function names, e.g. ``py.codac4matlab.Interval`` for ``Interval`` to avoid potential name conflicts with other libraries, and/or do explicitely *e.g.* ``import py.codac4matlab.Interval; import py.codac4matlab.cos;`` instead of just ``import py.codac4matlab.*`` depending on your needs. + +* When we click on a Codac variable e.g. ``IntervalVector`` (``py.codac4matlab._core.IntervalVector``), we do not get help. Please check `codac.io <../..>`_ for the documentation. Also, you can try typing *e.g.* ``X0.`` followed by the TAB key (assuming ``X0`` is a ``py.codac4matlab._core.IntervalVector``) to get a list of suggestions of properties and methods. + +.. admonition:: Note + + Check `codac4matlab_usage.m `_ example for some MATLAB specificities. + + See also `Codac v1 documentation `_ for some remaining issues and potential workarounds. + + +Python to MATLAB conversions +---------------------------- + +* Be sure that Python multiline strings are correctly converted to multiline MATLAB strings between ``'``. Remember that multiline statements in MATLAB need ``...`` before next line. + +* Please also convert Python `for` loops to typical MATLAB `for` loops, same for `if-else`, `+=` statements. + + Potential problem with VIBes ---------------------------- @@ -40,4 +84,5 @@ A solution is to avoid using ``sudo``. If required, run MATLAB with the correct .. code:: - sudo HOME=/home/your_username matlab \ No newline at end of file + sudo HOME=/home/your_username matlab + diff --git a/scripts/MATLAB/codac4matlab_usage.m b/scripts/MATLAB/codac4matlab_usage.m new file mode 100644 index 000000000..ffb62ea28 --- /dev/null +++ b/scripts/MATLAB/codac4matlab_usage.m @@ -0,0 +1,106 @@ +import py.codac4matlab.* + +a=Interval(-1,2) +a.lb +a.ub + +x=[1,2] +X=IntervalVector(x) +X.size +X(2) +X(2).init(Interval(-10,10)) +X + +x={{-5,5},{-4,4}} +X=IntervalVector(x) +X.size + +M=[1,2;3,4;5,6] +C=arrayfun(@(i) num2cell(M(i,:)), 1:size(M,1), 'UniformOutput', false); % Inverse operation would be cell2mat(vertcat(C{:})) +A=IntervalMatrix(Matrix(C)) +A.rows +A.cols +A(3,2) +A(3,2).init(Interval(-10,10)) +A.set_item({1,2},Interval(11,12)) +A + +A_lb = Matrix({{-1,-2},{-3,-4},{-5,-6}}); +A_ub = Matrix({{1,2},{3,4},{5,6}}); +A=IntervalMatrix(A_lb,A_ub) +A.rows +A.cols +A(3,2) + +A_ub.set_item({1,2},10) +A_ub + +function C = Matrix2cell(M) + import py.codac4matlab.* + r = int32(M.rows); + c = int32(M.cols); + C = cell(1,r); + for i=1:r + rl = cell(1,c); + for j=1:c + rl{j} = double(M(i,j)); + end + C{i} = rl; + end +end + +%M=Matrix({{1,2},{3,4},{5,6}}) +%Matrix2cell(M) +%return + +% It seems we cannot do A(i,j).init(x) if A is a Matrix... But we can do +% A.set_item({i,j},x)... +function B = setitemM(A,i,j,x) +%SETITEMM Set element (i,j) of a Codac Matrix. +% +% B = setitemM(A,i,j,x) +% +% A : Codac Matrix +% i : row index (1-based MATLAB indexing) +% j : column index (1-based MATLAB indexing) +% x : double + + import py.codac4matlab.* + C = Matrix2cell(A); + C{i}{j} = x; + B = Matrix(C); +end + +% Does the same as A(i,j).init(x), probably in a slower way... +function B = setitemIM(A,i,j,x) +%SETITEMIM Set element (i,j) of a Codac IntervalMatrix. +% +% B = setitemIM(A,i,j,x) +% +% A : Codac IntervalMatrix +% i : row index (1-based MATLAB indexing) +% j : column index (1-based MATLAB indexing) +% x : Codac Interval + + import py.codac4matlab.* + C_lb = Matrix2cell(A.lb); + C_lb{i}{j} = x.lb; + C_ub = Matrix2cell(A.ub); + C_ub{i}{j} = x.ub; + B = IntervalMatrix(Matrix(C_lb),Matrix(C_ub)); +end + +A = Matrix({{1,2},{3,4},{5,6}}); +A = setitemM(A,3,2,99) + +A(3,2) +A + +A_lb = Matrix({{-1,-2},{-3,-4},{-5,-6}}); +A_ub = Matrix({{1,2},{3,4},{5,6}}); +A = IntervalMatrix(A_lb,A_ub); + +A = setitemIM(A,2,1,Interval(100,200)); + +A(2,1) +A diff --git a/scripts/MATLAB/codac4matlabonline.m b/scripts/MATLAB/codac4matlabonline.m new file mode 100644 index 000000000..4335ab54f --- /dev/null +++ b/scripts/MATLAB/codac4matlabonline.m @@ -0,0 +1,50 @@ +% +% Known issues: +% It might not be possible to view the VIBes windows, possibly use the VIBes functions to save the desired pictures as a workaround... +% +% TODO: +% Determine automatically the Python version used by MATLAB Online (e.g. by analyzing pyenv output) and download the latest codac and vibes versions from PyPi. +% Replace "/MATLAB Drive" with the output of pwd (warning: might be different than $HOME...). +% Add hint at the end to indicate how to uninstall (e.g. remove pythonlibs folder...)... +% + +!lsb_release -a +!uname -a +pyenv + +disp('Downloading and installing prerequisites...'); + +% pip install --user codac4matlab +!mkdir -p "/MATLAB Drive/pythonlibs" +if ~isfolder("/MATLAB Drive/pythonlibs/codac4matlab") + % For another version, get the exact link and name from https://pypi.org/project/codac4matlab/#files + !cd "/MATLAB Drive/pythonlibs" ; rm -f codac4matlab-2.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + !cd "/MATLAB Drive/pythonlibs" ; wget -q --show-progress --progress=dot:giga https://files.pythonhosted.org/packages/cd/8f/260a85ecd832120922510f6952d264c3c0aaa279741bffa98bea67baf07b/codac4matlab-2.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + !cd "/MATLAB Drive/pythonlibs" ; unzip -q codac4matlab-2.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl +end +if ~isfolder("/MATLAB Drive/pythonlibs/vibes-0.2.2") + % For another version, get the exact link and name from https://pypi.org/project/vibes/#files + !cd "/MATLAB Drive/pythonlibs" ; rm -f vibes-0.2.2.tar.gz + !cd "/MATLAB Drive/pythonlibs" ; wget -q --show-progress --progress=dot:giga https://files.pythonhosted.org/packages/20/3a/c19179d02310793aae928682a6435f97fe57e892f057048f1624b3675474/vibes-0.2.2.tar.gz + !cd "/MATLAB Drive/pythonlibs" ; tar xfz vibes-0.2.2.tar.gz +end + +% sudo apt-get install -y vibes +if ~isfolder("/MATLAB Drive/vibes") + !mkdir -p "/MATLAB Drive/vibes" + !cd "/MATLAB Drive/vibes" ; rm -f VIBes-viewer_amd64.AppImage + !cd "/MATLAB Drive/vibes" ; wget -q --show-progress --progress=dot:giga https://github.com/ENSTABretagneRobotics/VIBES/releases/latest/download/VIBes-viewer_amd64.AppImage + !cd "/MATLAB Drive/vibes" ; ./VIBes-viewer_amd64.AppImage --appimage-extract >/dev/null + !cd "/MATLAB Drive/vibes" ; mv squashfs-root/lib/x86_64-linux-gnu/libc.so.6 libc.so.6.bak + % To run in the background, but probably no way to make the window might visible... + !QT_QPA_PLATFORM_PLUGIN_PATH="/MATLAB Drive/vibes/squashfs-root/usr/lib/x86_64-linux-gnu/qt5/plugins/platforms/" ./squashfs-root/usr/bin/VIBes-viewer & + !xwininfo -root -tree | grep VIBes + % Maybe capture it with [~,out] = system('xwininfo -root -tree | grep "Codac - default figure"'); wid = regexp(out,'0x[0-9A-Fa-f]+','match','once'); system(sprintf('xwd -id %s -silent -out vibes.xwd', wid));... +end + +disp('End of prerequisites installation...'); + +% This is probably necessary every time... +if count(py.sys.path, "/MATLAB Drive/pythonlibs") == 0 + insert(py.sys.path, int32(0), "/MATLAB Drive/pythonlibs"); +end From 27cdc277cca77a38f144df47868af0d3b39d08f8 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 15 Oct 2026 22:47:38 +0200 Subject: [PATCH 11/16] Update codac4matlabonline.m [no ci] --- scripts/MATLAB/codac4matlabonline.m | 35 +++++++++++++++-------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/scripts/MATLAB/codac4matlabonline.m b/scripts/MATLAB/codac4matlabonline.m index 4335ab54f..20893be21 100644 --- a/scripts/MATLAB/codac4matlabonline.m +++ b/scripts/MATLAB/codac4matlabonline.m @@ -1,36 +1,33 @@ % +% Since pip and AppImage do not seem to work on MATLAB Online, this script downloads and extracts codac4matlab and VIBes in the "/MATLAB Drive" folder. +% % Known issues: % It might not be possible to view the VIBes windows, possibly use the VIBes functions to save the desired pictures as a workaround... % % TODO: -% Determine automatically the Python version used by MATLAB Online (e.g. by analyzing pyenv output) and download the latest codac and vibes versions from PyPi. -% Replace "/MATLAB Drive" with the output of pwd (warning: might be different than $HOME...). -% Add hint at the end to indicate how to uninstall (e.g. remove pythonlibs folder...)... +% Replace "/MATLAB Drive" with the output of matlabdrive command (warning: might be different than $HOME...)? Might not work with shell commands prefixed by "!", but maybe with e.g. drive = matlabdrive; system('wget ... "' + drive + '/..."')...? % !lsb_release -a !uname -a pyenv -disp('Downloading and installing prerequisites...'); +disp('Checking, downloading and installing codac4matlab prerequisites in "/MATLAB Drive/pythonlibs" and "/MATLAB Drive/vibes"...'); -% pip install --user codac4matlab +% Equivalent of pip install --user codac4matlab !mkdir -p "/MATLAB Drive/pythonlibs" if ~isfolder("/MATLAB Drive/pythonlibs/codac4matlab") - % For another version, get the exact link and name from https://pypi.org/project/codac4matlab/#files - !cd "/MATLAB Drive/pythonlibs" ; rm -f codac4matlab-2.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - !cd "/MATLAB Drive/pythonlibs" ; wget -q --show-progress --progress=dot:giga https://files.pythonhosted.org/packages/cd/8f/260a85ecd832120922510f6952d264c3c0aaa279741bffa98bea67baf07b/codac4matlab-2.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - !cd "/MATLAB Drive/pythonlibs" ; unzip -q codac4matlab-2.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + % If it does not work, please download the necessary .whl from https://pypi.org/project/codac4matlab/#files and unzip it in "/MATLAB Drive/pythonlibs"... + !PY_V_MAJ=`python3 -c "import sys; print(sys.version_info[0])"`; PY_V_MIN=`python3 -c "import sys; print(sys.version_info[1])"`; CP="cp${PY_V_MAJ}${PY_V_MIN}"; VER=$(wget -qO- https://pypi.org/pypi/codac4matlab/json | grep -o '"version":"[^"]*' | head -n 1 | cut -d'"' -f4); URL=$(wget -qO- https://pypi.org/pypi/codac4matlab/json | grep -o '"url":"[^"]*' | cut -d'"' -f4 | grep "/codac4matlab-${VER}-${CP}-${CP}.*manylinux.*x86_64\.whl$" | head -n 1); cd "/MATLAB Drive/pythonlibs" && wget -q -O "$(basename "$URL")" --show-progress --progress=dot:giga "$URL" && unzip -q "$(basename "$URL")" end -if ~isfolder("/MATLAB Drive/pythonlibs/vibes-0.2.2") - % For another version, get the exact link and name from https://pypi.org/project/vibes/#files - !cd "/MATLAB Drive/pythonlibs" ; rm -f vibes-0.2.2.tar.gz - !cd "/MATLAB Drive/pythonlibs" ; wget -q --show-progress --progress=dot:giga https://files.pythonhosted.org/packages/20/3a/c19179d02310793aae928682a6435f97fe57e892f057048f1624b3675474/vibes-0.2.2.tar.gz - !cd "/MATLAB Drive/pythonlibs" ; tar xfz vibes-0.2.2.tar.gz +d = dir("/MATLAB Drive/pythonlibs/vibes-*"); +if ~any([d.isdir]) + % If it does not work, please download the necessary .tar.gz or .whl from https://pypi.org/project/vibes/#files and extract it in "/MATLAB Drive/pythonlibs"... + !VER=$(wget -qO- https://pypi.org/pypi/vibes/json | grep -o '"version":"[^"]*' | head -n 1 | cut -d'"' -f4); URL=$(wget -qO- https://pypi.org/pypi/vibes/json | grep -o '"url":"[^"]*' | cut -d'"' -f4 | grep "/vibes-${VER}\.tar\.gz$" | head -n 1); cd "/MATLAB Drive/pythonlibs" && wget -q -O "$(basename "$URL")" --show-progress --progress=dot:giga "$URL" && tar xfz "$(basename "$URL")" end -% sudo apt-get install -y vibes -if ~isfolder("/MATLAB Drive/vibes") +% VIBes-viewer +if ~isfolder("/MATLAB Drive/vibes/squashfs-root") !mkdir -p "/MATLAB Drive/vibes" !cd "/MATLAB Drive/vibes" ; rm -f VIBes-viewer_amd64.AppImage !cd "/MATLAB Drive/vibes" ; wget -q --show-progress --progress=dot:giga https://github.com/ENSTABretagneRobotics/VIBES/releases/latest/download/VIBes-viewer_amd64.AppImage @@ -42,9 +39,13 @@ % Maybe capture it with [~,out] = system('xwininfo -root -tree | grep "Codac - default figure"'); wid = regexp(out,'0x[0-9A-Fa-f]+','match','once'); system(sprintf('xwd -id %s -silent -out vibes.xwd', wid));... end -disp('End of prerequisites installation...'); +disp('End of codac4matlab prerequisites checks.'); % This is probably necessary every time... if count(py.sys.path, "/MATLAB Drive/pythonlibs") == 0 insert(py.sys.path, int32(0), "/MATLAB Drive/pythonlibs"); end + +disp('Notes:'); +disp('_ This script needs to be run every time MATLAB Online is started.'); +disp('_ To uninstall, remove the "/MATLAB Drive/pythonlibs" and "/MATLAB Drive/vibes" folders, and remove related entries from py.sys.path.'); From 4b5745bd551a1813effcb040097513117f3b9578 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Mon, 19 Oct 2026 22:16:52 +0200 Subject: [PATCH 12/16] Avoid the install part of Eigen3 to override the system-wide Eigen3 --- CMakeLists.txt | 46 ++++++++++++++++++++++------- src/CMakeLists.txt | 4 ++- src/core/CMakeLists.txt | 2 +- src/extensions/capd/CMakeLists.txt | 2 +- src/extensions/sympy/CMakeLists.txt | 2 +- src/graphics/CMakeLists.txt | 2 +- src/unsupported/CMakeLists.txt | 2 +- 7 files changed, 43 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c727d35e..89aa66f1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,7 @@ # Compilation configuration ################################################################################ + include(GNUInstallDirs) include(FetchContent) if(NOT CMAKE_BUILD_TYPE) @@ -130,23 +131,46 @@ # Looking for Eigen3 ################################################################################ - option(ENABLE_FIND_PACKAGE_EIGEN3 "ENABLE_FIND_PACKAGE_EIGEN3" OFF) - if(ENABLE_FIND_PACKAGE_EIGEN3) - find_package(Eigen3 3.4.0 QUIET) # Use Eigen3_DIR or CMAKE_PREFIX_PATH to specify a custom Eigen3 installation path... - endif() - if(Eigen3_FOUND) - message(STATUS "Found Eigen3 version ${Eigen3_VERSION} in ${Eigen3_DIR}") - else() - message(STATUS "Attempt to install custom Eigen3 automatically...") + #option(ENABLE_FIND_PACKAGE_EIGEN3 "ENABLE_FIND_PACKAGE_EIGEN3" OFF) + #if(ENABLE_FIND_PACKAGE_EIGEN3) + # find_package(Eigen3 3.4.0 QUIET) # Use Eigen3_DIR or CMAKE_PREFIX_PATH to specify a custom Eigen3 installation path... + #endif() + #if(Eigen3_FOUND) + # message(STATUS "Found Eigen3 version ${Eigen3_VERSION} in ${Eigen3_DIR}") + #else() + #message(STATUS "Attempt to get Eigen3 automatically...") + message(STATUS "Attempt to get custom Eigen3...") # Over HTTPS, not plain HTTP: the connections to port 80 of github.com timed # out now and then on the GitHub Actions runners, failing the whole job at # configuration time ("Failed to connect to github.com port 80"), and HTTPS # also guarantees that the archive comes from GitHub unaltered. FetchContent_Declare(Eigen3 URL https://github.com/codac-team/eigen/archive/refs/heads/eigen-5.0.zip) #FetchContent_Declare(Eigen3 URL ${CMAKE_CURRENT_SOURCE_DIR}/3rd/eigen-a0dc3994bd5d4e804dab58decb78c56a5af516ba.zip) # If needed to be self-contained... - FetchContent_MakeAvailable(Eigen3) - endif() - # Adds Eigen3::Eigen + # + #FetchContent_MakeAvailable(Eigen3) + # + # This avoids the install part of Eigen3 to override the system-wide Eigen3 installation if any... + cmake_policy(PUSH) + if (POLICY CMP0169) + cmake_policy(SET CMP0169 OLD) + endif() + FetchContent_Populate(Eigen3) + cmake_policy(POP) + add_library(${PROJECT_NAME}-eigen INTERFACE) + target_include_directories(${PROJECT_NAME}-eigen + INTERFACE + "$" + "$" + ) + #if (CODAC_INSTALL) + install(DIRECTORY ${eigen3_SOURCE_DIR}/unsupported/Eigen DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}-eigen/unsupported) + install(DIRECTORY ${eigen3_SOURCE_DIR}/Eigen DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}-eigen) + #endif() + add_library(${PROJECT_NAME}::${PROJECT_NAME}-eigen ALIAS ${PROJECT_NAME}-eigen) + # + #endif() + ## Adds Eigen3::Eigen + # Adds ${PROJECT_NAME}-eigen ################################################################################ # Looking for Threads diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 28fd1c4fc..c284c553e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -53,6 +53,8 @@ "# Try to find Codac # This file has been generated by CMake + find_path(CODAC_EIGEN_INCLUDE_DIR Eigen/Core + PATH_SUFFIXES include/${PROJECT_NAME}-eigen) find_path(CODAC_CORE_INCLUDE_DIR ${PROJECT_NAME}-core.h PATH_SUFFIXES include/${PROJECT_NAME}-core) find_path(CODAC_GRAPHICS_INCLUDE_DIR ${PROJECT_NAME}-graphics.h @@ -71,7 +73,7 @@ set(CODAC_VERSION ${PROJECT_VERSION}) set(CODAC_LIBRARIES \${CODAC_CORE_LIBRARY} \${CODAC_GRAPHICS_LIBRARY} \${CODAC_UNSUPPORTED_LIBRARY} Ibex::ibex) - set(CODAC_INCLUDE_DIRS \${CODAC_CORE_INCLUDE_DIR}/../ \${CODAC_CORE_INCLUDE_DIR}/../eigen3/ \${CODAC_CORE_INCLUDE_DIR} \${CODAC_GRAPHICS_INCLUDE_DIR} \${CODAC_UNSUPPORTED_INCLUDE_DIR}) + set(CODAC_INCLUDE_DIRS \${CODAC_CORE_INCLUDE_DIR}/../ \${CODAC_EIGEN_INCLUDE_DIR} \${CODAC_CORE_INCLUDE_DIR} \${CODAC_GRAPHICS_INCLUDE_DIR} \${CODAC_UNSUPPORTED_INCLUDE_DIR}) set(CODAC_C_FLAGS \"\") set(CODAC_CXX_FLAGS \"\") diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 1573630ce..7c3752756 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -331,7 +331,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/tools ${CMAKE_CURRENT_SOURCE_DIR}/trajectory ) - target_link_libraries(${PROJECT_NAME}-core PUBLIC Ibex::ibex Eigen3::Eigen Threads::Threads) + target_link_libraries(${PROJECT_NAME}-core PUBLIC ${PROJECT_NAME}-eigen Ibex::ibex Threads::Threads) #Eigen3::Eigen ################################################################################ diff --git a/src/extensions/capd/CMakeLists.txt b/src/extensions/capd/CMakeLists.txt index 0214cb955..72c364450 100644 --- a/src/extensions/capd/CMakeLists.txt +++ b/src/extensions/capd/CMakeLists.txt @@ -22,7 +22,7 @@ list(APPEND CODAC_CAPD_SRC #endif() add_library(${PROJECT_NAME}-capd ${CODAC_CAPD_SRC}) - target_link_libraries(${PROJECT_NAME}-capd PUBLIC ${PROJECT_NAME}-core Ibex::ibex Eigen3::Eigen capd::capd) + target_link_libraries(${PROJECT_NAME}-capd PUBLIC ${PROJECT_NAME}-core ${PROJECT_NAME}-eigen Ibex::ibex capd::capd) # Eigen3::Eigen ################################################################################ diff --git a/src/extensions/sympy/CMakeLists.txt b/src/extensions/sympy/CMakeLists.txt index d92bae572..00e893260 100644 --- a/src/extensions/sympy/CMakeLists.txt +++ b/src/extensions/sympy/CMakeLists.txt @@ -31,7 +31,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) add_library(${PROJECT_NAME}-sympy ${CODAC_SYMPY_SRC}) - target_link_libraries(${PROJECT_NAME}-sympy PUBLIC ${PROJECT_NAME}-core Ibex::ibex Eigen3::Eigen) + target_link_libraries(${PROJECT_NAME}-sympy PUBLIC ${PROJECT_NAME}-core ${PROJECT_NAME}-eigen Ibex::ibex) # Eigen3::Eigen target_link_libraries(${PROJECT_NAME}-sympy PRIVATE pybind11::pybind11) ################################################################################ diff --git a/src/graphics/CMakeLists.txt b/src/graphics/CMakeLists.txt index 453a547d1..3a08363f5 100644 --- a/src/graphics/CMakeLists.txt +++ b/src/graphics/CMakeLists.txt @@ -53,7 +53,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/paver # deprecated, to be removed ${CMAKE_CURRENT_SOURCE_DIR}/styles ) - target_link_libraries(${PROJECT_NAME}-graphics PUBLIC ${PROJECT_NAME}-core Ibex::ibex Eigen3::Eigen ${PROJECT_NAME}-core) + target_link_libraries(${PROJECT_NAME}-graphics PUBLIC ${PROJECT_NAME}-core ${PROJECT_NAME}-eigen Ibex::ibex ${PROJECT_NAME}-core) # Eigen3::Eigen ################################################################################ diff --git a/src/unsupported/CMakeLists.txt b/src/unsupported/CMakeLists.txt index a0a0f8d8f..f229909a1 100644 --- a/src/unsupported/CMakeLists.txt +++ b/src/unsupported/CMakeLists.txt @@ -25,7 +25,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/paver ${CMAKE_CURRENT_SOURCE_DIR}/styles ) - target_link_libraries(${PROJECT_NAME}-unsupported PUBLIC ${PROJECT_NAME}-core Eigen3::Eigen) + target_link_libraries(${PROJECT_NAME}-unsupported PUBLIC ${PROJECT_NAME}-core ${PROJECT_NAME}-eigen) # Eigen3::Eigen ################################################################################ From 934331641648415a889e642bfaaa8bc435ca0bc3 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 24 Oct 2026 16:21:01 +0200 Subject: [PATCH 13/16] Update cpp.rst --- doc/manual/manual/installation/cpp.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/manual/installation/cpp.rst b/doc/manual/manual/installation/cpp.rst index d92dc75b9..e4bf9a3f9 100644 --- a/doc/manual/manual/installation/cpp.rst +++ b/doc/manual/manual/installation/cpp.rst @@ -100,7 +100,7 @@ Steps .. warning:: - **GAOL prerequisite:** On some platforms, you might need to install manually `MathLib `_ and `GAOL `_ with CMake and `specify where they are `_ in order to build IBEX successfully and have accurate computations. + **GAOL prerequisite:** On some platforms, you might need to install manually `MathLib `_ and `GAOL `_ with CMake and `specify where they are `_ in order to build IBEX successfully and have accurate computations. 3. **Install the Codac library**: From 32a60af8ce959b05ff7b9892ae1e69f4bd067f21 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 24 Oct 2026 18:42:51 +0200 Subject: [PATCH 14/16] git config --global --add safe.directory ${PWD}/.git --- .github/workflows/dockermatrix.yml | 2 +- packages/temporary/gennewcodacpi_armhf.sh | 1 + scripts/docker/build_pybinding.sh | 1 + scripts/docker/build_pybinding_codac4matlab.sh | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dockermatrix.yml b/.github/workflows/dockermatrix.yml index 5045d0245..6a0452741 100644 --- a/.github/workflows/dockermatrix.yml +++ b/.github/workflows/dockermatrix.yml @@ -68,7 +68,7 @@ jobs: # if: (matrix.cfg.arch!='amd64')&&(matrix.cfg.arch!='x86_64')&&(matrix.cfg.arch!='i386') - run: | docker run ${{ matrix.cfg.docker_flags }} -i -v "${PWD}/..:${PWD}/.." ${{ matrix.cfg.img }} /bin/bash -c "uname -a ; cat /etc/os-release ; lsb_release -a ; cd ${PWD} && pwd && \ - git config --global --add safe.directory ${PWD} && \ + git config --global --add safe.directory ${PWD} && git config --global --add safe.directory ${PWD}/.git || true && \ if [ \"${{ matrix.cfg.deb }}\" = \"true\" ]; then \ sudo sh -c 'echo \"deb [trusted=yes] https://webperso.ensta.fr/packages/\$(if [ -z \"\$(. /etc/os-release && echo \$UBUNTU_CODENAME)\" ]; then echo debian/\$(. /etc/os-release && echo \$VERSION_CODENAME); else echo ubuntu/\$(. /etc/os-release && echo \$UBUNTU_CODENAME); fi) ./\" > /etc/apt/sources.list.d/ensta-bretagne.list' && \ #sudo apt-get -q update ; sudo apt-get -y install libeigen3-dev catch2 dpkg-dev || true && \\ diff --git a/packages/temporary/gennewcodacpi_armhf.sh b/packages/temporary/gennewcodacpi_armhf.sh index 7ec2b7a02..95ca54a41 100644 --- a/packages/temporary/gennewcodacpi_armhf.sh +++ b/packages/temporary/gennewcodacpi_armhf.sh @@ -34,6 +34,7 @@ rm -Rf ibex_armhf_\$(lsb_release -cs).zip && \ sudo cp -Rf ibex/* /usr/local/ && \ \ git config --global --add safe.directory /io && \ +git config --global --add safe.directory /io/.git || true && \ cd /io && \ \ # Issues when some packages are upgraded or not...? \\ diff --git a/scripts/docker/build_pybinding.sh b/scripts/docker/build_pybinding.sh index ab98904f3..c86aa2592 100755 --- a/scripts/docker/build_pybinding.sh +++ b/scripts/docker/build_pybinding.sh @@ -8,6 +8,7 @@ rm -Rf ibex_$(uname -m)_manylinux_2_28.zip sudo cp -Rf ibex/* /usr/local/ git config --global --add safe.directory /io +git config --global --add safe.directory /io/.git || true cd /io # Read optional constraints from environment diff --git a/scripts/docker/build_pybinding_codac4matlab.sh b/scripts/docker/build_pybinding_codac4matlab.sh index b25fd262d..b7fd2d74b 100644 --- a/scripts/docker/build_pybinding_codac4matlab.sh +++ b/scripts/docker/build_pybinding_codac4matlab.sh @@ -8,6 +8,7 @@ rm -Rf ibex_$(uname -m)_manylinux_2_28.zip sudo cp -Rf ibex/* /usr/local/ git config --global --add safe.directory /io +git config --global --add safe.directory /io/.git || true cd /io # Read optional constraints from environment From da94bfd3f910e4422f9109d390c82406c46c5813 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 24 Oct 2026 15:27:15 +0200 Subject: [PATCH 15/16] Update gennewcodacpi_armhf.sh [no ci] --- packages/temporary/gennewcodacpi_armhf.sh | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/temporary/gennewcodacpi_armhf.sh b/packages/temporary/gennewcodacpi_armhf.sh index 95ca54a41..a87972290 100644 --- a/packages/temporary/gennewcodacpi_armhf.sh +++ b/packages/temporary/gennewcodacpi_armhf.sh @@ -12,11 +12,11 @@ cd codac git submodule init git submodule update -for DIST in bookworm bullseye; do +for DIST in trixie bookworm bullseye; do docker pull lebarsfa/pi:$DIST-for-codac docker run --rm -v `pwd`:/io lebarsfa/pi:$DIST-for-codac /bin/bash -c "uname -a ; cat /etc/os-release ; lsb_release -a ; \ -if [ \"\$(lsb_release -cs)\" = \"bookworm\" ]; then \ +if [ \"\$(lsb_release -cs)\" = \"bookworm\" ] || [ \"\$(lsb_release -cs)\" = \"trixie\" ]; then \ PIP_OPTIONS=--break-system-packages ; \ #python3 -m pip install \$PIP_OPTIONS --upgrade \"auditwheel\" ; \\ else \ @@ -25,7 +25,10 @@ else \ fi && \ #sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev python3-dev patchelf python3-pip python3-wheel python3-setuptools || true && \\ sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install python3-dev patchelf python3-pip python3-wheel python3-setuptools || true && \ -python3 -m pip install \$PIP_OPTIONS --upgrade patchelf --prefer-binary --extra-index-url https://www.piwheels.org/simple || true && \ +# On bookworm (and possibly earlier), auditwheel repair asks for a recent version of patchelf... \\ +if [ \"\$(lsb_release -cs)\" = \"buster\" ] || [ \"\$(lsb_release -cs)\" = \"bullseye\" ] || [ \"\$(lsb_release -cs)\" = \"bookworm\" ]; then \ + python3 -m pip install \$PIP_OPTIONS --upgrade patchelf --prefer-binary --extra-index-url https://www.piwheels.org/simple || true ; \ +fi && \ python3 -m pip install \$PIP_OPTIONS --upgrade auditwheel --prefer-binary --extra-index-url https://www.piwheels.org/simple && \ # wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20260819/ibex_armhf_\$(lsb_release -cs).zip --no-check-certificate -nv is causing illegal instruction on a Mac M1... \\ curl -L -O https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20260819/ibex_armhf_\$(lsb_release -cs).zip --insecure && \ @@ -38,9 +41,13 @@ git config --global --add safe.directory /io/.git || true && \ cd /io && \ \ # Issues when some packages are upgraded or not...? \\ -python3 -m pip install \$PIP_OPTIONS --upgrade pip --prefer-binary --extra-index-url https://www.piwheels.org/simple && \ +if [ \"\$(lsb_release -cs)\" = \"buster\" ] || [ \"\$(lsb_release -cs)\" = \"bullseye\" ]; then \ + python3 -m pip install \$PIP_OPTIONS --upgrade pip --prefer-binary --extra-index-url https://www.piwheels.org/simple || true ; \ +fi && \ # Without wheel upgrade from pip, the architecture might be wrong for bookworm (and possibly earlier) when building the .whl... \\ -python3 -m pip install \$PIP_OPTIONS --upgrade wheel --prefer-binary --extra-index-url https://www.piwheels.org/simple && \ +if [ \"\$(lsb_release -cs)\" = \"buster\" ] || [ \"\$(lsb_release -cs)\" = \"bullseye\" ] || [ \"\$(lsb_release -cs)\" = \"bookworm\" ]; then \ + python3 -m pip install \$PIP_OPTIONS --upgrade wheel --prefer-binary --extra-index-url https://www.piwheels.org/simple || true ; \ +fi && \ # With setuptools upgrade from pip, building the .whl might fail for bullseye... \\ #python3 -m pip install \$PIP_OPTIONS --upgrade setuptools --prefer-binary --extra-index-url https://www.piwheels.org/simple && \\ mkdir -p build_dir_\$(lsb_release -cs) && cd build_dir_\$(lsb_release -cs) && \ @@ -69,6 +76,7 @@ sudo apt-get -y install libopenblas0-pthread || true && \ sudo apt-get -y install libgfortran5 || true && \ python3 -m pip install \$PIP_OPTIONS numpy --prefer-binary --extra-index-url https://www.piwheels.org/simple && \ #sudo apt-get -y install python3-numpy || true && \\ +python3 -m pip install \$PIP_OPTIONS sympy --prefer-binary --extra-index-url https://www.piwheels.org/simple && \ python3 -m unittest discover codac.tests && \ \ if [ \"\$(lsb_release -cs)\" = \"buster\" ]; then \ From f989a62c51ec8e577578ecc68e84d48dd7dac8cc Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Fri, 30 Oct 2026 17:09:14 +0100 Subject: [PATCH 16/16] [doc] added ENSTA + labSTICC logos --- doc/manual/_static/logos/logo_ensta.jpeg | Bin 0 -> 32302 bytes doc/manual/_static/logos/logo_labsticc.jpeg | Bin 0 -> 35973 bytes doc/manual/index.rst | 17 +++++++++++++++-- 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 doc/manual/_static/logos/logo_ensta.jpeg create mode 100644 doc/manual/_static/logos/logo_labsticc.jpeg diff --git a/doc/manual/_static/logos/logo_ensta.jpeg b/doc/manual/_static/logos/logo_ensta.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..61dabdc1e03e9ae82cf2b502336ef31589488b44 GIT binary patch literal 32302 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%0AnV3e^kuVO_dQArpL1okS>GOl3^KmAbkYI3UJ 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 literal 0 HcmV?d00001 diff --git a/doc/manual/index.rst b/doc/manual/index.rst index efd0cb49b..b64f24cf4 100644 --- a/doc/manual/index.rst +++ b/doc/manual/index.rst @@ -160,6 +160,21 @@ This list is in alphabetical order by surname. We appreciate all contributions, whether code, documentation, bug reports, or suggestions. If you believe your name should be included here and it is not, please contact us so we can update the list. +Codac is primarily developed and maintained by researchers at `Lab-STICC `_ (`Robex team `_), particularly those based at `ENSTA `_. +The project also benefits from contributions from researchers and developers at other institutions and welcomes new participants regardless of their affiliation. + +.. |labsticc-logo| image:: /_static/logos/logo_labsticc.jpeg + :width: 95px + :alt: Lab-STICC + +.. |ensta-logo| image:: /_static/logos/logo_ensta.jpeg + :width: 245px + :alt: ENSTA, Institut Polytechnique de Paris + +|labsticc-logo| |ensta-logo| + + +| Provisional Plan ^^^^^^^^^^^^^^^^ @@ -168,8 +183,6 @@ Below is a provisional outline for the structure of this manual. Some pages are already available. Please note that sections may be revised or added as the content continues to evolve. -| - Overview of Codac -----------------