Skip to content

Commit 57dde3f

Browse files
authored
Revert "Try to get nightly wheel build work with qnn (#14633)"
This reverts commit 19be2a3.
1 parent 53ccfd0 commit 57dde3f

File tree

3 files changed

+79
-218
lines changed

3 files changed

+79
-218
lines changed

backends/qualcomm/__init__.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
11
import os
22

3-
from .scripts.download_qnn_sdk import install_qnn_sdk, is_linux_x86
3+
from .scripts.download_qnn_sdk import (
4+
check_glibc_exist_and_validate,
5+
install_qnn_sdk,
6+
is_linux_x86,
7+
)
48

59

610
env_flag = os.getenv("EXECUTORCH_BUILDING_WHEEL", "0").lower()
711
# If users have preinstalled QNN_SDK_ROOT, we will use it.
812
qnn_sdk_root_flag = os.getenv("QNN_SDK_ROOT", None)
913

10-
if env_flag not in ("1", "true", "yes") and not qnn_sdk_root_flag and is_linux_x86():
14+
if (
15+
env_flag not in ("1", "true", "yes")
16+
and not qnn_sdk_root_flag
17+
and is_linux_x86()
18+
and check_glibc_exist_and_validate()
19+
):
1120
ok = install_qnn_sdk()
21+
1222
if not ok:
1323
raise RuntimeError("Failed to install QNN SDK. Please check the logs above.")

backends/qualcomm/scripts/download_qnn_sdk.py

Lines changed: 65 additions & 215 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,12 @@
66
import platform
77
import re
88
import shutil
9-
import subprocess
10-
import sys
119
import tarfile
1210
import tempfile
1311
import urllib.request
1412
import zipfile
1513
from typing import Dict, List, Optional, Tuple
1614

17-
1815
logger = logging.getLogger(__name__)
1916
logger.addHandler(logging.NullHandler())
2017

@@ -37,81 +34,68 @@ def is_linux_x86() -> bool:
3734
)
3835

3936

40-
#########################
41-
# Cache directory helper
42-
#########################
43-
44-
APP_NAMESPACE = ["executorch", "qnn"]
45-
46-
47-
def _get_staging_dir(*parts: str) -> pathlib.Path:
48-
r"""
49-
Return a cross-platform staging directory for staging SDKs/libraries.
50-
51-
- On Linux:
52-
~/.cache/executorch/qnn/<parts...>
53-
(falls back to $HOME/.cache if $XDG_CACHE_HOME is unset)
54-
55-
- On Windows (not supported yet, but as placeholder):
56-
%LOCALAPPDATA%\executorch\qnn\<parts...>
57-
(falls back to $HOME/AppData/Local if %LOCALAPPDATA% is unset)
58-
59-
- Override:
60-
If QNN_STAGING_DIR is set in the environment, that path is used instead.
61-
62-
Args:
63-
parts (str): Subdirectories to append under the root staging dir.
37+
import subprocess
6438

65-
Returns:
66-
pathlib.Path: Fully qualified staging path.
67-
"""
68-
# Environment override wins
69-
base = os.environ.get("QNN_STAGING_DIR")
70-
if base:
71-
return pathlib.Path(base).joinpath(*parts)
72-
73-
system = platform.system().lower()
74-
if system == "windows":
75-
# On Windows, prefer %LOCALAPPDATA%, fallback to ~/AppData/Local
76-
base = pathlib.Path(
77-
os.environ.get("LOCALAPPDATA", pathlib.Path.home() / "AppData" / "Local")
78-
)
79-
elif is_linux_x86():
80-
# On Linux/Unix, prefer $XDG_CACHE_HOME, fallback to ~/.cache
81-
base = pathlib.Path(
82-
os.environ.get("XDG_CACHE_HOME", pathlib.Path.home() / ".cache")
83-
)
84-
else:
85-
raise ValueError(f"Unsupported platform: {system}")
39+
MINIMUM_LIBC_VERSION = 2.29
8640

