Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions .ci/scripts/test_wheel_package_qnn.sh
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ EOF
# ----------------------------
echo "=== Building Wheel Package ==="
source .ci/scripts/utils.sh

# Ensure QNN SDK is available so setup.py auto-detects it.
source backends/qualcomm/scripts/install_qnn_sdk.sh
install_qnn

# Make QNN SDK libraries available for runtime loading (e.g. libQnnHtp.so)
export LD_LIBRARY_PATH="${QNN_SDK_ROOT}/lib/x86_64-linux-clang/:${LD_LIBRARY_PATH:-}"

install_executorch
EXECUTORCH_BUILDING_WHEEL=1 python setup.py bdist_wheel
unset EXECUTORCH_BUILDING_WHEEL
Expand Down
13 changes: 13 additions & 0 deletions .ci/scripts/wheel/pre_build_script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,16 @@ fi
# able to see the installed torch package.

"${GITHUB_WORKSPACE}/${REPOSITORY}/install_requirements.sh" --example

# Download Qualcomm QNN SDK on Linux x86_64 so the wheel build can include the
# QNN backend. The SDK is large, so we download it here (outside CMake) rather
# than during cmake configure.
if [[ "$(uname -s)" == "Linux" && "$(uname -m)" == "x86_64" ]]; then
echo "Downloading Qualcomm QNN SDK..."
QNN_SDK_ROOT=$(python3 \
"${GITHUB_WORKSPACE}/${REPOSITORY}/backends/qualcomm/scripts/download_qnn_sdk.py" \
--print-sdk-path)
export QNN_SDK_ROOT
echo "QNN_SDK_ROOT=${QNN_SDK_ROOT}" >> "${GITHUB_ENV}"
echo "QNN SDK downloaded to ${QNN_SDK_ROOT}"
fi
15 changes: 15 additions & 0 deletions .ci/scripts/wheel/test_linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,25 @@
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.

import platform

import test_base
from examples.models import Backend, Model

if __name__ == "__main__":
# On Linux x86_64 the wheel is built with the Qualcomm backend.
# Verify that it was registered correctly.
if platform.system() == "Linux" and platform.machine() in ("x86_64", "amd64"):
from executorch.extension.pybindings.portable_lib import (
_get_registered_backend_names,
)

registered = _get_registered_backend_names()
assert (
"QnnBackend" in registered
), f"QnnBackend not found in registered backends: {registered}"
print("✓ QnnBackend is registered")

test_base.run_tests(
model_tests=[
test_base.ModelTest(
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/pull.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ jobs:
test-qnn-wheel-packages-linux:
name: test-qnn-wheel-packages-linux
uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
if: false
permissions:
id-token: write
contents: read
Expand Down
18 changes: 14 additions & 4 deletions backends/qualcomm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,19 @@ get_filename_component(
_common_include_directories "${EXECUTORCH_SOURCE_DIR}/.." ABSOLUTE
)

# We only download QNN SDK when we build pip wheel for ExecuTorch. Please don't
# change this code unless you know what you are doing.
if(EXECUTORCH_BUILD_WHEEL_DO_NOT_USE)
# If QNN_SDK_ROOT was not passed as a CMake variable, fall back to the
# environment variable. Prefer downloading the SDK *outside* of CMake (e.g. via
# backends/qualcomm/scripts/download_qnn_sdk.py) and passing the path in.
if(NOT DEFINED QNN_SDK_ROOT AND DEFINED ENV{QNN_SDK_ROOT})
set(QNN_SDK_ROOT
$ENV{QNN_SDK_ROOT}
CACHE PATH "Qualcomm SDK root directory" FORCE
)
endif()

# Last-resort fallback: download during cmake configure when building wheels and
# QNN_SDK_ROOT was not provided externally.
if(NOT DEFINED QNN_SDK_ROOT AND EXECUTORCH_BUILD_WHEEL_DO_NOT_USE)
set(_qnn_default_sdk_dir "${CMAKE_CURRENT_BINARY_DIR}/sdk/qnn")

if(EXISTS "${_qnn_default_sdk_dir}" AND EXISTS "${_qnn_default_sdk_dir}/lib")
Expand All @@ -35,7 +45,7 @@ if(EXECUTORCH_BUILD_WHEEL_DO_NOT_USE)
CACHE PATH "Qualcomm SDK root directory" FORCE
)
else()
message(STATUS "Downloading Qualcomm SDK")
message(STATUS "Downloading Qualcomm SDK (fallback)")
execute_process(
COMMAND
${PYTHON_EXECUTABLE}
Expand Down
8 changes: 7 additions & 1 deletion backends/qualcomm/utils/qnn_manager_lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,13 @@ def get_or_create_qnn_manager(
) -> PyQnnManager.QnnManager:
if backend_type not in self._registry:
qnn_manager = PyQnnManager.QnnManager(option)
qnn_manager.InitBackend()
err = qnn_manager.InitBackend()
if err.value != 0:
raise RuntimeError(
f"Failed to initialize QNN backend for {backend_type.name}. "
"Ensure QNN SDK libraries are available "
"(e.g. LD_LIBRARY_PATH includes $QNN_SDK_ROOT/lib/x86_64-linux-clang/)."
)
self._registry[backend_type] = qnn_manager
return self._registry[backend_type]

Expand Down
11 changes: 11 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,17 @@ def run(self): # noqa C901
):
cmake_configuration_args += ["-DEXECUTORCH_BUILD_CUDA=ON"]

# Check if QNN SDK is available (via QNN_SDK_ROOT env var), and if so,
# enable building the Qualcomm backend by default.
qnn_sdk_root = os.environ.get("QNN_SDK_ROOT", "").strip()
if qnn_sdk_root and install_utils.is_cmake_option_on(
cmake_configuration_args, "EXECUTORCH_BUILD_QNN", default=True
):
cmake_configuration_args += [
"-DEXECUTORCH_BUILD_QNN=ON",
f"-DQNN_SDK_ROOT={qnn_sdk_root}",
]

with Buck2EnvironmentFixer():
# Generate the cmake cache from scratch to ensure that the cache state
# is predictable.
Expand Down
Loading