Skip to content
Closed
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
251 changes: 251 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
name: CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:

env:
CARGO_TERM_COLOR: always

jobs:
test:
name: Test ${{ matrix.os }} (${{ matrix.arch }}) - XGBoost ${{ matrix.xgboost_version }}
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
# macOS ARM64 (M1/M2/M3) - Test multiple versions
- os: macos
arch: arm64
runner: macos-14
xgboost_version: "3.1.1"
- os: macos
arch: arm64
runner: macos-14
xgboost_version: "3.0.5"
- os: macos
arch: arm64
runner: macos-14
xgboost_version: "2.1.4"

# macOS x86_64 (Intel)
- os: macos
arch: x86_64
runner: macos-15-large
xgboost_version: "3.1.1"

Comment on lines +36 to +40
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Switch off paid macOS large runner

macos-15-large targets a paid larger runner that only works when the repository belongs to an organization with the larger-runner entitlement; on a personal/open-source repo this job will fail before any step runs.(docs.github.com) Please move to the standard Intel label (for example macos-15-intel) so the Intel coverage remains but the workflow still executes.

-          - os: macos
-            arch: x86_64
-            runner: macos-15-large
+          - os: macos
+            arch: x86_64
+            runner: macos-15-intel
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- os: macos
arch: x86_64
runner: macos-15-large
xgboost_version: "3.1.1"
- os: macos
arch: x86_64
runner: macos-15-intel
xgboost_version: "3.1.1"
🤖 Prompt for AI Agents
.github/workflows/ci.yml around lines 36 to 40: the job is using the paid larger
runner "macos-15-large" which will fail on personal/open-source repos; edit the
workflow to use the standard Intel runner by replacing the runner value
"macos-15-large" with "macos-15-intel" (keep os: macos and arch: x86_64
unchanged) so the job runs on GitHub-hosted Intel macOS runners.

# Linux x86_64 - Test multiple versions including thread-safety boundary
- os: linux
arch: x86_64
runner: ubuntu-latest
xgboost_version: "3.1.1"
- os: linux
arch: x86_64
runner: ubuntu-latest
xgboost_version: "1.7.6"
- os: linux
arch: x86_64
runner: ubuntu-latest
xgboost_version: "1.4.2" # First thread-safe version

# Linux ARM64
- os: linux
arch: arm64
runner: ubuntu-latest
xgboost_version: "3.1.1"

Comment on lines +55 to +60
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix linux arm64 runner selection

Setting runner: ubuntu-latest still provisions an x86_64 VM; GitHub exposes dedicated labels (ubuntu-24.04-arm / ubuntu-22.04-arm) for hosted arm64 runners.(docs.github.com) Without switching labels, this matrix leg never validates arm64 builds despite claiming to. Update the label so the job actually runs on arm hardware.

-          - os: linux
-            arch: arm64
-            runner: ubuntu-latest
+          - os: linux
+            arch: arm64
+            runner: ubuntu-24.04-arm
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Linux ARM64
- os: linux
arch: arm64
runner: ubuntu-latest
xgboost_version: "3.1.1"
# Linux ARM64
- os: linux
arch: arm64
runner: ubuntu-24.04-arm
xgboost_version: "3.1.1"
🤖 Prompt for AI Agents
.github/workflows/ci.yml around lines 55 to 60: the matrix entry for "Linux
ARM64" incorrectly uses runner: ubuntu-latest which yields an x86_64 VM; replace
the runner label with a hosted ARM runner label (e.g., ubuntu-24.04-arm or
ubuntu-22.04-arm) so the job actually runs on arm64 hardware, and ensure any
OS/version matrix values remain consistent with the chosen ARM label.

# Windows x86_64
- os: windows
arch: x86_64
runner: windows-latest
xgboost_version: "3.1.1"

steps:
- uses: actions/checkout@v4

- name: Install Rust
uses: dtolnay/rust-toolchain@stable