87-
return base.joinpath(*APP_NAMESPACE, *parts)
41+
REQUIRED_LIBC_LIBS = [
42+
"/lib/x86_64-linux-gnu/libc.so.6",
43+
"/lib64/libc.so.6",
44+
"/lib/libc.so.6",
45+
]
8846

8947

90-
def _atomic_download(url: str, dest: pathlib.Path):
48+
def check_glibc_exist_and_validate() -> bool:
9149
"""
92-
Download URL into dest atomically:
93-
- Write to a temp file in the same dir
94-
- Move into place if successful
50+
Check if users have glibc installed.
9551
"""
96-
dest.parent.mkdir(parents=True, exist_ok=True)
97-
98-
# Temp file in same dir (guarantees atomic rename)
99-
with tempfile.NamedTemporaryFile(dir=dest.parent, delete=False) as tmp:
100-
tmp_path = pathlib.Path(tmp.name)
101-
102-
try:
103-
urllib.request.urlretrieve(url, tmp_path)
104-
tmp_path.replace(dest) # atomic rename
105-
except Exception:
106-
# Clean up partial file on failure
107-
if tmp_path.exists():
108-
tmp_path.unlink(missing_ok=True)
109-
raise
52+
exists = False
53+
for path in REQUIRED_LIBC_LIBS:
54+
try:
55+
output = subprocess.check_output(
56+
[path, "--version"], stderr=subprocess.STDOUT
57+
)
58+
output = output.decode().split("\n")[0]
59+
logger.debug(f"[QNN] glibc version for path {path} is: {output}")
60+
match = re.search(r"version (\d+\.\d+)", output)
61+
if match:
62+
version = match.group(1)
63+
if float(version) >= MINIMUM_LIBC_VERSION:
64+
logger.debug(f"[QNN] glibc version is {version}.")
65+
exists = True
66+
return True
67+
else:
68+
logger.error(
69+
f"[QNN] glibc version is too low. The minimum libc version is {MINIMUM_LIBC_VERSION} Please install glibc following the commands below."
70+
)
71+
else:
72+
logger.error("[QNN] glibc version not found.")
11073

74+
except Exception:
75+
continue
11176

112-
####################
113-
# qnn sdk download management
114-
####################
77+
if not exists:
78+
logger.error(
79+
r""""
80+
[QNN] glibc not found or the version is too low. Please install glibc following the commands below.
81+
Ubuntu/Debian:
82+
sudo apt update
83+
sudo apt install libc6
84+
85+
Fedora/Red Hat:
86+
sudo dnf install glibc
87+
88+
Arch Linux:
89+
sudo pacman -S glibc
90+
91+
Also please make sure the glibc version is >= MINIMUM_LIBC_VERSION. You can verify the glibc version by running the following command:
92+
Option 1:
93+
ldd --version
94+
Option 2:
95+
/path/to/libc.so.6 --version
96+
"""
97+
)
98+
return exists
11599

116100

117101
def _download_archive(url: str, archive_path: pathlib.Path) -> bool:
@@ -194,6 +178,9 @@ def _download_qnn_sdk(dst_folder=SDK_DIR) -> Optional[pathlib.Path]:
194178
if not is_linux_x86():
195179
logger.info("[QNN] Skipping Qualcomm SDK (only supported on Linux x86).")
196180
return None
181+
elif not check_glibc_exist_and_validate():
182+
logger.info("[QNN] Skipping Qualcomm SDK (glibc not found or version too old).")
183+
return None
197184
else:
198185
logger.info("[QNN] Downloading Qualcomm SDK for Linux x86")
199186

