Skip to content
Merged
Show file tree
Hide file tree
Changes from 67 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
9dcfc9f
changes
cccclai Aug 29, 2025
9ed62ec
resolve conflict
cccclai Aug 29, 2025
e4dd734
update
cccclai Aug 29, 2025
e6a9631
fix error and add libcxx
cccclai Sep 1, 2025
9306c6f
update
cccclai Sep 1, 2025
98ab9f2
short log
cccclai Sep 1, 2025
e7222a6
debug
cccclai Sep 1, 2025
af8cd52
debug
cccclai Sep 1, 2025
c3dd8d9
debug
cccclai Sep 1, 2025
4cf36c7
debug
cccclai Sep 2, 2025
c940f70
debug
cccclai Sep 2, 2025
9ed6f52
update
cccclai Sep 2, 2025
00f9f3f
update
cccclai Sep 2, 2025
6fe92d5
update
cccclai Sep 2, 2025
99dc584
update
cccclai Sep 2, 2025
4a70ba0
update
cccclai Sep 2, 2025
3fe29fe
update
cccclai Sep 2, 2025
8d01449
debug
cccclai Sep 2, 2025
f2f6bf9
debug
cccclai Sep 2, 2025
fa50f10
debug
cccclai Sep 3, 2025
102f4ec
debug
cccclai Sep 3, 2025
b6b095b
debug
cccclai Sep 3, 2025
9c403ee
debug
cccclai Sep 3, 2025
f151198
optimize
cccclai Sep 3, 2025
7afa46f
fix the path
cccclai Sep 3, 2025
8b5ca01
restore
cccclai Sep 3, 2025
f68e12c
clean up path
cccclai Sep 3, 2025
4a08f84
test
cccclai Sep 3, 2025
00ce3f3
try init
cccclai Sep 4, 2025
aa46088
fix
cccclai Sep 4, 2025
8617b0b
debug
cccclai Sep 4, 2025
26be4f8
add EXECUTORCH_BUILDING_WHEEL=1
cccclai Sep 4, 2025
ad68643
add logs
cccclai Sep 4, 2025
7336e56
fix type
cccclai Sep 4, 2025
6271fe4
add check to make sure no qualcomm/sdk
cccclai Sep 4, 2025
e6a08a0
clena up
cccclai Sep 4, 2025
11dd161
restore ci jobs
cccclai Sep 4, 2025
76a587e
restore more in setup
cccclai Sep 4, 2025
425d368
use raw docker image
cccclai Sep 4, 2025
6487a66
clean up logs in init
cccclai Sep 4, 2025
ab2f790
use qnn 2.37
cccclai Sep 4, 2025
9db77e2
address some feedback and disable some jobs for now
cccclai Sep 5, 2025
c2c3362
rebase
cccclai Sep 8, 2025
c91587f
add different pyhton version and virtual env
cccclai Sep 8, 2025
4e645fc
check qnn graph
cccclai Sep 8, 2025
a5f29a3
fix ci
cccclai Sep 8, 2025
dfa6c2b
fix ci
cccclai Sep 8, 2025
9dc6c4c
fix ci
cccclai Sep 8, 2025
4885aa0
stop download/load if exist
cccclai Sep 9, 2025
addb28c
seperate qnn sdk and libcxx - both logic are the same, pass if it exi…
cccclai Sep 9, 2025
942d9dd
fix install executorch
cccclai Sep 9, 2025
5745bc3
force install torch cpu
cccclai Sep 9, 2025
b2c847c
install executorch seems work
cccclai Sep 9, 2025
e0728d1
fix pull.yml after rebase
cccclai Sep 9, 2025
bd0ae6a
add different py versions back
cccclai Sep 9, 2025
8fe177e
temp for sdk and fix libcxx
cccclai Sep 9, 2025
457b17b
avoid load twice
cccclai Sep 10, 2025
465e5bd
avoid load twice
cccclai Sep 10, 2025
dc2ed74
remove debug code
cccclai Sep 10, 2025
8addeda
remove unused func
cccclai Sep 10, 2025
f0d52bb
fix python 2.11 2.12
cccclai Sep 10, 2025
88fba9c
rename the script.py and remove hardcoded python version in the test …
cccclai Sep 10, 2025
bd94001
remove tqdm, use boolean return and recover trunk/pull jobs
cccclai Sep 10, 2025
2bb5c42
remove standalone install_qnn_sdk func call
cccclai Sep 10, 2025
b76c374
more recover and replace print with logging
cccclai Sep 10, 2025
92cc32f
more print to logging
cccclai Sep 10, 2025
ec23d13
fix ci
cccclai Sep 10, 2025
e928e00
cleanup
cccclai Sep 10, 2025
396b21d
fix lint
cccclai Sep 10, 2025
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
202 changes: 202 additions & 0 deletions .ci/scripts/test_wheel_package_qnn.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
#!/bin/bash
# === CI Wheel Build & Test Script ===

