Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions .github/workflows/check-r-api.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: Check R API symbols

on:
# Run weekly (offset from non-api-call.yml's schedule)
schedule:
- cron: '17 4 * * 1'
# This can also be run manually
workflow_dispatch: {}

# Since in-tree bindgen was removed (#250), the per-version bindings under
# bindings/ are maintained by hand and nothing notices when R's C API surface
# shifts, so they can silently go stale (e.g. no R 4.5 / 4.6 bindings). This
# workflow snapshots the symbols exported by R-devel's shared library and opens
# a PR whenever they change, as an early warning that the bindings may need
# updating. It does not generate bindings; it only flags drift.

jobs:
extract_r_symbols:
runs-on: ${{ matrix.config.os }}
name: Extract R symbols on ${{ matrix.config.os }}
strategy:
fail-fast: false
matrix:
config:
- {os: macOS-latest}
- {os: ubuntu-latest}

steps:
- uses: actions/checkout@v4

- name: Set up R
uses: r-lib/actions/setup-r@v2
with:
r-version: 'devel'

- name: Extract exported R symbols
run: bash tools/r-symbols.sh | tee r-symbols.txt
shell: bash

- name: Upload r-symbols.txt
uses: actions/upload-artifact@v4
with:
name: r-symbols-${{ matrix.config.os }}
path: r-symbols.txt

commit_r_symbols:
needs: extract_r_symbols
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master'
steps:
- uses: actions/checkout@v4

- uses: actions/download-artifact@v4

- name: Switch branch
run: |
# 1) If there's already a check_r_api branch, check it out.
# 2) Otherwise create it from the default branch.
if git ls-remote --exit-code --heads origin check_r_api 2>&1 >/dev/null; then
git fetch origin --no-tags --prune --depth=1 check_r_api
git checkout check_r_api
else
git switch -c check_r_api
fi

- name: Commit and create a pull request
run: |
# Union the per-OS lists: a symbol exported on any platform counts.
# (On the first run r-symbols.txt does not exist yet, so this PR just
# establishes the baseline snapshot.)
cat r-symbols-*/r-symbols.txt | tr -d '\r' | sort -u | tee r-symbols.txt

# detect changes (derived from https://stackoverflow.com/a/3879077)
git add r-symbols.txt
git update-index --refresh
if ! git diff-index --quiet HEAD -- r-symbols.txt; then
git config --local user.name "${GITHUB_ACTOR}"
git config --local user.email "${GITHUB_ACTOR}@users.noreply.github.com"
git commit -m "R API symbols changed [skip ci]"
git push origin check_r_api
gh pr create --title "R's exported C symbols changed" --body "R-devel's exported symbols differ from the committed snapshot. Review the diff in \`r-symbols.txt\`: added symbols may be new C API the hand-maintained \`bindings/\` should expose (cross-check against \`nonAPI.txt\` to tell API from non-API); removed symbols may break existing bindings. Snapshot produced by \`tools/r-symbols.sh\`."
else
echo "No changes"
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
25 changes: 25 additions & 0 deletions tools/r-symbols.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env bash
# Print the sorted C symbols exported by the installed R shared library.
#
# libR-sys ships hand-maintained, per-version bindings (in-tree bindgen was
# removed in #250). Nothing automatically notices when R's C API surface
# shifts, so bindings silently go stale. `check-r-api.yml` snapshots this list
# and opens a PR when it changes, as an early warning that the bindings may
# need updating. No bindgen/clang required.
set -euo pipefail

r_home="$(R RHOME)"
case "$(uname -s)" in
Darwin) lib="$r_home/lib/libR.dylib"; nm_args=(-g -j -U) ;;
*) lib="$r_home/lib/libR.so"; nm_args=(-D --defined-only) ;;
esac

[ -f "$lib" ] || { echo "libR not found at $lib" >&2; exit 1; }

# `$NF` is the symbol name on both macOS (-j: name only) and Linux
# (addr type name). Strip the Mach-O leading underscore; keep C identifiers.
nm "${nm_args[@]}" "$lib" \
| awk 'NF {print $NF}' \
| sed 's/^_//' \
| grep -E '^[A-Za-z_][A-Za-z0-9_]*$' \
| sort -u
Loading