Skip to content

Commit 5bea01a

Browse files
committed
seperate qnn sdk and libcxx - both logic are the same, pass if it exists, otherwise download and load
1 parent 2a2ad57 commit 5bea01a

File tree

3 files changed

+118
-39
lines changed

3 files changed

+118
-39
lines changed

.ci/scripts/test_wheel_package_qnn.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def main() -> None:
7373
delegated_program = to_edge_transform_and_lower_to_qnn(m, example_inputs, compile_spec)
7474
output_graph = format_delegated_graph(delegated_program.exported_program().graph_module)
7575
# Ensure QnnBackend is in the output graph
76-
assert "QnnBackend" in output
76+
assert "QnnBackend" in output_graph
7777
executorch_program = delegated_program.to_executorch(
7878
config=ExecutorchBackendConfig(extract_delegate_segments=False)
7979
)
@@ -88,7 +88,7 @@ EOF
8888
# ----------------------------
8989
echo "=== Building Wheel Package ==="
9090
# pip install torch=="2.9.0.dev20250801" --index-url "https://download.pytorch.org/whl/nightly/cpu"
91-
./install_executorch.sh --minimal
91+
PYTHON_EXECUTABLE=python bash .ci/scripts/setup-linux.sh --build-tool cmake
9292
EXECUTORCH_BUILDING_WHEEL=1 python setup.py bdist_wheel
9393
unset EXECUTORCH_BUILDING_WHEEL
9494

backends/qualcomm/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@
77
qnn_sdk_root_flag = os.getenv("QNN_SDK_ROOT", None)
88

99
if not env_flag in ("1", "true", "yes") and not qnn_sdk_root_flag:
10-
install_qnn_sdk()
10+
ok = install_qnn_sdk()
11+
if not ok:
12+
raise RuntimeError("Failed to install QNN SDK. Please check the logs above.")

backends/qualcomm/scripts/download_qnn_sdk.py

Lines changed: 113 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ def is_linux_x86() -> bool:
3131
)
3232

3333

34+
QNN_VERSION = "2.37.0.250724"
35+
36+
37+
def get_qnn_version() -> str:
38+
return QNN_VERSION
39+
40+
3441
def _download_qnn_sdk() -> Optional[pathlib.Path]:
3542
"""
3643
Download and extract the Qualcomm SDK into SDK_DIR.
@@ -39,7 +46,6 @@ def _download_qnn_sdk() -> Optional[pathlib.Path]:
3946
- Only runs on Linux x86 platforms. Skips otherwise.
4047
"""
4148
print("Downloading Qualcomm SDK...")
42-
QNN_VERSION = "2.37.0.250724"
4349
QAIRT_URL = f"https://softwarecenter.qualcomm.com/api/download/software/sdks/Qualcomm_AI_Runtime_Community/All/{QNN_VERSION}/v{QNN_VERSION}.zip"
4450
QAIRT_CONTENT_DIR = f"qairt/{QNN_VERSION}"
4551

