diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d70e3a5..654efa3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,8 +35,16 @@ jobs: run: | python3 -m pip install pip -U python3 -m pip install setuptools -U + cd python pip3 install . python3 -c 'import ruapu; print(ruapu.supports("neon")); print(ruapu.supports(isa="avx"))' + - name: build-test-rust + run: | + rustup update stable + rustup default stable + cd rust + cargo build --verbose + cargo test --verbose macos: runs-on: macos-latest @@ -50,8 +58,16 @@ jobs: run: | python3 -m pip install pip -U python3 -m pip install setuptools -U + cd python pip3 install . python3 -c 'import ruapu; print(ruapu.supports("neon")); print(ruapu.supports(isa="avx"))' + - name: build-test-rust + run: | + rustup update stable + rustup default stable + cd rust + cargo build --verbose + cargo test --verbose macos-arm64: runs-on: macos-14 @@ -65,8 +81,16 @@ jobs: run: | python3 -m pip install pip -U python3 -m pip install setuptools -U + cd python pip3 install . python3 -c 'import ruapu; print(ruapu.supports("neon")); print(ruapu.supports(isa="avx"))' + - name: build-test-rust + run: | + rustup update stable + rustup default stable + cd rust + cargo build --verbose + cargo test --verbose windows: runs-on: windows-latest @@ -86,8 +110,16 @@ jobs: run: | python3 -m pip install pip -U python3 -m pip install setuptools -U + cd python pip3 install . python3 -c 'import ruapu; print(ruapu.supports("neon")); print(ruapu.supports(isa="avx"))' + - name: build-test-rust + run: | + rustup update stable + rustup default stable + cd rust + cargo build --verbose + cargo test --verbose qemu: runs-on: ubuntu-latest diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index 237c8da..05de637 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -31,6 +31,7 @@ jobs: CIBW_BUILD: "cp311*" CIBW_BUILD_VERBOSITY: 1 with: + package-dir: python output-dir: wheelhouse - uses: actions/upload-artifact@v4 diff --git a/.gitignore b/.gitignore index b07d6d1..ff1ae49 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ build/ dist/ *.egg-info/ *.pyc + +# Rust files +target diff --git a/README.md b/README.md index 957e9c3..d214cff 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ pip3 install ruapu ```shell # from source code -pip3 install . +pip3 install ./python ``` @@ -121,6 +121,36 @@ ruapu.supports(isa="avx2") +### ruapu with Rust + + + + + +
+ +Compile ruapu library + +```shell +# from source code +cd rust +cargo build --release +``` + + +Use ruapu in Rust + +```rust +extern crate ruapu; + +fn main() { + println!("supports neon: {}", ruapu::supports("neon").unwrap()); + println!("supports avx2: {}", ruapu::supports("avx2").unwrap()); + println!("rua: {:?}", ruapu::rua()); +} +``` +
+
Github-hosted runner result (Linux) diff --git a/pyproject.toml b/python/pyproject.toml similarity index 100% rename from pyproject.toml rename to python/pyproject.toml diff --git a/ruapu-py.c b/python/ruapu-binding.c similarity index 97% rename from ruapu-py.c rename to python/ruapu-binding.c index c6ca524..03c2712 100644 --- a/ruapu-py.c +++ b/python/ruapu-binding.c @@ -1,9 +1,9 @@ +#define RUAPU_IMPLEMENTATION +#include "../ruapu.h" + #define PY_SSIZE_T_CLEAN #include -#define RUAPU_IMPLEMENTATION -#include "ruapu.h" - static PyObject *ruapu_supports_py(PyObject *self, PyObject *args, PyObject *kwargs) { static char *kwlist[] = {"isa", NULL}; diff --git a/setup.py b/python/setup.py similarity index 94% rename from setup.py rename to python/setup.py index 1baaca1..b62f97d 100644 --- a/setup.py +++ b/python/setup.py @@ -16,7 +16,7 @@ class bdist_wheel_abi3(bdist_wheel): ext = Extension( name = 'ruapu', - sources = ['ruapu-py.c'], + sources = ['ruapu-binding.c'], py_limited_api = True ) diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 0000000..82c468a --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,39 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "cc" +version = "1.0.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc" + +[[package]] +name = "claim" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81099d6bb72e1df6d50bb2347224b666a670912bb7f06dbe867a4a070ab3ce8" +dependencies = [ + "autocfg", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "ruapu" +version = "0.1.0" +dependencies = [ + "cc", + "claim", + "lazy_static", +] diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 0000000..5932f1a --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "ruapu" +version = "0.1.0" +edition = "2021" + +[build-dependencies] +cc = "1.0" + +[dev-dependencies] +claim = "0" + +[dependencies] +lazy_static = "1" diff --git a/rust/build.rs b/rust/build.rs new file mode 100644 index 0000000..e318b36 --- /dev/null +++ b/rust/build.rs @@ -0,0 +1,6 @@ +fn main() { + cc::Build::new() + .file("src/ruapu-binding.c") + .compile("ruapu-rs"); + println!("cargo:rerun-if-changed=ruapu-binding.c"); +} diff --git a/rust/examples/ruapu.rs b/rust/examples/ruapu.rs new file mode 100644 index 0000000..6da90cb --- /dev/null +++ b/rust/examples/ruapu.rs @@ -0,0 +1,7 @@ +extern crate ruapu; + +fn main() { + println!("supports neon: {}", ruapu::supports("neon").unwrap()); + println!("supports avx2: {}", ruapu::supports("avx2").unwrap()); + println!("rua: {:?}", ruapu::rua()); +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs new file mode 100644 index 0000000..c8a7e86 --- /dev/null +++ b/rust/src/lib.rs @@ -0,0 +1,76 @@ +#[macro_use] +extern crate lazy_static; + +use std::ffi::{CString, CStr, NulError}; +use std::os::raw::c_char; +use std::sync::Mutex; +use std::result::Result; + +lazy_static! { + static ref RUAPU_INITILIASED: Mutex = Mutex::new(false); +} + +extern "C" { + fn ruapu_init(); + fn ruapu_supports(isa: *const c_char) -> i32; + fn ruapu_rua() -> *const *const c_char; +} + +fn init_ruapu() { + if !*(RUAPU_INITILIASED.lock().unwrap()) { + unsafe { + ruapu_init(); + } + *(RUAPU_INITILIASED.lock().unwrap()) = true; + } +} + +pub fn supports(isa: &str) -> Result { + init_ruapu(); + let isa = CString::new(isa)?; + let isa_ptr = isa.as_ptr(); + unsafe { + Ok(ruapu_supports(isa_ptr) != 0) + } +} + +pub fn rua() -> Vec { + init_ruapu(); + let mut result = Vec::new(); + unsafe { + let mut i = 0; + let ptr = ruapu_rua(); + loop { + let ptr = ptr.offset(i); + if *ptr == std::ptr::null() { + break; + } + let c_str = CStr::from_ptr(*ptr); + let str = c_str.to_str().unwrap(); + result.push(str.to_string()); + i += 1; + } + } + return result; +} + +#[cfg(test)] +mod tests { + use super::*; + use claim::assert_ok; + + #[test] + fn test_supports() { + assert_ok!(supports("avx2")); + assert_ok!(supports("non_exists_feature")); + assert_eq!(supports("non_exists_feature").unwrap(), false); + + let supported_features = rua(); + for feature in supported_features { + assert_eq!(supports(&feature).unwrap(), true); + } + + let nul_error = supports("neon\0avx2").unwrap_err(); + assert_eq!(nul_error.into_vec(), b"neon\0avx2"); + } +} diff --git a/rust/src/ruapu-binding.c b/rust/src/ruapu-binding.c new file mode 100644 index 0000000..b21d5b4 --- /dev/null +++ b/rust/src/ruapu-binding.c @@ -0,0 +1,2 @@ +#define RUAPU_IMPLEMENTATION +#include "../../ruapu.h"