# Exit immediately on error, print each command, and capture all output to build.log
set -e
set -x
exec > >(tee -i build.log) 2>&1

# Save repo root
REPO_ROOT=$(pwd)

# ----------------------------
# Dynamically create script_qnn_wheel_test.py
# ----------------------------
cat > "/tmp/script_qnn_wheel_test.py" << 'EOF'
# pyre-ignore-all-errors
import argparse

import torch
from executorch.backends.qualcomm.quantizer.quantizer import QnnQuantizer
from executorch.backends.qualcomm.utils.utils import (
generate_htp_compiler_spec,
generate_qnn_executorch_compiler_spec,
get_soc_to_chipset_map,
to_edge_transform_and_lower_to_qnn,
)
from executorch.exir.backend.utils import format_delegated_graph
from executorch.examples.models.model_factory import EagerModelFactory
from executorch.exir.capture._config import ExecutorchBackendConfig
from executorch.extension.export_util.utils import save_pte_program
from torchao.quantization.pt2e.quantize_pt2e import convert_pt2e, prepare_pt2e, prepare_qat_pt2e

def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("-f", "--output_folder", type=str, default="", help="The folder to store the exported program")
parser.add_argument("--soc", type=str, default="SM8650", help="Specify the SoC model.")
parser.add_argument("-q", "--quantization", choices=["ptq", "qat"], help="Run post-traininig quantization.")
args = parser.parse_args()

class LinearModule(torch.nn.Module):
def __init__(self):
super().__init__()
self.linear = torch.nn.Linear(3, 3)
def forward(self, arg):
return self.linear(arg)
def get_example_inputs(self):
return (torch.randn(3, 3),)

model = LinearModule()
example_inputs = model.get_example_inputs()

if args.quantization:
quantizer = QnnQuantizer()
m = torch.export.export(model.eval(), example_inputs, strict=True).module()
if args.quantization == "qat":
m = prepare_qat_pt2e(m, quantizer)
m(*example_inputs)
elif args.quantization == "ptq":
m = prepare_pt2e(m, quantizer)
m(*example_inputs)
m = convert_pt2e(m)
else:
m = model

use_fp16 = True if args.quantization is None else False
backend_options = generate_htp_compiler_spec(use_fp16=use_fp16)
compile_spec = generate_qnn_executorch_compiler_spec(
soc_model=get_soc_to_chipset_map()[args.soc],
backend_options=backend_options,
)
delegated_program = to_edge_transform_and_lower_to_qnn(m, example_inputs, compile_spec)
output_graph = format_delegated_graph(delegated_program.exported_program().graph_module)
# Ensure QnnBackend is in the output graph
assert "QnnBackend" in output_graph
executorch_program = delegated_program.to_executorch(
config=ExecutorchBackendConfig(extract_delegate_segments=False)
)
save_pte_program(executorch_program, "linear", args.output_folder)

if __name__ == "__main__":
main()
EOF

# ----------------------------
# Wheel build and .so checks
# ----------------------------
echo "=== Building Wheel Package ==="
# pip install torch=="2.9.0.dev20250801" --index-url "https://download.pytorch.org/whl/nightly/cpu"
source .ci/scripts/utils.sh
install_executorch
EXECUTORCH_BUILDING_WHEEL=1 python setup.py bdist_wheel
unset EXECUTORCH_BUILDING_WHEEL

