Skip to content

Output Formats

abicheck supports multiple output formats for different use cases:

Format Flag Best for
Markdown --format markdown (default) Human review, PRs, terminals
JSON --format json CI pipelines, machine processing
SARIF --format sarif GitHub Code Scanning, SAST platforms
HTML --format html Standalone reports, ABICC migration
JUnit XML --format junit GitLab CI, Jenkins, Azure DevOps test dashboards

All five formats support the report filtering options described below. The ABICC-compatible XML output (via abicheck compat check) includes redundancy annotations but does not support --show-only filtering.

In addition to report formats, abicheck can emit GitHub Actions workflow command annotations (--annotate) that appear as inline comments on PR diffs. See GitHub PR Annotations for details.

Redundancy filtering

When a root type change (e.g. struct size change) causes many derived changes (e.g. 30 FUNC_PARAMS_CHANGED entries for functions using that struct), abicheck automatically collapses the derived changes. The root type change is annotated with:

  • caused_count — number of derived changes collapsed
  • affected_symbols — list of affected interface names

This keeps reports focused on root causes. Use --show-redundant to disable filtering and see all changes.

How it appears in each format

Markdown: An info note at the bottom:

> ℹ️ 12 redundant change(s) hidden (derived from root type changes).
> Use `--show-redundant` to show all.

JSON: A top-level redundant_count field, and per-change caused_by_type and caused_count annotations on root type changes.

SARIF: caused_by_type and caused_count in result properties; redundant_count in run-level properties.

HTML: A highlighted banner showing the redundant count.

XML (ABICC compat): <redundant_changes> element in <problem_summary>, <caused_by> and <caused_count> elements on individual problems. Both binary and source sections include their own redundant counts.

JUnit XML: Redundant changes are filtered upstream before the formatter receives them, so derived changes do not appear as test cases. No JUnit-specific redundancy metadata is emitted.

--show-only filter

Limit displayed changes by severity, element, or action (AND across dimensions, OR within each). Does not affect the verdict or exit codes.

abicheck compare old.json new.json --show-only breaking,functions,removed

Markdown / JSON / HTML: Changes are filtered before rendering. A note shows how many changes matched: > Filtered by: --show-only ... (5 of 42 changes shown).

SARIF: The show_only parameter filters which results appear in the SARIF output.

JUnit XML: The show_only parameter filters which test cases appear in the output. Filtered-out changes are omitted entirely.

--stat mode

One-line summary for CI gates:

$ abicheck compare old.json new.json --stat
BREAKING: 3 breaking, 1 risk (42 total) [12 redundant hidden]

$ abicheck compare old.json new.json --stat --format json
{"library": "libfoo", "verdict": "BREAKING", "summary": {...}}

--report-mode leaf

Groups output by root type changes with affected interface lists, instead of listing every change individually. Available in Markdown and JSON formats.

abicheck compare old.json new.json --report-mode leaf

--show-impact

Appends an impact summary table to the report, showing root changes and how many interfaces each affects. Available in Markdown and HTML formats.

abicheck compare old.json new.json --show-impact

SARIF Output

abicheck supports SARIF 2.1.0 output for integration with GitHub Code Scanning and other SAST platforms.

Usage

abicheck compare old.json new.json --format sarif -o results.sarif

GitHub Code Scanning integration

# .github/workflows/abi-check.yml
name: ABI Check

on: [pull_request]

jobs:
  abi-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install abicheck
        run: |
          sudo apt-get update && sudo apt-get install -y castxml g++
          pip install abicheck

      - name: Dump ABI (baseline)
        run: |
          abicheck dump lib/libfoo.so.1 -H include/foo.h \
            --version ${{ github.base_ref }} -o old.json

      - name: Dump ABI (PR)
        run: |
          abicheck dump lib/libfoo.so.2 -H include/foo.h \
            --version ${{ github.head_ref }} -o new.json

      - name: Compare ABI
        run: |
          abicheck compare old.json new.json --format sarif -o abi.sarif
        continue-on-error: true

      - name: Upload SARIF
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: abi.sarif

