Imported from jschwe/skills (
ohos-rust/SKILL.md). Install upstream withnpx skills add jschwe/skills --skill ohos-rust. Copyright stays with the author.
name: ohos-rust description: Set up cross-compilation of Rust binaries for OpenHarmony / OHOS / HarmonyOS using the standalone command-line SDK. Trigger when the user wants to build a Rust crate for an OHOS device, mentions targets like aarch64-unknown-linux-ohos / armv7-unknown-linux-ohos / x86_64-unknown-linux-ohos, hits linker errors building for OHOS, or asks how to push a Rust binary to a HarmonyOS device. crates: *
ohos-rust
Cross-compile Rust standalone binaries for OpenHarmony devices using the standalone command-line SDK and stable rustup targets.
The skill assumes:
- Output is a standalone binary (no NAPI / cdylib loaded by an ArkTS app).
- Rust is installed via rustup, stable channel — the
*-unknown-linux-ohostargets ship prebuilt. - The target device has root access via
hdc shellor signing is not required for running binaries.
Locate the SDK before doing anything else
Resolve $OHOS_SDK_NATIVE once, in this order — stop at the first hit.
OHOS_SDK_NATIVE is the authoritative variable for this skill; it points
at the native/ directory of the SDK. Env vars in steps 1–2 already
point at native/; the ones in step 3 point at an SDK root that
contains native/ (possibly nested under an api-level directory).
-
$OHOS_SDK_NATIVEset → use it directly. -
$OHOS_NDK_HOMEset → use it directly. -
SDK-root env vars, first hit wins:
$OHOS_SDK_HOME$OHOS_BASE_SDK_HOME$DEVECO_SDK_HOME(DevEco Studio layout — typically<root>/default/openharmony/<api>/native/; resolve through the intermediate dirs as needed)
For each: if
<root>/nativeexists, use it; otherwise pick the highest-numbered<api>/native(recursively, for the DevEco case). -
Bundled with a DevEco Studio installation:
<DevEcoStudioInstallationDir>/sdk/default/openharmony/native. Common install locations:- macOS:
/Applications/DevEco-Studio.app/Contents/sdk/default/openharmony/native - Windows:
C:\Program Files\Huawei\DevEco Studio\sdk\default\openharmony\native - Linux: DevEco Studio is not distributed for Linux — skip this step and ask the user for the SDK root.
- macOS:
-
Nothing matched → ask the user for the SDK root and offer to record as a memory.
Verify by checking that $OHOS_SDK_NATIVE/llvm/bin/clang and
$OHOS_SDK_NATIVE/sysroot both exist. If the SDK is api-level-versioned
(<sdk>/<api>/native/), pick the highest numeric <api> directory.
Picking the API level when a device is connected
If multiple API levels are installed under the SDK root and a target
device is reachable via hdc, prefer the SDK whose API level matches
the device. Query the device with:
hdc shell 'param get const.ohos.apiversion'
# → 12 (or 11, 14, …)
Then point $OHOS_SDK_NATIVE at the matching <api>/native/ directory.
Building against a newer SDK than the device runs is the usual cause of
undefined reference / missing-symbol errors at load time on the
device — the binary references libc / OHOS APIs that don't exist in the
device's older runtime.
If hdc list targets is empty, see the sandbox-visibility note in
@ohos-rust/resources/run-on-device.md before falling back to "highest
installed API level".
Add the required rustup targets
# Pick the ones you need for the project.
rustup target add aarch64-unknown-linux-ohos \
armv7-unknown-linux-ohos \
x86_64-unknown-linux-ohos
Stable channel only — no nightly needed. If rustup target add reports the
target as unknown, the local rustup is too old; rustup self update && rustup update stable first.
Wire up the linker
The OHOS SDK ships per-target clang wrapper scripts in
$OHOS_SDK_NATIVE/llvm/bin/, named after the Rust triple:
aarch64-unknown-linux-ohos-clang
armv7-unknown-linux-ohos-clang
x86_64-unknown-linux-ohos-clang
Each wrapper is a small shell script that invokes clang with the right
--target=, --sysroot=, and (for armv7) -march=/-mfloat-abi= baked
in. Pointing cargo at the wrapper means you don't have to spell any of
that out, and you don't have to remember that armv7-unknown-linux-ohos
maps to clang target arm-linux-ohos.
NDK="$OHOS_SDK_NATIVE"
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_OHOS_LINKER="$NDK/llvm/bin/aarch64-unknown-linux-ohos-clang"
export CARGO_TARGET_ARMV7_UNKNOWN_LINUX_OHOS_LINKER="$NDK/llvm/bin/armv7-unknown-linux-ohos-clang"
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_OHOS_LINKER="$NDK/llvm/bin/x86_64-unknown-linux-ohos-clang"
Then build:
cargo build --release --target aarch64-unknown-linux-ohos
The skill deliberately uses env vars only rather than touching
~/.cargo/config.toml, so nothing about a particular project's setup
leaks into the user's global cargo config.
Windows hosts: the wrappers above are POSIX shell scripts and won't
execute under cmd.exe / PowerShell. On Windows, set up the linker via
plain env vars (PowerShell-syntax) plus rustflags carrying --target=
and --sysroot= directly — see @ohos-rust/resources/windows-setup.md.
Bypassing the wrappers (advanced, e.g. when overriding the sysroot) requires the Rust→clang triple mapping the wrappers normally hide:
| Rust triple | clang --target= |
|---|---|
aarch64-unknown-linux-ohos |
aarch64-linux-ohos |
armv7-unknown-linux-ohos |
arm-linux-ohos (+ -march=armv7-a -mfloat-abi=softfp) |
x86_64-unknown-linux-ohos |
x86_64-linux-ohos |
For the long-form walkthrough — including SDK layout, picking the right sysroot when there are multiple, common linker errors and their fixes — see @ohos-rust/resources/setup.md.
Running cargo test / cargo bench / cargo run on a device
Cargo's target.<triple>.runner mechanism lets you offload the
"push to device → execute → return exit code + stdout" dance to a
helper. ohos-test-runner
is a purpose-built runner for OpenHarmony devices — it wraps hdc and
correctly propagates the device-side exit code, so cargo test actually
fails when a test fails on the device.
cargo install --locked ohos-test-runner
# Wire it up as the runner for whichever target(s) you build for.
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_OHOS_RUNNER=ohos-test-runner
export CARGO_TARGET_ARMV7_UNKNOWN_LINUX_OHOS_RUNNER=ohos-test-runner
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_OHOS_RUNNER=ohos-test-runner
cargo test --target aarch64-unknown-linux-ohos
cargo bench --target aarch64-unknown-linux-ohos
cargo run --target aarch64-unknown-linux-ohos --bin myapp
This composes with the linker setup above: the *_LINKER env var
governs the build, the *_RUNNER env var governs execution. Both are
per-target, so leaving them set for OHOS triples doesn't affect host
cargo test.
If hdc list targets is empty when the runner tries to execute, see the
sandbox-visibility note in @ohos-rust/resources/run-on-device.md — the
runner will hit the same wall as a hand-rolled hdc shell invocation.
Manual pushing and running on a device
Full hdc recipe (incl. /data/local/tmp permissions
and capturing the device-side exit code, since hdc shell swallows it):
@ohos-rust/resources/run-on-device.md.
The hdc skill covers the device-connector itself — particularly the silent-exit-code gotcha that bites any "build → push → run → check $?" script.
Crates with C dependencies (cc-rs, bindgen, *-sys)
When a dependency builds C/C++ via cc-rs or generates bindings via
bindgen, the Rust linker env vars above are not enough — those crates
read CC_<triple>, CXX_<triple>, AR_<triple>, and
BINDGEN_EXTRA_CLANG_ARGS_<triple> directly. Setup and a worked example:
@ohos-rust/resources/c-deps.md.
OHOS docs
Authoritative OHOS docs are available at gitcode.com/openharmony/docs.