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
104 changes: 104 additions & 0 deletions .github/workflows/build-native.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
name: Build Native Libraries

on:
push:
branches: [main]
paths:
- 'bindings/c/**'
- 'bindings/dotnet/**'
- 'core/**'
pull_request:
branches: [main]
workflow_dispatch:

jobs:
build-native:
strategy:
matrix:
include:
- os: windows-latest
target: x86_64-pc-windows-msvc
lib_name: zen_ffi.dll
runtime: win-x64
- os: ubuntu-latest

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Would you be able to add unknown-linux arm?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Sure, added. wish you guys a happy holiday!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thank you, we have couple more days of holidays and we will soon be back - we will take a look as soon as we are back

target: x86_64-unknown-linux-gnu
lib_name: libzen_ffi.so
runtime: linux-x64
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
lib_name: libzen_ffi.so
runtime: linux-arm64
- os: macos-latest
target: x86_64-apple-darwin
lib_name: libzen_ffi.dylib
runtime: osx-x64
- os: macos-latest
target: aarch64-apple-darwin
lib_name: libzen_ffi.dylib
runtime: osx-arm64

runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v4

- name: Install Rust
uses: dtolnay/rust-action@stable
with:
targets: ${{ matrix.target }}

- name: Install Linux ARM64 toolchain
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu

- name: Build native library
run: cargo build --release -p zen-ffi --no-default-features --target ${{ matrix.target }}

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: native-${{ matrix.runtime }}
path: target/${{ matrix.target }}/release/${{ matrix.lib_name }}

package-nuget:
needs: build-native
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'

- name: Copy native libraries to runtime folders
run: |
mkdir -p bindings/dotnet/runtimes/win-x64/native
mkdir -p bindings/dotnet/runtimes/linux-x64/native
mkdir -p bindings/dotnet/runtimes/linux-arm64/native
mkdir -p bindings/dotnet/runtimes/osx-x64/native
mkdir -p bindings/dotnet/runtimes/osx-arm64/native

cp artifacts/native-win-x64/zen_ffi.dll bindings/dotnet/runtimes/win-x64/native/
cp artifacts/native-linux-x64/libzen_ffi.so bindings/dotnet/runtimes/linux-x64/native/
cp artifacts/native-linux-arm64/libzen_ffi.so bindings/dotnet/runtimes/linux-arm64/native/
cp artifacts/native-osx-x64/libzen_ffi.dylib bindings/dotnet/runtimes/osx-x64/native/
cp artifacts/native-osx-arm64/libzen_ffi.dylib bindings/dotnet/runtimes/osx-arm64/native/

- name: Build NuGet package
working-directory: bindings/dotnet
run: dotnet pack -c Release

- name: Upload NuGet package
uses: actions/upload-artifact@v4
with:
name: nuget-package
path: bindings/dotnet/bin/Release/*.nupkg
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ members = [
"core/*",
"bindings/*"
]
exclude = [
"bindings/dotnet"
]

[workspace.dependencies]
ahash = "0.8"
Expand Down
2 changes: 1 addition & 1 deletion bindings/c/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ zen-expression = { path = "../../core/expression" }
zen-tmpl = { path = "../../core/template" }

[lib]
crate-type = ["staticlib"]
crate-type = ["staticlib", "cdylib"]

[build-dependencies]
cbindgen = "0.28"
Expand Down
48 changes: 48 additions & 0 deletions bindings/c/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,51 @@ mod languages;
mod loader;
mod mt;
mod result;

use std::ffi::{c_char, c_int, c_void, CString};

/// Allocates a string using Rust's allocator.
/// The caller must free the returned pointer using zen_free_string.
/// Returns null if the input is null or if allocation fails.
#[no_mangle]
pub extern "C" fn zen_alloc_string(ptr: *const c_char, len: usize) -> *mut c_char {
if ptr.is_null() {
return std::ptr::null_mut();
}

let slice = unsafe { std::slice::from_raw_parts(ptr as *const u8, len) };
match CString::new(slice) {
Ok(cstring) => cstring.into_raw(),
Err(_) => std::ptr::null_mut(),
}
}

/// Frees a string that was allocated by Rust.
/// This must be called for any string returned by zen_* functions to avoid memory leaks.
/// This is safe to call with a null pointer.
#[no_mangle]
pub extern "C" fn zen_free_string(ptr: *mut c_char) {
if !ptr.is_null() {
unsafe {
let _ = CString::from_raw(ptr);
}
}
}

/// Frees an integer pointer that was allocated by Rust.
/// This must be called for ZenResult<c_int> result field when it's not null.
#[no_mangle]
pub extern "C" fn zen_free_int(ptr: *mut c_int) {
if !ptr.is_null() {
let _ = unsafe { Box::from_raw(ptr) };
}
}

/// Generic free function for any Rust-allocated memory.
/// Use zen_free_string for strings returned by Rust functions.
#[no_mangle]
pub extern "C" fn zen_free(ptr: *mut c_void) {
if !ptr.is_null() {
let _ = unsafe { Box::from_raw(ptr as *mut u8) };
}
}
26 changes: 26 additions & 0 deletions bindings/c/zen_engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,32 @@ typedef struct ZenCustomNodeResult {

typedef struct ZenCustomNodeResult (*ZenCustomNodeNativeCallback)(const char *request);

/**
* Allocates a string using Rust's allocator.
* The caller must free the returned pointer using zen_free_string.
* Returns null if the input is null or if allocation fails.
*/
char *zen_alloc_string(const char *ptr, uintptr_t len);

/**
* Frees a string that was allocated by Rust.
* This must be called for any string returned by zen_* functions to avoid memory leaks.
* This is safe to call with a null pointer.
*/
void zen_free_string(char *ptr);

/**
* Frees an integer pointer that was allocated by Rust.
* This must be called for ZenResult<c_int> result field when it's not null.
*/
void zen_free_int(int *ptr);

/**
* Generic free function for any Rust-allocated memory.
* Use zen_free_string for strings returned by Rust functions.
*/
void zen_free(void *ptr);

/**
* Frees ZenDecision
*/
Expand Down
6 changes: 6 additions & 0 deletions bindings/dotnet/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
bin
bin/
obj
obj/
runtimes/
Sequence_*.xml
Loading