# --------------------------------------------------------------------- # Copyright (c) 2025 Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause # --------------------------------------------------------------------- """Shared helpers for QDC on-device test runners (Linux IoT).""" import logging import os import shutil import subprocess log = logging.getLogger(__name__) # --------------------------------------------------------------------------- # On-device paths # --------------------------------------------------------------------------- BUNDLE_PATH = "/tmp/llama_cpp_bundle" QDC_LOGS_PATH = "/tmp/QDC_logs" LIB_PATH = f"{BUNDLE_PATH}/lib" BIN_PATH = f"{BUNDLE_PATH}/bin" ENV_PREFIX = ( f"export LD_LIBRARY_PATH={LIB_PATH}:$LD_LIBRARY_PATH && " f"export ADSP_LIBRARY_PATH={LIB_PATH} && " f"chmod +x {BIN_PATH}/* &&" ) CMD_PREFIX = f"cd {BUNDLE_PATH} && {ENV_PREFIX}" # --------------------------------------------------------------------------- # Shell helpers # --------------------------------------------------------------------------- def run_shell_command(cmd: str, *, check: bool = True, timeout: int = 600) -> subprocess.CompletedProcess: log.info("Running: %s", cmd[:200]) try: result = subprocess.run( ["sh", "-c", cmd], text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, timeout=timeout, ) except subprocess.TimeoutExpired as e: output = e.stdout or "" print(output) raise AssertionError(f"Command timed out after {timeout}s") from e print(result.stdout) if check: assert result.returncode == 0, ( f"Command failed (exit {result.returncode}):\n" f" cmd: {cmd[:200]}\n" f" last 500 chars of output: {(result.stdout or '')[-500:]}" ) return result def write_qdc_log(filename: str, content: str) -> None: """Write content as a log file to QDC_LOGS_PATH for QDC log collection.""" os.makedirs(QDC_LOGS_PATH, exist_ok=True) with open(f"{QDC_LOGS_PATH}/{filename}", "w") as f: f.write(content) def _find_bundle_source() -> str: """Locate the llama_cpp_bundle directory from the QDC extraction.""" candidates = [ os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "llama_cpp_bundle"), "/qdc/appium/llama_cpp_bundle", "/qdc/bash/llama_cpp_bundle", ] for c in candidates: real = os.path.realpath(c) if os.path.isdir(real): log.info("Found bundle source at %s", real) return real # Debug: dump known directories to help diagnose for check_dir in ["/qdc", "/qdc/appium", "/qdc/bash", os.path.dirname(os.path.abspath(__file__)), os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")]: if os.path.isdir(check_dir): log.info("[debug] %s: %s", check_dir, os.listdir(check_dir)) else: log.info("[debug] %s: does not exist", check_dir) raise FileNotFoundError("llama_cpp_bundle not found in any known QDC extraction path") def push_bundle_if_needed(check_binary: str) -> None: """Copy llama_cpp_bundle to /tmp if check_binary is not already present.""" if os.path.exists(check_binary): log.info("Binary already present: %s", check_binary) return src = _find_bundle_source() log.info("Copying bundle from %s to %s", src, BUNDLE_PATH) shutil.copytree(src, BUNDLE_PATH, dirs_exist_ok=True) subprocess.run(["chmod", "-R", "+x", BIN_PATH], check=False) if not os.path.exists(check_binary): available = os.listdir(BIN_PATH) if os.path.isdir(BIN_PATH) else [] raise FileNotFoundError( f"Binary {check_binary} not found after bundle copy. " f"Available binaries: {available}" ) log.info("Bundle installed, verified %s exists", check_binary) def log_environment() -> None: """Log environment info for debugging.""" script_dir = os.path.dirname(os.path.abspath(__file__)) parent_dir = os.path.dirname(script_dir) lines = [ f"__file__: {os.path.abspath(__file__)}", f"script_dir: {script_dir}", f"parent_dir contents: {os.listdir(parent_dir) if os.path.isdir(parent_dir) else 'missing'}", f"BUNDLE_PATH exists: {os.path.isdir(BUNDLE_PATH)}", f"BIN_PATH contents: {os.listdir(BIN_PATH) if os.path.isdir(BIN_PATH) else 'missing'}", f"LIB_PATH contents: {os.listdir(LIB_PATH) if os.path.isdir(LIB_PATH) else 'missing'}", f"LD_LIBRARY_PATH: {os.environ.get('LD_LIBRARY_PATH', 'not set')}", f"/qdc/appium/ contents: {os.listdir('/qdc/appium/') if os.path.isdir('/qdc/appium/') else 'missing'}", f"/qdc/bash/ contents: {os.listdir('/qdc/bash/') if os.path.isdir('/qdc/bash/') else 'missing'}", ] for line in lines: log.info("[env] %s", line) write_qdc_log("environment.log", "\n".join(lines) + "\n")