WHEEL_FILE=$(ls dist/*.whl | head -n 1)
echo "Found wheel: $WHEEL_FILE"

PYTHON_VERSION=$1
# ----------------------------
# Check wheel does NOT contain qualcomm/sdk
# ----------------------------
echo "Checking wheel does not contain qualcomm/sdk..."
SDK_FILES=$(unzip -l "$WHEEL_FILE" | awk '{print $4}' | grep "executorch/backends/qualcomm/sdk" || true)
if [ -n "$SDK_FILES" ]; then
echo "ERROR: Wheel package contains unexpected qualcomm/sdk files:"
echo "$SDK_FILES"
exit 1
else
echo "OK: No qualcomm/sdk files found in wheel"
fi

# ----------------------------
# Check .so files in the wheel
# ----------------------------
echo "Checking for .so files inside the wheel..."
WHEEL_SO_FILES=$(unzip -l "$WHEEL_FILE" | awk '{print $4}' | grep "executorch/backends/qualcomm/python" || true)
if [ -z "$WHEEL_SO_FILES" ]; then
echo "ERROR: No .so files found in wheel under executorch/backends/qualcomm/python"
exit 1
else
echo "Wheel contains the following .so files:"
echo "$WHEEL_SO_FILES"
fi

# ----------------------------
# Helpers
# ----------------------------
get_site_packages_dir () {
local PYBIN="$1"
"$PYBIN" - <<'PY'
import sysconfig, sys
print(sysconfig.get_paths().get("purelib") or sysconfig.get_paths().get("platlib"))
PY
}

run_core_tests () {
local PYBIN="$1" # path to python
local PIPBIN="$2" # path to pip
local LABEL="$3" # label to print (conda/venv)

echo "=== [$LABEL] Installing wheel & deps ==="
"$PIPBIN" install --upgrade pip
"$PIPBIN" install "$WHEEL_FILE"
"$PIPBIN" install torch=="2.9.0.dev20250801" --index-url "https://download.pytorch.org/whl/nightly/cpu"
"$PIPBIN" install --pre torchao --index-url "https://download.pytorch.org/whl/nightly/cpu"

echo "=== [$LABEL] Import smoke tests ==="
"$PYBIN" -c "import executorch; print('executorch imported successfully')"
"$PYBIN" -c "import executorch.backends.qualcomm; print('executorch.backends.qualcomm imported successfully')"

echo "=== [$LABEL] List installed executorch/backends/qualcomm/python ==="
local SITE_DIR
SITE_DIR="$(get_site_packages_dir "$PYBIN")"
local SO_DIR="$SITE_DIR/executorch/backends/qualcomm/python"
ls -l "$SO_DIR" || echo "Folder does not exist!"

echo "=== [$LABEL] Run export script to generate linear.pte ==="
(cd "$REPO_ROOT" && "$PYBIN" "/tmp/script_qnn_wheel_test.py")

if [ -f "$REPO_ROOT/linear.pte" ]; then
echo "[$LABEL] Model file linear.pte successfully created"
else
echo "ERROR: [$LABEL] Model file linear.pte was not created"
exit 1
fi
}

# ----------------------------
# Conda environment setup & tests
# ----------------------------
echo "=== Testing in Conda env ==="
TEMP_ENV_DIR=$(mktemp -d)
echo "Using temporary directory for conda: $TEMP_ENV_DIR"
conda create -y -p "$TEMP_ENV_DIR/env" python=$PYTHON_VERSION
# derive python/pip paths inside the conda env
CONDA_PY="$TEMP_ENV_DIR/env/bin/python"
CONDA_PIP="$TEMP_ENV_DIR/env/bin/pip"
# Some images require conda run; keep pip/python direct to simplify path math
run_core_tests "$CONDA_PY" "$CONDA_PIP" "conda"

# Cleanup conda env
conda env remove -p "$TEMP_ENV_DIR/env" -y || true
rm -rf "$TEMP_ENV_DIR"

# ----------------------------
# Python venv setup & tests
# ----------------------------
echo "=== Testing in Python venv ==="
TEMP_VENV_DIR=$(mktemp -d)
echo "Using temporary directory for venv: $TEMP_VENV_DIR"
python3 -m venv "$TEMP_VENV_DIR/venv"
VENV_PY="$TEMP_VENV_DIR/venv/bin/python"
VENV_PIP="$TEMP_VENV_DIR/venv/bin/pip"

# Ensure venv has wheel/build basics if needed
"$VENV_PIP" install --upgrade pip

run_core_tests "$VENV_PY" "$VENV_PIP" "venv"

# Cleanup venv
rm -rf "$TEMP_VENV_DIR"

echo "=== All tests completed! ==="
27 changes: 27 additions & 0 deletions .github/workflows/pull.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,33 @@ concurrency:
cancel-in-progress: true

jobs:
test-qnn-wheel-packages-linux:
name: test-qnn-wheel-packages-linux
uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
permissions:
id-token: write
contents: read
strategy:
fail-fast: false
matrix:
python-version: [ "3.10", "3.11", "3.12" ]
with:
runner: linux.2xlarge
docker-image: ci-image:executorch-ubuntu-22.04-qnn-sdk
submodules: 'recursive'
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
timeout: 180
script: |
# The generic Linux job chooses to use base env, not the one setup by the image
CONDA_ENV=$(conda env list --json | jq -r ".envs | .[-1]")
conda activate "${CONDA_ENV}"

# Create a clean env for each python version
conda create -y -n test_env_${{ matrix.python-version }} python=${{ matrix.python-version }}
conda activate test_env_${{ matrix.python-version }}

PYTHON_EXECUTABLE=python bash .ci/scripts/test_wheel_package_qnn.sh "${{ matrix.python-version }}"

test-setup-linux-gcc:
name: test-setup-linux-gcc
uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ cmake-android-out/
cmake-ios-out/
cmake-out*
cmake-out-android/
build-android/
build-x86/
dist/
ethos-u-scratch/
executorch.egg-info
Expand Down
13 changes: 13 additions & 0 deletions backends/qualcomm/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import os

from .scripts.download_qnn_sdk import install_qnn_sdk


env_flag = os.getenv("EXECUTORCH_BUILDING_WHEEL", "0").lower()
Copy link
Contributor

Choose a reason for hiding this comment

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

Gate on another flat like BUILDING_WHEEL_FOR_LINUX or something similar

# If users have preinstalled QNN_SDK_ROOT, we will use it.
qnn_sdk_root_flag = os.getenv("QNN_SDK_ROOT", None)
if not env_flag in ("1", "true", "yes") and not qnn_sdk_root_flag:
ok = install_qnn_sdk()

if not ok:
Copy link
Contributor

Choose a reason for hiding this comment

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

On windows this will always be not ok. So you want to raise this error only if we are linux.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oh I see what you meant, okay let me add it

raise RuntimeError("Failed to install QNN SDK. Please check the logs above.")
7 changes: 5 additions & 2 deletions backends/qualcomm/runtime/backends/QnnImplementation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree.
*/
#include <executorch/backends/qualcomm/runtime/backends/QnnImplementation.h>

#include "QnnInterface.h"
namespace executorch {
namespace backends {
Expand Down Expand Up @@ -52,7 +51,11 @@ Error QnnImplementation::StartBackend(
const QnnSaver_Config_t** saver_config) {
Qnn_ErrorHandle_t error = QNN_SUCCESS;
void* lib_handle = nullptr;
lib_handle = dlopen(lib_path.c_str(), RTLD_NOW | RTLD_GLOBAL);
// If the library is already loaded, return the handle.
lib_handle = dlopen(lib_path.c_str(), RTLD_NOW | RTLD_NOLOAD);
if (!lib_handle) {
lib_handle = dlopen(lib_path.c_str(), RTLD_NOW | RTLD_GLOBAL);
}
if (lib_handle == nullptr) {
QNN_EXECUTORCH_LOG_ERROR(
"Cannot Open QNN library %s, with error: %s",
Expand Down
1 change: 1 addition & 0 deletions backends/qualcomm/scripts/build.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#!/usr/bin/env bash
# Copyright (c) Qualcomm Innovation Center, Inc.
# All rights reserved
#
Expand Down
Loading
Loading