Skip to content

Core CLI Workflows

What abicheck is

abicheck checks C/C++ library compatibility on both API and ABI layers. It is designed to be a practical, modern replacement for legacy ABI tooling in CI, especially when you need structured output and automation.

abicheck is inspired by:

Huge thanks to both projects for pioneering ABI compatibility analysis.

Not sure which command fits your situation? See Choose Your Workflow — a decision guide that maps your artifacts (single library, release bundle, package, application, stripped binaries…) and CI policy to the exact command and options. This page covers the core dump/compare workflow; for the exhaustive, generated per-command flag list see the CLI Reference.

How to use abicheck

1) Compare two libraries directly (primary flow)

The simplest way — pass .so files and their public headers directly to compare. Each library version gets its own header(s):

# Each version has its own header
abicheck compare libfoo.so.1 libfoo.so.2 \
  --header old=include/v1/foo.h --header new=include/v2/foo.h

# Multiple headers per version, with include dirs and version labels
abicheck compare libfoo.so.1 libfoo.so.2 \
  --header old=include/v1/foo.h --header old=include/v1/bar.h \
  --header new=include/v2/foo.h --header new=include/v2/bar.h \
  -I include/ --version old=1.0 --version new=2.0

# Shorthand: -H applies the same header to both sides
# (only when the header itself didn't change between versions)
abicheck compare libfoo.so.1 libfoo.so.2 -H include/foo.h

# Header directory input is supported (recursive)
abicheck compare libfoo.so.1 libfoo.so.2 -H include/

# Output formats
abicheck compare libfoo.so.1 libfoo.so.2 \
  --header old=v1/foo.h --header new=v2/foo.h --format sarif -o abi.sarif
abicheck compare libfoo.so.1 libfoo.so.2 \
  --header old=v1/foo.h --header new=v2/foo.h --format junit -o results.xml

Public headers vs. include roots

-H/--header and -I/--include look similar but answer different questions:

Flag Question Role
-H / --header (--header old=/--header new=) What to analyse The public headers — the files a consumer #includes. These are the API surface abicheck parses to decide what's public and to read types. Pass a directory to establish a public/internal boundary.
-I / --include (--include old=/--include new=) How to parse it The include roots — directories added to the parser's search path so the public headers' own #include "…"/<…> lines resolve. They are not analysed; they only make the parse succeed.

Often a single include/ is both the public header dir and the include root. But they diverge when a public header pulls in a dependency from elsewhere — e.g. include/foo/api.h doing #include <bar/baz.h> needs third_party/ added as an include root (-I third_party) even though bar/baz.h itself is not part of foo's public API. If the parser can't find an included file, add its directory as an include root.

compare auto-detects each input: .so files are dumped on-the-fly, .json snapshots and ABICC Perl dumps (Data::Dumper .dump files) are loaded directly. You can mix them freely (see below).

If ELF headers are not provided, compare falls back to symbols-only analysis and prints a warning. This mode is useful for quick checks but may miss signature/type-level ABI breaks.

2) Dump snapshots and compare later (for CI baselines)

When you want to cache ABI baselines as CI artifacts or commit them to the repo:

# Step 1: Dump snapshots (each version uses its own header)
abicheck dump libfoo.so.1 -H include/v1/foo.h --version 1.0 -o libfoo-1.0.json
abicheck dump libfoo.so.2 -H include/v2/foo.h --version 2.0 -o libfoo-2.0.json

# Step 2: Compare snapshots (no headers needed — already baked in)
abicheck compare libfoo-1.0.json libfoo-2.0.json

If ELF headers are not provided, compare falls back to symbols-only analysis and prints a warning. This mode is useful for quick checks but may miss signature/type-level ABI breaks.

Going beyond a plain .so + headers? C vs C++ mode, cross-compilation, feeding in the exact build flags (-p build/, evidence layer L3), embedding build/source evidence packs (L3/L4), resolving debug info that isn't in the binary itself, and -v/--verbose are all on their own reference page: Evidence, Build-Context, and Debug Flags.

