diff --git a/.github/actions/rust-toolchain-setup/action.yml b/.github/actions/rust-toolchain-setup/action.yml new file mode 100644 index 0000000000000..bf73fede16c7f --- /dev/null +++ b/.github/actions/rust-toolchain-setup/action.yml @@ -0,0 +1,44 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-action.json + +name: 'Rust toolchain setup' +description: 'Common setup steps for GitHub workflows for Rust projects' + +runs: + using: composite + steps: + - uses: dtolnay/rust-toolchain@1.71.0 + with: + components: clippy, rustfmt + - uses: extractions/setup-just@v1 + with: + just-version: '1.15.0' # optional semver specification, otherwise latest + + ### + ### Linux setup + ### + - name: rustup + # We need to use the nightly rust tool change to enable registry-auth / to connect to ADO feeds. + if: ${{ (runner.os == 'Linux') }} + run: | + rustup set profile minimal + rustup install + shell: bash + # - name: Cargo login + # if: ${{ (runner.os == 'Linux') }} + # run: just cargo-login-ci + # shell: bash + + ### + ### Windows setup + ### + - name: rustup + # We need to use the nightly rust tool change to enable registry-auth / to connect to ADO feeds. + if: ${{ (runner.os == 'Windows') }} + run: | + rustup set profile minimal + rustup install + shell: pwsh + # - name: Cargo login + # if: ${{ (runner.os == 'Windows') }} + # run: just cargo-login-ci-windows + # shell: pwsh diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml new file mode 100644 index 0000000000000..6c3f2eb0fbbe1 --- /dev/null +++ b/.github/workflows/rust-ci.yml @@ -0,0 +1,132 @@ +name: Rust + +on: [pull_request] + +env: + CARGO_TERM_COLOR: always + RUST_LOG: onnxruntime=debug,onnxruntime-sys=debug + RUST_BACKTRACE: 1 + MANIFEST_PATH: ${{ github.workspace }}/rust/Cargo.toml + +jobs: + fmt: + name: Rustfmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/rust-toolchain-setup + - name: vendor onnxruntime source + run: just vendor + - name: fmt + run: cargo fmt --all -- --check + + download: + name: Download prebuilt ONNX Runtime archive from build.rs + runs-on: ubuntu-latest + env: + ORT_RUST_STRATEGY=download + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/rust-toolchain-setup + - run: rustup target install x86_64-unknown-linux-gnu + - run: rustup target install x86_64-apple-darwin + - run: rustup target install i686-pc-windows-msvc + - run: rustup target install x86_64-pc-windows-msvc + # ****************************************************************** + - name: Download prebuilt archive (CPU, x86_64-unknown-linux-gnu) + run: cargo build --target x86_64-unknown-linux-gnu --manifest-path ${{ env.MANIFEST_PATH }} + - name: Verify prebuilt archive downloaded (CPU, x86_64-unknown-linux-gnu) + run: ls -lh target/x86_64-unknown-linux-gnu/debug/build/onnxruntime-sys-*/out/onnxruntime-linux-x64-1.*.tgz + # ****************************************************************** + - name: Download prebuilt archive (CPU, x86_64-apple-darwin) + run: cargo build --target x86_64-apple-darwin --manifest-path ${{ env.MANIFEST_PATH }} + - name: Verify prebuilt archive downloaded (CPU, x86_64-apple-darwin) + run: ls -lh target/x86_64-apple-darwin/debug/build/onnxruntime-sys-*/out/onnxruntime-osx-x64-1.*.tgz + # ****************************************************************** + - name: Download prebuilt archive (CPU, i686-pc-windows-msvc) + run: cargo build --target i686-pc-windows-msvc --manifest-path ${{ env.MANIFEST_PATH }} + - name: Verify prebuilt archive downloaded (CPU, i686-pc-windows-msvc) + run: ls -lh target/i686-pc-windows-msvc/debug/build/onnxruntime-sys-*/out/onnxruntime-win-x86-1.*.zip + # ****************************************************************** + - name: Download prebuilt archive (CPU, x86_64-pc-windows-msvc) + run: cargo build --target x86_64-pc-windows-msvc --manifest-path ${{ env.MANIFEST_PATH }} + - name: Verify prebuilt archive downloaded (CPU, x86_64-pc-windows-msvc) + run: ls -lh target/x86_64-pc-windows-msvc/debug/build/onnxruntime-sys-*/out/onnxruntime-win-x64-1.*.zip + # ****************************************************************** + - name: Download prebuilt archive (GPU, x86_64-unknown-linux-gnu) + env: + ORT_USE_CUDA: "yes" + run: cargo build --target x86_64-unknown-linux-gnu --manifest-path ${{ env.MANIFEST_PATH }} + - name: Verify prebuilt archive downloaded (GPU, x86_64-unknown-linux-gnu) + run: ls -lh target/x86_64-unknown-linux-gnu/debug/build/onnxruntime-sys-*/out/onnxruntime-linux-x64-gpu-1.*.tgz + # ****************************************************************** + - name: Download prebuilt archive (GPU, x86_64-pc-windows-msvc) + env: + ORT_USE_CUDA: "yes" + run: cargo build --target x86_64-pc-windows-msvc --manifest-path ${{ env.MANIFEST_PATH }} + - name: Verify prebuilt archive downloaded (GPU, x86_64-pc-windows-msvc) + run: ls -lh target/x86_64-pc-windows-msvc/debug/build/onnxruntime-sys-*/out/onnxruntime-win-gpu-x64-1.*.zip + + test: + name: Test Suite + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + target: + [ + x86_64-unknown-linux-gnu, + x86_64-apple-darwin, + x86_64-pc-windows-msvc, + i686-pc-windows-msvc, + ] + include: + - target: x86_64-unknown-linux-gnu + os: ubuntu-latest + - target: x86_64-apple-darwin + os: macos-latest + - target: x86_64-pc-windows-msvc + os: windows-latest + - target: i686-pc-windows-msvc + os: windows-latest + env: + CARGO_BUILD_TARGET: ${{ matrix.target }} + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/rust-toolchain-setup + - name: vendor onnxruntime source + run: just vendor + - run: rustup target install ${{ matrix.target }} + - name: Install additional packages (macOS) + if: contains(matrix.target, 'x86_64-apple-darwin') + run: brew install libomp + - name: Build (cargo build) + run: cargo build --all --manifest-path ${{ env.MANIFEST_PATH }} + - name: Build tests (cargo test) + run: cargo test --no-run --manifest-path ${{ env.MANIFEST_PATH }} + - name: Build onnxruntime with 'model-fetching' feature + run: cargo build --manifest-path ${{ env.MANIFEST_PATH }} --features model-fetching + - name: Test onnxruntime-sys + run: cargo build --package onnxruntime-sys -- --test-threads=1 --nocapture + - name: Test onnxruntime + run: cargo test --manifest-path ${{ env.MANIFEST_PATH }} --features model-fetching -- --test-threads=1 --nocapture + + clippy: + name: Clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/rust-toolchain-setup + - name: vendor onnxruntime source + run: just vendor + - run: clippy --all-features --manifest-path ${{ env.MANIFEST_PATH }} -- -D warnings + + package-sys: + name: Package onnxruntime-sys + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/rust-toolchain-setup + - name: vendor onnxruntime source + run: just vendor + - run: cargo package --allow-dirty --package onnxruntime-sys diff --git a/rust/justfile b/rust/justfile new file mode 100644 index 0000000000000..fd7b12d60a25e --- /dev/null +++ b/rust/justfile @@ -0,0 +1,13 @@ + + +vendor: + mkdir -p ./onnxruntime-sys/vendor/onnxruntime-src + cp -rf ../onnxruntime ./onnxruntime-sys/vendor/onnxruntime-src + cp -rf ../cmake ./onnxruntime-sys/vendor/onnxruntime-src + rm -rf ./onnxruntime-sys/vendor/onnxruntime-src/cmake/external/onnx + cp -rf ../include ./onnxruntime-sys/vendor/onnxruntime-src + mkdir -p ./onnxruntime-sys/vendor/onnxruntime-src/tools + cp -rf ../tools/ci_build ./onnxruntime-sys/vendor/onnxruntime-src/tools + cp -rf ../samples ./onnxruntime-sys/vendor/onnxruntime-src + cp -f ../requirements.txt.in ./onnxruntime-sys/vendor/onnxruntime-src + cp -f ../VERSION_NUMBER ./onnxruntime-sys/vendor/onnxruntime-src diff --git a/rust/onnxruntime-sys/.gitignore b/rust/onnxruntime-sys/.gitignore new file mode 100644 index 0000000000000..22d0d82f8095e --- /dev/null +++ b/rust/onnxruntime-sys/.gitignore @@ -0,0 +1 @@ +vendor diff --git a/rust/onnxruntime-sys/Cargo.toml b/rust/onnxruntime-sys/Cargo.toml index 4806e6ca2953c..236c2c1fae860 100644 --- a/rust/onnxruntime-sys/Cargo.toml +++ b/rust/onnxruntime-sys/Cargo.toml @@ -3,18 +3,16 @@ authors = ["Nicolas Bigaouette "] edition = "2018" name = "onnxruntime-sys" version = "0.0.14" - links = "onnxruntime" - description = "Unsafe wrapper around Microsoft's ONNX Runtime" documentation = "https://docs.rs/onnxruntime-sys" homepage = "https://github.com/microsoft/onnxruntime" license = "MIT OR Apache-2.0" readme = "../README.md" repository = "https://github.com/microsoft/onnxruntime" - categories = ["science"] keywords = ["neuralnetworks", "onnx", "bindings"] +include = ["src", "example", "vendor", "build.rs"] [dependencies] libloading = "0.7" @@ -22,6 +20,7 @@ libloading = "0.7" [build-dependencies] bindgen = "0.63" cmake = "0.1" +anyhow = "1.0" # Used on unix flate2 = "1.0" diff --git a/rust/onnxruntime-sys/build.rs b/rust/onnxruntime-sys/build.rs index f59ee99fa29a7..b2a4f7b16141f 100644 --- a/rust/onnxruntime-sys/build.rs +++ b/rust/onnxruntime-sys/build.rs @@ -8,12 +8,16 @@ use std::{ str::FromStr, }; +// use cmake::build; + +use anyhow::{anyhow, Context, Result}; + /// ONNX Runtime version /// /// WARNING: If version is changed, bindings for all platforms will have to be re-generated. /// To do so, run this: /// cargo build --package onnxruntime-sys --features generate-bindings -const ORT_VERSION: &str = include_str!("../../VERSION_NUMBER"); +const ORT_VERSION: &str = include_str!("./vendor/onnxruntime-src/VERSION_NUMBER"); /// Base Url from which to download pre-built releases/ const ORT_RELEASE_BASE_URL: &str = "https://github.com/microsoft/onnxruntime/releases/download"; @@ -34,8 +38,8 @@ const ORT_RUST_ENV_GPU: &str = "ORT_RUST_USE_CUDA"; /// Subdirectory (of the 'target' directory) into which to extract the prebuilt library. const ORT_PREBUILT_EXTRACT_DIR: &str = "onnxruntime"; -fn main() { - let libort_install_dir = prepare_libort_dir(); +fn main() -> Result<()> { + let libort_install_dir = prepare_libort_dir().context("preparing libort directory")?; let include_dir = libort_install_dir.join("include"); let lib_dir = libort_install_dir.join("lib"); @@ -55,6 +59,7 @@ fn main() { ); generate_bindings(&include_dir); + Ok(()) } fn generate_bindings(include_dir: &Path) { @@ -70,11 +75,7 @@ fn generate_bindings(include_dir: &Path) { ), ]; - let path = include_dir - .join("onnxruntime") - .join("core") - .join("session") - .join("onnxruntime_c_api.h"); + let path = include_dir.join("onnxruntime").join("onnxruntime_c_api.h"); // The bindgen::Builder is the main entry point // to bindgen, and lets you build up options for @@ -106,7 +107,7 @@ fn generate_bindings(include_dir: &Path) { let generated_file = PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs"); bindings - .write_to_file(&generated_file) + .write_to_file(generated_file) .expect("Couldn't write bindings!"); } @@ -144,7 +145,7 @@ fn extract_archive(filename: &Path, output: &Path) { } fn extract_tgz(filename: &Path, output: &Path) { - let file = fs::File::open(&filename).unwrap(); + let file = fs::File::open(filename).unwrap(); let buf = io::BufReader::new(file); let tar = flate2::read::GzDecoder::new(buf); let mut archive = tar::Archive::new(tar); @@ -152,7 +153,7 @@ fn extract_tgz(filename: &Path, output: &Path) { } fn extract_zip(filename: &Path, outpath: &Path) { - let file = fs::File::open(&filename).unwrap(); + let file = fs::File::open(filename).unwrap(); let buf = io::BufReader::new(file); let mut archive = zip::ZipArchive::new(buf).unwrap(); for i in 0..archive.len() { @@ -168,7 +169,7 @@ fn extract_zip(filename: &Path, outpath: &Path) { ); if let Some(p) = outpath.parent() { if !p.exists() { - fs::create_dir_all(&p).unwrap(); + fs::create_dir_all(p).unwrap(); } } let mut outfile = fs::File::create(&outpath).unwrap(); @@ -190,15 +191,15 @@ enum Architecture { } impl FromStr for Architecture { - type Err = String; + type Err = anyhow::Error; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { match s.to_lowercase().as_str() { "x86" => Ok(Architecture::X86), "x86_64" => Ok(Architecture::X86_64), "arm" => Ok(Architecture::Arm), "aarch64" => Ok(Architecture::Arm64), - _ => Err(format!("Unsupported architecture: {}", s)), + _ => Err(anyhow!("Unsupported architecture: {s}")), } } } @@ -233,14 +234,14 @@ impl Os { } impl FromStr for Os { - type Err = String; + type Err = anyhow::Error; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { match s.to_lowercase().as_str() { "windows" => Ok(Os::Windows), "macos" => Ok(Os::MacOs), "linux" => Ok(Os::Linux), - _ => Err(format!("Unsupported os: {}", s)), + _ => Err(anyhow!("Unsupported os: {s}")), } } } @@ -262,9 +263,9 @@ enum Accelerator { } impl FromStr for Accelerator { - type Err = String; + type Err = anyhow::Error; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { match s.to_lowercase().as_str() { "1" | "yes" | "true" | "on" => Ok(Accelerator::Cuda), _ => Ok(Accelerator::Cpu), @@ -393,36 +394,37 @@ fn prepare_libort_dir_prebuilt() -> PathBuf { extract_dir.join(prebuilt_archive.file_stem().unwrap()) } -fn prepare_libort_dir() -> PathBuf { +fn prepare_libort_dir() -> Result { let strategy = env::var(ORT_RUST_ENV_STRATEGY); println!( "strategy: {:?}", strategy.as_ref().map_or_else(|_| "unknown", String::as_str) ); match strategy.as_ref().map(String::as_str) { - Ok("download") => prepare_libort_dir_prebuilt(), - Ok("system") => PathBuf::from(match env::var(ORT_RUST_ENV_SYSTEM_LIB_LOCATION) { - Ok(p) => p, - Err(e) => { - panic!( - "Could not get value of environment variable {:?}: {:?}", - ORT_RUST_ENV_SYSTEM_LIB_LOCATION, e - ); - } - }), + Ok("download") => Ok(prepare_libort_dir_prebuilt()), + Ok("system") => { + let location = env::var(ORT_RUST_ENV_SYSTEM_LIB_LOCATION).context(format!( + "Could not get value of environment variable {:?}", + ORT_RUST_ENV_SYSTEM_LIB_LOCATION + ))?; + Ok(PathBuf::from(location)) + } Ok("compile") | Err(_) => prepare_libort_dir_compiled(), - _ => panic!("Unknown value for {:?}", ORT_RUST_ENV_STRATEGY), + _ => Err(anyhow!("Unknown value for {:?}", ORT_RUST_ENV_STRATEGY)), } } -fn prepare_libort_dir_compiled() -> PathBuf { - let mut config = cmake::Config::new("../../cmake"); +fn prepare_libort_dir_compiled() -> Result { + let manifest_dir_string = env::var("CARGO_MANIFEST_DIR").unwrap(); + let mut config = cmake::Config::new(format!( + "{manifest_dir_string}/vendor/onnxruntime-src/cmake" + )); config.define("onnxruntime_BUILD_SHARED_LIB", "ON"); - if env::var(ORT_RUST_ENV_GPU).unwrap_or_default().parse() == Ok(Accelerator::Cuda) { + if let Ok(Accelerator::Cuda) = env::var(ORT_RUST_ENV_GPU).unwrap_or_default().parse() { config.define("onnxruntime_USE_CUDA", "ON"); - } + }; - config.build() + Ok(config.build()) } diff --git a/rust/onnxruntime-sys/examples/c_api_sample.rs b/rust/onnxruntime-sys/examples/c_api_sample.rs index 499f1548de396..e8c9ca8f09a5a 100644 --- a/rust/onnxruntime-sys/examples/c_api_sample.rs +++ b/rust/onnxruntime-sys/examples/c_api_sample.rs @@ -307,7 +307,7 @@ fn main() { let output_node_names_cstring: Vec = output_node_names .iter() - .map(|n| std::ffi::CString::new(n.clone()).unwrap()) + .map(|n| std::ffi::CString::new(*n).unwrap()) .collect(); let output_node_names_ptr: Vec<*const i8> = output_node_names_cstring .iter() diff --git a/rust/onnxruntime/src/tensor/ort_output_tensor.rs b/rust/onnxruntime/src/tensor/ort_output_tensor.rs index 5176a58c423ea..006fbdba6cdb8 100644 --- a/rust/onnxruntime/src/tensor/ort_output_tensor.rs +++ b/rust/onnxruntime/src/tensor/ort_output_tensor.rs @@ -290,9 +290,6 @@ impl<'a> TryFrom for OrtOutput<'a> { .unwrap()(shape_info); match element_type { - sys::ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_UNDEFINED => { - unimplemented!() - } sys::ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT => { WithOutputTensor::try_from(value).map(OrtOutput::Float) } @@ -317,12 +314,6 @@ impl<'a> TryFrom for OrtOutput<'a> { sys::ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_STRING => { WithOutputTensor::try_from(value).map(OrtOutput::String) } - sys::ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_BOOL => { - unimplemented!() - } - sys::ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16 => { - unimplemented!() - } sys::ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_DOUBLE => { WithOutputTensor::try_from(value).map(OrtOutput::Double) } @@ -332,14 +323,18 @@ impl<'a> TryFrom for OrtOutput<'a> { sys::ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64 => { WithOutputTensor::try_from(value).map(OrtOutput::UInt64) } - sys::ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_COMPLEX64 => { - unimplemented!() - } - sys::ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_COMPLEX128 => { - unimplemented!() - } - sys::ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_BFLOAT16 => { - unimplemented!() + // Unimplemented output tensor data types + sys::ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_COMPLEX64 + | sys::ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_UNDEFINED + | sys::ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_BOOL + | sys::ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16 + | sys::ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_COMPLEX128 + | sys::ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_BFLOAT16 + | sys::ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT8E4M3FN + | sys::ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT8E4M3FNUZ + | sys::ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT8E5M2FNUZ + | sys::ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT8E5M2 => { + unimplemented!("{:?}", element_type) } } }