Skip to content

Commit 9e86a89

Browse files
committed
Support static link for Rust package
1 parent 15e2648 commit 9e86a89

10 files changed

Lines changed: 296 additions & 15 deletions

File tree

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
os_name="${RUNNER_OS:-}"
6+
if [[ -z "$os_name" ]]; then
7+
case "$(uname -s)" in
8+
Linux*) os_name="Linux" ;;
9+
Darwin*) os_name="macOS" ;;
10+
MINGW*|MSYS*|CYGWIN*) os_name="Windows" ;;
11+
*) os_name="$(uname -s)" ;;
12+
esac
13+
fi
14+
15+
show_one() {
16+
local bin="$1"
17+
18+
echo "=== Binary info: $bin ==="
19+
ls -lh "$bin"
20+
echo
21+
echo "=== Binary dependencies ($os_name) ==="
22+
23+
case "$os_name" in
24+
Linux)
25+
ldd "$bin"
26+
;;
27+
macOS)
28+
otool -L "$bin"
29+
;;
30+
Windows)
31+
if command -v dumpbin >/dev/null 2>&1; then
32+
dumpbin /dependents "$(cygpath -w "$PWD/$bin")"
33+
else
34+
echo "dumpbin is not available on PATH"
35+
return 1
36+
fi
37+
;;
38+
*)
39+
echo "Unsupported OS for dependency inspection: $os_name"
40+
return 1
41+
;;
42+
esac
43+
}
44+
45+
if [[ "${1:-}" == "--all" ]]; then
46+
shopt -s nullglob
47+
bins=()
48+
49+
case "$os_name" in
50+
Windows)
51+
for bin in target/debug/examples/*.exe; do
52+
bins+=("$bin")
53+
done
54+
;;
55+
*)
56+
for bin in target/debug/examples/*; do
57+
if [[ -f "$bin" && -x "$bin" && "$bin" != *.d ]]; then
58+
bins+=("$bin")
59+
fi
60+
done
61+
;;
62+
esac
63+
64+
if [[ ${#bins[@]} -eq 0 ]]; then
65+
echo "No built example executables found in target/debug/examples"
66+
exit 0
67+
fi
68+
69+
printf '%s\n' "${bins[@]}" | sort | while IFS= read -r bin; do
70+
show_one "$bin"
71+
echo
72+
done
73+
else
74+
name="${1:?Please provide an example binary name or use --all}"
75+
exe_suffix=""
76+
case "$os_name" in
77+
Windows) exe_suffix=".exe" ;;
78+
esac
79+
80+
bin="target/debug/examples/${name}${exe_suffix}"
81+
82+
if [[ ! -f "$bin" ]]; then
83+
echo "Binary not found: $bin"
84+
exit 1
85+
fi
86+
87+
show_one "$bin"
88+
fi
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/usr/bin/env bash
2+
3+
set -ex
4+
5+
cargo() {
6+
if [[ $# -gt 0 && "$1" == "run" ]]; then
7+
command cargo run --features static "${@:2}"
8+
else
9+
command cargo "$@"
10+
fi
11+
}
12+
13+
export -f cargo
14+
15+
./.github/scripts/test-rust.sh

.github/scripts/test-rust.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ set -ex
44

55
cd rust-api-examples
66

7+
trap 'bash ../.github/scripts/show-rust-binary-info.sh --all || true' EXIT
8+
79
./run-audio-tagging-zipformer.sh
810
rm -rf sherpa-onnx-zipformer-small-audio-tagging-2024-04-15
911

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
name: Test rust package static
2+
3+
on:
4+
push:
5+
branches:
6+
- rust-static
7+
workflow_dispatch:
8+
9+
concurrency:
10+
group: test-rust-package-static-${{ github.ref }}
11+
cancel-in-progress: true
12+
13+
jobs:
14+
test-rust-package-static:
15+
name: Test static ${{ matrix.os }}
16+
runs-on: ${{ matrix.os }}
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
# os: [ubuntu-latest, macos-latest, macos-15-intel, ubuntu-22.04-arm, windows-latest]
21+
os: [macos-latest, macos-15-intel]
22+
23+
env:
24+
SHERPA_ONNX_LIB_DIR: ""
25+
RUSTFLAGS: ""
26+
27+
steps:
28+
- uses: actions/checkout@v4
29+
30+
- uses: actions-rust-lang/setup-rust-toolchain@v1
31+
with:
32+
toolchain: stable
33+
34+
- name: Set up MSVC
35+
if: matrix.os == 'windows-latest'
36+
uses: ilammy/msvc-dev-cmd@v1
37+
38+
- name: Select MSVC linker
39+
if: matrix.os == 'windows-latest'
40+
shell: pwsh
41+
run: |
42+
$linker = Join-Path $env:VCToolsInstallDir 'bin\HostX64\x64\link.exe'
43+
if (-not (Test-Path $linker)) {
44+
throw "MSVC linker not found: $linker"
45+
}
46+
"CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER=$linker" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
47+
Write-Host "Using linker: $linker"
48+
& where.exe link
49+
50+
- name: Download prebuilt Sherpa-ONNX static libraries
51+
shell: bash
52+
run: |
53+
SHERPA_ONNX_VERSION=$(grep "SHERPA_ONNX_VERSION" ./CMakeLists.txt | cut -d " " -f 2 | cut -d '"' -f 2)
54+
55+
if [[ "${{ matrix.os }}" == "macos-latest" ]]; then
56+
d=sherpa-onnx-v$SHERPA_ONNX_VERSION-osx-universal2-static
57+
elif [[ "${{ matrix.os }}" == "macos-15-intel" ]]; then
58+
d=sherpa-onnx-v$SHERPA_ONNX_VERSION-osx-universal2-static
59+
elif [[ "${{ matrix.os }}" == "ubuntu-latest" ]]; then
60+
d=sherpa-onnx-v$SHERPA_ONNX_VERSION-linux-x64-static
61+
elif [[ "${{ matrix.os }}" == "ubuntu-22.04-arm" ]]; then
62+
d=sherpa-onnx-v$SHERPA_ONNX_VERSION-linux-aarch64-static
63+
elif [[ "${{ matrix.os }}" == "windows-latest" ]]; then
64+
d=sherpa-onnx-v$SHERPA_ONNX_VERSION-win-x64-static-MT-Release
65+
else
66+
echo "Unknown ${{ matrix.os }}"
67+
exit 1
68+
fi
69+
70+
LIB_URL="https://github.com/k2-fsa/sherpa-onnx/releases/download/v$SHERPA_ONNX_VERSION/$d.tar.bz2"
71+
72+
curl -SsL -O "$LIB_URL"
73+
tar -xvf $d.tar.bz2
74+
75+
ls -lh $d/lib
76+
echo "SHERPA_ONNX_LIB_DIR=$PWD/$d/lib" >> $GITHUB_ENV
77+
echo "RUSTFLAGS=" >> $GITHUB_ENV
78+
79+
- name: Show libs
80+
shell: bash
81+
run: |
82+
echo "SHERPA_ONNX_LIB_DIR: $SHERPA_ONNX_LIB_DIR"
83+
ls -lh $SHERPA_ONNX_LIB_DIR
84+
if [[ "${{ matrix.os }}" == "windows-latest" ]]; then
85+
echo "CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER: $CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER"
86+
fi
87+
88+
- name: Use local sherpa-onnx crate
89+
shell: bash
90+
run: |
91+
cd rust-api-examples
92+
93+
sed -i.bak 's|^sherpa-onnx *=.*|sherpa-onnx = { path = "../sherpa-onnx/rust/sherpa-onnx" }|' Cargo.toml
94+
95+
git diff .
96+
97+
- name: Run test
98+
shell: bash
99+
run: |
100+
./.github/scripts/test-rust-static.sh

.github/workflows/test-rust.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ jobs:
8181
8282
echo "RUSTFLAGS: $RUSTFLAGS"
8383
84-
- name: Test locally
84+
- name: Use local sherpa-onnx crate
8585
shell: bash
8686
run: |
8787
cd rust-api-examples

rust-api-examples/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ default = []
1818

1919
# Feature for using microphone
2020
mic = ["cpal"]
21+
static = ["sherpa-onnx/static"]
2122

2223
[[example]]
2324
name = "streaming_zipformer_microphone"

sherpa-onnx/rust/sherpa-onnx-sys/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,7 @@ include = [
1818
"README.md",
1919
"LICENSE*",
2020
]
21+
22+
[features]
23+
default = []
24+
static = []
Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,74 @@
11
use std::env;
22

3+
const SHERPA_ONNX_STATIC_LIBS: &[&str] = &[
4+
"sherpa-onnx-c-api",
5+
"sherpa-onnx-core",
6+
"kaldi-decoder-core",
7+
"sherpa-onnx-kaldifst-core",
8+
"sherpa-onnx-fstfar",
9+
"sherpa-onnx-fst",
10+
"kaldi-native-fbank-core",
11+
"kissfft-float",
12+
"piper_phonemize",
13+
"espeak-ng",
14+
"ucd",
15+
"onnxruntime",
16+
"ssentencepiece_core",
17+
];
18+
319
fn main() {
4-
// Try to get library directory from environment variable
520
let lib_dir = env::var("SHERPA_ONNX_LIB_DIR").ok();
21+
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
22+
let static_link = env::var_os("CARGO_FEATURE_STATIC").is_some();
623

724
match &lib_dir {
825
Some(path) => {
9-
println!("cargo:warning=SHERPA_ONNX_LIB_DIR={}", path);
10-
11-
// Tell Rust/Cargo where to find the libraries at build time
12-
println!("cargo:rustc-link-search=native={}", path);
26+
println!("cargo:warning=SHERPA_ONNX_LIB_DIR={path}");
27+
println!("cargo:rustc-link-search=native={path}");
1328

14-
// Add rpath for Linux/macOS
15-
if cfg!(any(target_os = "linux", target_os = "macos")) {
16-
println!("cargo:rustc-link-arg=-Wl,-rpath,{}", path);
29+
if !static_link && matches!(target_os.as_str(), "linux" | "macos") {
30+
println!("cargo:rustc-link-arg=-Wl,-rpath,{path}");
1731
}
1832
}
1933
None => {
20-
println!("cargo:warning=SHERPA_ONNX_LIB_DIR not set. You may need to set it to the folder containing libsherpa-onnx-c-api and libonnxruntime.");
34+
println!(
35+
"cargo:warning=SHERPA_ONNX_LIB_DIR not set. You may need to set it to the folder containing sherpa-onnx libraries."
36+
);
2137
}
2238
}
2339

24-
// Always link against the public sherpa-onnx C API import library.
25-
println!("cargo:rustc-link-lib=dylib=sherpa-onnx-c-api");
40+
if static_link {
41+
emit_static_link_directives(&target_os);
42+
} else {
43+
emit_dynamic_link_directives();
44+
}
2645

46+
println!("cargo:rerun-if-env-changed=SHERPA_ONNX_LIB_DIR");
47+
}
48+
49+
fn emit_dynamic_link_directives() {
50+
println!("cargo:rustc-link-lib=dylib=sherpa-onnx-c-api");
2751
println!("cargo:rustc-link-lib=dylib=onnxruntime");
52+
}
2853

29-
// Rebuild if the env variable changes
30-
println!("cargo:rerun-if-env-changed=SHERPA_ONNX_LIB_DIR");
54+
fn emit_static_link_directives(target_os: &str) {
55+
println!("cargo:warning=Static linking is enabled for sherpa-onnx");
56+
57+
for lib in SHERPA_ONNX_STATIC_LIBS {
58+
println!("cargo:rustc-link-lib=static={lib}");
59+
}
60+
61+
match target_os {
62+
"linux" => {
63+
println!("cargo:rustc-link-lib=dylib=stdc++");
64+
println!("cargo:rustc-link-lib=dylib=m");
65+
println!("cargo:rustc-link-lib=dylib=pthread");
66+
println!("cargo:rustc-link-lib=dylib=dl");
67+
}
68+
"macos" => {
69+
println!("cargo:rustc-link-lib=dylib=c++");
70+
println!("cargo:rustc-link-lib=framework=Foundation");
71+
}
72+
_ => {}
73+
}
3174
}

sherpa-onnx/rust/sherpa-onnx/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ include = [
1919
"LICENSE*",
2020
]
2121

22+
[features]
23+
default = []
24+
static = ["sherpa-onnx-sys/static"]
25+
2226
[dependencies]
2327
sherpa-onnx-sys = { path = "../sherpa-onnx-sys", version = "1.12.31" }
2428
serde = { version = "1.0", features = ["derive"] }

sherpa-onnx/rust/sherpa-onnx/src/lib.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,15 @@
2525
//! - download a prebuilt shared-library package from
2626
//! [GitHub releases](https://github.com/k2-fsa/sherpa-onnx/releases)
2727
//!
28+
//! By default this crate links against the shared libraries. To link against the
29+
//! static libraries instead, enable the `static` cargo feature:
30+
//!
31+
//! ```toml
32+
//! sherpa-onnx = { version = "1.12.31", features = ["static"] }
33+
//! ```
34+
//!
2835
//! In both cases, set `SHERPA_ONNX_LIB_DIR` to the directory that contains the
29-
//! sherpa-onnx shared libraries.
36+
//! sherpa-onnx libraries.
3037
//!
3138
//! Example download URLs for `v1.12.31`:
3239
//!
@@ -39,20 +46,37 @@
3946
//! - Windows x64:
4047
//! [sherpa-onnx-v1.12.31-win-x64-shared-MT-Release.tar.bz2](https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.12.31/sherpa-onnx-v1.12.31-win-x64-shared-MT-Release.tar.bz2)
4148
//!
49+
//! Static-library packages for the same release:
50+
//!
51+
//! - Linux x86_64:
52+
//! [sherpa-onnx-v1.12.31-linux-x64-static.tar.bz2](https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.12.31/sherpa-onnx-v1.12.31-linux-x64-static.tar.bz2)
53+
//! - Linux aarch64:
54+
//! [sherpa-onnx-v1.12.31-linux-aarch64-static.tar.bz2](https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.12.31/sherpa-onnx-v1.12.31-linux-aarch64-static.tar.bz2)
55+
//! - macOS:
56+
//! [sherpa-onnx-v1.12.31-osx-universal2-static.tar.bz2](https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.12.31/sherpa-onnx-v1.12.31-osx-universal2-static.tar.bz2)
57+
//! - Windows x64:
58+
//! [sherpa-onnx-v1.12.31-win-x64-static-MT-Release.tar.bz2](https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.12.31/sherpa-onnx-v1.12.31-win-x64-static-MT-Release.tar.bz2)
59+
//!
4260
//! ## Linux
4361
//!
4462
//! ```bash
4563
//! export SHERPA_ONNX_LIB_DIR=/path/to/sherpa-onnx/lib
4664
//! export RUSTFLAGS="-C link-arg=-Wl,-rpath,$SHERPA_ONNX_LIB_DIR"
4765
//! ```
4866
//!
67+
//! When using `features = ["static"]`, you do not need the `RUSTFLAGS` rpath
68+
//! setting above because the sherpa-onnx libraries are linked statically.
69+
//!
4970
//! ## macOS
5071
//!
5172
//! ```bash
5273
//! export SHERPA_ONNX_LIB_DIR=/path/to/sherpa-onnx/lib
5374
//! export RUSTFLAGS="-C link-arg=-Wl,-rpath,$SHERPA_ONNX_LIB_DIR"
5475
//! ```
5576
//!
77+
//! When using `features = ["static"]`, you do not need the `RUSTFLAGS` rpath
78+
//! setting above because the sherpa-onnx libraries are linked statically.
79+
//!
5680
//! ## Windows
5781
//!
5882
//! ```bash

0 commit comments

Comments
 (0)