@@ -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+
3441def _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
156162LLVM_VERSION = "14.0.0"
157163LIBCXX_BASE_NAME = f"clang+llvm-{ LLVM_VERSION } -x86_64-linux-gnu-ubuntu-18.04"
158164LLVM_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
168174def _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
211217def _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
216223def _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+
237296def _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