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
71 changes: 29 additions & 42 deletions backends/qualcomm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,53 +33,40 @@ if(NOT DEFINED QNN_SDK_ROOT AND DEFINED ENV{QNN_SDK_ROOT})
)
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")
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 (fallback)")
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
# Auto-download QNN SDK when QNN_SDK_ROOT was not provided externally. The SDK
# is cached in ~/.cache/executorch/qnn/ so it survives build dir cleans and is
# shared across editable installs, build.sh, and wheel builds.
if(NOT DEFINED QNN_SDK_ROOT)
message(STATUS "QNN_SDK_ROOT not set. Auto-downloading QNN SDK...")
execute_process(
COMMAND
${PYTHON_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
""
)
message(
FATAL_ERROR
"Failed to download Qualcomm SDK.\n"
"stderr: ${_qnn_sdk_download_error}\n"
"Set QNN_SDK_ROOT manually, e.g. cmake <..> -DQNN_SDK_ROOT=<...>"
)
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()

if(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$")
if(CMAKE_TOOLCHAIN_FILE MATCHES ".*(iOS|ios\.toolchain)\.cmake$")
message(FATAL_ERROR "ios is not supported by Qualcomm AI Engine Direct")
endif()

Expand Down
51 changes: 35 additions & 16 deletions backends/qualcomm/scripts/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,34 @@ if [[ "$(uname -s)" == "Darwin" ]]; then
exit 1
fi

if [[ -z ${QNN_SDK_ROOT} ]]; then
echo "Please export QNN_SDK_ROOT=/path/to/qnn_sdk"
exit -1
fi
SCRIPT_DIR="$( cd "$(dirname "$0")" ; pwd -P)"

# If QNN_SDK_ROOT is set, pass it to cmake. Otherwise cmake will
# auto-download the SDK via download_qnn_sdk.py during configure.
if [[ -n ${QNN_SDK_ROOT} ]]; then
QNN_SDK_CMAKE_FLAG="-DQNN_SDK_ROOT=${QNN_SDK_ROOT}"

# Ensure LD_LIBRARY_PATH includes QNN SDK libs
QNN_LIB_DIR="${QNN_SDK_ROOT}/lib/x86_64-linux-clang"
if [[ -d "${QNN_LIB_DIR}" ]] && [[ ":${LD_LIBRARY_PATH:-}:" != *":${QNN_LIB_DIR}:"* ]]; then
export LD_LIBRARY_PATH="${QNN_LIB_DIR}:${LD_LIBRARY_PATH:-}"
fi
else
QNN_SDK_CMAKE_FLAG=""
echo "[QNN] QNN_SDK_ROOT not set. SDK will be auto-downloaded during cmake configure."
fi

set -o xtrace

usage() {
echo "Usage: Build the aarch64 version of executor runner or the python interface of Qnn Manager"
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 ""
echo "QNN SDK and Android NDK will be auto-downloaded if not set."
echo "To use a custom SDK, export QNN_SDK_ROOT=/path/to/qnn_sdk"
echo "To use a custom NDK, export ANDROID_NDK_ROOT=/path/to/android_ndkXX"
echo "(or export TOOLCHAIN_ROOT_HOST=/path/to/sysroots/xx_host, "
echo "TOOLCHAIN_ROOT_TARGET=/path/to/sysroots/xx_target for linux embedded with --enable_linux_embedded)"
echo ""
echo "e.g.: executorch$ ./backends/qualcomm/scripts/build.sh --skip_x86_64"
exit 1
}
Expand All @@ -51,13 +64,13 @@ BUILD_TYPE="RelWithDebInfo"
BUILD_JOB_NUMBER="16"

# Default to use CDSP for now
DSP_TYPE=3
DSP_TYPE=3

if [ -z PYTHON_EXECUTABLE ]; then
if [ -z "$PYTHON_EXECUTABLE" ]; then
PYTHON_EXECUTABLE="python3"
fi

if [ -z BUCK2 ]; then
if [ -z "$BUCK2" ]; then
BUCK2="buck2"
fi

Expand Down Expand Up @@ -85,8 +98,14 @@ PRJ_ROOT="$( cd "$(dirname "$0")/../../.." ; pwd -P)"

if [ "$BUILD_ANDROID" = true ]; then
if [[ -z ${ANDROID_NDK_ROOT} ]]; then
echo "Please export ANDROID_NDK_ROOT=/path/to/android_ndkXX"
exit -1
echo "[QNN] ANDROID_NDK_ROOT not set. Auto-downloading Android NDK..."
source "${SCRIPT_DIR}/install_qnn_sdk.sh"
setup_android_ndk
if [[ -z ${ANDROID_NDK_ROOT} ]]; then
echo "[QNN] Error: Failed to download Android NDK."
echo "[QNN] Set ANDROID_NDK_ROOT manually."
exit 1
fi
fi

BUILD_ROOT=$PRJ_ROOT/$CMAKE_ANDROID
Expand All @@ -112,7 +131,7 @@ if [ "$BUILD_ANDROID" = true ]; then
-DEXECUTORCH_BUILD_EXTENSION_TENSOR=ON \
-DEXECUTORCH_ENABLE_EVENT_TRACER=ON \
-DEXECUTORCH_ENABLE_LOGGING=ON \
-DQNN_SDK_ROOT=$QNN_SDK_ROOT \
${QNN_SDK_CMAKE_FLAG} \
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_ROOT/build/cmake/android.toolchain.cmake \
-DANDROID_ABI='arm64-v8a' \
-DEXECUTORCH_BUILD_KERNELS_QUANTIZED=ON \
Expand All @@ -126,7 +145,7 @@ if [ "$BUILD_ANDROID" = true ]; then
CMAKE_PREFIX_PATH="${BUILD_ROOT};${BUILD_ROOT}/third-party/gflags;"

# DSP_TYPE variable only matters when building direct_mode.
# Ignore the variable for traditional mode.
# Ignore the variable for traditional mode.

if [ "$BUILD_HEXAGON" = "true" ]; then
DIRECT_MODE_FLAG="-DBUILD_DIRECT_MODE=ON"
Expand Down Expand Up @@ -274,7 +293,7 @@ if [ "$BUILD_OE_LINUX" = true ]; then
-DEXECUTORCH_BUILD_EXTENSION_TENSOR=ON \
-DEXECUTORCH_ENABLE_EVENT_TRACER=ON \
-DEXECUTORCH_ENABLE_LOGGING=ON \
-DQNN_SDK_ROOT=$QNN_SDK_ROOT \
${QNN_SDK_CMAKE_FLAG} \
-DEXECUTORCH_BUILD_KERNELS_QUANTIZED=ON \
-DPYTHON_EXECUTABLE=$PYTHON_EXECUTABLE \
-B$BUILD_ROOT
Expand Down Expand Up @@ -335,7 +354,7 @@ if [ "$BUILD_X86_64" = true ]; then
cmake \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DCMAKE_INSTALL_PREFIX=$BUILD_ROOT \
-DQNN_SDK_ROOT=${QNN_SDK_ROOT} \
${QNN_SDK_CMAKE_FLAG} \
-DEXECUTORCH_BUILD_QNN=ON \
-DEXECUTORCH_BUILD_DEVTOOLS=ON \
-DEXECUTORCH_BUILD_EXTENSION_LLM=ON \
Expand Down
21 changes: 19 additions & 2 deletions backends/qualcomm/scripts/download_qnn_sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,23 @@
_handler.setFormatter(logging.Formatter("%(message)s"))
logger.addHandler(_handler)
logger.setLevel(logging.INFO)
logger.propagate = False

PKG_ROOT = pathlib.Path(__file__).parent.parent

# Output stream for progress messages. Defaults to stdout, but redirected to
# stderr when --print-sdk-path is used (so stdout only contains the path).
_output_stream = sys.stdout


def _progress(msg: str) -> None:
"""Print a progress line with carriage return (no newline). Not suited for logging."""
print(msg, end="", flush=True)
print(msg, end="", flush=True, file=_output_stream)


def _progress_newline() -> None:
"""End a progress line."""
print(flush=True)
print(flush=True, file=_output_stream)


##########################
Expand Down Expand Up @@ -317,6 +322,10 @@ def _download_qnn_sdk(
logger.info("[QNN] Skipping Qualcomm SDK (only supported on Linux x86).")
return None

if dst_folder.exists() and any(dst_folder.iterdir()):
logger.info(f"[QNN] Using cached QNN SDK v{QNN_VERSION} at {dst_folder}")
return dst_folder

logger.info(f"[QNN] Downloading Qualcomm AI Runtime SDK v{QNN_VERSION}...")

dst_folder.mkdir(parents=True, exist_ok=True)
Expand Down Expand Up @@ -761,6 +770,14 @@ def main(argv: Optional[List[str]] = None) -> int:
)
args = parser.parse_args(argv)

# When --print-sdk-path is used, stdout must contain ONLY the SDK path.
# Redirect all logger and progress output to stderr.
if args.print_sdk_path:
global _output_stream
_output_stream = sys.stderr
for handler in logger.handlers:
handler.stream = sys.stderr

logging.basicConfig(level=logging.INFO)

dst = args.dst_folder if args.dst_folder else _get_sdk_dir()
Expand Down
2 changes: 1 addition & 1 deletion tools/cmake/preset/pybind.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
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 OFF)
set_overridable_option(EXECUTORCH_BUILD_QNN ON)
endif()
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows" OR CMAKE_SYSTEM_NAME STREQUAL
"WIN32"
Expand Down
Loading