From 6409c12dc71ab20109b75f75314c176d76c7d77f Mon Sep 17 00:00:00 2001 From: Mengwei Liu Date: Mon, 3 Nov 2025 23:09:14 -0800 Subject: [PATCH 01/14] Move QNN SDK into backends/qualcomm/CMakeLists.txt --- backends/qualcomm/CMakeLists.txt | 43 ++++++-- backends/qualcomm/scripts/download_qnn_sdk.py | 45 ++++++++- setup.py | 99 ++++--------------- tools/cmake/preset/pybind.cmake | 3 + 4 files changed, 102 insertions(+), 88 deletions(-) diff --git a/backends/qualcomm/CMakeLists.txt b/backends/qualcomm/CMakeLists.txt index 07166b92ea2..aa417d5995e 100644 --- a/backends/qualcomm/CMakeLists.txt +++ b/backends/qualcomm/CMakeLists.txt @@ -23,13 +23,34 @@ get_filename_component( _common_include_directories "${EXECUTORCH_SOURCE_DIR}/.." ABSOLUTE ) -if(NOT DEFINED QNN_SDK_ROOT) - message( - FATAL_ERROR - "Please define QNN_SDK_ROOT, e.g. cmake <..> -DQNN_SDK_ROOT=<...>" +if(EXECUTORCH_BUILD_PYBIND) + find_package(Python3 COMPONENTS Interpreter REQUIRED) + message(STATUS "QNN_SDK_ROOT not provided, downloading Qualcomm SDK") + execute_process( + COMMAND + ${Python3_EXECUTABLE} + ${EXECUTORCH_SOURCE_DIR}/backends/qualcomm/scripts/download_qnn_sdk.py + --print-sdk-path + WORKING_DIRECTORY ${EXECUTORCH_SOURCE_DIR} + RESULT_VARIABLE _qnn_sdk_download_result + OUTPUT_VARIABLE _qnn_sdk_download_output + ERROR_VARIABLE _qnn_sdk_download_error + OUTPUT_STRIP_TRAILING_WHITESPACE ) -elseif(CMAKE_TOOLCHAIN_FILE MATCHES ".*(iOS|ios\.toolchain)\.cmake$") - message(FATAL_ERROR "ios is not supported by Qualcomm AI Engine Direct") + if(NOT _qnn_sdk_download_result EQUAL 0 + OR _qnn_sdk_download_output STREQUAL "") + message( + FATAL_ERROR + "Failed to download Qualcomm SDK. stdout: ${_qnn_sdk_download_output}\n" + "stderr: ${_qnn_sdk_download_error}" + ) + endif() + set( + QNN_SDK_ROOT + ${_qnn_sdk_download_output} + CACHE PATH "Qualcomm SDK root directory" FORCE + ) + set(ENV{QNN_SDK_ROOT} ${QNN_SDK_ROOT}) endif() message(STATUS "Using qnn sdk root ${QNN_SDK_ROOT}") @@ -214,7 +235,9 @@ add_subdirectory( install( TARGETS qnn_executorch_backend EXPORT ExecuTorchTargets - DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/executorch/backends/qualcomm + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/executorch/backends/qualcomm + RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}/executorch/backends/qualcomm ) # QNN pybind @@ -275,4 +298,10 @@ if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64") ${QNN_EXECUTORCH_ROOT_DIR}/aot/python ${CMAKE_CURRENT_BINARY_DIR}/qnn_executorch/python ) + + install( + TARGETS PyQnnManagerAdaptor PyQnnWrapperAdaptor + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/executorch/backends/qualcomm/python + RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}/executorch/backends/qualcomm/python + ) endif() diff --git a/backends/qualcomm/scripts/download_qnn_sdk.py b/backends/qualcomm/scripts/download_qnn_sdk.py index 747524a0e5b..1bd844e2288 100644 --- a/backends/qualcomm/scripts/download_qnn_sdk.py +++ b/backends/qualcomm/scripts/download_qnn_sdk.py @@ -1,4 +1,4 @@ -# Add these imports for additional logging +import argparse import ctypes import logging import os @@ -592,3 +592,46 @@ def install_qnn_sdk() -> bool: # libc++ and QNN SDK setup return _ensure_libcxx_stack() and _ensure_qnn_sdk_lib() + + +def main(argv: Optional[List[str]] = None) -> int: + parser = argparse.ArgumentParser( + description="Helper utility for Qualcomm SDK staging." + ) + parser.add_argument( + "--dst-folder", + type=pathlib.Path, + default=SDK_DIR, + help="Destination directory for the Qualcomm SDK.", + ) + parser.add_argument( + "--print-sdk-path", + action="store_true", + help="Print the resolved Qualcomm SDK path to stdout.", + ) + parser.add_argument( + "--install-sdk", + action="store_true", + help="Ensure the SDK and runtime libraries are staged and loaded.", + ) + args = parser.parse_args(argv) + + logging.basicConfig(level=logging.INFO) + + sdk_path: Optional[pathlib.Path] + if args.install_sdk: + if not install_qnn_sdk(): + return 1 + sdk_path = pathlib.Path(os.environ.get("QNN_SDK_ROOT", args.dst_folder)) + else: + sdk_path = _download_qnn_sdk(dst_folder=args.dst_folder) + if sdk_path is None: + return 1 + + if args.print_sdk_path and sdk_path is not None: + print(sdk_path) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/setup.py b/setup.py index 8e75ea47cd6..723bca484f2 100644 --- a/setup.py +++ b/setup.py @@ -57,8 +57,6 @@ import site import subprocess import sys -import sysconfig -import tempfile from distutils import log # type: ignore[import-not-found] from distutils.sysconfig import get_python_lib # type: ignore[import-not-found] @@ -463,84 +461,6 @@ def run(self): if self._ran_build: return - try: - # Following code is for building the Qualcomm backend. - from backends.qualcomm.scripts.download_qnn_sdk import ( - _download_qnn_sdk, - is_linux_x86, - ) - - if is_linux_x86(): - os.environ["EXECUTORCH_BUILDING_WHEEL"] = "1" - - with tempfile.TemporaryDirectory() as tmpdir: - tmp_path = Path(tmpdir) - sdk_path = _download_qnn_sdk(dst_folder=tmp_path) - - if not sdk_path: - raise RuntimeError( - "Qualcomm SDK not found, cannot build backend" - ) - - # Determine paths - prj_root = Path(__file__).parent.resolve() - build_sh = prj_root / "backends/qualcomm/scripts/build.sh" - build_root = prj_root / "build-x86" - - if not build_sh.exists(): - raise FileNotFoundError(f"{build_sh} not found") - - # Run build.sh with SDK path exported - env = dict(**os.environ) - env["QNN_SDK_ROOT"] = str(sdk_path) - subprocess.check_call( - [ - str(build_sh), - "--skip_linux_android", - "--skip_linux_embedded", - ], - env=env, - ) - - # Copy the main .so into the wheel package - so_src = ( - build_root / "backends/qualcomm/libqnn_executorch_backend.so" - ) - so_dst = Path( - self.get_ext_fullpath( - "executorch.backends.qualcomm.qnn_backend" - ) - ) - self.mkpath(str(so_dst.parent)) # ensure destination exists - self.copy_file(str(so_src), str(so_dst)) - logging.info(f"Copied Qualcomm backend: {so_src} -> {so_dst}") - - # Copy Python adaptor .so files - ext_suffix = sysconfig.get_config_var("EXT_SUFFIX") - - so_files = [ - ( - "executorch.backends.qualcomm.python.PyQnnManagerAdaptor", - prj_root - / f"backends/qualcomm/python/PyQnnManagerAdaptor{ext_suffix}", - ), - ( - "executorch.backends.qualcomm.python.PyQnnWrapperAdaptor", - prj_root - / f"backends/qualcomm/python/PyQnnWrapperAdaptor{ext_suffix}", - ), - ] - - for module_name, so_src in so_files: - so_dst = Path(self.get_ext_fullpath(module_name)) - self.mkpath(str(so_dst.parent)) - self.copy_file(str(so_src), str(so_dst)) - logging.info(f"Copied Qualcomm backend: {so_src} -> {so_dst}") - - except ImportError: - logging.error("Fail to build Qualcomm backend") - logging.exception("Import error") - if self.editable_mode: self._ran_build = True self.run_command("build") @@ -924,5 +844,24 @@ def run(self): # noqa C901 dst="executorch/data/lib/", dependent_cmake_flags=[], ), + BuiltFile( + src_dir="%CMAKE_CACHE_DIR%/backends/qualcomm/%BUILD_TYPE%/", + src_name="qnn_executorch_backend", + dst="executorch/backends/qualcomm/", + is_dynamic_lib=True, + dependent_cmake_flags=["EXECUTORCH_BUILD_QNN"], + ), + BuiltExtension( + src_dir="%CMAKE_CACHE_DIR%/backends/qualcomm/python/%BUILD_TYPE%/", + src="PyQnnManagerAdaptor.*", + modpath="executorch.backends.qualcomm.python.PyQnnManagerAdaptor", + dependent_cmake_flags=["EXECUTORCH_BUILD_QNN"], + ), + BuiltExtension( + src_dir="%CMAKE_CACHE_DIR%/backends/qualcomm/python/%BUILD_TYPE%/", + src="PyQnnWrapperAdaptor.*", + modpath="executorch.backends.qualcomm.python.PyQnnWrapperAdaptor", + dependent_cmake_flags=["EXECUTORCH_BUILD_QNN"], + ), ], ) diff --git a/tools/cmake/preset/pybind.cmake b/tools/cmake/preset/pybind.cmake index c71c10ad01f..7c4de793721 100644 --- a/tools/cmake/preset/pybind.cmake +++ b/tools/cmake/preset/pybind.cmake @@ -35,6 +35,9 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") set_overridable_option(EXECUTORCH_BUILD_EXTENSION_TRAINING ON) set_overridable_option(EXECUTORCH_BUILD_EXTENSION_LLM_RUNNER ON) set_overridable_option(EXECUTORCH_BUILD_EXTENSION_LLM ON) + if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|amd64|i.86)$") + set_overridable_option(EXECUTORCH_BUILD_QNN ON) + endif() elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows" OR CMAKE_SYSTEM_NAME STREQUAL "WIN32" ) From deb216b2ee44ff7046654a608d2f9284f3ecb452 Mon Sep 17 00:00:00 2001 From: Mengwei Liu Date: Mon, 3 Nov 2025 23:17:02 -0800 Subject: [PATCH 02/14] Lint --- backends/qualcomm/CMakeLists.txt | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/backends/qualcomm/CMakeLists.txt b/backends/qualcomm/CMakeLists.txt index aa417d5995e..24ed1ed404d 100644 --- a/backends/qualcomm/CMakeLists.txt +++ b/backends/qualcomm/CMakeLists.txt @@ -24,7 +24,11 @@ get_filename_component( ) if(EXECUTORCH_BUILD_PYBIND) - find_package(Python3 COMPONENTS Interpreter REQUIRED) + find_package( + Python3 + COMPONENTS Interpreter + REQUIRED + ) message(STATUS "QNN_SDK_ROOT not provided, downloading Qualcomm SDK") execute_process( COMMAND @@ -37,18 +41,18 @@ if(EXECUTORCH_BUILD_PYBIND) ERROR_VARIABLE _qnn_sdk_download_error OUTPUT_STRIP_TRAILING_WHITESPACE ) - if(NOT _qnn_sdk_download_result EQUAL 0 - OR _qnn_sdk_download_output STREQUAL "") + if(NOT _qnn_sdk_download_result EQUAL 0 OR _qnn_sdk_download_output STREQUAL + "" + ) message( FATAL_ERROR "Failed to download Qualcomm SDK. stdout: ${_qnn_sdk_download_output}\n" "stderr: ${_qnn_sdk_download_error}" ) endif() - set( - QNN_SDK_ROOT - ${_qnn_sdk_download_output} - CACHE PATH "Qualcomm SDK root directory" FORCE + set(QNN_SDK_ROOT + ${_qnn_sdk_download_output} + CACHE PATH "Qualcomm SDK root directory" FORCE ) set(ENV{QNN_SDK_ROOT} ${QNN_SDK_ROOT}) endif() @@ -301,7 +305,9 @@ if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64") install( TARGETS PyQnnManagerAdaptor PyQnnWrapperAdaptor - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/executorch/backends/qualcomm/python - RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}/executorch/backends/qualcomm/python + LIBRARY + DESTINATION ${CMAKE_INSTALL_LIBDIR}/executorch/backends/qualcomm/python + RUNTIME + DESTINATION ${CMAKE_INSTALL_LIBDIR}/executorch/backends/qualcomm/python ) endif() From 73be53a91a17ccf5f0669551fc3db03611419263 Mon Sep 17 00:00:00 2001 From: Mengwei Liu Date: Tue, 4 Nov 2025 09:44:10 -0800 Subject: [PATCH 03/14] Fix src path --- setup.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 723bca484f2..71aa4c543d4 100644 --- a/setup.py +++ b/setup.py @@ -757,6 +757,11 @@ def run(self): # noqa C901 cmake_build_args += ["--target", "custom_ops_aot_lib"] cmake_build_args += ["--target", "quantized_ops_aot_lib"] + if cmake_cache.is_enabled("EXECUTORCH_BUILD_QNN"): + cmake_build_args += ["--target", "qnn_executorch_backend"] + cmake_build_args += ["--target", "PyQnnManagerAdaptor"] + cmake_build_args += ["--target", "PyQnnWrapperAdaptor"] + # Set PYTHONPATH to the location of the pip package. os.environ["PYTHONPATH"] = ( site.getsitepackages()[0] + ";" + os.environ.get("PYTHONPATH", "") @@ -852,13 +857,13 @@ def run(self): # noqa C901 dependent_cmake_flags=["EXECUTORCH_BUILD_QNN"], ), BuiltExtension( - src_dir="%CMAKE_CACHE_DIR%/backends/qualcomm/python/%BUILD_TYPE%/", + src_dir="%CMAKE_CACHE_DIR%/backends/qualcomm/%BUILD_TYPE%/", src="PyQnnManagerAdaptor.*", modpath="executorch.backends.qualcomm.python.PyQnnManagerAdaptor", dependent_cmake_flags=["EXECUTORCH_BUILD_QNN"], ), BuiltExtension( - src_dir="%CMAKE_CACHE_DIR%/backends/qualcomm/python/%BUILD_TYPE%/", + src_dir="%CMAKE_CACHE_DIR%/backends/qualcomm/%BUILD_TYPE%/", src="PyQnnWrapperAdaptor.*", modpath="executorch.backends.qualcomm.python.PyQnnWrapperAdaptor", dependent_cmake_flags=["EXECUTORCH_BUILD_QNN"], From d9f7233841c587a80c4caedf311d1ac76a8e6c05 Mon Sep 17 00:00:00 2001 From: Mengwei Liu Date: Tue, 4 Nov 2025 16:14:53 -0800 Subject: [PATCH 04/14] Fix rpath --- CMakeLists.txt | 16 +++++---- backends/qualcomm/CMakeLists.txt | 62 ++++++++++++++++++-------------- 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 51573d276b3..cd6a16b94b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -801,6 +801,9 @@ if(EXECUTORCH_BUILD_PYBIND) torch ) + # RPATH for _portable_lib.so + set(_portable_lib_rpath "$ORIGIN/../../../torch/lib") + if(EXECUTORCH_BUILD_EXTENSION_MODULE) # Always use static linking for pybindings to avoid runtime symbol # resolution issues @@ -835,6 +838,7 @@ if(EXECUTORCH_BUILD_PYBIND) if(EXECUTORCH_BUILD_QNN) list(APPEND _dep_libs qnn_executorch_backend) + string(APPEND _portable_lib_rpath ":$ORIGIN/../../backends/qualcomm") endif() if(EXECUTORCH_BUILD_ENN) @@ -886,10 +890,10 @@ if(EXECUTORCH_BUILD_PYBIND) target_compile_options(portable_lib PUBLIC ${_pybind_compile_options}) target_link_libraries(portable_lib PRIVATE ${_dep_libs}) - # Set RPATH to find PyTorch libraries relative to the installation location - # This goes from executorch/extension/pybindings up to site-packages, then to - # torch/lib. Don't do this to APPLE, as it will error out on the following - # error: + # Set RPATH to find PyTorch and backend libraries relative to the installation + # location. This goes from executorch/extension/pybindings up to site-packages, + # then to torch/lib. If QNN is enabled, also add backends/qualcomm/. + # Don't do this to APPLE, as it will error out on the following error: # if(APPLE) # Skip setting @loader_path for APPLE, since it causes error like ld: @@ -897,8 +901,8 @@ if(EXECUTORCH_BUILD_PYBIND) # libtorch_cpu.dylib' else() set_target_properties( - portable_lib PROPERTIES BUILD_RPATH "$ORIGIN/../../../torch/lib" - INSTALL_RPATH "$ORIGIN/../../../torch/lib" + portable_lib PROPERTIES BUILD_RPATH "${_portable_lib_rpath}" + INSTALL_RPATH "${_portable_lib_rpath}" ) endif() diff --git a/backends/qualcomm/CMakeLists.txt b/backends/qualcomm/CMakeLists.txt index 24ed1ed404d..7813d79171a 100644 --- a/backends/qualcomm/CMakeLists.txt +++ b/backends/qualcomm/CMakeLists.txt @@ -24,36 +24,44 @@ get_filename_component( ) if(EXECUTORCH_BUILD_PYBIND) - find_package( - Python3 - COMPONENTS Interpreter - REQUIRED - ) - message(STATUS "QNN_SDK_ROOT not provided, downloading Qualcomm SDK") - execute_process( - COMMAND - ${Python3_EXECUTABLE} - ${EXECUTORCH_SOURCE_DIR}/backends/qualcomm/scripts/download_qnn_sdk.py - --print-sdk-path - WORKING_DIRECTORY ${EXECUTORCH_SOURCE_DIR} - RESULT_VARIABLE _qnn_sdk_download_result - OUTPUT_VARIABLE _qnn_sdk_download_output - ERROR_VARIABLE _qnn_sdk_download_error - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - if(NOT _qnn_sdk_download_result EQUAL 0 OR _qnn_sdk_download_output STREQUAL - "" + set(_qnn_default_sdk_dir "${CMAKE_CURRENT_BINARY_DIR}/sdk/qnn") + + if(EXISTS "${_qnn_default_sdk_dir}" AND EXISTS + "${_qnn_default_sdk_dir}/lib" ) - message( - FATAL_ERROR - "Failed to download Qualcomm SDK. stdout: ${_qnn_sdk_download_output}\n" - "stderr: ${_qnn_sdk_download_error}" + message(STATUS "Found cached Qualcomm SDK at ${_qnn_default_sdk_dir}") + set(QNN_SDK_ROOT + ${_qnn_default_sdk_dir} + CACHE PATH "Qualcomm SDK root directory" FORCE + ) + else() + message(STATUS "Downloading Qualcomm SDK") + execute_process( + COMMAND + ${Python3_EXECUTABLE} + ${EXECUTORCH_SOURCE_DIR}/backends/qualcomm/scripts/download_qnn_sdk.py + --dst-folder ${_qnn_default_sdk_dir} + --print-sdk-path + WORKING_DIRECTORY ${EXECUTORCH_SOURCE_DIR} + RESULT_VARIABLE _qnn_sdk_download_result + OUTPUT_VARIABLE _qnn_sdk_download_output + ERROR_VARIABLE _qnn_sdk_download_error + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(NOT _qnn_sdk_download_result EQUAL 0 OR _qnn_sdk_download_output STREQUAL + "" + ) + message( + FATAL_ERROR + "Failed to download Qualcomm SDK. stdout: ${_qnn_sdk_download_output}\n" + "stderr: ${_qnn_sdk_download_error}" + ) + endif() + set(QNN_SDK_ROOT + ${_qnn_sdk_download_output} + CACHE PATH "Qualcomm SDK root directory" FORCE ) endif() - set(QNN_SDK_ROOT - ${_qnn_sdk_download_output} - CACHE PATH "Qualcomm SDK root directory" FORCE - ) set(ENV{QNN_SDK_ROOT} ${QNN_SDK_ROOT}) endif() From 5c971c0969e3c64c648aff4aa6448b167c93f5f1 Mon Sep 17 00:00:00 2001 From: Mengwei Liu Date: Tue, 4 Nov 2025 16:25:09 -0800 Subject: [PATCH 05/14] Test --- CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cd6a16b94b7..536c10e8b59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -891,9 +891,10 @@ if(EXECUTORCH_BUILD_PYBIND) target_link_libraries(portable_lib PRIVATE ${_dep_libs}) # Set RPATH to find PyTorch and backend libraries relative to the installation - # location. This goes from executorch/extension/pybindings up to site-packages, - # then to torch/lib. If QNN is enabled, also add backends/qualcomm/. - # Don't do this to APPLE, as it will error out on the following error: + # location. This goes from executorch/extension/pybindings up to + # site-packages, then to torch/lib. If QNN is enabled, also add + # backends/qualcomm/. Don't do this to APPLE, as it will error out on the + # following error: # if(APPLE) # Skip setting @loader_path for APPLE, since it causes error like ld: From 216f9a7c7676c2d69a95e23ba904ca6c87ae80ea Mon Sep 17 00:00:00 2001 From: Mengwei Liu Date: Tue, 4 Nov 2025 21:01:02 -0800 Subject: [PATCH 06/14] Retry --- backends/qualcomm/CMakeLists.txt | 34 ++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/backends/qualcomm/CMakeLists.txt b/backends/qualcomm/CMakeLists.txt index 7813d79171a..36c848cb2e2 100644 --- a/backends/qualcomm/CMakeLists.txt +++ b/backends/qualcomm/CMakeLists.txt @@ -34,6 +34,7 @@ if(EXECUTORCH_BUILD_PYBIND) ${_qnn_default_sdk_dir} CACHE PATH "Qualcomm SDK root directory" FORCE ) +<<<<<<< HEAD else() message(STATUS "Downloading Qualcomm SDK") execute_process( @@ -55,6 +56,39 @@ if(EXECUTORCH_BUILD_PYBIND) FATAL_ERROR "Failed to download Qualcomm SDK. stdout: ${_qnn_sdk_download_output}\n" "stderr: ${_qnn_sdk_download_error}" +======= + message(STATUS "Found cached Qualcomm SDK at ${_qnn_default_sdk_dir}") + set(QNN_SDK_ROOT + ${_qnn_default_sdk_dir} + CACHE PATH "Qualcomm SDK root directory" FORCE + ) + else() + message(STATUS "QNN_SDK_ROOT not provided, downloading Qualcomm SDK") + execute_process( + COMMAND + ${PYTHON_EXECUTABLE} + ${EXECUTORCH_SOURCE_DIR}/backends/qualcomm/scripts/download_qnn_sdk.py + --dst-folder ${_qnn_default_sdk_dir} + --print-sdk-path + WORKING_DIRECTORY ${EXECUTORCH_SOURCE_DIR} + RESULT_VARIABLE _qnn_sdk_download_result + OUTPUT_VARIABLE _qnn_sdk_download_output + ERROR_VARIABLE _qnn_sdk_download_error + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(NOT _qnn_sdk_download_result EQUAL 0 OR _qnn_sdk_download_output + STREQUAL "" + ) + message( + FATAL_ERROR + "Failed to download Qualcomm SDK. stdout: ${_qnn_sdk_download_output}\n" + "stderr: ${_qnn_sdk_download_error}" + ) + endif() + set(QNN_SDK_ROOT + ${_qnn_sdk_download_output} + CACHE PATH "Qualcomm SDK root directory" FORCE +>>>>>>> e13507fb51 (Retry) ) endif() set(QNN_SDK_ROOT From 106c3c738e24265fc8922fd378fad8a38a720838 Mon Sep 17 00:00:00 2001 From: Mengwei Liu Date: Tue, 4 Nov 2025 21:28:49 -0800 Subject: [PATCH 07/14] Turn off QNN in cuda.yml --- .github/workflows/cuda.yml | 6 +++--- backends/qualcomm/CMakeLists.txt | 36 +------------------------------- 2 files changed, 4 insertions(+), 38 deletions(-) diff --git a/.github/workflows/cuda.yml b/.github/workflows/cuda.yml index 7cc937fe6ca..0572adf0ba3 100644 --- a/.github/workflows/cuda.yml +++ b/.github/workflows/cuda.yml @@ -44,7 +44,7 @@ jobs: # Test ExecuTorch CUDA build - ExecuTorch will automatically detect CUDA version # and install the appropriate PyTorch wheel - source .ci/scripts/test-cuda-build.sh "${{ matrix.cuda-version }}" + CMAKE_ARGS="-DEXECUTORCH_BUILD_QNN=OFF" source .ci/scripts/test-cuda-build.sh "${{ matrix.cuda-version }}" # This job will fail if any of the CUDA versions fail check-all-cuda-builds: @@ -83,7 +83,7 @@ jobs: script: | set -eux - PYTHON_EXECUTABLE=python ./install_executorch.sh + CMAKE_ARGS="-DEXECUTORCH_BUILD_QNN=OFF" PYTHON_EXECUTABLE=python ./install_executorch.sh export LD_LIBRARY_PATH=/opt/conda/lib:$LD_LIBRARY_PATH PYTHON_EXECUTABLE=python source .ci/scripts/test_model.sh "${{ matrix.model }}" cmake cuda @@ -132,7 +132,7 @@ jobs: set -eux echo "::group::Setup ExecuTorch" - ./install_executorch.sh + CMAKE_ARGS="-DEXECUTORCH_BUILD_QNN=OFF" ./install_executorch.sh echo "::endgroup::" echo "::group::Setup Huggingface" diff --git a/backends/qualcomm/CMakeLists.txt b/backends/qualcomm/CMakeLists.txt index 36c848cb2e2..6dd59b7afbb 100644 --- a/backends/qualcomm/CMakeLists.txt +++ b/backends/qualcomm/CMakeLists.txt @@ -34,12 +34,11 @@ if(EXECUTORCH_BUILD_PYBIND) ${_qnn_default_sdk_dir} CACHE PATH "Qualcomm SDK root directory" FORCE ) -<<<<<<< HEAD else() message(STATUS "Downloading Qualcomm SDK") execute_process( COMMAND - ${Python3_EXECUTABLE} + ${PYTHON_EXECUTABLE} ${EXECUTORCH_SOURCE_DIR}/backends/qualcomm/scripts/download_qnn_sdk.py --dst-folder ${_qnn_default_sdk_dir} --print-sdk-path @@ -56,39 +55,6 @@ if(EXECUTORCH_BUILD_PYBIND) FATAL_ERROR "Failed to download Qualcomm SDK. stdout: ${_qnn_sdk_download_output}\n" "stderr: ${_qnn_sdk_download_error}" -======= - message(STATUS "Found cached Qualcomm SDK at ${_qnn_default_sdk_dir}") - set(QNN_SDK_ROOT - ${_qnn_default_sdk_dir} - CACHE PATH "Qualcomm SDK root directory" FORCE - ) - else() - message(STATUS "QNN_SDK_ROOT not provided, downloading Qualcomm SDK") - execute_process( - COMMAND - ${PYTHON_EXECUTABLE} - ${EXECUTORCH_SOURCE_DIR}/backends/qualcomm/scripts/download_qnn_sdk.py - --dst-folder ${_qnn_default_sdk_dir} - --print-sdk-path - WORKING_DIRECTORY ${EXECUTORCH_SOURCE_DIR} - RESULT_VARIABLE _qnn_sdk_download_result - OUTPUT_VARIABLE _qnn_sdk_download_output - ERROR_VARIABLE _qnn_sdk_download_error - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - if(NOT _qnn_sdk_download_result EQUAL 0 OR _qnn_sdk_download_output - STREQUAL "" - ) - message( - FATAL_ERROR - "Failed to download Qualcomm SDK. stdout: ${_qnn_sdk_download_output}\n" - "stderr: ${_qnn_sdk_download_error}" - ) - endif() - set(QNN_SDK_ROOT - ${_qnn_sdk_download_output} - CACHE PATH "Qualcomm SDK root directory" FORCE ->>>>>>> e13507fb51 (Retry) ) endif() set(QNN_SDK_ROOT From 8c829efa137a345d3a59f6324a0cbea9bc15964f Mon Sep 17 00:00:00 2001 From: Mengwei Liu Date: Wed, 12 Nov 2025 14:54:55 -0800 Subject: [PATCH 08/14] Cleanup --- .github/workflows/cuda.yml | 6 +++--- backends/qualcomm/CMakeLists.txt | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cuda.yml b/.github/workflows/cuda.yml index 0572adf0ba3..7cc937fe6ca 100644 --- a/.github/workflows/cuda.yml +++ b/.github/workflows/cuda.yml @@ -44,7 +44,7 @@ jobs: # Test ExecuTorch CUDA build - ExecuTorch will automatically detect CUDA version # and install the appropriate PyTorch wheel - CMAKE_ARGS="-DEXECUTORCH_BUILD_QNN=OFF" source .ci/scripts/test-cuda-build.sh "${{ matrix.cuda-version }}" + source .ci/scripts/test-cuda-build.sh "${{ matrix.cuda-version }}" # This job will fail if any of the CUDA versions fail check-all-cuda-builds: @@ -83,7 +83,7 @@ jobs: script: | set -eux - CMAKE_ARGS="-DEXECUTORCH_BUILD_QNN=OFF" PYTHON_EXECUTABLE=python ./install_executorch.sh + PYTHON_EXECUTABLE=python ./install_executorch.sh export LD_LIBRARY_PATH=/opt/conda/lib:$LD_LIBRARY_PATH PYTHON_EXECUTABLE=python source .ci/scripts/test_model.sh "${{ matrix.model }}" cmake cuda @@ -132,7 +132,7 @@ jobs: set -eux echo "::group::Setup ExecuTorch" - CMAKE_ARGS="-DEXECUTORCH_BUILD_QNN=OFF" ./install_executorch.sh + ./install_executorch.sh echo "::endgroup::" echo "::group::Setup Huggingface" diff --git a/backends/qualcomm/CMakeLists.txt b/backends/qualcomm/CMakeLists.txt index 6dd59b7afbb..68be407d50c 100644 --- a/backends/qualcomm/CMakeLists.txt +++ b/backends/qualcomm/CMakeLists.txt @@ -23,6 +23,7 @@ get_filename_component( _common_include_directories "${EXECUTORCH_SOURCE_DIR}/.." ABSOLUTE ) +# We only download QNN SDK when we build pip wheel for ExecuTorch. if(EXECUTORCH_BUILD_PYBIND) set(_qnn_default_sdk_dir "${CMAKE_CURRENT_BINARY_DIR}/sdk/qnn") From cc05d3b9180c9be8bf0fcac78e2eeb73d2b2d605 Mon Sep 17 00:00:00 2001 From: Mengwei Liu Date: Wed, 12 Nov 2025 15:02:28 -0800 Subject: [PATCH 09/14] Fix --- backends/qualcomm/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/backends/qualcomm/CMakeLists.txt b/backends/qualcomm/CMakeLists.txt index 68be407d50c..e0a90b59da5 100644 --- a/backends/qualcomm/CMakeLists.txt +++ b/backends/qualcomm/CMakeLists.txt @@ -64,6 +64,13 @@ if(EXECUTORCH_BUILD_PYBIND) ) endif() set(ENV{QNN_SDK_ROOT} ${QNN_SDK_ROOT}) +elseif(NOT DEFINED QNN_SDK_ROOT) + message( + FATAL_ERROR + "Please define QNN_SDK_ROOT, e.g. cmake <..> -DQNN_SDK_ROOT=<...>" + ) +elseif(CMAKE_TOOLCHAIN_FILE MATCHES ".*(iOS|ios\.toolchain)\.cmake$") + message(FATAL_ERROR "ios is not supported by Qualcomm AI Engine Direct") endif() message(STATUS "Using qnn sdk root ${QNN_SDK_ROOT}") From 4a65e0145323f6d5c9629b83a54ed4738617768e Mon Sep 17 00:00:00 2001 From: Mengwei Liu Date: Wed, 12 Nov 2025 22:12:53 -0800 Subject: [PATCH 10/14] Address comments --- backends/qualcomm/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backends/qualcomm/CMakeLists.txt b/backends/qualcomm/CMakeLists.txt index e0a90b59da5..bc31a6e3f2b 100644 --- a/backends/qualcomm/CMakeLists.txt +++ b/backends/qualcomm/CMakeLists.txt @@ -64,7 +64,9 @@ if(EXECUTORCH_BUILD_PYBIND) ) endif() set(ENV{QNN_SDK_ROOT} ${QNN_SDK_ROOT}) -elseif(NOT DEFINED QNN_SDK_ROOT) +endif() + +if(NOT DEFINED QNN_SDK_ROOT) message( FATAL_ERROR "Please define QNN_SDK_ROOT, e.g. cmake <..> -DQNN_SDK_ROOT=<...>" From 5a6dc243bfc3721324df185f4dc3553d65221523 Mon Sep 17 00:00:00 2001 From: Mengwei Liu Date: Wed, 12 Nov 2025 22:13:53 -0800 Subject: [PATCH 11/14] Lint --- backends/qualcomm/CMakeLists.txt | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/backends/qualcomm/CMakeLists.txt b/backends/qualcomm/CMakeLists.txt index bc31a6e3f2b..a6abf4d1638 100644 --- a/backends/qualcomm/CMakeLists.txt +++ b/backends/qualcomm/CMakeLists.txt @@ -27,9 +27,7 @@ get_filename_component( if(EXECUTORCH_BUILD_PYBIND) set(_qnn_default_sdk_dir "${CMAKE_CURRENT_BINARY_DIR}/sdk/qnn") - if(EXISTS "${_qnn_default_sdk_dir}" AND EXISTS - "${_qnn_default_sdk_dir}/lib" - ) + if(EXISTS "${_qnn_default_sdk_dir}" AND EXISTS "${_qnn_default_sdk_dir}/lib") message(STATUS "Found cached Qualcomm SDK at ${_qnn_default_sdk_dir}") set(QNN_SDK_ROOT ${_qnn_default_sdk_dir} @@ -41,16 +39,15 @@ if(EXECUTORCH_BUILD_PYBIND) COMMAND ${PYTHON_EXECUTABLE} ${EXECUTORCH_SOURCE_DIR}/backends/qualcomm/scripts/download_qnn_sdk.py - --dst-folder ${_qnn_default_sdk_dir} - --print-sdk-path + --dst-folder ${_qnn_default_sdk_dir} --print-sdk-path WORKING_DIRECTORY ${EXECUTORCH_SOURCE_DIR} RESULT_VARIABLE _qnn_sdk_download_result OUTPUT_VARIABLE _qnn_sdk_download_output ERROR_VARIABLE _qnn_sdk_download_error OUTPUT_STRIP_TRAILING_WHITESPACE ) - if(NOT _qnn_sdk_download_result EQUAL 0 OR _qnn_sdk_download_output STREQUAL - "" + if(NOT _qnn_sdk_download_result EQUAL 0 OR _qnn_sdk_download_output + STREQUAL "" ) message( FATAL_ERROR From 16932190fe917a21f3f24c5d07f4e0becb56a5a1 Mon Sep 17 00:00:00 2001 From: Mengwei Liu Date: Wed, 12 Nov 2025 22:21:39 -0800 Subject: [PATCH 12/14] Address more comments --- backends/qualcomm/CMakeLists.txt | 2 +- setup.py | 2 ++ tools/cmake/preset/default.cmake | 6 ++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/backends/qualcomm/CMakeLists.txt b/backends/qualcomm/CMakeLists.txt index a6abf4d1638..5401e1929ed 100644 --- a/backends/qualcomm/CMakeLists.txt +++ b/backends/qualcomm/CMakeLists.txt @@ -24,7 +24,7 @@ get_filename_component( ) # We only download QNN SDK when we build pip wheel for ExecuTorch. -if(EXECUTORCH_BUILD_PYBIND) +if(EXECUTORCH_BUILD_WHEEL_DEV_USE_ONLY) set(_qnn_default_sdk_dir "${CMAKE_CURRENT_BINARY_DIR}/sdk/qnn") if(EXISTS "${_qnn_default_sdk_dir}" AND EXISTS "${_qnn_default_sdk_dir}/lib") diff --git a/setup.py b/setup.py index 71aa4c543d4..29214a9be2d 100644 --- a/setup.py +++ b/setup.py @@ -676,6 +676,8 @@ def run(self): # noqa C901 # like `TorchConfig.cmake` that are provided by pip packages. f"-DCMAKE_PREFIX_PATH={cmake_prefix_path}", f"-DCMAKE_BUILD_TYPE={cmake_build_type}", + # Turn on the wheel building option for downstream CMakeLists.txt. + f"-DEXECUTORCH_BUILD_WHEEL_DEV_USE_ONLY=ON", ] # Use ClangCL on Windows. diff --git a/tools/cmake/preset/default.cmake b/tools/cmake/preset/default.cmake index 0dcec0df531..f0bbb0529ae 100644 --- a/tools/cmake/preset/default.cmake +++ b/tools/cmake/preset/default.cmake @@ -288,6 +288,12 @@ define_overridable_option( BOOL FALSE ) +define_overridable_option( + EXECUTORCH_BUILD_WHEEL_DEV_USE_ONLY + "On if in the wheel building process. Should only be used to guard code that is only needed for building the wheel." + BOOL + FALSE +) # ------------------------------------------------------------------------------ # Validations From 137052eab7454cf7fae95692d002e8992a10d256 Mon Sep 17 00:00:00 2001 From: Mengwei Liu Date: Thu, 13 Nov 2025 12:04:17 -0800 Subject: [PATCH 13/14] Address more comments --- backends/qualcomm/CMakeLists.txt | 3 ++- setup.py | 2 -- tools/cmake/preset/default.cmake | 2 +- tools/cmake/preset/pybind.cmake | 1 + 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backends/qualcomm/CMakeLists.txt b/backends/qualcomm/CMakeLists.txt index 5401e1929ed..48ea2fc35d9 100644 --- a/backends/qualcomm/CMakeLists.txt +++ b/backends/qualcomm/CMakeLists.txt @@ -24,7 +24,8 @@ get_filename_component( ) # We only download QNN SDK when we build pip wheel for ExecuTorch. -if(EXECUTORCH_BUILD_WHEEL_DEV_USE_ONLY) +# Please don't change this code unless you know what you are doing. +if(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") diff --git a/setup.py b/setup.py index 29214a9be2d..71aa4c543d4 100644 --- a/setup.py +++ b/setup.py @@ -676,8 +676,6 @@ def run(self): # noqa C901 # like `TorchConfig.cmake` that are provided by pip packages. f"-DCMAKE_PREFIX_PATH={cmake_prefix_path}", f"-DCMAKE_BUILD_TYPE={cmake_build_type}", - # Turn on the wheel building option for downstream CMakeLists.txt. - f"-DEXECUTORCH_BUILD_WHEEL_DEV_USE_ONLY=ON", ] # Use ClangCL on Windows. diff --git a/tools/cmake/preset/default.cmake b/tools/cmake/preset/default.cmake index f0bbb0529ae..1d12924bfa4 100644 --- a/tools/cmake/preset/default.cmake +++ b/tools/cmake/preset/default.cmake @@ -289,7 +289,7 @@ define_overridable_option( FALSE ) define_overridable_option( - EXECUTORCH_BUILD_WHEEL_DEV_USE_ONLY + EXECUTORCH_BUILD_WHEEL_DO_NOT_USE "On if in the wheel building process. Should only be used to guard code that is only needed for building the wheel." BOOL FALSE diff --git a/tools/cmake/preset/pybind.cmake b/tools/cmake/preset/pybind.cmake index 7c4de793721..a0d06d74d17 100644 --- a/tools/cmake/preset/pybind.cmake +++ b/tools/cmake/preset/pybind.cmake @@ -22,6 +22,7 @@ set_overridable_option(EXECUTORCH_BUILD_EXTENSION_FLAT_TENSOR ON) set_overridable_option(EXECUTORCH_BUILD_EXTENSION_DATA_LOADER ON) set_overridable_option(EXECUTORCH_BUILD_EXTENSION_MODULE ON) set_overridable_option(EXECUTORCH_BUILD_EXTENSION_NAMED_DATA_MAP ON) +set_overridable_option(EXECUTORCH_BUILD_WHEEL_DO_NOT_USE ON) # TODO(larryliu0820): Temporarily disable building llm_runner for Windows wheel # due to the issue of tokenizer file path length limitation. From 1acb1519c50f28289c6f404017485071366dd586 Mon Sep 17 00:00:00 2001 From: Mengwei Liu Date: Mon, 17 Nov 2025 11:23:53 -0800 Subject: [PATCH 14/14] Lint --- backends/qualcomm/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backends/qualcomm/CMakeLists.txt b/backends/qualcomm/CMakeLists.txt index 48ea2fc35d9..cc7957dfdbe 100644 --- a/backends/qualcomm/CMakeLists.txt +++ b/backends/qualcomm/CMakeLists.txt @@ -23,8 +23,8 @@ 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. +# 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) set(_qnn_default_sdk_dir "${CMAKE_CURRENT_BINARY_DIR}/sdk/qnn")