For the exhaustive, generated list of every command/subcommand/option (the same help= text --help shows), see the CLI Reference.

Beyond the core compare/dump flow:

  • Evidence, Build-Context, and Debug Flags — language mode, cross-compilation, compile_commands.json (L3), evidence packs (L3/L4), debug artifact resolution, --dry-run.
  • Output Formats--show-only filtering, --stat, --report-mode leaf, --show-impact, redundancy filtering, SARIF/JUnit output, evidence-tier confidence, JSON schema.
  • --used-by/--required-symbol(s) on compare scope the comparison to an application's actual imports or a plugin host's required entrypoints — see Application Compatibility and Plugin Systems.
  • Generating/validating/diffing Debian dpkg-gensymbols-style symbols files is a Python API only now (abicheck.debian_symbols), not a CLI subcommand — see Debian Symbols File Integration.

--severity-* (controlling exit codes and report labels) is covered in full on Severity Configuration; --profile below is core CLI flag mechanics, so it stays on this page.

--profile: one token for a whole workflow

Common invocations bundle the same handful of flags. --profile NAME expands to a named set of workflow defaults so you don't retype them (ADR-040). An explicit flag always overrides the profile, so a profile is a starting point, not a straitjacket.

Profile Expands to Use when
ci-gate --depth headers --format review --exit-code-scheme severity Blocking a PR in CI
release-cut --depth source --format markdown --recommend Deciding a version bump at release time
quick --depth binary --stat A fast "just tell me" look

Precedence is explicit flag > profile > project config > default: a --profile is a per-run choice you typed, so it overrides .abicheck.yml defaults, while any flag you type still overrides the profile. Public-surface scoping is on by default, so the profiles don't restate it.

Profiles are single-pair-only — they bundle single-pair knobs (--depth, --exit-code-scheme, the review digest) that the directory/package release fan-out doesn't accept. Passing --profile with two directories/packages is a usage error; configure release defaults (format, severity, scheme) in .abicheck.yml, which the fan-out reads.

# CI gate — equivalent to the three flags in the table
abicheck compare old.json new.json --profile ci-gate

# Start from the release-cut profile but force JSON output (explicit flag wins)
abicheck compare old.json new.json --profile release-cut --format json

--show-only filtering, --show-redundant, --stat, --report-mode leaf, and --show-impact are covered in full on Output Formats — all are display-only and do not affect the verdict or exit code.

3) Mixed mode: snapshot baseline vs live build

# CI baseline snapshot vs current build
abicheck compare baseline-1.0.json ./build/libfoo.so \
  --header new=include/foo.h --version new=2.0-dev

# Live old build vs stored new snapshot
abicheck compare ./build-old/libfoo.so new-release.json \
  --header old=include/foo.h --version old=1.0-rc1

4) ABICC-compatible invocation (for migration)

For teams migrating from abi-compliance-checker — same flags, same XML descriptors — abicheck compat check/compat dump are a drop-in replacement. See Migrating from ABICC for the full flag table, behavior differences (-strict semantics, XML descriptor format), and worked examples, and the ABICC Flag Reference for the exhaustive flag list.

Change classification and detection coverage

What each verdict (BREAKING/API_BREAK/COMPATIBLE/COMPATIBLE_WITH_RISK/NO_CHANGE) means is covered in full on Verdicts; the per-case matrix comparing abicheck, abidiff, and ABICC detection coverage across the example catalog is the Tool Comparison & Benchmarks reference.

Dependency-stack commands

deps tree/deps compare (full dependency-closure resolution and cross-sysroot ABI diffing, Linux ELF) and the --follow-deps flag are covered with worked examples and exit codes on Migrating to the Current CLI. Packaging integration — generating/validating/diffing Debian dpkg-gensymbols-style symbols files — is its own page: Debian Symbols File Integration.

Architecture and runtime dependencies

For the internal pipeline and module map (dumper → checker → resolver → reporters), see the Codebase Overview and the Architecture concept page. For the runtime dependencies (Python 3.10+, castxml, pyelftools, …) and per-platform setup, see Install abicheck.