Tests #1512
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Tests | |
on: | |
push: | |
branches: | |
- main | |
- master | |
pull_request: | |
branches: | |
- main | |
- master | |
issue_comment: | |
types: | |
- created | |
# Run this once per three days | |
schedule: | |
- cron: '29 17 */3 * *' | |
# This can also manually run | |
workflow_dispatch: {} | |
jobs: | |
test_with_bindgen: | |
# When the event is not issue_comment, always run the tests. When it is, | |
# check if (1) the comment is on pull request, (2) the comment author is the | |
# member of the organization, and (3) the comment starts with '/bindings'. | |
# | |
# ref. | |
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issue_comment | |
if: | | |
github.event_name != 'issue_comment' | |
|| (github.event.issue.pull_request && github.event.comment.author_association == 'MEMBER' && startsWith(github.event.comment.body, '/bindings')) | |
runs-on: ${{ matrix.config.os }} | |
name: ${{ matrix.config.os }} (R-${{ matrix.config.r }} rust-${{ matrix.config.rust-version }}-${{ matrix.config.target || 'default' }}) | |
strategy: | |
fail-fast: false | |
matrix: | |
config: | |
# On Windows, | |
# | |
# * for R >= 4.2, both the MSVC toolchain and the GNU toolchain should | |
# work. Since we primarily support MSVC, we focus on testing MSVC | |
# here. Also, at least one GNU case should be included so that we | |
# can detect when something gets broken. | |
# * for R < 4.2, the MSVC toolchain must be used to support | |
# cross-compilation to 32-bit. | |
# | |
# options: | |
# - targets: Targets to build and run tests against. If not | |
# specified, 'default' will be used. | |
# - no-test-targets: Targets to skip tests. | |
# - emit-bindings: If 'true', emit bindings. In principle, we choose | |
# only one stable Rust toolchain per combination of a platform and | |
# an R version (e.g. Windows and R-release) to emit bindings. | |
- {os: windows-latest, r: 'release', rust-version: 'stable-msvc', target: 'x86_64-pc-windows-gnu', emit-bindings: 'true'} | |
- {os: windows-latest, r: 'release', rust-version: 'nightly-msvc', target: 'x86_64-pc-windows-gnu'} | |
- {os: windows-latest, r: 'devel', rust-version: 'stable-msvc', target: 'x86_64-pc-windows-gnu', emit-bindings: 'true'} | |
- {os: windows-latest, r: 'release', rust-version: 'stable-gnu', target: 'x86_64-pc-windows-gnu'} | |
- {os: windows-latest, r: 'oldrel', rust-version: 'stable-msvc', target: 'x86_64-pc-windows-gnu', emit-bindings: 'true'} | |
- {os: windows-latest, r: '4.2', rust-version: 'stable-msvc', target: 'x86_64-pc-windows-gnu', emit-bindings: 'true' } | |
- {os: macOS-latest, r: 'release', rust-version: 'nightly'} | |
- {os: macOS-latest, r: 'devel', rust-version: 'stable', emit-bindings: 'true'} | |
- {os: macOS-latest, r: 'oldrel', rust-version: 'stable', emit-bindings: 'true'} | |
- {os: macOS-latest, r: 'release', rust-version: 'stable', emit-bindings: 'true'} | |
- {os: macOS-latest, r: 'release', rust-version: 'stable', target: 'x86_64-apple-darwin', skip-tests: 'true', emit-bindings: 'true'} | |
- {os: macOS-latest, r: '4.2', rust-version: 'stable', emit-bindings: 'true' } | |
- {os: macOS-latest, r: '4.2', rust-version: 'stable', target: 'x86_64-apple-darwin', skip-tests: 'true', emit-bindings: 'true'} | |
- {os: ubuntu-latest, r: 'release', rust-version: 'nightly'} | |
- {os: ubuntu-latest, r: 'release', rust-version: 'stable', emit-bindings: 'true'} | |
- {os: ubuntu-latest, r: 'release', rust-version: 'stable', target: 'aarch64-unknown-linux-gnu', skip-tests: 'true', emit-bindings: 'true'} | |
- {os: ubuntu-latest, r: 'devel', rust-version: 'stable', emit-bindings: 'true'} | |
- {os: ubuntu-latest, r: 'devel', rust-version: 'stable', target: 'aarch64-unknown-linux-gnu', skip-tests: 'true', emit-bindings: 'true'} | |
- {os: ubuntu-latest, r: 'oldrel', rust-version: 'stable', emit-bindings: 'true'} | |
- {os: ubuntu-latest, r: 'oldrel', rust-version: 'stable', target: 'aarch64-unknown-linux-gnu', skip-tests: 'true', emit-bindings: 'true'} | |
- {os: ubuntu-latest, r: '4.2', rust-version: 'stable', emit-bindings: 'true' } | |
- {os: ubuntu-latest, r: '4.2', rust-version: 'stable', target: 'aarch64-unknown-linux-gnu', skip-tests: 'true', emit-bindings: 'true'} | |
env: | |
RSPM: ${{ matrix.config.rspm }} | |
# PowerShell core is available on all platforms and can be used to unify scripts | |
defaults: | |
run: | |
shell: pwsh | |
steps: | |
- uses: actions/checkout@v4 | |
# When invoked by an issue comment event, the GitHub Actions runner runs | |
# on the default branch, so we need to switch the branch of the pull | |
# request. Since the branch name is not easily accessible via variables | |
# provided GitHub Actions, we use r-lib/actions, which is well-maintained. | |
- name: Switch branch (/bindings command) | |
if: github.event_name == 'issue_comment' | |
uses: r-lib/actions/pr-fetch@v2 | |
with: | |
repo-token: ${{ secrets.GITHUB_TOKEN }} | |
- name: Set up R | |
uses: r-lib/actions/setup-r@v2 | |
with: | |
r-version: ${{ matrix.config.r }} | |
use-public-rspm: true | |
- name: Set up Rust | |
uses: dtolnay/rust-toolchain@master | |
with: | |
toolchain: ${{ matrix.config.rust-version }} | |
components: rustfmt, clippy | |
targets: ${{ matrix.config.target }} | |
# All configurations for Windows go here | |
# 1. Configure linker | |
# 2. Create libgcc_eh mock | |
# 3. Add R bin path to PATH | |
# 4. Add Rtools' GCC to PATH (required to find linker) | |
# 5. Add include path (required to resolve standard headers like stdio.h) | |
- name: Configure Windows | |
if: runner.os == 'Windows' | |
run: | | |
# Configure linker | |
echo "RUSTFLAGS=-C linker=x86_64-w64-mingw32.static.posix-gcc.exe" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
# Create libgcc_eh mock | |
New-Item -Path libgcc_mock -Type Directory | |
New-Item -Path libgcc_mock\libgcc_eh.a -Type File | |
New-Item -Path libgcc_mock\libgcc_s.a -Type File | |
$pwd_slash = echo "${PWD}" | % {$_ -replace '\\','/'} | |
echo "LIBRARY_PATH=${pwd_slash}/libgcc_mock" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
# Add R bin path to PATH | |
echo "$(Rscript.exe --vanilla -e 'cat(normalizePath(R.home()))')\bin\x64" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append ; | |
# Add Rtools' GCC to PATH | |
# Source: https://github.com/r-lib/actions/blob/b7e68d63e51bdf225997973e2add36d551f60f02/setup-r/lib/installer.js#L471 | |
$directories = @( | |
"C:\rtools44-aarch64\aarch64-w64-mingw32.static.posix", | |
"C:\rtools44\x86_64-w64-mingw32.static.posix", | |
"C:\rtools43\x86_64-w64-mingw32.static.posix", | |
"C:\rtools42\x86_64-w64-mingw32.static.posix", | |
"C:\rtools40\ucrt64", | |
"C:\Rtools\mingw_64" | |
) | |
$mingw_root = $null | |
foreach ($dir in $directories) { | |
if (Test-Path $dir) { | |
$mingw_root = $dir | |
Write-Host "Found Rtools directory at: $mingw_root" | |
break | |
} | |
} | |
echo "MINGW_ROOT=$mingw_root" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
if ($null -eq $mingw_root) { | |
Write-Host "No Rtools directory found." | |
} else { | |
Write-Host "Mingw root set to: $mingw_root" | |
} | |
echo "$mingw_root\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append | |
# Add include path | |
echo "LIBRSYS_LIBCLANG_INCLUDE_PATH=$mingw_root\include" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
env: | |
RUST_TARGET: ${{ matrix.config.target }} | |
# macOS configurations, mainly llvm and path to libclang | |
# Because of this R installation issue on macOS-11.0 | |
# https://github.com/r-lib/actions/issues/200 | |
# Symlinks to R/Rscript are not properly set up, so we do it by hand, using this trick | |
# https://github.com/r-lib/ps/commit/a24f2c4d1bdba63be14e7729b9ab81d0ed9f719e | |
# Environment variables are required fir Mac-OS-11.0, see | |
# https://github.com/extendr/libR-sys/issues/35 | |
- name: Configure macOS | |
if: runner.os == 'macOS' | |
run: | | |
brew install llvm | |
echo "LIBCLANG_PATH=$(brew --prefix llvm)/lib" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
$env:LLVM_CONFIG_PATH = "$(brew --prefix llvm)/bin/llvm-config" | |
echo "LLVM_CONFIG_PATH=$env:LLVM_CONFIG_PATH" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
echo "LIBRSYS_LIBCLANG_INCLUDE_PATH=$(. $env:LLVM_CONFIG_PATH --libdir)/clang/$(. $env:LLVM_CONFIG_PATH --version)/include" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
if ((Get-ChildItem -Path /usr/local/bin -Filter R | Measure-Object).Count -eq 0) { | |
echo "::warning:: Found no R symlink in /usr/local/bin, setting up manually..." | |
ln -s /Library/Frameworks/R.framework/Versions/Current/Resources/bin/R /usr/local/bin/ | |
} | |
if ((Get-ChildItem -Path /usr/local/bin -Filter Rscript | Measure-Object).Count -eq 0) { | |
echo "::warning:: Found no Rscript symlink in /usr/local/bin, setting up manually..." | |
ln -s /Library/Frameworks/R.framework/Versions/Current/Resources/bin/Rscript /usr/local/bin/ | |
} | |
# This is required for ubuntu r-devel | |
# 'Del alias:R' removes R alias which prevents running R | |
- name: Configure Linux | |
if: runner.os == 'linux' | |
run: | | |
Del alias:R | |
echo "LD_LIBRARY_PATH=$(R -s -e 'cat(normalizePath(R.home()))')/lib" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
if($env:RUST_TARGET -eq 'aarch64-unknown-linux-gnu') { | |
sudo apt-get update | |
sudo apt-get install gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu | |
# https://github.com/rust-lang/rust-bindgen/issues/1229 | |
echo 'BINDGEN_EXTRA_CLANG_ARGS=--sysroot=/usr/aarch64-linux-gnu' >> $GITHUB_ENV | |
# https://github.com/rust-lang/rust/issues/28924 | |
echo 'CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc' >> $GITHUB_ENV | |
} | |
env: | |
RUST_TARGET: ${{ matrix.config.target }} | |
# Build & run bindings with layout tests | |
- name: Run tests | |
id: test | |
if: matrix.config.skip-tests != 'true' | |
run: | | |
. ./ci-cargo.ps1 | |
# ci-cargo test -vv --features use-bindgen,layout_tests $(if ($env:RUST_TARGET -ne '') {"--target=$env:RUST_TARGET"} ) '--' --nocapture -ActionName "Running bindgen tests for target: $env:RUST_TARGET" | |
# ci-cargo test -vv --features use-bindgen,non-api,layout_tests $(if ($env:RUST_TARGET -ne '') {"--target=$env:RUST_TARGET"} ) '--' --nocapture -ActionName "Running bindgen tests for target: $env:RUST_TARGET (with non-API)" | |
ci-cargo test -vv $(if ($env:RUST_TARGET -ne '') {"--target=$env:RUST_TARGET"} ) '--' --nocapture -ActionName "Running bindgen tests for target: $env:RUST_TARGET" | |
env: | |
RUST_TARGET: ${{ matrix.config.target }} | |
# Build and emit bindings to './generated_bindings' | |
- name: Build & Emit bindings | |
id: build | |
run: | | |
. ./ci-cargo.ps1 | |
# ci-cargo build -vv --features use-bindgen $(if ($env:RUST_TARGET -ne '') {"--target=$env:RUST_TARGET"} ) -ActionName "Building for target: $env:RUST_TARGET" | |
env: | |
LIBRSYS_BINDINGS_OUTPUT_PATH: generated_bindings | |
RUST_TARGET: ${{ matrix.config.target }} | |
- name: Upload generated bindings | |
if: | |
steps.build.outcome == 'success' && | |
matrix.config.emit-bindings == 'true' | |
uses: actions/upload-artifact@v4 | |
with: | |
name: generated_binding-${{ matrix.config.os }}-R-${{ matrix.config.r }}-rust-${{ matrix.config.rust-version }}-${{ matrix.config.target || 'default'}} | |
path: generated_bindings | |
check_generate_bindings_flag: | |
name: Check if [generate bindings] is in latest commit message | |
runs-on: ubuntu-latest | |
outputs: | |
head_commit_message: ${{ steps.get_head_commit_message.outputs.HEAD_COMMIT_MESSAGE }} | |
# generate_bindings: ${{ contains(steps.get_head_commit_message.outputs.HEAD_COMMIT_MESSAGE, '[generate bindings]') }} | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
ref: ${{ github.event.pull_request.head.sha }} | |
- name: Get Head Commit Message | |
id: get_head_commit_message | |
run: echo "HEAD_COMMIT_MESSAGE=$(git show -s --format=%s)" >> "$GITHUB_OUTPUT" | |
- name: Show commit message | |
run: | | |
echo "${{ steps.get_head_commit_message.outputs.HEAD_COMMIT_MESSAGE }}" | |
echo "${{ contains(steps.get_head_commit_message.outputs.HEAD_COMMIT_MESSAGE, '[generate bindings]') }}" | |
pr_generated_bindings: | |
name: Make PR with generated bindings | |
needs: [test_with_bindgen, check_generate_bindings_flag] | |
if: ${{ contains(needs.check_generate_bindings_flag.outputs.head_commit_message, '[generate bindings]') }} | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
ref: ${{ github.event.pull_request.head.ref }} | |
- uses: actions/download-artifact@v4 | |
- name: Update bindings | |
run: | | |
# Update or add the bindings | |
cp generated_binding-*/*.rs bindings/ | |
# Replace the default bindings | |
cd bindings | |
for x in linux-aarch64 linux-x86_64 macos-aarch64 macos-x86_64 windows-x86_64; do | |
# Choose the newest version except for devel | |
ln --force -s "$(ls -1 ./bindings-${x}-*.rs | grep -v devel | sort | tail -1)" ./bindings-${x}.rs | |
done | |
cd .. | |
- name: Add generated bindings | |
run: | | |
git add bindings/ | |
git config --local user.name "${GITHUB_ACTOR}" | |
git config --local user.email "${GITHUB_ACTOR}@users.noreply.github.com" | |
git commit -m "Update bindings [skip ci]" | |
- name: Push to PR branch | |
run: git push | |
# Gather the generated bindings and push them to generated_bindings branch. | |
# If we need to update the bindings, create a pull request from that branch. | |
commit_generated_bindings: | |
needs: test_with_bindgen | |
runs-on: ubuntu-latest | |
# In the case of /bindings command, we don't need to check anything else | |
# because it should have checked in test_with_bindings job. In the other | |
# cases, we only want to invoke this on the master branch. | |
if: github.event_name == 'issue_comment' || github.ref == 'refs/heads/master' | |
steps: | |
- uses: actions/checkout@v4 | |
- uses: actions/download-artifact@v4 | |
- name: Switch branch | |
if: github.event_name != 'issue_comment' | |
run: | | |
# 1) If there's already generated_bindings branch, checkout it. | |
# 2) If generated_binding branch is not created, create it from the default branch. | |
if git ls-remote --exit-code --heads origin generated_bindings 2>&1 >/dev/null; then | |
git fetch origin --no-tags --prune --depth=1 generated_bindings | |
git checkout generated_bindings | |
else | |
git switch -c generated_bindings | |
fi | |
- name: Switch branch (/bindings command) | |
if: github.event_name == 'issue_comment' | |
uses: r-lib/actions/pr-fetch@v2 | |
with: | |
repo-token: ${{ secrets.GITHUB_TOKEN }} | |
- name: Commit the generated bindings | |
run: | | |
# Update or add the bindings | |
cp generated_binding-*/*.rs bindings/ | |
# Replace the default bindings | |
cd bindings | |
for x in linux-aarch64 linux-x86_64 macos-aarch64 macos-x86_64 windows-x86_64; do | |
# Choose the newest version except for devel | |
ln --force -s "$(ls -1 ./bindings-${x}-*.rs | grep -v devel | sort | tail -1)" ./bindings-${x}.rs | |
done | |
cd .. | |
# detect changes (the code is derived from https://stackoverflow.com/a/3879077) | |
git add bindings/ | |
git update-index --refresh | |
if ! git diff-index --quiet HEAD -- bindings/; then | |
git config --local user.name "${GITHUB_ACTOR}" | |
git config --local user.email "${GITHUB_ACTOR}@users.noreply.github.com" | |
git commit -m "Update bindings [skip ci]" | |
else | |
echo "No changes" | |
fi | |
- name: Push | |
if: github.event_name != 'issue_comment' | |
run: git push origin generated_bindings | |
- name: Push (/bindings command) | |
if: github.event_name == 'issue_comment' | |
uses: r-lib/actions/pr-push@v2 | |
with: | |
repo-token: ${{ secrets.GITHUB_TOKEN }} |