77import tarfile
88import tempfile
99import urllib .request
10+ import zipfile
1011from typing import Optional
1112
13+ from tqdm import tqdm
14+
1215SDK_DIR = pathlib .Path (__file__ ).parent .parent / "sdk" / "qnn"
1316PKG_ROOT = pathlib .Path (__file__ ).parent
1417
@@ -110,63 +113,90 @@ def _extract_zip(archive_path, content_dir, target_dir):
110113 print (f"Extracting { archive_path } to { target_dir } " )
111114 print (f"Looking for content in subdirectory: { content_dir } " )
112115
113- # Add your zip extraction code here, with additional logging
114- # For example:
115- import zipfile
116+ target_dir .mkdir (parents = True , exist_ok = True )
116117
117118 with zipfile .ZipFile (archive_path , "r" ) as zip_ref :
118- # List all files in the archive
119- print ("Files in archive:" )
120- for file in zip_ref .namelist ():
121- print (f" { file } " )
122-
123- # Extract only the specific content directory
124- for file in zip_ref .namelist ():
125- if file .startswith (content_dir ):
126- # Extract with path relative to content_dir
127- relative_path = os .path .relpath (file , content_dir )
128- if relative_path == "." :
129- continue # Skip the directory entry itself
130- target_path = target_dir / relative_path
131- if file .endswith ("/" ):
132- # Create directory
133- target_path .mkdir (parents = True , exist_ok = True )
134- else :
135- # Extract file
136- with zip_ref .open (file ) as source , open (
137- target_path , "wb"
138- ) as target :
139- shutil .copyfileobj (source , target )
140- print (f"Extracted: { relative_path } " )
119+ # Filter files that start with content_dir
120+ files_to_extract = [f for f in zip_ref .namelist () if f .startswith (content_dir )]
141121
122+ for file in tqdm (files_to_extract , desc = "Extracting files" ):
123+ relative_path = os .path .relpath (file , content_dir )
124+ if relative_path == "." :
125+ continue # skip the root directory itself
142126
143- LLVM_VERSION = "14.0.0"
144- LIBCXX_LIB_DIR = (
145- PKG_ROOT / "executorch" / "backends" / "qualcomm" / "sdk" / f"libcxx-{ LLVM_VERSION } "
146- )
147- LIBCXX_BASE_NAME = f"clang+llvm-{ LLVM_VERSION } -x86_64-linux-gnu-ubuntu-20.04"
127+ target_path = target_dir / relative_path
128+
129+ if file .endswith ("/" ):
130+ target_path .mkdir (parents = True , exist_ok = True )
131+ else :
132+ target_path .parent .mkdir (
133+ parents = True , exist_ok = True
134+ ) # ensure parent exists
135+ with zip_ref .open (file ) as source , open (target_path , "wb" ) as target :
136+ shutil .copyfileobj (source , target )
148137
149138
150- def stage_libcxx ( target_dir : pathlib .Path ):
139+ def _extract_tar ( archive_path : pathlib . Path , prefix : str , target_dir : pathlib .Path ):
151140 """
152- Download (if needed) and stage libc++ shared libraries into the wheel package.
153- - target_dir: destination folder in the wheel, e.g.
154- executorch/backends/qualcomm/sdk/libcxx-14.0.0
141+ Extract files from a tar.gz archive into target_dir, stripping a prefix.
142+
143+ Args:
144+ archive_path (pathlib.Path): Path to the .tar.gz or .tgz archive.
145+ prefix (str): Prefix folder inside the archive to strip.
146+ target_dir (pathlib.Path): Destination directory.
155147 """
148+ with tarfile .open (archive_path , "r:gz" ) as tf :
149+ for m in tf .getmembers ():
150+ if not m .name .startswith (prefix + "/" ):
151+ continue
152+ relpath = pathlib .Path (m .name ).relative_to (prefix )
153+ if not relpath .parts or relpath .parts [0 ] == ".." :
154+ continue
155+
156+ out_path = target_dir / relpath
157+ if m .isdir ():
158+ out_path .mkdir (parents = True , exist_ok = True )
159+ else :
160+ out_path .parent .mkdir (parents = True , exist_ok = True )
161+ src = tf .extractfile (m )
162+ if src is None :
163+ # Skip non-regular files (links, devices, etc.)
164+ continue
165+ with src , open (out_path , "wb" ) as dst :
166+ dst .write (src .read ())
167+
168+
169+ LLVM_VERSION = "14.0.0"
170+ LIBCXX_BASE_NAME = f"clang+llvm-{ LLVM_VERSION } -x86_64-linux-gnu-ubuntu-18.04"
171+ LLVM_URL = f"https://github.com/llvm/llvm-project/releases/download/llvmorg-{ LLVM_VERSION } /{ LIBCXX_BASE_NAME } .tar.xz"
172+
173+ REQUIRED_LIBS = [
174+ "libc++.so.1.0" ,
175+ "libc++abi.so.1.0" ,
176+ "libunwind.so.1" ,
177+ "libm.so.6" ,
178+ "libpython3.10.so.1.0" , # optional, include if needed
179+ ]
180+
181+
182+ def _get_libcxx_dir (pkg_root : pathlib .Path ) -> pathlib .Path :
183+ """Path where libc++ should be staged in the wheel."""
184+ return pkg_root / "sdk" / f"libcxx-{ LLVM_VERSION } "
185+
186+
187+ def _stage_libcxx (target_dir : pathlib .Path ):
188+ """Download LLVM tarball and stage only the needed .so files."""
156189 target_dir .mkdir (parents = True , exist_ok = True )
157190
158- # Check if already staged
159- existing_files = list (target_dir .glob ("*" ))
160- if existing_files :
161- print (f"[libcxx] Already staged at { target_dir } , skipping" )
191+ # Already staged?
192+ if all ((target_dir / libname ).exists () for libname in REQUIRED_LIBS ):
193+ print (f"[libcxx] Already staged at { target_dir } , skipping download" )
162194 return
163195
164- # URL to a tarball with prebuilt libc++ shared libraries
165- LLVM_URL = f"https://github.com/llvm/llvm-project/releases/download/llvmorg-{ LLVM_VERSION } /{ LIBCXX_BASE_NAME } .tar.xz"
166196 temp_tar = pathlib .Path ("/tmp" ) / f"{ LIBCXX_BASE_NAME } .tar.xz"
167- temp_extract = pathlib .Path ("/tmp" ) / f" { LIBCXX_BASE_NAME } "
197+ temp_extract = pathlib .Path ("/tmp" ) / LIBCXX_BASE_NAME
168198
169- # Download if not already exists
199+ # Download tarball if missing
170200 if not temp_tar .exists ():
171201 print (f"[libcxx] Downloading { LLVM_URL } " )
172202 urllib .request .urlretrieve (LLVM_URL , temp_tar )
@@ -176,26 +206,24 @@ def stage_libcxx(target_dir: pathlib.Path):
176206 with tarfile .open (temp_tar , "r:xz" ) as tar :
177207 tar .extractall (temp_extract .parent )
178208
179- # Copy only the required .so files
209+ # Copy required .so files
180210 lib_src = temp_extract / "lib"
181- required_files = [
182- "libc++.so.1.0" ,
183- "libc++abi.so.1.0" ,
184- "libunwind.so.1" ,
185- "libm.so.6" ,
186- "libpython3.10.so.1.0" ,
187- ]
188- for fname in required_files :
211+ for fname in REQUIRED_LIBS :
189212 src_path = lib_src / fname
190213 if not src_path .exists ():
191- raise FileNotFoundError (f"{ fname } not found in extracted LLVM" )
214+ print (f"[libcxx] Warning: { fname } not found in extracted LLVM" )
215+ continue
192216 shutil .copy (src_path , target_dir / fname )
193217
194- # Create symlinks
195- os .symlink ("libc++.so.1.0" , target_dir / "libc++.so.1" )
196- os .symlink ("libc++.so.1" , target_dir / "libc++.so" )
197- os .symlink ("libc++abi.so.1.0" , target_dir / "libc++abi.so.1" )
198- os .symlink ("libc++abi.so.1" , target_dir / "libc++abi.so" )
218+ # Create symlinks for libc++/abi
219+ libcxx = target_dir / "libc++.so.1.0"
220+ libcxx_abi = target_dir / "libc++abi.so.1.0"
221+ if libcxx .exists ():
222+ os .symlink ("libc++.so.1.0" , target_dir / "libc++.so.1" )
223+ os .symlink ("libc++.so.1" , target_dir / "libc++.so" )
224+ if libcxx_abi .exists ():
225+ os .symlink ("libc++abi.so.1.0" , target_dir / "libc++abi.so.1" )
226+ os .symlink ("libc++abi.so.1" , target_dir / "libc++abi.so" )
199227
200228 print (f"[libcxx] Staged libc++ to { target_dir } " )
201229
0 commit comments