Skip to content

Commit 14ef611

Browse files
authored
fix: add ABI check CI (ada-url#1099)
1 parent 8d50724 commit 14ef611

3 files changed

Lines changed: 119 additions & 0 deletions

File tree

.github/workflows/abi-check.yml

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
name: ABI Check
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened, ready_for_review]
6+
paths-ignore:
7+
- '**.md'
8+
- 'docs/**'
9+
push:
10+
branches:
11+
- main
12+
paths-ignore:
13+
- '**.md'
14+
- 'docs/**'
15+
16+
permissions:
17+
contents: read
18+
19+
concurrency:
20+
group: ${{ github.workflow }}-${{ github.ref }}
21+
cancel-in-progress: true
22+
23+
jobs:
24+
abi-check:
25+
name: ABI compatibility check
26+
runs-on: ubuntu-24.04
27+
steps:
28+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
29+
with:
30+
fetch-depth: 0 # need full history to find latest tag
31+
32+
- name: Install abigail-tools
33+
run: |
34+
sudo apt-get update -q
35+
sudo apt-get install -y --no-install-recommends abigail-tools cmake ninja-build g++
36+
37+
- name: Find latest release tag
38+
id: baseline
39+
run: |
40+
# Find the most recent vX.Y.Z tag reachable from the current commit's history
41+
LATEST_TAG=$(git tag --list 'v*.*.*' --sort=-version:refname | head -1)
42+
echo "Latest release tag: $LATEST_TAG"
43+
echo "tag=$LATEST_TAG" >> "$GITHUB_OUTPUT"
44+
45+
- name: Build baseline (latest release)
46+
run: |
47+
git worktree add /tmp/ada-baseline ${{ steps.baseline.outputs.tag }}
48+
cmake -G Ninja -B /tmp/ada-baseline-build /tmp/ada-baseline \
49+
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
50+
-DBUILD_SHARED_LIBS=ON \
51+
-DADA_TESTING=OFF
52+
cmake --build /tmp/ada-baseline-build -j4
53+
54+
- name: Build current
55+
run: |
56+
cmake -G Ninja -B /tmp/ada-current-build \
57+
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
58+
-DBUILD_SHARED_LIBS=ON \
59+
-DADA_TESTING=OFF
60+
cmake --build /tmp/ada-current-build -j4
61+
62+
- name: Compare ABI
63+
run: |
64+
abidiff \
65+
--drop-private-types \
66+
--no-added-syms \
67+
--suppressions abi-suppressions.abignore \
68+
--headers-dir1 /tmp/ada-baseline/include \
69+
--headers-dir2 include \
70+
/tmp/ada-baseline-build/src/libada.so \
71+
/tmp/ada-current-build/src/libada.so

CLAUDE.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,46 @@ cmake --build build
313313
ls build/benchmarks/ # Check what was built
314314
```
315315

316+
## ABI Compatibility Rules
317+
318+
Ada is a shared library used by downstream distributors (e.g., Debian packages). Breaking the ABI causes runtime failures for users who upgrade without recompiling.
319+
320+
### Rules for Public API Changes
321+
322+
- **Never remove or rename a public method** declared in `include/ada/`. Removing a method removes its exported symbol from the shared library, which is an ABI break.
323+
- **Never change the signature** of a public method (parameter types, return type, `const`/`noexcept` qualifiers).
324+
- **Never make a non-inline method inline** (or vice versa) if it is part of the public API — this changes whether the symbol is emitted in the `.so`.
325+
- Adding new public methods is always safe.
326+
327+
### Keeping Methods Exported
328+
329+
Internal-use methods that must remain exported (e.g., called from templates or inline functions in headers) **must be defined in a `.cpp` file**, not in a `*-inl.h` header. Inline definitions in headers produce weak symbols that the compiler may optimize away, silently breaking the ABI.
330+
331+
### Checking for ABI Breakage
332+
333+
CI runs `abidiff` (from `libabigail-tools`) to compare the shared library against the latest release tag. You can run the same check locally:
334+
335+
```bash
336+
# Build the latest release tag
337+
git worktree add /tmp/ada-baseline <latest-tag>
338+
cmake -G Ninja -B /tmp/ada-baseline-build /tmp/ada-baseline \
339+
-DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED_LIBS=ON -DADA_TESTING=OFF
340+
cmake --build /tmp/ada-baseline-build -j4
341+
342+
# Build the current code
343+
cmake -G Ninja -B /tmp/ada-current-build \
344+
-DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED_LIBS=ON -DADA_TESTING=OFF
345+
cmake --build /tmp/ada-current-build -j4
346+
347+
# Compare ABIs (exit code non-zero = ABI break)
348+
abidiff \
349+
--drop-private-types --no-added-syms \
350+
--headers-dir1 /tmp/ada-baseline/include \
351+
--headers-dir2 include \
352+
/tmp/ada-baseline-build/src/libada.so \
353+
/tmp/ada-current-build/src/libada.so
354+
```
355+
316356
## Additional Resources
317357

318358
- **README.md**: General project overview and API usage

abi-suppressions.abignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Suppress changes to standard library symbols that may be incidentally
2+
# emitted as weak symbols by different compiler inlining decisions.
3+
# These are not part of ada's public ABI.
4+
[suppress_function]
5+
name_regexp = ^std::
6+
7+
[suppress_type]
8+
name_regexp = ^std::

0 commit comments

Comments
 (0)