@@ -156,7 +162,7 @@ def _extract_tar(archive_path: pathlib.Path, prefix: str, target_dir: pathlib.Pa
156162
LLVM_VERSION = "14.0.0"
157163
LIBCXX_BASE_NAME = f"clang+llvm-{LLVM_VERSION}-x86_64-linux-gnu-ubuntu-18.04"
158164
LLVM_URL = f"https://github.com/llvm/llvm-project/releases/download/llvmorg-{LLVM_VERSION}/{LIBCXX_BASE_NAME}.tar.xz"
159-
REQUIRED_LIBC_LIBS = [
165+
REQUIRED_LIBCXX_LIBS = [
160166
"libc++.so.1.0",
161167
"libc++abi.so.1.0",
162168
"libunwind.so.1",
@@ -168,7 +174,7 @@ def _extract_tar(archive_path: pathlib.Path, prefix: str, target_dir: pathlib.Pa
168174
def _stage_libcxx(target_dir: pathlib.Path):
169175
target_dir.mkdir(parents=True, exist_ok=True)
170176

171-
if all((target_dir / libname).exists() for libname in REQUIRED_LIBC_LIBS):
177+
if all((target_dir / libname).exists() for libname in REQUIRED_LIBCXX_LIBS):
172178
print(f"[libcxx] Already staged at {target_dir}, skipping download")
173179
return
174180

@@ -184,7 +190,7 @@ def _stage_libcxx(target_dir: pathlib.Path):
184190
tar.extractall(temp_extract.parent)
185191

186192
lib_src = temp_extract / "lib"
187-
for fname in REQUIRED_LIBC_LIBS:
193+
for fname in REQUIRED_LIBCXX_LIBS:
188194
src_path = lib_src / fname
189195
if not src_path.exists():
190196
print(f"[libcxx] Warning: {fname} not found in extracted LLVM")
@@ -203,37 +209,90 @@ def _stage_libcxx(target_dir: pathlib.Path):
203209
print(f"[libcxx] Staged libc++ to {target_dir}")
204210

205211

206-
REQUIRED_LIBS: List[str] = [
212+
REQUIRED_QNN_LIBS: List[str] = [
207213
"libQnnHtp.so",
208-
] + REQUIRED_LIBC_LIBS
214+
]
209215

210216

211217
def _ld_library_paths() -> List[pathlib.Path]:
218+
"""Split LD_LIBRARY_PATH into ordered directories (skip empties)."""
212219
raw = os.environ.get("LD_LIBRARY_PATH", "")
213220
return [pathlib.Path(p) for p in raw.split(":") if p.strip()]
214221

215222

216223
def _find_lib_in_ld_paths(
217224
libname: str, ld_dirs: Optional[List[pathlib.Path]] = None
218225
) -> Optional[pathlib.Path]:
226+
"""Return first matching path to `libname` in LD_LIBRARY_PATH, or None."""
219227
if ld_dirs is None:
220228
ld_dirs = _ld_library_paths()
221229
for d in ld_dirs:
222230
candidate = d / libname
223-
if candidate.exists():
224-
return candidate.resolve()
231+
try:
232+
if candidate.exists():
233+
return candidate.resolve()
234+
except Exception:
235+
# Ignore unreadable / permission issues, keep looking.
236+
pass
225237
return None
226238

227239

228-
def _check_required_libs_in_ld() -> Tuple[bool, Dict[str, Optional[pathlib.Path]]]:
240+
def _check_libs_in_ld(
241+
libnames: List[str],
242+
) -> Tuple[bool, Dict[str, Optional[pathlib.Path]]]:
243+
"""
244+
Check if each lib in `libnames` exists in LD_LIBRARY_PATH directories.
245+
246+
Returns:
247+
all_present: True iff every lib was found
248+
locations: mapping lib -> path (or None if missing)
249+
"""
229250
ld_dirs = _ld_library_paths()
230251
locations: Dict[str, Optional[pathlib.Path]] = {}
231-
for lib in REQUIRED_LIBS:
252+
for lib in libnames:
232253
locations[lib] = _find_lib_in_ld_paths(lib, ld_dirs)
233-
all_present = all(locations[lib] is not None for lib in REQUIRED_LIBS)
254+
all_present = all(locations[lib] is not None for lib in libnames)
234255
return all_present, locations
235256

236257

258+
# -----------------------
259+
# Ensure QNN SDK library
260+
# -----------------------
261+
def _ensure_qnn_sdk_lib() -> bool:
262+
"""
263+
Ensure libQnnHtp.so is available.
264+
- If found in LD_LIBRARY_PATH: do nothing, return True.
265+
- Otherwise: ensure packaged SDK is present, then load libQnnHtp.so from it.
266+
"""
267+
all_present, locs = _check_libs_in_ld(REQUIRED_QNN_LIBS)
268+
if all_present:
269+
print("[QNN] libQnnHtp.so found in LD_LIBRARY_PATH; skipping SDK install.")
270+
for lib, p in locs.items():
271+
print(f" - {lib}: {p}")
272+
return True
273+
274+
# Not found → use packaged SDK
275+
qnn_sdk_dir = SDK_DIR
276+
print(f"[QNN] libQnnHtp.so not found in LD_LIBRARY_PATH.")
277+
if not qnn_sdk_dir.exists():
278+
print("[QNN] SDK dir missing; downloading...")
279+
_download_qnn_sdk()
280+
else:
281+
print(f"[QNN] Using existing SDK at {qnn_sdk_dir}")
282+
283+
os.environ["QNN_SDK_ROOT"] = str(qnn_sdk_dir)
284+
285+
qnn_lib = qnn_sdk_dir / "lib" / "x86_64-linux-clang" / "libQnnHtp.so"
286+
print(f"[QNN] Loading {qnn_lib}")
287+
try:
288+
ctypes.CDLL(str(qnn_lib), mode=ctypes.RTLD_GLOBAL)
289+
print("[QNN] Loaded libQnnHtp.so from packaged SDK.")
290+
return True
291+
except OSError as e:
292+
print(f"[QNN][ERROR] Failed to load {qnn_lib}: {e}")
293+
return False
294+
295+
237296
def _load_libcxx_libs(lib_path):
238297
candidates = list(lib_path.glob("*.so*"))
239298
priority = ["libc++abi", "libc++"]
@@ -250,36 +309,54 @@ def _load_libcxx_libs(lib_path):
250309
print(f"[WARN] Failed to load {sofile.name}: {e}")
251310

252311

253-
def install_qnn_sdk(force_download: bool = True) -> bool:
254-
all_present, locations = _check_required_libs_in_ld()
255-
if all_present and not force_download:
256-
print("[INIT] All required libraries already present in LD_LIBRARY_PATH:")
257-
for lib, path in locations.items():
258-
print(f" - {lib}: {path}")
312+
# ---------------------
313+
# Ensure libc++ family
314+
# ---------------------
315+
def _ensure_libcxx_stack() -> bool:
316+
"""
317+
Ensure libc++ stack is available.
318+
- If all required libc++ libs are found in LD_LIBRARY_PATH: do nothing.
319+
- Otherwise: stage and load the packaged libc++ bundle.
320+
"""
321+
all_present, locs = _check_libs_in_ld(REQUIRED_LIBCXX_LIBS)
322+
if all_present:
323+
print("[libcxx] All libc++ libs present in LD_LIBRARY_PATH; skipping staging.")
324+
for lib, p in locs.items():
325+
print(f" - {lib}: {p}")
259326
return True
260327

261-
print(f"[INIT] SDK_DIR: {SDK_DIR}")
262-
if not SDK_DIR.exists():
263-
print("[INIT] Qualcomm SDK not found. Downloading...")
264-
_download_qnn_sdk()
265-
266-
os.environ["QNN_SDK_ROOT"] = str(SDK_DIR)
267-
268-
qnn_lib = SDK_DIR / "lib" / "x86_64-linux-clang" / "libQnnHtp.so"
269-
print(f"[INIT] qnn_lib: {qnn_lib}")
270-
try:
271-
ctypes.CDLL(str(qnn_lib), mode=ctypes.RTLD_GLOBAL)
272-
print(f"[INIT] Loaded QNN library from {qnn_lib}")
273-
except OSError as e:
274-
print(f"[ERROR] Failed to load QNN library at {qnn_lib}: {e}")
275-
return False
276-
328+
print(
329+
"[libcxx] Some libc++ libs missing in LD_LIBRARY_PATH; staging packaged libc++..."
330+
)
277331
try:
278332
libcxx_dir = PKG_ROOT / "sdk" / f"libcxx-{LLVM_VERSION}"
279333
_stage_libcxx(libcxx_dir)
280334
_load_libcxx_libs(libcxx_dir)
281-
print(f"[INIT] Loaded libc++ from {libcxx_dir}")
335+
print(f"[libcxx] Staged and loaded libc++ from {libcxx_dir}")
336+
return True
282337
except Exception as e:
283-
print(f"[libcxx] Warning: failed to stage/load libc++: {e}")
338+
print(f"[libcxx][ERROR] Failed to stage/load libc++: {e}")
339+
return False
340+
284341

285-
return True
342+
# ---------------
343+
# Public entrypoint
344+
# ---------------
345+
def install_qnn_sdk() -> bool:
346+
"""
347+
Initialize Qualcomm backend with separated logic:
348+
349+
QNN SDK:
350+
- If libQnnHtp.so exists in LD_LIBRARY_PATH: do nothing.
351+
- Else: ensure packaged SDK, load libQnnHtp.so.
352+
353+
libc++ stack:
354+
- If required libc++ libs exist in LD_LIBRARY_PATH: do nothing.
355+
- Else: stage and load packaged libc++.
356+
357+
Returns:
358+
True if both steps succeeded (or were already satisfied), else False.
359+
"""
360+
ok_qnn = _ensure_qnn_sdk_lib()
361+
ok_libcxx = _ensure_libcxx_stack()
362+
return bool(ok_qnn and ok_libcxx)

0 commit comments

Comments
 (0)