From 9c48a12fc634af4d4473b5e16f8a408aeeef2357 Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Fri, 9 Oct 2026 23:22:53 +0200 Subject: [PATCH 1/8] [doc] Refine Doxygen 1.17 configuration --- doc/api/Doxyfile.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/api/Doxyfile.in b/doc/api/Doxyfile.in index 8e621a420..4252ee59e 100644 --- a/doc/api/Doxyfile.in +++ b/doc/api/Doxyfile.in @@ -290,8 +290,8 @@ TAB_SIZE = 4 # with the commands \{ and \} for these it is advised to use the version @{ and # @} or use a double escape (\\{ and \\}) -ALIASES = "license=@par License:" \ - "bugfixes=@par Bug fixes:" +ALIASES = "license=@par License: " \ + "bugfixes=@par Bug fixes: " # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For @@ -630,7 +630,7 @@ HIDE_UNDOC_CLASSES = YES # option has no effect if EXTRACT_ALL is enabled. # The default value is: YES. -HIDE_UNDOC_NAMESPACES = YES +HIDE_UNDOC_NAMESPACES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all friend # declarations. If set to NO, these declarations will be included in the From c71e20adfe32488887f1dd1ec070b71cd8ba8d2f Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Sat, 10 Oct 2026 09:51:20 +0200 Subject: [PATCH 2/8] [doc] Improve Doxygen error diagnostics with verbose retry --- doc/CMakeLists.txt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index f586e660d..0f20617de 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -41,10 +41,17 @@ RESULT_VARIABLE DOXYGEN_RESULT ) if(NOT DOXYGEN_RESULT EQUAL 0) - if(EXISTS "${CMAKE_BINARY_DIR}/doc/api/xml/index.xml") - message(WARNING "Doxygen exited with '${DOXYGEN_RESULT}' but generated XML output. Continuing so Python docstrings can still be generated.") + message(WARNING "Doxygen failed (exit code ${DOXYGEN_RESULT}). Retrying with full output:") + execute_process( + COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_INPUT} + RESULT_VARIABLE DOXYGEN_RETRY_RESULT + ) + if(DOXYGEN_RETRY_RESULT EQUAL 0) + message(STATUS "Doxygen succeeded on the second attempt.") + elseif(EXISTS "${CMAKE_BINARY_DIR}/doc/api/xml/index.xml") + message(WARNING "Doxygen retry exited with '${DOXYGEN_RETRY_RESULT}' but generated XML output. Continuing so Python docstrings can still be generated.") else() - message(FATAL_ERROR "Doxygen failed to generate the API documentation (exit code ${DOXYGEN_RESULT}), which is required for the Python docstrings. Doxygen errors: ${DOXYGEN_ERROR}") + message(FATAL_ERROR "Doxygen failed to generate the API documentation (first exit code ${DOXYGEN_RESULT}, retry exit code ${DOXYGEN_RETRY_RESULT}), which is required for the Python docstrings. Retry output is shown above. Initial Doxygen errors: ${DOXYGEN_ERROR}") endif() endif() add_custom_target(api COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_INPUT} OUTPUT_QUIET) From 9d3b06e513398be5874ba539449d5686210ea42f Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Sat, 10 Oct 2026 10:25:26 +0200 Subject: [PATCH 3/8] [doc] Improve README badges and CI status links --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2e6b43720..b770d8e54 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,12 @@ -# [Codac: constraint-programming for robotics](http://codac.io) [![Build Status](https://github.com/codac-team/codac/workflows/.github/workflows/tests.yml/badge.svg)](https://github.com/codac-team/codac/actions) +# [Codac: constraint-programming for robotics](https://www.codac.io/) -See the official website: http://codac.io +[![Tests](https://github.com/codac-team/codac/actions/workflows/tests.yml/badge.svg?branch=codac2&event=push)](https://github.com/codac-team/codac/actions/workflows/tests.yml?query=branch%3Acodac2+event%3Apush) +[![PyPI](https://img.shields.io/pypi/v/codac)](https://pypi.org/project/codac/) +[![Documentation](https://img.shields.io/badge/docs-codac.io-blue)](https://www.codac.io/) +[![License](https://img.shields.io/pypi/l/codac)](https://pypi.org/project/codac/) + +See the official website: https://www.codac.io +[View all CI workflows on the codac2 branch](https://github.com/codac-team/codac/actions?query=branch%3Acodac2+event%3Apush) Codac (Catalog Of Domains And Contractors) is a C++/Python/Matlab library providing tools for interval computations and constraint programming over real numbers, trajectories and sets. It has numerous applications in parameter estimation, guaranteed integration, robot localization, and provides reliable outputs. From a83e5141092aec5b7c91b442c2f0399232939dbc Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Sat, 10 Oct 2026 10:46:07 +0200 Subject: [PATCH 4/8] [doc] Improve Doxygen crash diagnostics on macOS --- .github/workflows/macosmatrix.yml | 40 ++++++++++++++++++++++++++++--- doc/CMakeLists.txt | 3 +-- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 20ba35698..b957064eb 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -7,9 +7,8 @@ on: - codac2_codac4matlab tags-ignore: ['**'] # Ignore all tag pushes pull_request: - jobs: - macosmatrix: + macosmatrix: runs-on: ${{ matrix.cfg.os }} env: MACOSX_DEPLOYMENT_TARGET: ${{ matrix.cfg.trgt }} @@ -33,7 +32,7 @@ jobs: - { 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: 9 , desc: 'macOS Sequoia Python 3.9 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: 8 , desc: 'macOS Sequoia Python 3.8 x86_64' } name: ${{ matrix.cfg.desc }} - steps: + steps: - uses: actions/checkout@v7 with: submodules: true @@ -69,6 +68,41 @@ jobs: cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` cd .. shell: bash + - name: Diagnose Doxygen crash + if: failure() + shell: bash + run: | + # Keep this diagnostic step from hiding the original CMake failure. + set +e + echo "=== Doxygen executable and version ===" + command -v doxygen + doxygen --version + + if [[ ! -f build/doc/api/Doxyfile ]]; then + echo "No generated Doxyfile: CMake failed before Doxygen configuration." + exit 0 + fi + + echo "=== Check Doxyfile parsing ===" + doxygen -x build/doc/api/Doxyfile > /tmp/codac-doxygen-config.log 2>&1 + echo "Exit code: $?" + tail -n 50 /tmp/codac-doxygen-config.log + + echo "=== Reproduce Doxygen failure ===" + doxygen build/doc/api/Doxyfile > /tmp/codac-doxygen-run.log 2>&1 + echo "Exit code: $?" + tail -n 50 /tmp/codac-doxygen-run.log + + echo "=== LLDB crash backtrace (if permitted by the runner) ===" + /usr/bin/lldb --batch -o "run" -k "thread backtrace all" -- "$(command -v doxygen)" "${GITHUB_WORKSPACE}/build/doc/api/Doxyfile" + echo "LLDB exit code: $?" + + echo "=== macOS crash reports (fallback) ===" + sleep 2 + if [[ -d "$HOME/Library/Logs/DiagnosticReports" ]]; then + find "$HOME/Library/Logs/DiagnosticReports" -maxdepth 1 -type f \( -iname 'doxygen*.ips' -o -iname 'doxygen*.crash' \) -mmin -15 -print -exec head -n 110 {} \; + fi + exit 0 - uses: xresloader/upload-to-github-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 0f20617de..27eb92267 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -48,9 +48,8 @@ ) if(DOXYGEN_RETRY_RESULT EQUAL 0) message(STATUS "Doxygen succeeded on the second attempt.") - elseif(EXISTS "${CMAKE_BINARY_DIR}/doc/api/xml/index.xml") - message(WARNING "Doxygen retry exited with '${DOXYGEN_RETRY_RESULT}' but generated XML output. Continuing so Python docstrings can still be generated.") else() + # Do not accept partial XML output produced by a crashed Doxygen process. message(FATAL_ERROR "Doxygen failed to generate the API documentation (first exit code ${DOXYGEN_RESULT}, retry exit code ${DOXYGEN_RETRY_RESULT}), which is required for the Python docstrings. Retry output is shown above. Initial Doxygen errors: ${DOXYGEN_ERROR}") endif() endif() From 7b0a10db0cc429977617b7f027fd7368fd92655e Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Sat, 10 Oct 2026 11:16:35 +0200 Subject: [PATCH 5/8] [doc] Use official Doxygen binaries to fix macOS CI crashes --- .github/workflows/macosmatrix.yml | 37 ++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index b957064eb..b9dc73509 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -53,7 +53,42 @@ jobs: # if: runner.os=='macOS' - run: brew install catch2 # Issues with binary packages when cross-compiling... if: (runner.os=='macOS')&&(matrix.cfg.cross!=true) - - run: brew install graphviz ; brew install --formula doxygen ; 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 + # The Doxygen 1.18.0 Homebrew bottle crashes in commentscanYYlex on macOS. + # Use the official release archive instead (with a verified SHA-256). + - name: Install official Doxygen 1.18.0 + if: runner.os=='macOS' + shell: bash + run: | + set -euo pipefail + case "${{ matrix.cfg.arch }}" in + x86_64) + doxygen_arch="intel" + expected_sha="8045d72f6f900fd430042339c1493d6eefcc85a7f990361fb57685682f16a2f9" + ;; + arm64) + doxygen_arch="arm" + expected_sha="31d1c74467a9f6f456b4e6a4eff0c03e93d1ffa92da5fdf2d53a2f14ebaabbd7" + ;; + *) echo "Unsupported architecture: ${{ matrix.cfg.arch }}" >&2; exit 1 ;; + esac + archive="doxygen-1.18.0-mac-${doxygen_arch}.zip" + tmpdir="$(mktemp -d)" + trap 'rm -rf "$tmpdir"' EXIT + curl --fail --location --silent --show-error --retry 3 \ + "https://github.com/doxygen/doxygen/releases/download/Release_1_18_0/${archive}" \ + --output "$tmpdir/$archive" + echo "${expected_sha} ${tmpdir}/${archive}" | shasum -a 256 --check + unzip -q "$tmpdir/$archive" -d "$tmpdir/unpacked" + doxygen_bin="$(find "$tmpdir/unpacked" -type f -name doxygen -print -quit)" + if [[ -z "$doxygen_bin" ]]; then + echo "Doxygen executable not found in ${archive}" >&2 + exit 1 + 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 + - 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: | wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20260819/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv From 283b6930146720708e60ccf5352131f57ecc7b18 Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Sat, 10 Oct 2026 11:41:19 +0200 Subject: [PATCH 6/8] [actions] Back to Doxygen to 1.16.1 on macOS to avoid parser crash --- .github/workflows/macosmatrix.yml | 32 ++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index b9dc73509..8f0473564 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -53,29 +53,35 @@ jobs: # if: runner.os=='macOS' - run: brew install catch2 # Issues with binary packages when cross-compiling... if: (runner.os=='macOS')&&(matrix.cfg.cross!=true) - # The Doxygen 1.18.0 Homebrew bottle crashes in commentscanYYlex on macOS. - # Use the official release archive instead (with a verified SHA-256). - - name: Install official Doxygen 1.18.0 + # Doxygen 1.18.0 crashes in commentscanYYlex on macOS Intel, + # including with the official binary. Test the pinned 1.16.1 release. + - name: Install official Doxygen 1.16.1 if: runner.os=='macOS' shell: bash + env: + GH_TOKEN: ${{ github.token }} run: | set -euo pipefail case "${{ matrix.cfg.arch }}" in - x86_64) - doxygen_arch="intel" - expected_sha="8045d72f6f900fd430042339c1493d6eefcc85a7f990361fb57685682f16a2f9" - ;; - arm64) - doxygen_arch="arm" - expected_sha="31d1c74467a9f6f456b4e6a4eff0c03e93d1ffa92da5fdf2d53a2f14ebaabbd7" - ;; + x86_64) doxygen_arch="intel" ;; + arm64) doxygen_arch="arm" ;; *) echo "Unsupported architecture: ${{ matrix.cfg.arch }}" >&2; exit 1 ;; esac - archive="doxygen-1.18.0-mac-${doxygen_arch}.zip" + archive="doxygen-1.16.1-mac-${doxygen_arch}.zip" tmpdir="$(mktemp -d)" trap 'rm -rf "$tmpdir"' EXIT + + # Verify the downloaded release asset against its SHA-256 + # recorded in the official GitHub release metadata. + expected_sha="$( + curl --fail --location --silent --show-error --retry 3 \ + --header "Authorization: Bearer ${GH_TOKEN}" \ + --header "Accept: application/vnd.github+json" \ + "https://api.github.com/repos/doxygen/doxygen/releases/tags/Release_1_16_1" \ + | python3 -c 'import json, re, sys; a = next((x for x in json.load(sys.stdin).get("assets", []) if x.get("name") == sys.argv[1]), None); d = (a.get("digest") or "") if a else ""; sys.exit("Official SHA-256 digest unavailable for " + sys.argv[1]) if not re.fullmatch(r"sha256:[0-9a-fA-F]{64}", d) else print(d.split(":", 1)[1])' "$archive" + )" curl --fail --location --silent --show-error --retry 3 \ - "https://github.com/doxygen/doxygen/releases/download/Release_1_18_0/${archive}" \ + "https://github.com/doxygen/doxygen/releases/download/Release_1_16_1/${archive}" \ --output "$tmpdir/$archive" echo "${expected_sha} ${tmpdir}/${archive}" | shasum -a 256 --check unzip -q "$tmpdir/$archive" -d "$tmpdir/unpacked" From f107a45d96880693727033bc4e29016083ccbbe5 Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Sat, 10 Oct 2026 11:48:00 +0200 Subject: [PATCH 7/8] [actions] Build Doxygen 1.16.1 from source on macOS 14 ARM64 --- .github/workflows/macosmatrix.yml | 114 +++++++++++++++++++++--------- 1 file changed, 82 insertions(+), 32 deletions(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 8f0473564..72bb41966 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -53,47 +53,97 @@ 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 in commentscanYYlex on macOS Intel, - # including with the official binary. Test the pinned 1.16.1 release. - - name: Install official Doxygen 1.16.1 + # The official Doxygen 1.16.1 ARM binary requires macOS 15. + # Build the same version from source on macOS 14 (Sonoma) instead. + - name: Restore Doxygen 1.16.1 cache (Sonoma ARM64) + if: matrix.cfg.os=='macos-14' && matrix.cfg.arch=='arm64' + id: doxygen-cache + uses: actions/cache/restore@v4 + with: + path: ${{ runner.temp }}/codac-doxygen-bin/doxygen + key: doxygen-1.16.1-macos14-arm64-v1 + + - name: Install Doxygen 1.16.1 if: runner.os=='macOS' shell: bash env: GH_TOKEN: ${{ github.token }} run: | set -euo pipefail - case "${{ matrix.cfg.arch }}" in - x86_64) doxygen_arch="intel" ;; - arm64) doxygen_arch="arm" ;; - *) echo "Unsupported architecture: ${{ matrix.cfg.arch }}" >&2; exit 1 ;; - esac - archive="doxygen-1.16.1-mac-${doxygen_arch}.zip" - tmpdir="$(mktemp -d)" - trap 'rm -rf "$tmpdir"' EXIT + bin_dir="$RUNNER_TEMP/codac-doxygen-bin" + mkdir -p "$bin_dir" + if [[ "${{ matrix.cfg.os }}" == "macos-14" && "${{ matrix.cfg.arch }}" == "arm64" ]]; then + if [[ ! -x "$bin_dir/doxygen" ]]; then + echo "Building Doxygen 1.16.1 for macOS 14 ARM64" + brew install ninja flex bison + archive="doxygen-1.16.1.src.tar.gz" + tmpdir="$(mktemp -d)" + trap 'rm -rf "$tmpdir"' EXIT + + # Verify the official source archive against its GitHub release digest. + expected_sha="$( + curl --fail --location --silent --show-error --retry 3 \ + --header "Authorization: Bearer ${GH_TOKEN}" \ + --header "Accept: application/vnd.github+json" \ + "https://api.github.com/repos/doxygen/doxygen/releases/tags/Release_1_16_1" \ + | python3 -c 'import json, re, sys; a = next((x for x in json.load(sys.stdin).get("assets", []) if x.get("name") == sys.argv[1]), None); d = (a.get("digest") or "") if a else ""; sys.exit("Official SHA-256 digest unavailable for " + sys.argv[1]) if not re.fullmatch(r"sha256:[0-9a-fA-F]{64}", d) else print(d.split(":", 1)[1])' "$archive" + )" + curl --fail --location --silent --show-error --retry 3 \ + "https://github.com/doxygen/doxygen/releases/download/Release_1_16_1/${archive}" \ + --output "$tmpdir/$archive" + echo "${expected_sha} ${tmpdir}/${archive}" | shasum -a 256 --check + mkdir -p "$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 + install -m 755 "$tmpdir/build/bin/doxygen" "$bin_dir/doxygen" + else + echo "Using cached Doxygen 1.16.1 for macOS 14 ARM64" + fi + else + case "${{ matrix.cfg.arch }}" in + x86_64) doxygen_arch="intel" ;; + arm64) doxygen_arch="arm" ;; + *) echo "Unsupported architecture: ${{ matrix.cfg.arch }}" >&2; exit 1 ;; + esac + archive="doxygen-1.16.1-mac-${doxygen_arch}.zip" + tmpdir="$(mktemp -d)" + trap 'rm -rf "$tmpdir"' EXIT - # Verify the downloaded release asset against its SHA-256 - # recorded in the official GitHub release metadata. - expected_sha="$( + expected_sha="$( + curl --fail --location --silent --show-error --retry 3 \ + --header "Authorization: Bearer ${GH_TOKEN}" \ + --header "Accept: application/vnd.github+json" \ + "https://api.github.com/repos/doxygen/doxygen/releases/tags/Release_1_16_1" \ + | python3 -c 'import json, re, sys; a = next((x for x in json.load(sys.stdin).get("assets", []) if x.get("name") == sys.argv[1]), None); d = (a.get("digest") or "") if a else ""; sys.exit("Official SHA-256 digest unavailable for " + sys.argv[1]) if not re.fullmatch(r"sha256:[0-9a-fA-F]{64}", d) else print(d.split(":", 1)[1])' "$archive" + )" curl --fail --location --silent --show-error --retry 3 \ - --header "Authorization: Bearer ${GH_TOKEN}" \ - --header "Accept: application/vnd.github+json" \ - "https://api.github.com/repos/doxygen/doxygen/releases/tags/Release_1_16_1" \ - | python3 -c 'import json, re, sys; a = next((x for x in json.load(sys.stdin).get("assets", []) if x.get("name") == sys.argv[1]), None); d = (a.get("digest") or "") if a else ""; sys.exit("Official SHA-256 digest unavailable for " + sys.argv[1]) if not re.fullmatch(r"sha256:[0-9a-fA-F]{64}", d) else print(d.split(":", 1)[1])' "$archive" - )" - curl --fail --location --silent --show-error --retry 3 \ - "https://github.com/doxygen/doxygen/releases/download/Release_1_16_1/${archive}" \ - --output "$tmpdir/$archive" - echo "${expected_sha} ${tmpdir}/${archive}" | shasum -a 256 --check - unzip -q "$tmpdir/$archive" -d "$tmpdir/unpacked" - doxygen_bin="$(find "$tmpdir/unpacked" -type f -name doxygen -print -quit)" - if [[ -z "$doxygen_bin" ]]; then - echo "Doxygen executable not found in ${archive}" >&2 - exit 1 + "https://github.com/doxygen/doxygen/releases/download/Release_1_16_1/${archive}" \ + --output "$tmpdir/$archive" + echo "${expected_sha} ${tmpdir}/${archive}" | shasum -a 256 --check + unzip -q "$tmpdir/$archive" -d "$tmpdir/unpacked" + doxygen_bin="$(find "$tmpdir/unpacked" -type f -name doxygen -print -quit)" + if [[ -z "$doxygen_bin" ]]; then + echo "Doxygen executable not found in ${archive}" >&2 + exit 1 + fi + install -m 755 "$doxygen_bin" "$bin_dir/doxygen" 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 + echo "$bin_dir" >> "$GITHUB_PATH" + "$bin_dir/doxygen" --version + + - name: Save Doxygen 1.16.1 cache (Sonoma ARM64) + if: matrix.cfg.os=='macos-14' && matrix.cfg.arch=='arm64' && steps.doxygen-cache.outputs.cache-hit != 'true' + uses: actions/cache/save@v4 + with: + path: ${{ runner.temp }}/codac-doxygen-bin/doxygen + key: doxygen-1.16.1-macos14-arm64-v1 + - 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 7e4bcb09c2ffcb132fcdb1962edbc7eafe4fc3c5 Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Sat, 10 Oct 2026 12:47:49 +0200 Subject: [PATCH 8/8] [doc] Simplify macOS CI Doxygen setup --- .github/workflows/macosmatrix.yml | 155 ++++++++---------------------- 1 file changed, 39 insertions(+), 116 deletions(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 72bb41966..3fe504024 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -7,8 +7,9 @@ on: - codac2_codac4matlab tags-ignore: ['**'] # Ignore all tag pushes pull_request: + jobs: - macosmatrix: + macosmatrix: runs-on: ${{ matrix.cfg.os }} env: MACOSX_DEPLOYMENT_TARGET: ${{ matrix.cfg.trgt }} @@ -32,7 +33,7 @@ jobs: - { 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: 9 , desc: 'macOS Sequoia Python 3.9 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: 8 , desc: 'macOS Sequoia Python 3.8 x86_64' } name: ${{ matrix.cfg.desc }} - steps: + steps: - uses: actions/checkout@v7 with: submodules: true @@ -53,97 +54,54 @@ jobs: # if: runner.os=='macOS' - run: brew install catch2 # Issues with binary packages when cross-compiling... if: (runner.os=='macOS')&&(matrix.cfg.cross!=true) - # The official Doxygen 1.16.1 ARM binary requires macOS 15. - # Build the same version from source on macOS 14 (Sonoma) instead. - - name: Restore Doxygen 1.16.1 cache (Sonoma ARM64) - if: matrix.cfg.os=='macos-14' && matrix.cfg.arch=='arm64' - id: doxygen-cache - uses: actions/cache/restore@v4 - with: - path: ${{ runner.temp }}/codac-doxygen-bin/doxygen - key: doxygen-1.16.1-macos14-arm64-v1 - + # 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 - if: runner.os=='macOS' shell: bash env: GH_TOKEN: ${{ github.token }} run: | set -euo pipefail - bin_dir="$RUNNER_TEMP/codac-doxygen-bin" - mkdir -p "$bin_dir" - if [[ "${{ matrix.cfg.os }}" == "macos-14" && "${{ matrix.cfg.arch }}" == "arm64" ]]; then - if [[ ! -x "$bin_dir/doxygen" ]]; then - echo "Building Doxygen 1.16.1 for macOS 14 ARM64" - brew install ninja flex bison - archive="doxygen-1.16.1.src.tar.gz" - tmpdir="$(mktemp -d)" - trap 'rm -rf "$tmpdir"' EXIT + 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 official source archive against its GitHub release digest. - expected_sha="$( - curl --fail --location --silent --show-error --retry 3 \ - --header "Authorization: Bearer ${GH_TOKEN}" \ - --header "Accept: application/vnd.github+json" \ - "https://api.github.com/repos/doxygen/doxygen/releases/tags/Release_1_16_1" \ - | python3 -c 'import json, re, sys; a = next((x for x in json.load(sys.stdin).get("assets", []) if x.get("name") == sys.argv[1]), None); d = (a.get("digest") or "") if a else ""; sys.exit("Official SHA-256 digest unavailable for " + sys.argv[1]) if not re.fullmatch(r"sha256:[0-9a-fA-F]{64}", d) else print(d.split(":", 1)[1])' "$archive" - )" - curl --fail --location --silent --show-error --retry 3 \ - "https://github.com/doxygen/doxygen/releases/download/Release_1_16_1/${archive}" \ - --output "$tmpdir/$archive" - echo "${expected_sha} ${tmpdir}/${archive}" | shasum -a 256 --check - mkdir -p "$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 - install -m 755 "$tmpdir/build/bin/doxygen" "$bin_dir/doxygen" - else - echo "Using cached Doxygen 1.16.1 for macOS 14 ARM64" - fi - else - case "${{ matrix.cfg.arch }}" in - x86_64) doxygen_arch="intel" ;; - arm64) doxygen_arch="arm" ;; - *) echo "Unsupported architecture: ${{ matrix.cfg.arch }}" >&2; exit 1 ;; - esac - archive="doxygen-1.16.1-mac-${doxygen_arch}.zip" - 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 - expected_sha="$( - curl --fail --location --silent --show-error --retry 3 \ - --header "Authorization: Bearer ${GH_TOKEN}" \ - --header "Accept: application/vnd.github+json" \ - "https://api.github.com/repos/doxygen/doxygen/releases/tags/Release_1_16_1" \ - | python3 -c 'import json, re, sys; a = next((x for x in json.load(sys.stdin).get("assets", []) if x.get("name") == sys.argv[1]), None); d = (a.get("digest") or "") if a else ""; sys.exit("Official SHA-256 digest unavailable for " + sys.argv[1]) if not re.fullmatch(r"sha256:[0-9a-fA-F]{64}", d) else print(d.split(":", 1)[1])' "$archive" - )" - curl --fail --location --silent --show-error --retry 3 \ - "https://github.com/doxygen/doxygen/releases/download/Release_1_16_1/${archive}" \ - --output "$tmpdir/$archive" - echo "${expected_sha} ${tmpdir}/${archive}" | shasum -a 256 --check + 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)" - if [[ -z "$doxygen_bin" ]]; then - echo "Doxygen executable not found in ${archive}" >&2 - exit 1 - fi - install -m 755 "$doxygen_bin" "$bin_dir/doxygen" fi - echo "$bin_dir" >> "$GITHUB_PATH" - "$bin_dir/doxygen" --version - - - name: Save Doxygen 1.16.1 cache (Sonoma ARM64) - if: matrix.cfg.os=='macos-14' && matrix.cfg.arch=='arm64' && steps.doxygen-cache.outputs.cache-hit != 'true' - uses: actions/cache/save@v4 - with: - path: ${{ runner.temp }}/codac-doxygen-bin/doxygen - key: doxygen-1.16.1-macos14-arm64-v1 + 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 - 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: | @@ -159,41 +117,6 @@ jobs: cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` cd .. shell: bash - - name: Diagnose Doxygen crash - if: failure() - shell: bash - run: | - # Keep this diagnostic step from hiding the original CMake failure. - set +e - echo "=== Doxygen executable and version ===" - command -v doxygen - doxygen --version - - if [[ ! -f build/doc/api/Doxyfile ]]; then - echo "No generated Doxyfile: CMake failed before Doxygen configuration." - exit 0 - fi - - echo "=== Check Doxyfile parsing ===" - doxygen -x build/doc/api/Doxyfile > /tmp/codac-doxygen-config.log 2>&1 - echo "Exit code: $?" - tail -n 50 /tmp/codac-doxygen-config.log - - echo "=== Reproduce Doxygen failure ===" - doxygen build/doc/api/Doxyfile > /tmp/codac-doxygen-run.log 2>&1 - echo "Exit code: $?" - tail -n 50 /tmp/codac-doxygen-run.log - - echo "=== LLDB crash backtrace (if permitted by the runner) ===" - /usr/bin/lldb --batch -o "run" -k "thread backtrace all" -- "$(command -v doxygen)" "${GITHUB_WORKSPACE}/build/doc/api/Doxyfile" - echo "LLDB exit code: $?" - - echo "=== macOS crash reports (fallback) ===" - sleep 2 - if [[ -d "$HOME/Library/Logs/DiagnosticReports" ]]; then - find "$HOME/Library/Logs/DiagnosticReports" -maxdepth 1 -type f \( -iname 'doxygen*.ips' -o -iname 'doxygen*.crash' \) -mmin -15 -print -exec head -n 110 {} \; - fi - exit 0 - uses: xresloader/upload-to-github-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}