- name: Cache cargo registry
uses: actions/cache@v4
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-${{ matrix.arch }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-${{ matrix.arch }}-cargo-registry-

- name: Cache cargo index
uses: actions/cache@v4
with:
path: ~/.cargo/git
key: ${{ runner.os }}-${{ matrix.arch }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-${{ matrix.arch }}-cargo-index-

- name: Cache cargo build
uses: actions/cache@v4
with:
path: target
key: ${{ runner.os }}-${{ matrix.arch }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-${{ matrix.arch }}-cargo-build-target-

- name: Install system dependencies (Linux)
if: matrix.os == 'linux'
run: |
sudo apt-get update
sudo apt-get install -y libclang-dev

- name: Install system dependencies (macOS)
if: matrix.os == 'macos'
run: |
brew install libomp

- name: Check build (no features)
env:
XGBOOST_VERSION: ${{ matrix.xgboost_version }}
run: cargo check --verbose

- name: Build (no features)
env:
XGBOOST_VERSION: ${{ matrix.xgboost_version }}
run: cargo build --verbose

- name: Run tests (no features)
env:
XGBOOST_VERSION: ${{ matrix.xgboost_version }}
run: cargo test --verbose

- name: Build examples
env:
XGBOOST_VERSION: ${{ matrix.xgboost_version }}
run: |
cargo build --example basic_usage --verbose
cargo build --example advanced_usage --verbose

- name: Verify library architecture (macOS)
if: matrix.os == 'macos'
run: |
echo "Checking library architecture..."
file target/debug/libxgboost.dylib
lipo -info target/debug/libxgboost.dylib || otool -L target/debug/libxgboost.dylib

- name: Verify library architecture (Linux)
if: matrix.os == 'linux'
run: |
echo "Checking library architecture..."
file target/debug/libxgboost.so
readelf -h target/debug/libxgboost.so | grep Machine

- name: Verify library exists (Windows)
if: matrix.os == 'windows'
run: |
echo "Checking library exists..."
Get-Item target/debug/xgboost.dll

- name: Verify thread safety detection
env:
XGBOOST_VERSION: ${{ matrix.xgboost_version }}
run: |
echo "XGBoost version: ${{ matrix.xgboost_version }}"
cargo build --verbose 2>&1 | grep "thread-safe" || echo "No thread-safe message found"

clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: clippy

- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libclang-dev

- name: Run clippy (no features)
run: cargo clippy -- -D warnings

fmt:
name: Format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt

- name: Check formatting
run: cargo fmt -- --check

# Test checksum verification
security-checksums:
name: Verify SHA256 checksums
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install Rust
uses: dtolnay/rust-toolchain@stable

- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libclang-dev

- name: Test checksum verification for XGBoost 3.1.1
env:
XGBOOST_VERSION: "3.1.1"
run: |
cargo check --verbose 2>&1 | tee build.log
grep "✓ Verified SHA256" build.log
echo "Checksum verification working for 3.1.1"

- name: Test checksum verification for XGBoost 2.1.4
env:
XGBOOST_VERSION: "2.1.4"
run: |
cargo clean
cargo check --verbose 2>&1 | tee build.log
grep "✓ Verified SHA256" build.log
echo "Checksum verification working for 2.1.4"

# Test caching behavior
caching-test:
name: Test wheel caching
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install Rust
uses: dtolnay/rust-toolchain@stable

- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libclang-dev

- name: First build (should download)
env:
XGBOOST_VERSION: "3.1.1"
run: |
cargo check --verbose 2>&1 | tee build1.log
grep "Downloading XGBoost wheel" build1.log || true

- name: Second build (should use cache)
env:
XGBOOST_VERSION: "3.1.1"
run: |
touch src/lib.rs
cargo check --verbose 2>&1 | tee build2.log
grep "Using cached XGBoost library" build2.log
echo "Caching is working correctly"
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/target
Cargo.lock
*.json
*.bin
*.model
*.dylib
*.so
*.dll
28 changes: 28 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "xgboost-rust"
version = "0.1.0"
edition = "2021"
description = "Rust bindings for XGBoost, a gradient boosting library for machine learning. Downloads XGBoost binaries at build time for cross-platform compatibility."
license = "Apache-2.0"
keywords = ["machine-learning", "gradient-boosting", "xgboost", "ml"]
categories = ["science"]
readme = "README.md"
rust-version = "1.70"

[build-dependencies]
bindgen = "0.72.0"
ureq = "2.0"
zip = "0.6"
sha2 = "0.10"

[features]
default = []
gpu = []

[[example]]
name = "basic_usage"
path = "examples/basic_usage.rs"

[[example]]
name = "advanced_usage"
path = "examples/advanced_usage.rs"
25 changes: 22 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Rust bindings for XGBoost, a gradient boosting library for machine learning.
- **Automatic Binary Download**: Downloads XGBoost binaries at build time from PyPI wheels
- **Cross-Platform**: Supports Linux (x86_64, aarch64), macOS (x86_64, arm64), and Windows (x86_64)
- **Version Control**: Specify XGBoost version via `XGBOOST_VERSION` environment variable
- **Version-Aware Thread Safety**: Automatically enables `Send + Sync` for XGBoost ≥ 1.4
- **Easy to Use**: Simple, safe Rust API wrapping the XGBoost C API

## Installation
Expand Down Expand Up @@ -85,10 +86,28 @@ This crate downloads the appropriate XGBoost Python wheel from PyPI during the b

## Thread Safety

The `Booster` type is **not thread-safe**. For multi-threaded usage:
Thread safety is **version-aware**:

1. **Recommended**: Create one `Booster` per thread
2. **Alternative**: Wrap in `Arc<Mutex<Booster>>` for shared access
- **XGBoost ≥ 1.4**: `Booster` implements `Send + Sync` and is thread-safe for predictions on tree models. You can safely share `Arc<Booster>` across threads.
- **XGBoost < 1.4**: `Booster` does NOT implement `Send + Sync`. Use one booster per thread or wrap in `Arc<Mutex<Booster>>`.

### Example with XGBoost ≥ 1.4

```rust
use std::sync::Arc;
use std::thread;
use xgboost_rust::Booster;

let booster = Arc::new(Booster::load("model.json")?);
let booster_clone = booster.clone();

thread::spawn(move || {
// Safe concurrent predictions with XGBoost ≥ 1.4
let predictions = booster_clone.predict(&data, rows, cols, 0, false)?;
});
```

The version check happens automatically at build time based on the `XGBOOST_VERSION` environment variable.

## Examples

Expand Down
Loading