Severity mapping

ABI Change SARIF Level
Function/variable removed error
Type size/layout changed error
Return/parameter type changed error
Function/variable added warning

SARIF document structure

{
  "$schema": "https://raw.githubusercontent.com/.../sarif-schema-2.1.0.json",
  "version": "2.1.0",
  "runs": [{
    "tool": { "driver": { "name": "abicheck", "rules": [...] } },
    "results": [{
      "ruleId": "func_removed",
      "level": "error",
      "message": { "text": "Function foo() removed" },
      "locations": [{
        "physicalLocation": { "artifactLocation": { "uri": "libfoo.so.1" } },
        "logicalLocations": [{ "name": "_Z3foov" }]
      }],
      "properties": {
        "caused_by_type": null,
        "caused_count": 0
      }
    }]
  }]
}

JUnit XML Output

abicheck can produce JUnit XML reports for CI systems that display test results in their standard dashboards — GitLab CI, Jenkins, Azure DevOps, CircleCI, and others.

Usage

abicheck compare old.json new.json --format junit -o results.xml
abicheck compare-release release-1.0/ release-2.0/ --format junit -o abi-tests.xml

How it works

ABI changes are mapped to JUnit test cases:

  • Each library in a compare-release becomes a <testsuite>
  • Each exported symbol or type that was checked becomes a <testcase>
  • BREAKING and API_BREAK changes produce <failure> elements
  • COMPATIBLE changes (additions, no-change) are passing test cases
  • COMPATIBLE_WITH_RISK changes pass by default (unless their per-kind severity is overridden to "error")
  • Unchanged symbols from the old library also appear as passing test cases, so the pass-rate is meaningful
  • When a symbol has multiple breaking changes, the <testcase> contains multiple <failure> children (one per change)

Severity mapping

ABI Verdict JUnit Outcome
BREAKING <failure type="BREAKING">
API_BREAK <failure type="API_BREAK">
COMPATIBLE_WITH_RISK (severity=warning) Pass
COMPATIBLE Pass

Classname groups

Test cases are grouped by classname for CI dashboards that support hierarchical display:

Element classname
Functions functions
Variables variables
Types (struct/class/union) types
Enums enums
ELF metadata (SONAME, etc.) metadata

JUnit XML structure

<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="abicheck" tests="47" failures="3" errors="0">
  <testsuite name="libfoo.so.1" tests="47" failures="3" errors="0">
    <!-- Passing: no ABI change detected -->
    <testcase name="_ZN3foo3barEv" classname="functions" />

    <!-- Failure: binary-incompatible change -->
    <testcase name="_ZN3foo3bazEi" classname="functions">
      <failure message="func_param_type_changed: parameter 1 type changed from int to long"
               type="BREAKING">
parameter 1 type changed from int to long
(int  long)
Source: include/foo.h:42
      </failure>
    </testcase>

    <!-- Failure: removed symbol -->
    <testcase name="_ZN3foo6legacyEv" classname="functions">
      <failure message="func_removed: Function foo::legacy() was removed"
               type="BREAKING">
Function foo::legacy() was removed
      </failure>
    </testcase>

    <!-- Passing: addition is compatible -->
    <testcase name="_ZN3foo9new_thingEv" classname="functions" />
  </testsuite>
</testsuites>

CI integration examples

GitLab CI

abi-check:
  script:
    - abicheck compare old.so new.so -H include/ --format junit -o abi-results.xml || true
  artifacts:
    when: always
    reports:
      junit: abi-results.xml

Jenkins (JUnit plugin)

stage('ABI Check') {
    steps {
        sh 'abicheck compare old.so new.so -H include/ --format junit -o abi-results.xml'
    }
    post {
        always {
            junit 'abi-results.xml'
        }
    }
}

Azure DevOps

- task: CmdLine@2
  inputs:
    script: |
      abicheck compare old.so new.so -H include/ --format junit -o abi-results.xml
  continueOnError: true

- task: PublishTestResults@2
  inputs:
    testResultsFiles: 'abi-results.xml'
    testResultsFormat: 'JUnit'