Improve QNN backend build-from-source user experience#17990
Improve QNN backend build-from-source user experience#17990abhinaykukkadapu wants to merge 6 commits intogh/abhinaykukkadapu/17/basefrom
Conversation
- Remove EXECUTORCH_BUILD_WHEEL_DO_NOT_USE gate in CMakeLists.txt so QNN SDK auto-downloads for all build paths (editable installs, build.sh), not just wheel builds - Use shared cache dir (~/.cache/executorch/qnn/) instead of cmake build dir, so SDK survives build dir cleans and is shared across all build flows - Remove hard QNN_SDK_ROOT requirement from build.sh — cmake handles auto-download during configure when SDK is not set - Auto-download Android NDK in build.sh via install_qnn_sdk.sh when ANDROID_NDK_ROOT is not set - Fix PYTHON_EXECUTABLE default in build.sh (was checked before being used, with a buggy test that never triggered) - Default EXECUTORCH_BUILD_QNN to ON on Linux x86 in pybind preset, matching wheel behavior so editable installs build QNN out of the box - Redirect download_qnn_sdk.py progress output to stderr when --print-sdk-path is used, so cmake can capture the path cleanly [ghstack-poisoned]
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/17990
Note: Links to docs will display an error until the docs builds have been completed. ❌ 1 New Failure, 3 Unrelated FailuresAs of commit 716c04b with merge base 043a3a1 ( NEW FAILURE - The following job has failed:
BROKEN TRUNK - The following jobs failed but were present on the merge base:👉 Rebase onto the `viable/strict` branch to avoid these failures
This comment was automatically generated by Dr. CI and updates every 15 minutes. |
- Remove EXECUTORCH_BUILD_WHEEL_DO_NOT_USE gate in CMakeLists.txt so QNN SDK auto-downloads for all build paths (editable installs, build.sh), not just wheel builds - Use shared cache dir (~/.cache/executorch/qnn/) instead of cmake build dir, so SDK survives build dir cleans and is shared across all build flows - Remove hard QNN_SDK_ROOT requirement from build.sh — cmake handles auto-download during configure when SDK is not set - Auto-download Android NDK in build.sh via install_qnn_sdk.sh when ANDROID_NDK_ROOT is not set - Fix PYTHON_EXECUTABLE default in build.sh (was checked before being used, with a buggy test that never triggered) - Default EXECUTORCH_BUILD_QNN to ON on Linux x86 in pybind preset, matching wheel behavior so editable installs build QNN out of the box - Redirect download_qnn_sdk.py progress output to stderr when --print-sdk-path is used, so cmake can capture the path cleanly ghstack-source-id: 4cb0e5f Pull Request resolved: #17990
This PR needs a
|
- Remove EXECUTORCH_BUILD_WHEEL_DO_NOT_USE gate in CMakeLists.txt so QNN SDK auto-downloads for all build paths (editable installs, build.sh), not just wheel builds - Use shared cache dir (~/.cache/executorch/qnn/) instead of cmake build dir, so SDK survives build dir cleans and is shared across all build flows - Remove hard QNN_SDK_ROOT requirement from build.sh — cmake handles auto-download during configure when SDK is not set - Auto-download Android NDK in build.sh via install_qnn_sdk.sh when ANDROID_NDK_ROOT is not set - Fix PYTHON_EXECUTABLE default in build.sh (was checked before being used, with a buggy test that never triggered) - Default EXECUTORCH_BUILD_QNN to ON on Linux x86 in pybind preset, matching wheel behavior so editable installs build QNN out of the box - Redirect download_qnn_sdk.py progress output to stderr when --print-sdk-path is used, so cmake can capture the path cleanly [ghstack-poisoned]
- Remove EXECUTORCH_BUILD_WHEEL_DO_NOT_USE gate in CMakeLists.txt so QNN SDK auto-downloads for all build paths (editable installs, build.sh), not just wheel builds - Use shared cache dir (~/.cache/executorch/qnn/) instead of cmake build dir, so SDK survives build dir cleans and is shared across all build flows - Remove hard QNN_SDK_ROOT requirement from build.sh — cmake handles auto-download during configure when SDK is not set - Auto-download Android NDK in build.sh via install_qnn_sdk.sh when ANDROID_NDK_ROOT is not set - Fix PYTHON_EXECUTABLE default in build.sh (was checked before being used, with a buggy test that never triggered) - Default EXECUTORCH_BUILD_QNN to ON on Linux x86 in pybind preset, matching wheel behavior so editable installs build QNN out of the box - Redirect download_qnn_sdk.py progress output to stderr when --print-sdk-path is used, so cmake can capture the path cleanly ghstack-source-id: d3980d2 Pull Request resolved: #17990
- Remove EXECUTORCH_BUILD_WHEEL_DO_NOT_USE gate in CMakeLists.txt so QNN SDK auto-downloads for all build paths (editable installs, build.sh), not just wheel builds - Use shared cache dir (~/.cache/executorch/qnn/) instead of cmake build dir, so SDK survives build dir cleans and is shared across all build flows - Remove hard QNN_SDK_ROOT requirement from build.sh — cmake handles auto-download during configure when SDK is not set - Auto-download Android NDK in build.sh via install_qnn_sdk.sh when ANDROID_NDK_ROOT is not set - Fix PYTHON_EXECUTABLE default in build.sh (was checked before being used, with a buggy test that never triggered) - Default EXECUTORCH_BUILD_QNN to ON on Linux x86 in pybind preset, matching wheel behavior so editable installs build QNN out of the box - Redirect download_qnn_sdk.py progress output to stderr when --print-sdk-path is used, so cmake can capture the path cleanly [ghstack-poisoned]
- Remove EXECUTORCH_BUILD_WHEEL_DO_NOT_USE gate in CMakeLists.txt so QNN SDK auto-downloads for all build paths (editable installs, build.sh), not just wheel builds - Use shared cache dir (~/.cache/executorch/qnn/) instead of cmake build dir, so SDK survives build dir cleans and is shared across all build flows - Remove hard QNN_SDK_ROOT requirement from build.sh — cmake handles auto-download during configure when SDK is not set - Auto-download Android NDK in build.sh via install_qnn_sdk.sh when ANDROID_NDK_ROOT is not set - Fix PYTHON_EXECUTABLE default in build.sh (was checked before being used, with a buggy test that never triggered) - Default EXECUTORCH_BUILD_QNN to ON on Linux x86 in pybind preset, matching wheel behavior so editable installs build QNN out of the box - Redirect download_qnn_sdk.py progress output to stderr when --print-sdk-path is used, so cmake can capture the path cleanly ghstack-source-id: 6208f6d Pull Request resolved: #17990
|
Hi @cccclai, @haowhsu-quic, @shewu-quic, any thoughts or improvements you have in mind to improve user experience of ET- Qualcomm. Please see the userflows in both the PRs. Thanks! |
|
Hi @abhinaykukkadapu, |
Thanks, so, right now we set the |
- Remove EXECUTORCH_BUILD_WHEEL_DO_NOT_USE gate in CMakeLists.txt so QNN SDK auto-downloads for all build paths (editable installs, build.sh), not just wheel builds - Use shared cache dir (~/.cache/executorch/qnn/) instead of cmake build dir, so SDK survives build dir cleans and is shared across all build flows - Remove hard QNN_SDK_ROOT requirement from build.sh — cmake handles auto-download during configure when SDK is not set - Auto-download Android NDK in build.sh via install_qnn_sdk.sh when ANDROID_NDK_ROOT is not set - Fix PYTHON_EXECUTABLE default in build.sh (was checked before being used, with a buggy test that never triggered) - Default EXECUTORCH_BUILD_QNN to ON on Linux x86 in pybind preset, matching wheel behavior so editable installs build QNN out of the box - Redirect download_qnn_sdk.py progress output to stderr when --print-sdk-path is used, so cmake can capture the path cleanly ## Test Plan/ User journeys Verified end-to-end on Linux x86_64 with QNN SDK v2.37.0.250724. ### AOT user: `pip install executorch` and import **First-time import (no SDK on disk):** Unset `QNN_SDK_ROOT`, clear `~/.cache/executorch/qnn/`, then: ``` >>> import executorch.backends.qualcomm [QNN] Downloading Qualcomm AI Runtime SDK v2.37.0.250724... [QNN] Downloading: 645/1291 MB (50%) [QNN] Downloading: 1291/1291 MB (100%) [QNN] Download complete. [QNN] Extracting SDK... [QNN] Downloading libc++ (LLVM 14.0.0)... [QNN] QNN SDK v2.37.0.250724 ready at ~/.cache/executorch/qnn/sdk-2.37.0.250724 ``` User sees download progress instead of a silent multi-minute hang. SDK is stored in `~/.cache/`, not inside the pip package directory. **Second import (SDK already cached):** ``` >>> import executorch.backends.qualcomm [QNN] Using cached QNN SDK v2.37.0.250724 at ~/.cache/executorch/qnn/sdk-2.37.0.250724 ``` Instant — no re-download. **User has their own SDK installation:** ``` $ export QNN_SDK_ROOT=/opt/qnn/2.37.0 >>> import executorch.backends.qualcomm [QNN] Using QNN SDK at /opt/qnn/2.37.0 (from QNN_SDK_ROOT) ``` Respects the user's path, no download attempted. **Scripted usage (`--print-sdk-path`):** ``` $ SDK_PATH=$(python3 backends/qualcomm/scripts/download_qnn_sdk.py --print-sdk-path) $ echo $SDK_PATH /home/user/.cache/executorch/qnn/sdk-2.37.0.250724 ``` Stdout contains only the path — progress output goes to stderr, so shell capture works cleanly. ### Build-from-source user: `pip install -e .` or `build.sh` **Zero-config build (no env vars set):** ``` $ pip install -e . --no-build-isolation ... -- QNN_SDK_ROOT not set. Auto-downloading QNN SDK... -- EXECUTORCH_BUILD_QNN: ON ... Building target qnn_executorch_backend ``` QNN backend is built automatically. The SDK is auto-downloaded to `~/.cache/` during CMake configure. **Opting out of QNN:** ``` $ CMAKE_ARGS="-DEXECUTORCH_BUILD_QNN=OFF" pip install -e . --no-build-isolation ... -- EXECUTORCH_BUILD_QNN: OFF ``` No SDK download, no QNN targets built. **Build with pre-existing SDK:** ``` $ export QNN_SDK_ROOT=~/.cache/executorch/qnn/sdk-2.37.0.250724 $ pip install -e . --no-build-isolation ... -- EXECUTORCH_BUILD_QNN: ON Building target qnn_executorch_backend ``` Uses the provided SDK. No "Auto-downloading" message. **Shell build (`build.sh`) follows the same pattern:** ``` $ bash backends/qualcomm/scripts/build.sh --skip_linux_android [QNN] QNN_SDK_ROOT not set. SDK will be auto-downloaded during cmake configure. -- Auto-downloading QNN SDK... -- EXECUTORCH_BUILD_QNN: ON ``` With `QNN_SDK_ROOT` set, it skips the download and uses the provided path. **SDK survives build directory clean:** ``` $ rm -rf build-x86/ $ bash backends/qualcomm/scripts/build.sh --skip_linux_android ``` No re-download — SDK lives in `~/.cache/`, independent of the build directory. [ghstack-poisoned]
- Remove EXECUTORCH_BUILD_WHEEL_DO_NOT_USE gate in CMakeLists.txt so QNN SDK auto-downloads for all build paths (editable installs, build.sh), not just wheel builds - Use shared cache dir (~/.cache/executorch/qnn/) instead of cmake build dir, so SDK survives build dir cleans and is shared across all build flows - Remove hard QNN_SDK_ROOT requirement from build.sh — cmake handles auto-download during configure when SDK is not set - Auto-download Android NDK in build.sh via install_qnn_sdk.sh when ANDROID_NDK_ROOT is not set - Fix PYTHON_EXECUTABLE default in build.sh (was checked before being used, with a buggy test that never triggered) - Default EXECUTORCH_BUILD_QNN to ON on Linux x86 in pybind preset, matching wheel behavior so editable installs build QNN out of the box - Redirect download_qnn_sdk.py progress output to stderr when --print-sdk-path is used, so cmake can capture the path cleanly ghstack-source-id: 9bb1eeb Pull Request resolved: #17990
|
@haowhsu-quic bumping this PR up for review thanks, i will merge both at once including #17989 |
| echo "First, you need to set the environment variable for QNN_SDK_ROOT" | ||
| echo ", and if you want to build the android version of executor runner" | ||
| echo ", you need to export ANDROID_NDK_ROOT=/path/to/android_ndkXX" | ||
| echo "(or export TOOLCHAIN_ROOT_HOST=/path/to/sysroots/xx_host, " |
There was a problem hiding this comment.
Could you keep this for Yocto linux toolchain guide? thanks.
- Remove EXECUTORCH_BUILD_WHEEL_DO_NOT_USE gate in CMakeLists.txt so QNN SDK auto-downloads for all build paths (editable installs, build.sh), not just wheel builds - Use shared cache dir (~/.cache/executorch/qnn/) instead of cmake build dir, so SDK survives build dir cleans and is shared across all build flows - Remove hard QNN_SDK_ROOT requirement from build.sh — cmake handles auto-download during configure when SDK is not set - Auto-download Android NDK in build.sh via install_qnn_sdk.sh when ANDROID_NDK_ROOT is not set - Fix PYTHON_EXECUTABLE default in build.sh (was checked before being used, with a buggy test that never triggered) - Default EXECUTORCH_BUILD_QNN to ON on Linux x86 in pybind preset, matching wheel behavior so editable installs build QNN out of the box - Redirect download_qnn_sdk.py progress output to stderr when --print-sdk-path is used, so cmake can capture the path cleanly ## Test Plan/ User journeys Verified end-to-end on Linux x86_64 with QNN SDK v2.37.0.250724. ### AOT user: `pip install executorch` and import **First-time import (no SDK on disk):** Unset `QNN_SDK_ROOT`, clear `~/.cache/executorch/qnn/`, then: ``` >>> import executorch.backends.qualcomm [QNN] Downloading Qualcomm AI Runtime SDK v2.37.0.250724... [QNN] Downloading: 645/1291 MB (50%) [QNN] Downloading: 1291/1291 MB (100%) [QNN] Download complete. [QNN] Extracting SDK... [QNN] Downloading libc++ (LLVM 14.0.0)... [QNN] QNN SDK v2.37.0.250724 ready at ~/.cache/executorch/qnn/sdk-2.37.0.250724 ``` User sees download progress instead of a silent multi-minute hang. SDK is stored in `~/.cache/`, not inside the pip package directory. **Second import (SDK already cached):** ``` >>> import executorch.backends.qualcomm [QNN] Using cached QNN SDK v2.37.0.250724 at ~/.cache/executorch/qnn/sdk-2.37.0.250724 ``` Instant — no re-download. **User has their own SDK installation:** ``` $ export QNN_SDK_ROOT=/opt/qnn/2.37.0 >>> import executorch.backends.qualcomm [QNN] Using QNN SDK at /opt/qnn/2.37.0 (from QNN_SDK_ROOT) ``` Respects the user's path, no download attempted. **Scripted usage (`--print-sdk-path`):** ``` $ SDK_PATH=$(python3 backends/qualcomm/scripts/download_qnn_sdk.py --print-sdk-path) $ echo $SDK_PATH /home/user/.cache/executorch/qnn/sdk-2.37.0.250724 ``` Stdout contains only the path — progress output goes to stderr, so shell capture works cleanly. ### Build-from-source user: `pip install -e .` or `build.sh` **Zero-config build (no env vars set):** ``` $ pip install -e . --no-build-isolation ... -- QNN_SDK_ROOT not set. Auto-downloading QNN SDK... -- EXECUTORCH_BUILD_QNN: ON ... Building target qnn_executorch_backend ``` QNN backend is built automatically. The SDK is auto-downloaded to `~/.cache/` during CMake configure. **Opting out of QNN:** ``` $ CMAKE_ARGS="-DEXECUTORCH_BUILD_QNN=OFF" pip install -e . --no-build-isolation ... -- EXECUTORCH_BUILD_QNN: OFF ``` No SDK download, no QNN targets built. **Build with pre-existing SDK:** ``` $ export QNN_SDK_ROOT=~/.cache/executorch/qnn/sdk-2.37.0.250724 $ pip install -e . --no-build-isolation ... -- EXECUTORCH_BUILD_QNN: ON Building target qnn_executorch_backend ``` Uses the provided SDK. No "Auto-downloading" message. **Shell build (`build.sh`) follows the same pattern:** ``` $ bash backends/qualcomm/scripts/build.sh --skip_linux_android [QNN] QNN_SDK_ROOT not set. SDK will be auto-downloaded during cmake configure. -- Auto-downloading QNN SDK... -- EXECUTORCH_BUILD_QNN: ON ``` With `QNN_SDK_ROOT` set, it skips the download and uses the provided path. **SDK survives build directory clean:** ``` $ rm -rf build-x86/ $ bash backends/qualcomm/scripts/build.sh --skip_linux_android ``` No re-download — SDK lives in `~/.cache/`, independent of the build directory. [ghstack-poisoned]
- Remove EXECUTORCH_BUILD_WHEEL_DO_NOT_USE gate in CMakeLists.txt so QNN SDK auto-downloads for all build paths (editable installs, build.sh), not just wheel builds - Use shared cache dir (~/.cache/executorch/qnn/) instead of cmake build dir, so SDK survives build dir cleans and is shared across all build flows - Remove hard QNN_SDK_ROOT requirement from build.sh — cmake handles auto-download during configure when SDK is not set - Auto-download Android NDK in build.sh via install_qnn_sdk.sh when ANDROID_NDK_ROOT is not set - Fix PYTHON_EXECUTABLE default in build.sh (was checked before being used, with a buggy test that never triggered) - Default EXECUTORCH_BUILD_QNN to ON on Linux x86 in pybind preset, matching wheel behavior so editable installs build QNN out of the box - Redirect download_qnn_sdk.py progress output to stderr when --print-sdk-path is used, so cmake can capture the path cleanly ghstack-source-id: f468f13 Pull Request resolved: #17990
- Remove EXECUTORCH_BUILD_WHEEL_DO_NOT_USE gate in CMakeLists.txt so QNN SDK auto-downloads for all build paths (editable installs, build.sh), not just wheel builds - Use shared cache dir (~/.cache/executorch/qnn/) instead of cmake build dir, so SDK survives build dir cleans and is shared across all build flows - Remove hard QNN_SDK_ROOT requirement from build.sh — cmake handles auto-download during configure when SDK is not set - Auto-download Android NDK in build.sh via install_qnn_sdk.sh when ANDROID_NDK_ROOT is not set - Fix PYTHON_EXECUTABLE default in build.sh (was checked before being used, with a buggy test that never triggered) - Default EXECUTORCH_BUILD_QNN to ON on Linux x86 in pybind preset, matching wheel behavior so editable installs build QNN out of the box - Redirect download_qnn_sdk.py progress output to stderr when --print-sdk-path is used, so cmake can capture the path cleanly ## Test Plan/ User journeys Verified end-to-end on Linux x86_64 with QNN SDK v2.37.0.250724. ### AOT user: `pip install executorch` and import **First-time import (no SDK on disk):** Unset `QNN_SDK_ROOT`, clear `~/.cache/executorch/qnn/`, then: ``` >>> import executorch.backends.qualcomm [QNN] Downloading Qualcomm AI Runtime SDK v2.37.0.250724... [QNN] Downloading: 645/1291 MB (50%) [QNN] Downloading: 1291/1291 MB (100%) [QNN] Download complete. [QNN] Extracting SDK... [QNN] Downloading libc++ (LLVM 14.0.0)... [QNN] QNN SDK v2.37.0.250724 ready at ~/.cache/executorch/qnn/sdk-2.37.0.250724 ``` User sees download progress instead of a silent multi-minute hang. SDK is stored in `~/.cache/`, not inside the pip package directory. **Second import (SDK already cached):** ``` >>> import executorch.backends.qualcomm [QNN] Using cached QNN SDK v2.37.0.250724 at ~/.cache/executorch/qnn/sdk-2.37.0.250724 ``` Instant — no re-download. **User has their own SDK installation:** ``` $ export QNN_SDK_ROOT=/opt/qnn/2.37.0 >>> import executorch.backends.qualcomm [QNN] Using QNN SDK at /opt/qnn/2.37.0 (from QNN_SDK_ROOT) ``` Respects the user's path, no download attempted. **Scripted usage (`--print-sdk-path`):** ``` $ SDK_PATH=$(python3 backends/qualcomm/scripts/download_qnn_sdk.py --print-sdk-path) $ echo $SDK_PATH /home/user/.cache/executorch/qnn/sdk-2.37.0.250724 ``` Stdout contains only the path — progress output goes to stderr, so shell capture works cleanly. ### Build-from-source user: `pip install -e .` or `build.sh` **Zero-config build (no env vars set):** ``` $ pip install -e . --no-build-isolation ... -- QNN_SDK_ROOT not set. Auto-downloading QNN SDK... -- EXECUTORCH_BUILD_QNN: ON ... Building target qnn_executorch_backend ``` QNN backend is built automatically. The SDK is auto-downloaded to `~/.cache/` during CMake configure. **Opting out of QNN:** ``` $ CMAKE_ARGS="-DEXECUTORCH_BUILD_QNN=OFF" pip install -e . --no-build-isolation ... -- EXECUTORCH_BUILD_QNN: OFF ``` No SDK download, no QNN targets built. **Build with pre-existing SDK:** ``` $ export QNN_SDK_ROOT=~/.cache/executorch/qnn/sdk-2.37.0.250724 $ pip install -e . --no-build-isolation ... -- EXECUTORCH_BUILD_QNN: ON Building target qnn_executorch_backend ``` Uses the provided SDK. No "Auto-downloading" message. **Shell build (`build.sh`) follows the same pattern:** ``` $ bash backends/qualcomm/scripts/build.sh --skip_linux_android [QNN] QNN_SDK_ROOT not set. SDK will be auto-downloaded during cmake configure. -- Auto-downloading QNN SDK... -- EXECUTORCH_BUILD_QNN: ON ``` With `QNN_SDK_ROOT` set, it skips the download and uses the provided path. **SDK survives build directory clean:** ``` $ rm -rf build-x86/ $ bash backends/qualcomm/scripts/build.sh --skip_linux_android ``` No re-download — SDK lives in `~/.cache/`, independent of the build directory. [ghstack-poisoned]
- Remove EXECUTORCH_BUILD_WHEEL_DO_NOT_USE gate in CMakeLists.txt so QNN SDK auto-downloads for all build paths (editable installs, build.sh), not just wheel builds - Use shared cache dir (~/.cache/executorch/qnn/) instead of cmake build dir, so SDK survives build dir cleans and is shared across all build flows - Remove hard QNN_SDK_ROOT requirement from build.sh — cmake handles auto-download during configure when SDK is not set - Auto-download Android NDK in build.sh via install_qnn_sdk.sh when ANDROID_NDK_ROOT is not set - Fix PYTHON_EXECUTABLE default in build.sh (was checked before being used, with a buggy test that never triggered) - Default EXECUTORCH_BUILD_QNN to ON on Linux x86 in pybind preset, matching wheel behavior so editable installs build QNN out of the box - Redirect download_qnn_sdk.py progress output to stderr when --print-sdk-path is used, so cmake can capture the path cleanly ghstack-source-id: 46b5178 Pull Request resolved: #17990
Stack from ghstack (oldest at bottom):
QNN SDK auto-downloads for all build paths (editable installs,
build.sh), not just wheel builds
build dir, so SDK survives build dir cleans and is shared across
all build flows
auto-download during configure when SDK is not set
ANDROID_NDK_ROOT is not set
used, with a buggy test that never triggered)
matching wheel behavior so editable installs build QNN out of the box
--print-sdk-path is used, so cmake can capture the path cleanly
Test Plan/ User journeys
Verified end-to-end on Linux x86_64 with QNN SDK v2.37.0.250724.
AOT user:
pip install executorchand importFirst-time import (no SDK on disk):
Unset
QNN_SDK_ROOT, clear~/.cache/executorch/qnn/, then:User sees download progress instead of a silent multi-minute hang. SDK is stored in
~/.cache/, not inside the pip package directory.Second import (SDK already cached):
Instant — no re-download.
User has their own SDK installation:
Respects the user's path, no download attempted.
Scripted usage (
--print-sdk-path):Stdout contains only the path — progress output goes to stderr, so shell capture works cleanly.
Build-from-source user:
pip install -e .orbuild.shZero-config build (no env vars set):
QNN backend is built automatically. The SDK is auto-downloaded to
~/.cache/during CMake configure.Opting out of QNN:
No SDK download, no QNN targets built.
Build with pre-existing SDK:
Uses the provided SDK. No "Auto-downloading" message.
Shell build (
build.sh) follows the same pattern:With
QNN_SDK_ROOTset, it skips the download and uses the provided path.SDK survives build directory clean:
No re-download — SDK lives in
~/.cache/, independent of the build directory.