Back to Nexa Sdk

Build

notes/build.md

0.3.167.8 KB
Original Source

Build

The CLI is built with Bazelisk (Bazel launcher); the SDK bridge and native plugins are built with CMake. Bazel fetches all CLI-side dependencies automatically.

Install Bazelisk:

  • Windows: winget install --id Bazel.Bazelisk
  • Linux: install bazelisk from your package manager

[!IMPORTANT] Build in this order. In local-SDK mode (the default) the CLI links against the SDK bridge at sdk/pkg-geniex/ — Bazel expects sdk/pkg-geniex/lib/geniex.dll (Windows) or sdk/pkg-geniex/lib/libgeniex.so (Linux) to already exist. So build and install the SDK (Build the SDK) first, then build the CLI (Build and run the CLI).

Windows prerequisites

  1. Enable Developer Mode: Settings → Privacy & Security → For developers.
  2. Grant Create symbolic links rights via gpedit.msc → Computer Configuration → Windows Settings → Security Settings → Local Policies → User Rights Assignment, or set HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\LocalAccountTokenFilterPolicy = 1 (DWORD).
  3. Enable Long paths: Settings → Privacy & Security → For developers.
  4. If symlink errors persist, comment out startup --windows_enable_symlinks in .bazelrc — but be aware this can break other SDK paths.

Native SDKs (for full Snapdragon build)

The arm64-windows-snapdragon-release preset requires:

  • Hexagon SDKHEXAGON_SDK_ROOT, HEXAGON_TOOLS_ROOT
  • OpenCL SDKOPENCL_SDK_ROOT (headers + OpenCL.lib; runtime ICD ships with the Snapdragon GPU driver)
  • Windows Driver Kit — provides inf2cat.exe
  • Self-signed HTP cert (.pfx) and Windows test-signing enabled — see run.md § Self-signed fallback for cert generation and test-signing setup

Build the SDK

Windows ARM64 (Snapdragon)

[!NOTE] The Hexagon toolchain has a 250-character path limit. Shorten the source path with subst before building:

powershell
subst G: C:\path\to\geniex
cd G:\sdk
powershell
cd sdk
cmake --preset arm64-windows-snapdragon-release -B build
cmake --build build -j
cmake --install build --prefix pkg-geniex

Linux (cross-compile from x86_64)

Build the SDK inside the derived Snapdragon Linux toolchain container — it extends ghcr.io/snapdragon-toolchain/arm64-linux with build-essential, ccache, rustup, the aarch64-unknown-linux-gnu Rust target, and the cc-rs cross-compiler symlinks baked in (see .github/docker/toolchain-linux.Dockerfile). Run from the repo root.

One-shot:

bash
docker run --rm -u $(id -u):$(id -g) \
    --volume $(pwd):/workspace \
    --workdir /workspace/sdk \
    -e CCACHE_DIR=/workspace/.ccache \
    --platform linux/amd64 \
    docker.io/qualcomm/geniex-toolchain-linux:v0.0.1 \
    bash -c 'cmake --preset arm64-linux-snapdragon-debug -B build-linux . \
      && cmake --build build-linux -j \
      && cmake --install build-linux --prefix pkg-geniex'

Interactive:

bash
docker run --rm -it -u $(id -u):$(id -g) \
    --volume $(pwd):/workspace \
    --workdir /workspace/sdk \
    -e CCACHE_DIR=/workspace/.ccache \
    --platform linux/amd64 \
    docker.io/qualcomm/geniex-toolchain-linux:v0.0.1 bash
# then, inside the container:
cmake --preset arm64-linux-snapdragon-debug -B build-linux .
cmake --build build-linux -j
cmake --install build-linux --prefix pkg-geniex

Android (cross-compile from Linux)

Build the SDK inside the derived Snapdragon Android toolchain container — it extends ghcr.io/snapdragon-toolchain/arm64-android with build-essential, ccache, rustup, and the aarch64-linux-android Rust target baked in (see .github/docker/toolchain-android.Dockerfile). Run from the repo root.

One-shot:

bash
docker run --rm -u $(id -u):$(id -g) \
    --volume $(pwd):/workspace \
    --workdir /workspace/sdk \
    -e CCACHE_DIR=/workspace/.ccache \
    --platform linux/amd64 \
    docker.io/qualcomm/geniex-toolchain-android:v0.0.1 \
    bash -c 'cmake --preset arm64-android-snapdragon-debug -B build-android . \
      && cmake --build build-android -j \
      && cmake --install build-android --prefix pkg-geniex'

Interactive:

bash
docker run --rm -it -u $(id -u):$(id -g) \
    --volume $(pwd):/workspace \
    --workdir /workspace/sdk \
    -e CCACHE_DIR=/workspace/.ccache \
    --platform linux/amd64 \
    docker.io/qualcomm/geniex-toolchain-android:v0.0.1 bash
# then, inside the container:
cmake --preset arm64-android-snapdragon-debug -B build-android .
cmake --build build-android -j
cmake --install build-android --prefix pkg-geniex

Deploy and smoke-test on device:

bash
adb push pkg-geniex /data/local/tmp/geniex
adb push Qwen3-0.6B-Q4_0.gguf /data/local/tmp/geniex/modelfiles/llama_cpp/
adb shell "cd /data/local/tmp/geniex && \
  LD_LIBRARY_PATH=./lib:./lib/llama_cpp \
  GENIEX_PLUGIN_PATH=./lib \
  ./bin/geniex_test_llm"

The Android demo app is no longer hosted in this repo — it lives in qualcomm/ai-hub-apps. Build the AAR here, then point the demo app at it.

Build and run the CLI

With the SDK built and installed into sdk/pkg-geniex/, build and run the CLI. Quick smoke test:

bash
bazelisk run //cli -- infer Qwen/Qwen3-0.6B-GGUF

//cli is a convenience alias for //cli/cmd/geniex:geniex. Both are used interchangeably in these docs.

Flags

Flags for bazelisk build and bazelisk run:

FlagMeaning
--//sdk:sdk_type={local,s3,bazel}SDK source. local (default) links against sdk/pkg-geniex; s3 and bazel are WIP.
--config={linux_arm64,windows_arm64}Cross-compile to the target platform (Go toolchain + CGO + oci_image base). sdk/pkg-geniex/ must match.

Development and release targets

Development targets:

  • bazelisk run //cli/release/linux:docker — build and load the Docker image for the Linux release.

Package release artifacts:

TargetOutput
bazelisk build //cli:artifactbazel-bin/cli/artifact.zip
bazelisk build //cli/release/windowsbazel-bin/cli/release/windows/geniex-cli-setup.exe
bazelisk build //cli/release/linuxbazel-bin/cli/release/linux/geniex-cli-docker.tar

Generated executable (for manual invocation): bazel-bin/cli/cmd/geniex/geniex_/, with runtime files under geniex.runfiles/_main.

Test coverage

Use bazelisk coverage instead of bazelisk test:

bash
bazelisk coverage //... --combined_report=lcov

Bazel's default --instrumentation_filter only covers packages that own a test target, so every Go package ships at least a placeholder package_test.go to keep itself in the denominator. Combined lcov lands at bazel-out/_coverage/_coverage_report.dat. Render it with genhtml bazel-out/_coverage/_coverage_report.dat -o coverage-html (gitignored), or summarize on the CLI with lcov --list <report>.

Python bindings

See bindings/python/README.md.