Skip to content

Commit 3d04265

Browse files
committed
Update
[ghstack-poisoned]
2 parents 98cb422 + 707e40f commit 3d04265

File tree

22 files changed

+1224
-123
lines changed

22 files changed

+1224
-123
lines changed

.ci/scripts/setup-qnn-deps.sh

Lines changed: 2 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,47 +7,7 @@
77

88
set -ex
99

10-
verify_pkg_installed() {
11-
echo $(dpkg-query -W --showformat='${Status}\n' $1|grep "install ok installed")
12-
}
10+
source "$(dirname "${BASH_SOURCE[0]}")/../../backends/qualcomm/scripts/install_qnn_sdk.sh"
1311

14-
install_qnn() {
15-
echo "Start installing qnn."
16-
QNN_INSTALLATION_DIR=/tmp/qnn
17-
mkdir -p "${QNN_INSTALLATION_DIR}"
18-
19-
curl -Lo /tmp/v2.28.0.24.10.29.zip "https://softwarecenter.qualcomm.com/api/download/software/qualcomm_neural_processing_sdk/v2.28.0.241029.zip"
20-
echo "Finishing downloading qnn sdk."
21-
unzip -qo /tmp/v2.28.0.24.10.29.zip -d /tmp
22-
echo "Finishing unzip qnn sdk."
23-
24-
25-
# Print the content for manual verification
26-
ls -lah "/tmp/qairt"
27-
mv "/tmp/qairt"/* "${QNN_INSTALLATION_DIR}"
28-
echo "Finishing installing qnn '${QNN_INSTALLATION_DIR}' ."
29-
30-
ls -lah "${QNN_INSTALLATION_DIR}"
31-
}
32-
33-
setup_libc++() {
34-
clang_version=$1
35-
sudo apt-get update
36-
pkgs_to_check=("libc++-${clang_version}-dev")
37-
j=0
38-
while [ $j -lt ${#pkgs_to_check[*]} ]; do
39-
install_status=$(verify_pkg_installed ${pkgs_to_check[$j]})
40-
if [ "$install_status" == "" ]; then
41-
sudo apt-get install -y ${pkgs_to_check[$j]}
42-
if [[ $? -ne 0 ]]; then
43-
echo "ERROR: Failed to install required packages for libc++"
44-
exit 1
45-
fi
46-
fi
47-
j=$(( $j +1));
48-
done
49-
}
50-
51-
# This needs to match with the clang version from the Docker image
52-
setup_libc++ 12
12+
setup_libcpp 12
5313
install_qnn
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
set -ex
2+
3+
# Get the absolute path of this script
4+
SCRIPT_DIR="$( cd -- "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 ; pwd -P )"
5+
6+
# Source QNN configuration from the same directory
7+
source "${SCRIPT_DIR}/qnn_config.sh"
8+
9+
# Function to install Android NDK (only if not already set)
10+
setup_android_ndk() {
11+
# Check if ANDROID_NDK_ROOT is already set and valid
12+
if [ -n "${ANDROID_NDK_ROOT}" ] && [ -d "${ANDROID_NDK_ROOT}" ]; then
13+
echo "Android NDK already set to ${ANDROID_NDK_ROOT} - skipping installation"
14+
return
15+
fi
16+
17+
NDK_VERSION="r26c"
18+
NDK_INSTALL_DIR="/tmp/android-ndk"
19+
20+
if [ -d "${NDK_INSTALL_DIR}/ndk" ]; then
21+
echo "Android NDK already installed at ${NDK_INSTALL_DIR}/ndk"
22+
export ANDROID_NDK_ROOT="${NDK_INSTALL_DIR}/ndk"
23+
return
24+
fi
25+
26+
echo "Installing Android NDK ${NDK_VERSION}"
27+
mkdir -p "${NDK_INSTALL_DIR}"
28+
NDK_ZIP="android-ndk-${NDK_VERSION}-linux.zip"
29+
30+
curl -Lo "/tmp/${NDK_ZIP}" "https://dl.google.com/android/repository/${NDK_ZIP}"
31+
unzip -q "/tmp/${NDK_ZIP}" -d "${NDK_INSTALL_DIR}"
32+
mv "${NDK_INSTALL_DIR}/android-ndk-${NDK_VERSION}" "${NDK_INSTALL_DIR}/ndk"
33+
34+
export ANDROID_NDK_ROOT="${NDK_INSTALL_DIR}/ndk"
35+
echo "Android NDK installed to ${ANDROID_NDK_ROOT}"
36+
}
37+
38+
verify_pkg_installed() {
39+
dpkg-query -W --showformat='${Status}\n' "$1" | grep -q "install ok installed"
40+
}
41+
42+
install_qnn() {
43+
# Check if QNN_SDK_ROOT is already set and valid
44+
if [ -n "${QNN_SDK_ROOT}" ] && [ -d "${QNN_SDK_ROOT}" ]; then
45+
echo "QNN SDK already set to ${QNN_SDK_ROOT} - skipping installation"
46+
return
47+
fi
48+
49+
echo "Start installing qnn v${QNN_VERSION}"
50+
QNN_INSTALLATION_DIR="/tmp/qnn"
51+
52+
# Clean up any previous installation
53+
if [ -d "${QNN_INSTALLATION_DIR}" ]; then
54+
echo "Removing previous QNN installation at ${QNN_INSTALLATION_DIR}"
55+
rm -rf "${QNN_INSTALLATION_DIR}"
56+
fi
57+
58+
mkdir -p "${QNN_INSTALLATION_DIR}"
59+
60+
QNN_ZIP_FILE="v${QNN_VERSION}.zip"
61+
curl -Lo "/tmp/${QNN_ZIP_FILE}" "${QNN_ZIP_URL}"
62+
echo "Finishing downloading qnn sdk."
63+
unzip -qo "/tmp/${QNN_ZIP_FILE}" -d /tmp
64+
echo "Finishing unzip qnn sdk."
65+
66+
# Print the content for manual verification
67+
echo "Contents of /tmp/qairt:"
68+
ls -lah "/tmp/qairt"
69+
70+
# Move the specific version directory
71+
if [ -d "/tmp/qairt/${QNN_VERSION}" ]; then
72+
mv "/tmp/qairt/${QNN_VERSION}" "${QNN_INSTALLATION_DIR}"
73+
else
74+
mv "/tmp/qairt"/* "${QNN_INSTALLATION_DIR}"
75+
fi
76+
77+
echo "Finishing installing qnn '${QNN_INSTALLATION_DIR}' ."
78+
echo "Final QNN installation contents:"
79+
ls -lah "${QNN_INSTALLATION_DIR}"
80+
81+
# Set QNN_SDK_ROOT environment variable
82+
export QNN_SDK_ROOT="${QNN_INSTALLATION_DIR}"
83+
echo "Set QNN_SDK_ROOT=${QNN_SDK_ROOT}"
84+
}
85+
86+
setup_libcpp() {
87+
clang_version=$1
88+
LLVM_VERSION="14.0.0"
89+
INSTALL_DIR="/tmp/libcxx-${LLVM_VERSION}"
90+
91+
# Check if we already have a local installation
92+
if [ -d "${INSTALL_DIR}/include" ] && [ -d "${INSTALL_DIR}/lib" ]; then
93+
echo "Local libc++ already installed at ${INSTALL_DIR} - skipping"
94+
# Set environment variables
95+
export CPLUS_INCLUDE_PATH="${INSTALL_DIR}/include:$CPLUS_INCLUDE_PATH"
96+
export LD_LIBRARY_PATH="${INSTALL_DIR}/lib:$LD_LIBRARY_PATH"
97+
export LIBRARY_PATH="${INSTALL_DIR}/lib:$LIBRARY_PATH"
98+
return
99+
fi
100+
101+
echo "Installing libc++ manually to ${INSTALL_DIR}"
102+
103+
# Create temporary directory
104+
TEMP_DIR=$(mktemp -d)
105+
# Ensure cleanup on exit or return
106+
trap 'rm -rf "$TEMP_DIR"' RETURN
107+
108+
pushd "${TEMP_DIR}" >/dev/null
109+
110+
BASE_NAME="clang+llvm-${LLVM_VERSION}-x86_64-linux-gnu-ubuntu-18.04"
111+
LLVM_URL="https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/${BASE_NAME}.tar.xz"
112+
113+
echo "Downloading LLVM from ${LLVM_URL}"
114+
curl -fLO "${LLVM_URL}" || {
115+
echo "Error: Failed to download LLVM"
116+
exit 1
117+
}
118+
119+
echo "Extracting ${BASE_NAME}.tar.xz"
120+
tar -xf "${BASE_NAME}.tar.xz" || {
121+
echo "Error: Failed to extract LLVM archive"
122+
exit 1
123+
}
124+
125+
# Create installation directory
126+
mkdir -p "${INSTALL_DIR}/include"
127+
mkdir -p "${INSTALL_DIR}/lib"
128+
129+
# Copy libc++ headers and libraries
130+
cp -r "${BASE_NAME}/include/c++/v1/"* "${INSTALL_DIR}/include/"
131+
cp -r "${BASE_NAME}/lib/"*.so* "${INSTALL_DIR}/lib/"
132+
133+
popd >/dev/null
134+
135+
# Create necessary symlinks locally
136+
pushd "${INSTALL_DIR}/lib" >/dev/null
137+
ln -sf libc++.so.1.0 libc++.so.1
138+
ln -sf libc++.so.1 libc++.so
139+
ln -sf libc++abi.so.1.0 libc++abi.so.1
140+
ln -sf libc++abi.so.1 libc++abi.so
141+
popd >/dev/null
142+
143+
# Set environment variables
144+
export CPLUS_INCLUDE_PATH="${INSTALL_DIR}/include:$CPLUS_INCLUDE_PATH"
145+
export LD_LIBRARY_PATH="${INSTALL_DIR}/lib:$LD_LIBRARY_PATH"
146+
export LIBRARY_PATH="${INSTALL_DIR}/lib:$LIBRARY_PATH"
147+
148+
echo "libc++ installed to ${INSTALL_DIR}"
149+
}
150+
151+
setup_libcpp 12
152+
setup_android_ndk
153+
install_qnn
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/bin/bash
2+
# Copyright (c) Meta Platforms, Inc. and affiliates.
3+
# All rights reserved.
4+
#
5+
# This source code is licensed under the BSD-style license found in the
6+
# LICENSE file in the root directory of this source tree.
7+
8+
# QNN SDK Configuration
9+
QNN_VERSION="2.28.0.241029"
10+
QNN_ZIP_URL="https://softwarecenter.qualcomm.com/api/download/software/qualcomm_neural_processing_sdk/v${QNN_VERSION}.zip"

backends/test/suite/discovery.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ def _filter_tests(
6868

6969
def _is_test_enabled(test_case: unittest.TestCase, test_filter: TestFilter) -> bool:
7070
test_method = getattr(test_case, test_case._testMethodName)
71+
72+
if not hasattr(test_method, "_flow"):
73+
print(f"Test missing flow: {test_method}")
74+
7175
flow: TestFlow = test_method._flow
7276

7377
if test_filter.backends is not None and flow.backend not in test_filter.backends:
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
# All rights reserved.
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
# pyre-unsafe
8+
9+
import itertools
10+
import os
11+
import unittest
12+
from typing import Any, Callable
13+
14+
import torch
15+
from executorch.backends.test.harness import Tester
16+
from executorch.backends.test.suite import get_test_flows
17+
from executorch.backends.test.suite.context import get_active_test_context, TestContext
18+
from executorch.backends.test.suite.flow import TestFlow
19+
from executorch.backends.test.suite.reporting import log_test_summary
20+
from executorch.backends.test.suite.runner import run_test
21+
22+
23+
DTYPES: list[torch.dtype] = [
24+
torch.float16,
25+
torch.float32,
26+
torch.float64,
27+
]
28+
29+
30+
def load_tests(loader, suite, pattern):
31+
package_dir = os.path.dirname(__file__)
32+
discovered_suite = loader.discover(
33+
start_dir=package_dir, pattern=pattern or "test_*.py"
34+
)
35+
suite.addTests(discovered_suite)
36+
return suite
37+
38+
39+
def _create_test(
40+
cls,
41+
test_func: Callable,
42+
flow: TestFlow,
43+
dtype: torch.dtype,
44+
use_dynamic_shapes: bool,
45+
):
46+
def wrapped_test(self):
47+
params = {
48+
"dtype": dtype,
49+
"use_dynamic_shapes": use_dynamic_shapes,
50+
}
51+
with TestContext(test_name, flow.name, params):
52+
test_func(self, dtype, use_dynamic_shapes, flow.tester_factory)
53+
54+
dtype_name = str(dtype)[6:] # strip "torch."
55+
test_name = f"{test_func.__name__}_{flow.name}_{dtype_name}"
56+
if use_dynamic_shapes:
57+
test_name += "_dynamic_shape"
58+
59+
wrapped_test._name = test_func.__name__ # type: ignore
60+
wrapped_test._flow = flow # type: ignore
61+
62+
setattr(cls, test_name, wrapped_test)
63+
64+
65+
# Expand a test into variants for each registered flow.
66+
def _expand_test(cls, test_name: str) -> None:
67+
test_func = getattr(cls, test_name)
68+
supports_dynamic_shapes = getattr(test_func, "supports_dynamic_shapes", True)
69+
dynamic_shape_values = [True, False] if supports_dynamic_shapes else [False]
70+
dtypes = getattr(test_func, "dtypes", DTYPES)
71+
72+
for flow, dtype, use_dynamic_shapes in itertools.product(
73+
get_test_flows().values(), dtypes, dynamic_shape_values
74+
):
75+
_create_test(cls, test_func, flow, dtype, use_dynamic_shapes)
76+
delattr(cls, test_name)
77+
78+
79+
def model_test_cls(cls) -> Callable | None:
80+
"""Decorator for model tests. Handles generating test variants for each test flow and configuration."""
81+
for key in dir(cls):
82+
if key.startswith("test_"):
83+
_expand_test(cls, key)
84+
return cls
85+
86+
87+
def model_test_params(
88+
supports_dynamic_shapes: bool = True,
89+
dtypes: list[torch.dtype] | None = None,
90+
) -> Callable:
91+
"""Optional parameter decorator for model tests. Specifies test pararameters. Only valid with a class decorated by model_test_cls."""
92+
93+
def inner_decorator(func: Callable) -> Callable:
94+
func.supports_dynamic_shapes = supports_dynamic_shapes # type: ignore
95+
96+
if dtypes is not None:
97+
func.dtypes = dtypes # type: ignore
98+
99+
return func
100+
101+
return inner_decorator
102+
103+
104+
def run_model_test(
105+
model: torch.nn.Module,
106+
inputs: tuple[Any],
107+
dtype: torch.dtype,
108+
dynamic_shapes: Any | None,
109+
tester_factory: Callable[[], Tester],
110+
):
111+
model = model.to(dtype)
112+
context = get_active_test_context()
113+
114+
# This should be set in the wrapped test. See _create_test above.
115+
assert context is not None, "Missing test context."
116+
117+
run_summary = run_test(
118+
model,
119+
inputs,
120+
tester_factory,
121+
context.test_name,
122+
context.flow_name,
123+
context.params,
124+
dynamic_shapes=dynamic_shapes,
125+
)
126+
127+
log_test_summary(run_summary)
128+
129+
if not run_summary.result.is_success():
130+
if run_summary.result.is_backend_failure():
131+
raise RuntimeError("Test failure.") from run_summary.error
132+
else:
133+
# Non-backend failure indicates a bad test. Mark as skipped.
134+
raise unittest.SkipTest(
135+
f"Test failed for reasons other than backend failure. Error: {run_summary.error}"
136+
)

0 commit comments

Comments
 (0)