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 collapsedaffected_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.
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.
--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.
SARIF Output¶
abicheck supports SARIF 2.1.0 output for integration with GitHub Code Scanning and other SAST platforms.
Usage¶
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-releasebecomes 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'
}
}
}