Skip to content

Latest commit

 

History

History
258 lines (184 loc) · 12.7 KB

build.md

File metadata and controls

258 lines (184 loc) · 12.7 KB

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

Run every command below from PowerShell, not cmd.exe, Git Bash, or WSL. The cmd /c '"...\VsDevCmd.bat" ... && ...' commands below rely on PowerShell quoting (outer single quotes, inner double quotes).

Symlink support (Bazel and CMake)

Also needed by the SDK's CMake configure: a vendored submodule (sentencepiece, under third-party/geniex-qairt) creates a symlink and fails with "A required privilege is not held by the client" without it.

  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.
  3. Enable Long paths: Settings → Privacy & Security → For developers.
  4. Sign out and back in (or reboot) after enabling Developer Mode; open sessions keep the old token. Verify in a new terminal with whoami /priv | findstr SymbolicLink (no output means it hasn't taken effect).
  5. If symlink errors persist, comment out startup --windows_enable_symlinks in .bazelrc — but be aware this can break other SDK paths.

Corporate/domain-joined machines: Group Policy can block step 4 even after a reboot. In that case, run the build (cmake --preset ... and bazelisk ...) from an elevated terminal ("Run as Administrator"); local Administrators get SeCreateSymbolicLinkPrivilege once elevated. whoami /groups showing BUILTIN\Administrators as "Group used for deny only" confirms you're an admin filtered by UAC.

Toolchain (SDK build)

The SDK's Rust model manager (sdk/model-manager) is built by cargo from CMake, and its build scripts compile C code, so a native Windows ARM64 build also needs:

  • clang — used by cc-rs and the Snapdragon presets. Install the ARM64 MSYS2 build (the plain LLVM installer has no aarch64-hosted clang.exe):

    winget install --id MSYS2.MSYS2
    C:\msys64\usr\bin\pacman.exe -S --noconfirm mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-lld

    Then add C:\msys64\clangarm64\bin to PATH (ahead of any other clang.exe, e.g. one bundled with Visual Studio).

  • cargo and the Windows ARM64 target:

    winget install --id Rustlang.Rustup
    rustup target add aarch64-pc-windows-msvc
  • Visual Studio — for the MSVC libs/linker only. Install the "Desktop development with C++" workload with the ARM64 build tools component.

clang targets the MSVC ABI, so it needs MSVC's INCLUDE/LIB and lld-link on PATH. Without them, CMake's compiler check fails with lld-link: error: could not open 'oldnames.lib'. Configure and build from a Visual Studio Developer environment for ARM64 (see Build the SDK).

Native SDKs (for full Snapdragon build)

The arm64-windows-snapdragon-release preset requires:

  • Hexagon SDK — HEXAGON_SDK_ROOT, HEXAGON_TOOLS_ROOT
  • OpenCL SDK — OPENCL_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:

subst G: C:\path\to\geniex
cd G:\sdk

Run from a "Developer PowerShell for VS" (ARM64), elevated if the symlink privilege won't take effect (see Symlink support):

cd sdk
cmake --preset arm64-windows-snapdragon-release -B build
cmake --build build -j
cmake --install build --prefix pkg-geniex

Or from a plain PowerShell, chaining through VsDevCmd.bat (adjust the path/edition for your install):

cmd /c '"C:\Program Files\Microsoft Visual Studio\<edition>\Common7\Tools\VsDevCmd.bat" -arch=arm64 -host_arch=x64 && 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:

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.2.0 \
    bash -c 'cmake --preset arm64-linux-snapdragon-debug -B build-linux . \
      && cmake --build build-linux -j \
      && cmake --install build-linux --prefix pkg-geniex'

Interactive:

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.2.0 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

CPU-only variant (baseline armv8.0-a)

NPU-less Dragonwing IoT boards (unoq) are baseline ARMv8.0 and trap on the LSE atomics the snapdragon presets inline (see #1217). Swap the preset for arm64-linux-cpu-{debug,release} — same container, plain -march=armv8-a, and CPU-only (no QAIRT, Hexagon, or OpenCL):

cmake --preset arm64-linux-cpu-debug -B build-linux-cpu .
cmake --build build-linux-cpu -j
cmake --install build-linux-cpu --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:

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.2.0 \
    bash -c 'cmake --preset arm64-android-snapdragon-debug -B build-android . \
      && cmake --build build-android -j \
      && cmake --install build-android --prefix pkg-geniex'

Interactive:

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.2.0 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

CPU-only variant (baseline armv8.0-a)

minSdk is 27, so the AAR still targets phones that predate the armv8.7 ISA the snapdragon presets bake in and trap at startup (see #1217). Swap the preset for arm64-android-cpu-{debug,release} — same container, plain -march=armv8-a, and CPU-only (no QAIRT, Hexagon, or OpenCL):

cmake --preset arm64-android-cpu-debug -B build-android-cpu .
cmake --build build-android-cpu -j
cmake --install build-android-cpu --prefix pkg-geniex

bindings/android needs no changes: assembleRelease packages whatever sdk/pkg-geniex/lib/ holds, so the same gradle project yields the CPU-only AAR that CI publishes as geniex-android-aar-cpu-<tag>.aar.

Deploy and smoke-test on device:

adb push pkg-geniex /data/local/tmp/geniex
adb push Qwen3-0.6B-Q4_0.gguf /data/local/tmp/geniex/
adb shell "cd /data/local/tmp/geniex && \
  LD_LIBRARY_PATH=./lib:./lib/llama_cpp \
  ADSP_LIBRARY_PATH=./lib/llama_cpp \
  GENIEX_PLUGIN_PATH=./lib \
  ./bin/geniex-bench --plugin llama_cpp --device npu -m Qwen3-0.6B-Q4_0.gguf"

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.

Building against a different QAIRT SDK's headers

The qairt plugin currently compiles against QAIRT 2.50.40.260831 QNN C API 2.39 headers vendored in third-party/geniex-qairt/qnn-api/include/; its independent runtime floor remains QNN C API 2.27 (kMinApiMinor = 27). Provider-query checks do not establish end-to-end compatibility with every runtime version. To use another set (e.g. an internal QAIRT checkout), pass -DQAIRT_QNN_HEADERS=... to any preset above:

cmake --preset arm64-windows-snapdragon-release -B build -DQAIRT_QNN_HEADERS=C:\path\to\qairt\include\QNN

See the plugin README for the expected directory shape and caveats.

Build and run the CLI

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

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:

Flag Meaning
--//sdk:sdk_type={local,s3,bazel} SDK source. Only local (default, links against sdk/pkg-geniex) works; s3 and bazel are not wired up yet.
--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:

Target Output
bazelisk build //cli:artifact bazel-bin/cli/artifact.zip
bazelisk build //cli/release/windows bazel-bin/cli/release/windows/geniex-cli-setup.exe
bazelisk build //cli/release/linux bazel-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:

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>. lcov 2.x honours LCOV_EXCL_* markers with --filter region --rc c_file_extensions=c,h,cc,cpp,go; lcov 1.x (msys2, older distros) ignores them, so render on Linux with 2.x for a clean report.

Python bindings

See bindings/python/README.md.