@@ -254,136 +241,6 @@ def _extract_tar(archive_path: pathlib.Path, prefix: str, target_dir: pathlib.Pa
254241
dst.write(src.read())
255242

256243

257-
####################
258-
# libc management
259-
####################
260-
261-
GLIBC_VERSION = "2.34"
262-
GLIBC_REEXEC_GUARD = "QNN_GLIBC_REEXEC"
263-
MINIMUM_LIBC_VERSION = GLIBC_VERSION
264-
265-
266-
def _get_glibc_libdir() -> pathlib.Path:
267-
glibc_root = _get_staging_dir(f"glibc-{GLIBC_VERSION}")
268-
return glibc_root / "lib"
269-
270-
271-
def _parse_version(v: str) -> tuple[int, int]:
272-
"""Turn '2.34' → (2,34) so it can be compared."""
273-
parts = v.split(".")
274-
return int(parts[0]), int(parts[1]) if len(parts) > 1 else 0
275-
276-
277-
def _current_glibc_version() -> str:
278-
"""Return system glibc version string (via ctypes)."""
279-
try:
280-
libc = ctypes.CDLL("libc.so.6")
281-
func = libc.gnu_get_libc_version
282-
func.restype = ctypes.c_char_p
283-
return func().decode()
284-
except Exception as e:
285-
return f"error:{e}"
286-
287-
288-
def _resolve_glibc_loader() -> pathlib.Path | None:
289-
"""Return staged ld.so path if available."""
290-
for p in [
291-
_get_glibc_libdir() / f"ld-{GLIBC_VERSION}.so",
292-
_get_glibc_libdir() / "ld-linux-x86-64.so.2",
293-
]:
294-
if p.exists():
295-
return p
296-
return None
297-
298-
299-
def _stage_prebuilt_glibc():
300-
"""Download + extract Fedora 35 glibc RPM into /tmp."""
301-
logger.info(">>> Staging prebuilt glibc-%s from Fedora 35 RPM", GLIBC_VERSION)
302-
_get_glibc_libdir().mkdir(parents=True, exist_ok=True)
303-
rpm_path = _get_staging_dir("glibc") / "glibc.rpm"
304-
work_dir = _get_staging_dir("glibc") / "extracted"
305-
rpm_url = (
306-
"https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/35/"
307-
"Everything/x86_64/os/Packages/g/glibc-2.34-7.fc35.x86_64.rpm"
308-
)
309-
310-
rpm_path.parent.mkdir(parents=True, exist_ok=True)
311-
logger.info("[glibc] Downloading %s -> %s", rpm_url, rpm_path)
312-
try:
313-
urllib.request.urlretrieve(rpm_url, rpm_path)
314-
except Exception as e:
315-
logger.error("[glibc] Failed to download %s: %s", rpm_url, e)
316-
raise
317-
318-
# Extract
319-
if work_dir.exists():
320-
shutil.rmtree(work_dir)
321-
work_dir.mkdir(parents=True)
322-
subprocess.check_call(["bsdtar", "-C", str(work_dir), "-xf", str(rpm_path)])
323-
324-
# Copy runtime libs
325-
staged = [
326-
"ld-linux-x86-64.so.2",
327-
"libc.so.6",
328-
"libdl.so.2",
329-
"libpthread.so.0",
330-
"librt.so.1",
331-
"libm.so.6",
332-
"libutil.so.1",
333-
]
334-
for lib in staged:
335-
src = work_dir / "lib64" / lib
336-
if src.exists():
337-
shutil.copy2(src, _get_glibc_libdir() / lib)
338-
logger.info("[glibc] Staged %s", lib)
339-
else:
340-
logger.warning("[glibc] Missing %s in RPM", lib)
341-
342-
343-
def ensure_glibc_minimum(min_version: str = GLIBC_VERSION):
344-
"""
345-
Ensure process runs under glibc >= min_version.
346-
- If system glibc is new enough → skip.
347-
- Else → stage Fedora RPM and re-exec under staged loader.
348-
"""
349-
current = _current_glibc_version()
350-
logger.info("[glibc] Current loaded glibc: %s", current)
351-
352-
# If system glibc already sufficient → skip everything
353-
m = re.match(r"(\d+\.\d+)", current)
354-
if m and _parse_version(m.group(1)) >= _parse_version(min_version):
355-
logger.info("[glibc] System glibc >= %s, no staging needed.", min_version)
356-
return
357-
358-
# Avoid infinite loop
359-
if os.environ.get(GLIBC_REEXEC_GUARD) == "1":
360-
logger.info("[glibc] Already re-exec'd once, continuing.")
361-
return
362-
363-
# Stage prebuilt if not already staged
364-
if not (_get_glibc_libdir() / "libc.so.6").exists():
365-
_stage_prebuilt_glibc()
366-
367-
loader = _resolve_glibc_loader()
368-
if not loader:
369-
logger.error("[glibc] Loader not found in %s", _get_glibc_libdir())
370-
return
371-
372-
logger.info(
373-
"[glibc] Re-execing under loader %s with libdir %s", loader, _get_glibc_libdir()
374-
)
375-
os.environ[GLIBC_REEXEC_GUARD] = "1"
376-
os.execv(
377-
str(loader),
378-
[str(loader), "--library-path", str(_get_glibc_libdir()), sys.executable]
379-
+ sys.argv,
380-
)
381-
382-
383-
####################
384-
# libc++ management
385-
####################
386-
387244
LLVM_VERSION = "14.0.0"
388245
LIBCXX_BASE_NAME = f"clang+llvm-{LLVM_VERSION}-x86_64-linux-gnu-ubuntu-18.04"
389246
LLVM_URL = f"https://github.com/llvm/llvm-project/releases/download/llvmorg-{LLVM_VERSION}/{LIBCXX_BASE_NAME}.tar.xz"
@@ -401,17 +258,12 @@ def _stage_libcxx(target_dir: pathlib.Path):
401258
logger.info("[libcxx] Already staged at %s, skipping download", target_dir)
402259
return
403260

404-
libcxx_stage = _get_staging_dir(f"libcxx-{LLVM_VERSION}")
405-
temp_tar = libcxx_stage / f"{LIBCXX_BASE_NAME}.tar.xz"
406-
temp_extract = libcxx_stage / LIBCXX_BASE_NAME
261+
temp_tar = pathlib.Path("/tmp") / f"{LIBCXX_BASE_NAME}.tar.xz"
262+
temp_extract = pathlib.Path("/tmp") / LIBCXX_BASE_NAME
407263

408264
if not temp_tar.exists():
409265
logger.info("[libcxx] Downloading %s", LLVM_URL)
410-
_atomic_download(LLVM_URL, temp_tar)
411-
412-
# Sanity check before extracting
413-
if not temp_tar.exists() or temp_tar.stat().st_size == 0:
414-
raise FileNotFoundError(f"[libcxx] Tarball missing or empty: {temp_tar}")
266+
urllib.request.urlretrieve(LLVM_URL, temp_tar)
415267

416268
logger.info("[libcxx] Extracting %s", temp_tar)
417269
with tarfile.open(temp_tar, "r:xz") as tar:
@@ -585,10 +437,8 @@ def install_qnn_sdk() -> bool:
585437
Returns:
586438
True if both steps succeeded (or were already satisfied), else False.
587439
"""
588-
logger.info("[QNN] Starting SDK installation")
589-
590-
# Make sure we’re running under >= 2.34
591-
ensure_glibc_minimum(GLIBC_VERSION)
592-
593-
# libc++ and QNN SDK setup
594-
return _ensure_libcxx_stack() and _ensure_qnn_sdk_lib()
440+
if check_glibc_exist_and_validate():
441+
if _ensure_libcxx_stack():
442+
if _ensure_qnn_sdk_lib():
443+
return True
444+
return False

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,10 +467,11 @@ def run(self):
467467
# Following code is for building the Qualcomm backend.
468468
from backends.qualcomm.scripts.download_qnn_sdk import (
469469
_download_qnn_sdk,
470+
check_glibc_exist_and_validate,
470471
is_linux_x86,
471472
)
472473

473-
if is_linux_x86():
474+
if is_linux_x86() and check_glibc_exist_and_validate():
474475
os.environ["EXECUTORCH_BUILDING_WHEEL"] = "1"
475476

476477
with tempfile.TemporaryDirectory() as tmpdir:

0 commit comments

Comments
 (0)