1212
1313from tqdm import tqdm
1414
15- SDK_DIR = pathlib .Path (__file__ ).parent .parent / "sdk" / "qnn"
1615PKG_ROOT = pathlib .Path (__file__ ).parent .parent
16+ SDK_DIR = PKG_ROOT / "sdk" / "qnn"
1717
1818
1919def is_linux_x86 () -> bool :
@@ -23,10 +23,12 @@ def is_linux_x86() -> bool:
2323 Returns:
2424 bool: True if the system is Linux x86_64, False otherwise.
2525 """
26- system = platform .system ().lower ()
27- machine = platform .machine ().lower ()
28-
29- return system == "linux" and machine in ("x86_64" , "amd64" , "i386" , "i686" )
26+ return platform .system ().lower () == "linux" and platform .machine ().lower () in (
27+ "x86_64" ,
28+ "amd64" ,
29+ "i386" ,
30+ "i686" ,
31+ )
3032
3133
3234def _download_qnn_sdk () -> Optional [pathlib .Path ]:
@@ -58,16 +60,14 @@ def _download_qnn_sdk() -> Optional[pathlib.Path]:
5860
5961 print (f"Downloading Qualcomm SDK from { qairt_url } ..." )
6062 try :
61- # Use urlretrieve with a reporthook to show progress
63+
6264 def make_report_progress ():
63- last_reported = 0 # nonlocal variable
65+ last_reported = 0
6466
6567 def report_progress (block_num , block_size , total_size ):
6668 nonlocal last_reported
6769 downloaded = block_num * block_size
6870 percent = downloaded / total_size * 100
69-
70- # Report every 20%
7171 if percent - last_reported >= 20 or percent >= 100 :
7272 print (
7373 f"Downloaded: { downloaded } /{ total_size } bytes ({ percent :.2f} %)"
@@ -76,25 +76,19 @@ def report_progress(block_num, block_size, total_size):
7676
7777 return report_progress
7878
79- report_progress = make_report_progress ()
80- urllib .request .urlretrieve (qairt_url , archive_path , report_progress )
79+ urllib .request .urlretrieve (qairt_url , archive_path , make_report_progress ())
8180 print ("Download completed!" )
8281
83- # Check if file was downloaded successfully
84- if archive_path .exists ():
85- file_size = archive_path .stat ().st_size
86- print (f"Downloaded file size: { file_size } bytes" )
87- if file_size == 0 :
88- print ("WARNING: Downloaded file is empty!" )
89- else :
82+ if archive_path .exists () and archive_path .stat ().st_size == 0 :
83+ print ("WARNING: Downloaded file is empty!" )
84+ elif not archive_path .exists ():
9085 print ("ERROR: File was not downloaded!" )
9186 return None
9287
9388 except Exception as e :
9489 print (f"Error during download: { e } " )
9590 return None
9691
97- # Check extraction method
9892 if qairt_url .endswith (".zip" ):
9993 print ("Extracting ZIP archive..." )
10094 _extract_zip (archive_path , qairt_content_dir , SDK_DIR )
@@ -104,7 +98,6 @@ def report_progress(block_num, block_size, total_size):
10498 else :
10599 raise ValueError (f"Unsupported archive format: { qairt_url } " )
106100
107- # Verify extraction
108101 print (f"Verifying extraction to { SDK_DIR } " )
109102 if SDK_DIR .exists ():
110103 print (f"SDK directory exists. Contents:" )
@@ -118,43 +111,30 @@ def report_progress(block_num, block_size, total_size):
118111 return SDK_DIR
119112
120113
121- # You might also want to add detailed logging to your extraction functions
122114def _extract_zip (archive_path , content_dir , target_dir ):
123115 print (f"Extracting { archive_path } to { target_dir } " )
124116 print (f"Looking for content in subdirectory: { content_dir } " )
125117
126118 target_dir .mkdir (parents = True , exist_ok = True )
127119
128120 with zipfile .ZipFile (archive_path , "r" ) as zip_ref :
129- # Filter files that start with content_dir
130121 files_to_extract = [f for f in zip_ref .namelist () if f .startswith (content_dir )]
131122
132123 for file in tqdm (files_to_extract , desc = "Extracting files" ):
133- relative_path = os .path .relpath (file , content_dir )
134- if relative_path == "." :
135- continue # skip the root directory itself
136-
137- target_path = target_dir / relative_path
124+ relative_path = pathlib .Path (file ).relative_to (content_dir )
125+ if relative_path == pathlib .Path ("." ):
126+ continue
138127
128+ out_path = target_dir / relative_path
139129 if file .endswith ("/" ):
140- target_path .mkdir (parents = True , exist_ok = True )
130+ out_path .mkdir (parents = True , exist_ok = True )
141131 else :
142- target_path .parent .mkdir (
143- parents = True , exist_ok = True
144- ) # ensure parent exists
145- with zip_ref .open (file ) as source , open (target_path , "wb" ) as target :
146- shutil .copyfileobj (source , target )
132+ out_path .parent .mkdir (parents = True , exist_ok = True )
133+ with zip_ref .open (file ) as src , open (out_path , "wb" ) as dst :
134+ shutil .copyfileobj (src , dst )
147135
148136
149137def _extract_tar (archive_path : pathlib .Path , prefix : str , target_dir : pathlib .Path ):
150- """
151- Extract files from a tar.gz archive into target_dir, stripping a prefix.
152-
153- Args:
154- archive_path (pathlib.Path): Path to the .tar.gz or .tgz archive.
155- prefix (str): Prefix folder inside the archive to strip.
156- target_dir (pathlib.Path): Destination directory.
157- """
158138 with tarfile .open (archive_path , "r:gz" ) as tf :
159139 for m in tf .getmembers ():
160140 if not m .name .startswith (prefix + "/" ):
@@ -170,7 +150,6 @@ def _extract_tar(archive_path: pathlib.Path, prefix: str, target_dir: pathlib.Pa
170150 out_path .parent .mkdir (parents = True , exist_ok = True )
171151 src = tf .extractfile (m )
172152 if src is None :
173- # Skip non-regular files (links, devices, etc.)
174153 continue
175154 with src , open (out_path , "wb" ) as dst :
176155 dst .write (src .read ())
@@ -179,44 +158,33 @@ def _extract_tar(archive_path: pathlib.Path, prefix: str, target_dir: pathlib.Pa
179158LLVM_VERSION = "14.0.0"
180159LIBCXX_BASE_NAME = f"clang+llvm-{ LLVM_VERSION } -x86_64-linux-gnu-ubuntu-18.04"
181160LLVM_URL = f"https://github.com/llvm/llvm-project/releases/download/llvmorg-{ LLVM_VERSION } /{ LIBCXX_BASE_NAME } .tar.xz"
182-
183161REQUIRED_LIBS = [
184162 "libc++.so.1.0" ,
185163 "libc++abi.so.1.0" ,
186164 "libunwind.so.1" ,
187165 "libm.so.6" ,
188- "libpython3.10.so.1.0" , # optional, include if needed
166+ "libpython3.10.so.1.0" ,
189167]
190168
191169
192- def _get_libcxx_dir (pkg_root : pathlib .Path ) -> pathlib .Path :
193- """Path where libc++ should be staged in the wheel."""
194- return pkg_root / "sdk" / f"libcxx-{ LLVM_VERSION } "
195-
196-
197170def _stage_libcxx (target_dir : pathlib .Path ):
198- """Download LLVM tarball and stage only the needed .so files."""
199171 target_dir .mkdir (parents = True , exist_ok = True )
200172
201- # Already staged?
202173 if all ((target_dir / libname ).exists () for libname in REQUIRED_LIBS ):
203174 print (f"[libcxx] Already staged at { target_dir } , skipping download" )
204175 return
205176
206177 temp_tar = pathlib .Path ("/tmp" ) / f"{ LIBCXX_BASE_NAME } .tar.xz"
207178 temp_extract = pathlib .Path ("/tmp" ) / LIBCXX_BASE_NAME
208179
209- # Download tarball if missing
210180 if not temp_tar .exists ():
211181 print (f"[libcxx] Downloading { LLVM_URL } " )
212182 urllib .request .urlretrieve (LLVM_URL , temp_tar )
213183
214- # Extract
215184 print (f"[libcxx] Extracting { temp_tar } " )
216185 with tarfile .open (temp_tar , "r:xz" ) as tar :
217186 tar .extractall (temp_extract .parent )
218187
219- # Copy required .so files
220188 lib_src = temp_extract / "lib"
221189 for fname in REQUIRED_LIBS :
222190 src_path = lib_src / fname
@@ -225,7 +193,6 @@ def _stage_libcxx(target_dir: pathlib.Path):
225193 continue
226194 shutil .copy (src_path , target_dir / fname )
227195
228- # Create symlinks for libc++/abi
229196 libcxx = target_dir / "libc++.so.1.0"
230197 libcxx_abi = target_dir / "libc++abi.so.1.0"
231198 if libcxx .exists ():
@@ -239,24 +206,12 @@ def _stage_libcxx(target_dir: pathlib.Path):
239206
240207
241208def _load_libcxx_libs (lib_path ):
242- """
243- Load libc++ shared libraries from the given directory.
244-
245- Ensures libc++abi is loaded first, then libc++, then any other .so files.
246- """
247209 candidates = list (lib_path .glob ("*.so*" ))
248-
249210 priority = ["libc++abi" , "libc++" ]
250- sorted_candidates = []
251-
252- for name in priority :
253- for f in candidates :
254- if f .name .startswith (name ):
255- sorted_candidates .append (f )
256-
257- for f in candidates :
258- if f not in sorted_candidates :
259- sorted_candidates .append (f )
211+ sorted_candidates = [
212+ f for name in priority for f in candidates if f .name .startswith (name )
213+ ]
214+ sorted_candidates += [f for f in candidates if f not in sorted_candidates ]
260215
261216 for sofile in sorted_candidates :
262217 try :
@@ -267,27 +222,14 @@ def _load_libcxx_libs(lib_path):
267222
268223
269224def install_qnn_sdk (force_download : bool = True ) -> bool :
270- """
271- Initialize Qualcomm backend:
272- - Ensure SDK is available (env or packaged/downloaded copy)
273- - Load QNN libraries
274- - Stage and load libc++
275-
276- Returns:
277- bool: True if QNN was successfully loaded, False otherwise.
278- """
279-
280- # --- Qualcomm SDK handling ---
281- qnn_sdk_dir = SDK_DIR
282- print (f"[INIT] qnn_sdk_dir: { qnn_sdk_dir } " )
283- if not qnn_sdk_dir .exists ():
225+ print (f"[INIT] SDK_DIR: { SDK_DIR } " )
226+ if not SDK_DIR .exists ():
284227 print ("[INIT] Qualcomm SDK not found. Downloading..." )
285228 _download_qnn_sdk ()
286229
287- os .environ ["QNN_SDK_ROOT" ] = str (qnn_sdk_dir )
230+ os .environ ["QNN_SDK_ROOT" ] = str (SDK_DIR )
288231
289- # Load QNN library
290- qnn_lib = qnn_sdk_dir / "lib" / "x86_64-linux-clang" / "libQnnHtp.so"
232+ qnn_lib = SDK_DIR / "lib" / "x86_64-linux-clang" / "libQnnHtp.so"
291233 print (f"[INIT] qnn_lib: { qnn_lib } " )
292234 try :
293235 ctypes .CDLL (str (qnn_lib ), mode = ctypes .RTLD_GLOBAL )
@@ -296,9 +238,8 @@ def install_qnn_sdk(force_download: bool = True) -> bool:
296238 print (f"[ERROR] Failed to load QNN library at { qnn_lib } : { e } " )
297239 return False
298240
299- # --- libc++ handling ---
300241 try :
301- libcxx_dir = _get_libcxx_dir ( PKG_ROOT / "sdk" )
242+ libcxx_dir = PKG_ROOT / "sdk" / f"libcxx- { LLVM_VERSION } "
302243 _stage_libcxx (libcxx_dir )
303244 _load_libcxx_libs (libcxx_dir )
304245 print (f"[INIT] Loaded libc++ from { libcxx_dir } " )
0 commit comments