From aa9df72cc21be011e0d038f80e1644b3716e4c33 Mon Sep 17 00:00:00 2001 From: Scot Breitenfeld Date: Mon, 27 Jul 2026 07:28:18 -0600 Subject: [PATCH] Add Fortran ABI check and auto-detect the release baseline (#6550) * Add Fortran ABI compatibility check to abi-report workflow Mirrors the existing C/HL/C++ abi-dumper + abi-compliance-checker steps for libhdf5_fortran.so, plus a supplementary nm-based exported symbol diff since gfortran's mangled names don't encode argument lists and DWARF-based diffing has not been validated against Fortran-specific constructs (array descriptors, derived types). * Auto-detect ABI reference release instead of hardcoding it file_ref was pinned to '2.0.0' in both daily-build.yml and release.yml and had gone stale across two releases (2.1.0, 2.1.1) without being bumped, silently comparing against a two-versions-old baseline. Have abi-report.yml query the GitHub API for the latest published HDFGroup/hdf5 release when file_ref isn't explicitly given, so callers no longer need to remember to update a pinned tag after every release. Verified the resolution command returns 2.1.1 against the live API. Also documents the expected false-positive pattern in the new Fortran ABI check: abi-dumper misreads gfortran's DWARF encoding of assumed-shape array descriptors as fixed array bounds, producing bulk Low-severity noise on the KIND/RANK-generated H5_gen.F90 procedures that isn't a real interface change. --- .github/workflows/abi-report.yml | 61 ++++++++++++++++++++++++++++--- .github/workflows/daily-build.yml | 4 +- .github/workflows/release.yml | 4 +- 3 files changed, 59 insertions(+), 10 deletions(-) diff --git a/.github/workflows/abi-report.yml b/.github/workflows/abi-report.yml index b0d2a746b73..4827c6be5b4 100644 --- a/.github/workflows/abi-report.yml +++ b/.github/workflows/abi-report.yml @@ -19,9 +19,10 @@ on: required: true type: string file_ref: - description: "The reference name for the release binary" - required: true + description: "The reference release tag to compare against. Leave empty to auto-detect the latest published HDFGroup/hdf5 release." + required: false type: string + default: '' permissions: contents: read @@ -38,12 +39,27 @@ jobs: sudo apt install -q -y abi-compliance-checker abi-dumper sudo apt install -q -y japi-compliance-checker + - name: Resolve reference release tag + id: resolve-file-ref + run: | + if [[ -n "${{ inputs.file_ref }}" ]]; then + REF="${{ inputs.file_ref }}" + else + REF=$(curl -fsSL https://api.github.com/repos/HDFGroup/hdf5/releases/latest | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p') + if [[ -z "$REF" ]]; then + echo "::error::Could not auto-detect the latest HDFGroup/hdf5 release tag" + exit 1 + fi + fi + echo "Using reference release: $REF" + echo "FILE_REF=$REF" >> $GITHUB_OUTPUT + - name: Convert hdf5 reference name (Linux) id: convert-hdf5lib-refname run: | - FILE_DOTS=$(echo "${{ inputs.file_ref }}" | sed -r "s/([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+).*/\1\.\2\.\3-\4/") + FILE_DOTS=$(echo "${{ steps.resolve-file-ref.outputs.FILE_REF }}" | sed -r "s/([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+).*/\1\.\2\.\3-\4/") echo "HDF5R_DOTS=$FILE_DOTS" >> $GITHUB_OUTPUT - FILE_DOTSMAIN=$(echo "${{ inputs.file_ref }}" | sed -r "s/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1\.\2\.\3/") + FILE_DOTSMAIN=$(echo "${{ steps.resolve-file-ref.outputs.FILE_REF }}" | sed -r "s/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1\.\2\.\3/") echo "HDF5R_DOTSMAIN=$FILE_DOTSMAIN" >> $GITHUB_OUTPUT - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -84,7 +100,7 @@ jobs: run: | mkdir "${{ github.workspace }}/hdf5R" cd "${{ github.workspace }}/hdf5R" - wget -q https://github.com/HDFGroup/hdf5/releases/download/${{ inputs.file_ref }}/hdf5-${{ steps.convert-hdf5lib-refname.outputs.HDF5R_DOTS }}-ubuntu-2404_gcc.tar.gz + wget -q https://github.com/HDFGroup/hdf5/releases/download/${{ steps.resolve-file-ref.outputs.FILE_REF }}/hdf5-${{ steps.convert-hdf5lib-refname.outputs.HDF5R_DOTS }}-ubuntu-2404_gcc.tar.gz tar zxf hdf5-${{ steps.convert-hdf5lib-refname.outputs.HDF5R_DOTS }}-ubuntu-2404_gcc.tar.gz - name: List files for the space (Linux) @@ -94,7 +110,7 @@ jobs: - name: Uncompress hdf5 reference binary (Linux) run: | cd "${{ github.workspace }}/hdf5R" - tar -zxvf ${{ github.workspace }}/hdf5R/hdf5/HDF5-${{ inputs.file_ref }}-Linux.tar.gz --strip-components 1 + tar -zxvf ${{ github.workspace }}/hdf5R/hdf5/HDF5-${{ steps.resolve-file-ref.outputs.FILE_REF }}-Linux.tar.gz --strip-components 1 - name: List files for the HDFR space (Linux) run: | @@ -147,6 +163,35 @@ jobs: abi-compliance-checker -l ${{ inputs.file_base }}_hl_cpp -old ABI-6.dump -new ABI-7.dump continue-on-error: true + # NOTE ON READING THE FORTRAN REPORT: expect a large number of "Low" severity + # "Problems with Symbols" findings on the generic KIND/RANK-generated buffer + # procedures in H5_gen.F90 (h5aread_*, h5awrite_*, h5dread_*, h5dwrite_*), e.g. + # "Type of Nth parameter buf has been changed from integer(kind=K)[X] to + # integer(kind=K)[Y]". These are false positives: abi-dumper misreads gfortran's + # DWARF encoding of assumed-shape array descriptors as if the bound were a fixed + # compile-time array size, so the reported X/Y values are debug-info noise, not a + # real interface change. Real signal shows up in Added/Removed Symbols and in + # Medium/High severity findings (e.g. a module-level data size actually changing) - + # focus review there and disregard bulk Low-severity Symbol findings from H5_gen.F90. + - name: Run fortran ABI report + run: | + abi-dumper ${{ steps.set-hdf5lib-refname.outputs.HDF5R_ROOT }}/lib/libhdf5_fortran.so -o ABI-8.dump + abi-dumper ${{ steps.set-hdf5lib-name.outputs.HDF5_ROOT }}/lib/libhdf5_fortran.so -o ABI-9.dump + abi-compliance-checker -l ${{ inputs.file_base }}_fortran -old ABI-8.dump -new ABI-9.dump + continue-on-error: true + + - name: Run fortran exported symbol diff + run: | + # gfortran mangled names don't encode argument lists, so abi-compliance-checker + # (DWARF-based) is the primary signal above; this is a cheap supplementary check + # that still catches added/removed exported symbols even if debug info is stripped. + nm -D --defined-only ${{ steps.set-hdf5lib-refname.outputs.HDF5R_ROOT }}/lib/libhdf5_fortran.so | awk '{print $3}' | sort > old_fortran_syms.txt + nm -D --defined-only ${{ steps.set-hdf5lib-name.outputs.HDF5_ROOT }}/lib/libhdf5_fortran.so | awk '{print $3}' | sort > new_fortran_syms.txt + diff -u old_fortran_syms.txt new_fortran_syms.txt > ${{ inputs.file_base }}-hdf5_fortran_symbol_diff.txt || true + echo "=== Fortran exported symbol diff (reference vs new) ===" + cat ${{ inputs.file_base }}-hdf5_fortran_symbol_diff.txt + continue-on-error: true + - name: Copy ABI reports run: | cp compat_reports/jarhdf5-/${{ steps.convert-hdf5lib-refname.outputs.HDF5R_DOTSMAIN }}_to_${{ steps.set-hdf5lib-name.outputs.HDF5_VERS }}/compat_report.html ${{ inputs.file_base }}-java_compat_report.html @@ -158,6 +203,8 @@ jobs: cp compat_reports/${{ inputs.file_base }}_cpp/X_to_Y/compat_report.html ${{ inputs.file_base }}-hdf5_cpp_compat_report.html ls -l compat_reports/${{ inputs.file_base }}_hl_cpp/X_to_Y cp compat_reports/${{ inputs.file_base }}_hl_cpp/X_to_Y/compat_report.html ${{ inputs.file_base }}-hdf5_hl_cpp_compat_report.html + ls -l compat_reports/${{ inputs.file_base }}_fortran/X_to_Y + cp compat_reports/${{ inputs.file_base }}_fortran/X_to_Y/compat_report.html ${{ inputs.file_base }}-hdf5_fortran_compat_report.html continue-on-error: true - name: List files for the report spaces (Linux) @@ -173,6 +220,8 @@ jobs: cp ${{ inputs.file_base }}-hdf5_compat_report.html ${{ runner.workspace }}/buildabi/hdf5 cp ${{ inputs.file_base }}-hdf5_hl_compat_report.html ${{ runner.workspace }}/buildabi/hdf5 cp ${{ inputs.file_base }}-hdf5_cpp_compat_report.html ${{ runner.workspace }}/buildabi/hdf5 + cp ${{ inputs.file_base }}-hdf5_fortran_compat_report.html ${{ runner.workspace }}/buildabi/hdf5 + cp ${{ inputs.file_base }}-hdf5_fortran_symbol_diff.txt ${{ runner.workspace }}/buildabi/hdf5 cp ${{ inputs.file_base }}-java_compat_report.html ${{ runner.workspace }}/buildabi/hdf5 cd "${{ runner.workspace }}/buildabi" tar -zcvf ${{ inputs.file_base }}.html.abi.reports.tar.gz hdf5 diff --git a/.github/workflows/daily-build.yml b/.github/workflows/daily-build.yml index 02c98643a8d..5ccfa286d61 100644 --- a/.github/workflows/daily-build.yml +++ b/.github/workflows/daily-build.yml @@ -161,8 +161,8 @@ jobs: needs: [get-old-names, call-workflow-tarball, call-workflow-ctest] uses: ./.github/workflows/abi-report.yml with: - # previous release version - file_ref: '2.0.0' + # file_ref intentionally omitted: abi-report.yml auto-detects the latest + # published HDFGroup/hdf5 release when it isn't given one file_base: ${{ needs.call-workflow-tarball.outputs.file_base }} use_tag: snapshot use_environ: snapshots diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dddfb762bab..d267f43facf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -82,8 +82,8 @@ jobs: needs: [log-the-inputs, call-workflow-tarball, call-workflow-ctest] uses: ./.github/workflows/abi-report.yml with: - # previous release version - file_ref: '2.0.0' + # file_ref intentionally omitted: abi-report.yml auto-detects the latest + # published HDFGroup/hdf5 release when it isn't given one file_base: ${{ needs.call-workflow-tarball.outputs.file_base }} use_tag: ${{ needs.log-the-inputs.outputs.rel_tag }} use_environ: release