Skip to content

feat: mapping provenance — expose which §9.10.2 tier produced span te… #261

feat: mapping provenance — expose which §9.10.2 tier produced span te…

feat: mapping provenance — expose which §9.10.2 tier produced span te… #261

Workflow file for this run

name: C++ Bindings CI
on:
push:
branches: [ main, develop ]
paths:
- 'cpp/**'
- 'src/**'
- 'include/pdf_oxide_c/**'
- 'Cargo.toml'
- 'Cargo.lock'
- '.github/workflows/cpp.yml'
pull_request:
branches: [ main, develop ]
paths:
- 'cpp/**'
- 'src/**'
- 'include/pdf_oxide_c/**'
- 'Cargo.toml'
- 'Cargo.lock'
- '.github/workflows/cpp.yml'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name != 'release' }}
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
PDF_OXIDE_INCLUDE_DIR: ${{ github.workspace }}/include
PDF_OXIDE_LIB_DIR: ${{ github.workspace }}/target/release
jobs:
test:
name: C++ on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Set up Rust
uses: actions-rust-lang/setup-rust-toolchain@46268bd060767258de96ed93c1251119784f2ab6 # v1
with:
cache: false
rustflags: ''
- name: Cache Cargo build
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v4
with:
path: target
key: ${{ runner.os }}-cargo-build-release-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-build-release-
- name: Build cdylib (release, shipped binding feature set)
run: cargo build --release --lib --features ocr,rendering,signatures,barcodes,tsa-client,system-fonts
- name: Install clang tooling
run: sudo apt-get update && sudo apt-get install -y clang-format clang-tidy
if: runner.os == 'Linux'
- name: clang-format check (blocking)
# clang-format is apt-installed on Linux only (step above); formatting is
# platform-independent so the Linux check covers all sources. macOS
# runners don't ship clang-format (the unconditional step exited 127).
if: runner.os == 'Linux'
shell: bash
run: |
files=$(find cpp/include cpp/examples cpp/tests \( -name '*.hpp' -o -name '*.cpp' \))
echo "$files" | xargs clang-format --dry-run --Werror --style=file:cpp/.clang-format
- name: clang-tidy (static analysis)
if: runner.os == 'Linux'
shell: bash
run: |
for f in cpp/examples/*.cpp cpp/tests/*.cpp; do
clang-tidy "$f" --config-file=cpp/.clang-tidy -- \
-std=c++17 -Icpp/include -I"${PDF_OXIDE_INCLUDE_DIR:-include}"
done
- name: Configure (CMake)
run: cmake -S cpp -B cpp/build -DCMAKE_BUILD_TYPE=Release -DPDF_OXIDE_LIB_DIR="${PDF_OXIDE_LIB_DIR}"
- name: Build (examples + tests)
run: cmake --build cpp/build --config Release -j
- name: Run tests (ctest — includes api-coverage)
run: ctest --test-dir cpp/build --output-on-failure
- name: Run example (01_basic_extraction, with assertion)
shell: bash
run: |
set -uo pipefail
export LD_LIBRARY_PATH="${PDF_OXIDE_LIB_DIR}:${LD_LIBRARY_PATH:-}"
export DYLD_LIBRARY_PATH="${PDF_OXIDE_LIB_DIR}:${DYLD_LIBRARY_PATH:-}"
OUT="$(cpp/build/example_01_basic_extraction)"
echo "$OUT"
echo "$OUT" | grep -q "Hello pdf_oxide" || { echo "::error::example output missing expected text"; exit 1; }
- name: Run example (02_html_extraction, with assertion)
shell: bash
run: |
set -uo pipefail
export LD_LIBRARY_PATH="${PDF_OXIDE_LIB_DIR}:${LD_LIBRARY_PATH:-}"
export DYLD_LIBRARY_PATH="${PDF_OXIDE_LIB_DIR}:${DYLD_LIBRARY_PATH:-}"
OUT="$(cpp/build/example_02_html_extraction)"
echo "$OUT"
echo "$OUT" | grep -q "HTML OK" || { echo "::error::02_html_extraction missing 'HTML OK'"; exit 1; }
- name: Run example (03_words_geometry, with assertion)
shell: bash
run: |
set -uo pipefail
export LD_LIBRARY_PATH="${PDF_OXIDE_LIB_DIR}:${LD_LIBRARY_PATH:-}"
export DYLD_LIBRARY_PATH="${PDF_OXIDE_LIB_DIR}:${DYLD_LIBRARY_PATH:-}"
OUT="$(cpp/build/example_03_words_geometry)"
echo "$OUT"
echo "$OUT" | grep -q "WORDS OK" || { echo "::error::03_words_geometry missing 'WORDS OK'"; exit 1; }
- name: Run example (04_tables_extraction, with assertion)
shell: bash
run: |
set -uo pipefail
export LD_LIBRARY_PATH="${PDF_OXIDE_LIB_DIR}:${LD_LIBRARY_PATH:-}"
export DYLD_LIBRARY_PATH="${PDF_OXIDE_LIB_DIR}:${DYLD_LIBRARY_PATH:-}"
OUT="$(cpp/build/example_04_tables_extraction)"
echo "$OUT"
echo "$OUT" | grep -q "TABLES OK" || { echo "::error::04_tables_extraction missing 'TABLES OK'"; exit 1; }
# Sanitizer lane: compile the api-coverage test with ASan+UBSan and run it.
# Catches memory/lifetime bugs at the C-ABI boundary that clang-tidy misses
# (e.g. a heap-buffer-overflow from freeing a byte buffer as a C string).
# detect_leaks=0 — leaks inside the Rust cdylib are out of this wrapper's scope.
- name: ASan + UBSan (api-coverage test)
if: runner.os == 'Linux'
shell: bash
run: |
set -uo pipefail
c++ -std=c++17 -g -O1 -fsanitize=address,undefined -fno-omit-frame-pointer \
-Icpp/include -I"${PDF_OXIDE_INCLUDE_DIR}" \
cpp/tests/test_api_coverage.cpp -L"${PDF_OXIDE_LIB_DIR}" -lpdf_oxide -o /tmp/cov_asan
LD_LIBRARY_PATH="${PDF_OXIDE_LIB_DIR}" \
ASAN_OPTIONS=detect_leaks=0 UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1 \
/tmp/cov_asan