|
6 | 6 | import platform |
7 | 7 | import re |
8 | 8 | import shutil |
| 9 | +import sys |
9 | 10 | import tarfile |
10 | 11 | import tempfile |
11 | 12 | import urllib.request |
12 | 13 | import zipfile |
13 | 14 | from typing import Dict, List, Optional, Tuple |
14 | 15 |
|
| 16 | + |
15 | 17 | logger = logging.getLogger(__name__) |
16 | 18 | logger.addHandler(logging.NullHandler()) |
17 | 19 |
|
@@ -241,6 +243,115 @@ def _extract_tar(archive_path: pathlib.Path, prefix: str, target_dir: pathlib.Pa |
241 | 243 | dst.write(src.read()) |
242 | 244 |
|
243 | 245 |
|
| 246 | +#################### |
| 247 | +# libc management |
| 248 | +#################### |
| 249 | + |
| 250 | +GLIBC_ROOT = pathlib.Path("/tmp/glibc-2.34") |
| 251 | +GLIBC_LOADER = GLIBC_ROOT / "lib" / "ld-linux-x86-64.so.2" |
| 252 | +GLIBC_LIBDIR = GLIBC_ROOT / "lib" |
| 253 | +GLIBC_REEXEC_GUARD = "QNN_GLIBC_REEXEC" |
| 254 | + |
| 255 | + |
| 256 | +def _parse_ver_tuple(s: str) -> Tuple[int, int]: |
| 257 | + parts = re.findall(r"\d+", s) |
| 258 | + return (int(parts[0]), int(parts[1])) if len(parts) >= 2 else (0, 0) |
| 259 | + |
| 260 | + |
| 261 | +def _detect_glibc_version() -> Optional[Tuple[int, int]]: |
| 262 | + for path in REQUIRED_LIBC_LIBS: |
| 263 | + try: |
| 264 | + out = subprocess.check_output([path, "--version"], stderr=subprocess.STDOUT) |
| 265 | + first = out.decode(errors="ignore").split("\n", 1)[0] |
| 266 | + m = re.search(r"version\s+(\d+\.\d+)", first, re.IGNORECASE) |
| 267 | + if m: |
| 268 | + vt = _parse_ver_tuple(m.group(1)) |
| 269 | + logger.info("[glibc] Found %s version %s", path, vt) |
| 270 | + return vt |
| 271 | + except Exception as e: |
| 272 | + logger.info("[glibc] Skipped %s (%s)", path, e) |
| 273 | + return None |
| 274 | + |
| 275 | + |
| 276 | +def _install_glibc_234(): |
| 277 | + """Download and build glibc 2.34 into /tmp/glibc-2.34 if missing.""" |
| 278 | + if GLIBC_LOADER.exists(): |
| 279 | + logger.info("[glibc] Found existing glibc-2.34 at %s", GLIBC_ROOT) |
| 280 | + return |
| 281 | + |
| 282 | + logger.info( |
| 283 | + "[glibc] Installing glibc 2.34 into %s ... this may take a while", GLIBC_ROOT |
| 284 | + ) |
| 285 | + url = "https://ftp.gnu.org/gnu/libc/glibc-2.34.tar.xz" |
| 286 | + |
| 287 | + with tempfile.TemporaryDirectory() as tmpdir: |
| 288 | + tarball = pathlib.Path(tmpdir) / "glibc-2.34.tar.xz" |
| 289 | + urllib.request.urlretrieve(url, tarball) |
| 290 | + logger.info("[glibc] Downloaded %s", url) |
| 291 | + build_dir = pathlib.Path(tmpdir) / "glibc-build" |
| 292 | + src_dir = pathlib.Path(tmpdir) / "glibc-2.34" |
| 293 | + os.makedirs(build_dir, exist_ok=True) |
| 294 | + |
| 295 | + # Extract |
| 296 | + logger.info("[glibc] Extracting source...") |
| 297 | + with tarfile.open(tarball, "r:xz") as tf: |
| 298 | + tf.extractall(path=tmpdir) |
| 299 | + |
| 300 | + # Configure and build |
| 301 | + logger.info("[glibc] Configuring build...") |
| 302 | + subprocess.check_call( |
| 303 | + ["../glibc-2.34/configure", f"--prefix={GLIBC_ROOT}"], |
| 304 | + cwd=build_dir, |
| 305 | + ) |
| 306 | + logger.info("[glibc] Building (this may take several minutes)...") |
| 307 | + subprocess.check_call(["make", "-j", str(os.cpu_count())], cwd=build_dir) |
| 308 | + logger.info("[glibc] Installing...") |
| 309 | + subprocess.check_call(["make", "install"], cwd=build_dir) |
| 310 | + |
| 311 | + if GLIBC_LOADER.exists(): |
| 312 | + logger.info("[glibc] Successfully installed glibc 2.34 at %s", GLIBC_ROOT) |
| 313 | + else: |
| 314 | + logger.error( |
| 315 | + "[glibc] Install finished but loader not found at %s", GLIBC_LOADER |
| 316 | + ) |
| 317 | + |
| 318 | + |
| 319 | +def _reexec_with_new_glibc_if_needed(min_required=(2, 29)): |
| 320 | + if os.environ.get(GLIBC_REEXEC_GUARD) == "1": |
| 321 | + logger.debug("[glibc] Already re-executed once; skipping loop.") |
| 322 | + return |
| 323 | + |
| 324 | + vt = _detect_glibc_version() |
| 325 | + if vt is None: |
| 326 | + logger.warn("[glibc] Could not detect system glibc version.") |
| 327 | + elif vt < min_required: |
| 328 | + logger.warn("[glibc] System glibc %s < required %s", vt, min_required) |
| 329 | + else: |
| 330 | + logger.info("[glibc] System glibc %s >= required %s", vt, min_required) |
| 331 | + return |
| 332 | + |
| 333 | + _install_glibc_234() |
| 334 | + if not GLIBC_LOADER.exists(): |
| 335 | + logger.error("[glibc] Loader still missing at %s", GLIBC_LOADER) |
| 336 | + return |
| 337 | + |
| 338 | + argv = [ |
| 339 | + str(GLIBC_LOADER), |
| 340 | + "--library-path", |
| 341 | + str(GLIBC_LIBDIR), |
| 342 | + sys.executable, |
| 343 | + ] + sys.argv |
| 344 | + env = os.environ.copy() |
| 345 | + env[GLIBC_REEXEC_GUARD] = "1" |
| 346 | + |
| 347 | + logger.warn("[glibc] Re-executing under new loader: %s", argv) |
| 348 | + os.execvpe(str(GLIBC_LOADER), argv, env) |
| 349 | + |
| 350 | + |
| 351 | +#################### |
| 352 | +# libc++ management |
| 353 | +#################### |
| 354 | + |
244 | 355 | LLVM_VERSION = "14.0.0" |
245 | 356 | LIBCXX_BASE_NAME = f"clang+llvm-{LLVM_VERSION}-x86_64-linux-gnu-ubuntu-18.04" |
246 | 357 | LLVM_URL = f"https://github.com/llvm/llvm-project/releases/download/llvmorg-{LLVM_VERSION}/{LIBCXX_BASE_NAME}.tar.xz" |
@@ -437,6 +548,10 @@ def install_qnn_sdk() -> bool: |
437 | 548 | Returns: |
438 | 549 | True if both steps succeeded (or were already satisfied), else False. |
439 | 550 | """ |
| 551 | + logger.info("[QNN] Starting SDK installation") |
| 552 | + # Re-exec with glibc 2.34 if needed. |
| 553 | + _reexec_with_new_glibc_if_needed() |
| 554 | + |
440 | 555 | if _ensure_libcxx_stack(): |
441 | 556 | if _ensure_qnn_sdk_lib(): |
442 | 557 | return True |
|
0 commit comments