Files
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

342 lines
13 KiB
Python

# Copyright Advanced Micro Devices, Inc.
# SPDX-License-Identifier: MIT
"""ROCm detection for version, build info, and compatibility checking."""
import subprocess
import re
import os
from typing import Dict, Optional, Tuple
import logging
from packaging import version
from ..exceptions import ROCmVersionError
def _get_rocm_tool_path(tool_name: str) -> str:
"""Get full path to ROCm tool using THEROCK_BIN_DIR.
Args:
tool_name: Name of the tool ('rocm-smi', 'amd-smi', or 'rocminfo')
Returns:
str: Full path to tool if THEROCK_BIN_DIR is set, otherwise just tool name
"""
therock_bin_dir = os.getenv("THEROCK_BIN_DIR")
if therock_bin_dir:
return os.path.join(therock_bin_dir, tool_name)
return tool_name
def _get_rocm_path() -> str:
"""Get ROCm base path using THEROCK_BIN_DIR or fallback to /opt/rocm.
Returns:
str: ROCm base path (e.g., /opt/rocm-7.1.0 or /opt/rocm)
"""
therock_bin_dir = os.getenv("THEROCK_BIN_DIR")
if therock_bin_dir:
# THEROCK_BIN_DIR is typically /opt/rocm-x.y.z/bin, so parent is /opt/rocm-x.y.z
return os.path.dirname(therock_bin_dir)
return "/opt/rocm"
class ROCmDetector:
"""ROCm detector for version, build type, package manager, and compatibility validation."""
@staticmethod
def detect_rocm_info() -> Dict[str, str]:
"""Detect ROCm build information.
Returns:
Dictionary with ROCm build information
"""
logger = logging.getLogger(__name__)
build_info = {
"rocm_version": "Unknown",
"rocm_build_type": "Unknown",
"rocm_build_lib_type": "Unknown",
"rocm_package_manager": "Unknown",
"rocm_package_manager_version": "Unknown",
"install_type": "Unknown",
}
# Detect ROCm version
rocm_version = ROCmDetector._detect_rocm_version()
if rocm_version:
build_info["rocm_version"] = rocm_version
logger.debug(f"ROCm version: {rocm_version}")
# Detect package manager
pkg_manager, pkg_version = ROCmDetector._detect_package_manager()
if pkg_manager:
build_info["rocm_package_manager"] = pkg_manager
build_info["rocm_package_manager_version"] = pkg_version
logger.debug(f"Package manager: {pkg_manager} {pkg_version}")
# Detect install type
install_type = ROCmDetector._detect_install_type()
if install_type:
build_info["install_type"] = install_type
logger.debug(f"Install type: {install_type}")
# Detect build type
build_type = ROCmDetector._detect_build_type()
if build_type:
build_info["rocm_build_type"] = build_type
logger.debug(f"Build type: {build_type}")
# Detect library type
lib_type = ROCmDetector._detect_lib_type()
if lib_type:
build_info["rocm_build_lib_type"] = lib_type
logger.debug(f"Library type: {lib_type}")
return build_info
@staticmethod
def _detect_rocm_version() -> Optional[str]:
"""Detect ROCm version using multiple methods.
Returns:
str: Version in format X.Y.Z-BUILD (e.g., "7.10.1-36") or None
"""
logger = logging.getLogger(__name__)
try:
# Method 1: Try $ROCM_PATH/.info/version-rocm (most complete - includes build number)
rocm_path = _get_rocm_path()
version_rocm_file = os.path.join(rocm_path, ".info", "version-rocm")
logger.debug(f"Trying {version_rocm_file} file...")
if os.path.exists(version_rocm_file):
with open(version_rocm_file, "r") as f:
rocm_ver = f.read().strip()
if rocm_ver:
logger.debug(f"ROCm version from version-rocm file: {rocm_ver}")
return rocm_ver
else:
logger.debug(f"{version_rocm_file} not found")
except Exception as e:
logger.debug(f"Error reading version-rocm file: {e}")
try:
# Method 2: Try $ROCM_PATH/.info/version file
rocm_path = _get_rocm_path()
version_file = os.path.join(rocm_path, ".info", "version")
logger.debug(f"Trying {version_file} file...")
if os.path.exists(version_file):
with open(version_file, "r") as f:
rocm_ver = f.read().strip()
if rocm_ver:
logger.debug(f"ROCm version from version file: {rocm_ver}")
# Check if it has build number, if not try to append from version-rocm
if "-" not in rocm_ver:
logger.debug(
f"Version {rocm_ver} missing build number, trying to get full version..."
)
# Try to get build number from version-rocm
version_rocm_file = os.path.join(
rocm_path, ".info", "version-rocm"
)
if os.path.exists(version_rocm_file):
try:
with open(version_rocm_file, "r") as f2:
full_ver = f2.read().strip()
if full_ver and "-" in full_ver:
logger.debug(
f"Using full version from version-rocm: {full_ver}"
)
return full_ver
except Exception:
pass
return rocm_ver
else:
logger.debug(f"{version_file} not found")
except Exception as e:
logger.debug(f"Error reading version file: {e}")
try:
# Method 3: Try amd-smi version
amd_smi_cmd = _get_rocm_tool_path("amd-smi")
logger.debug(f"Trying {amd_smi_cmd} version for ROCm detection...")
result = subprocess.run(
[amd_smi_cmd, "version"], capture_output=True, text=True, timeout=10
)
if result.returncode == 0:
logger.debug(f"amd-smi version output:\n{result.stdout[:500]}")
# Look for "ROCm version:" field
match = re.search(
r"ROCm version:\s*(\S+)", result.stdout, re.IGNORECASE
)
if match:
rocm_ver = match.group(1)
logger.debug(f"ROCm version from amd-smi: {rocm_ver}")
return rocm_ver
else:
logger.debug(
f"amd-smi version failed with exit code {result.returncode}"
)
except FileNotFoundError:
logger.debug("amd-smi command not found")
except Exception as e:
logger.debug(f"amd-smi version error: {e}")
try:
# Method 4: Try rocminfo
rocminfo_cmd = _get_rocm_tool_path("rocminfo")
logger.debug(f"Trying {rocminfo_cmd} for ROCm detection...")
result = subprocess.run(
[rocminfo_cmd], capture_output=True, text=True, timeout=10
)
if result.returncode == 0:
# Look for "Runtime Version" or similar
match = re.search(r"Runtime Version:\s*(\S+)", result.stdout)
if match:
rocm_ver = match.group(1)
logger.debug(f"ROCm version from rocminfo: {rocm_ver}")
return rocm_ver
else:
logger.debug(f"rocminfo failed with exit code {result.returncode}")
except FileNotFoundError:
logger.debug("rocminfo command not found")
except Exception as e:
logger.debug(f"rocminfo error: {e}")
try:
# Method 5: Try rocm-smi --version
rocm_smi_cmd = _get_rocm_tool_path("rocm-smi")
logger.debug(f"Trying {rocm_smi_cmd} --version...")
result = subprocess.run(
[rocm_smi_cmd, "--version"], capture_output=True, text=True, timeout=5
)
if result.returncode == 0:
match = re.search(r"version:\s*(\S+)", result.stdout, re.IGNORECASE)
if match:
rocm_ver = match.group(1)
logger.debug(f"ROCm version from rocm-smi: {rocm_ver}")
return rocm_ver
else:
logger.debug(
f"rocm-smi --version failed with exit code {result.returncode}"
)
except FileNotFoundError:
logger.debug("rocm-smi command not found")
except Exception as e:
logger.debug(f"rocm-smi error: {e}")
logger.debug("All ROCm version detection methods failed")
return None
@staticmethod
def _detect_package_manager() -> tuple:
"""Detect system package manager (dpkg or rpm) and version.
Returns:
tuple: (package_manager_name, version) or ("Unknown", "Unknown")
"""
# Check for dpkg (Debian/Ubuntu)
try:
result = subprocess.run(
["dpkg", "--version"], capture_output=True, text=True, timeout=5
)
if result.returncode == 0:
match = re.search(r"version\s+(\S+)", result.stdout)
version = match.group(1) if match else "Unknown"
return ("dpkg", version)
except Exception:
pass
# Check for rpm (RHEL/CentOS/SLES)
try:
result = subprocess.run(
["rpm", "--version"], capture_output=True, text=True, timeout=5
)
if result.returncode == 0:
match = re.search(r"version\s+(\S+)", result.stdout)
version = match.group(1) if match else "Unknown"
return ("rpm", version)
except Exception:
pass
return ("Unknown", "Unknown")
@staticmethod
def _detect_install_type() -> Optional[str]:
"""Detect ROCm installation type (package manager or source build)."""
# Check if installed via package manager
try:
# Check for dpkg package
result = subprocess.run(
["dpkg", "-l", "rocm-dev"], capture_output=True, text=True, timeout=5
)
if result.returncode == 0 and "ii" in result.stdout:
return "package"
except Exception:
pass
try:
# Check for rpm package
result = subprocess.run(
["rpm", "-q", "rocm-dev"], capture_output=True, text=True, timeout=5
)
if result.returncode == 0:
return "package"
except Exception:
pass
# Check if ROCm path exists (could be source or package)
rocm_path = _get_rocm_path()
if os.path.exists(rocm_path):
# If package manager doesn't show it, likely source
return "source"
return "Unknown"
@staticmethod
def _detect_build_type() -> Optional[str]:
"""Detect ROCm build type (Release, Debug, or TheRock)."""
# Check for debug symbols or debug libraries
try:
rocm_path = _get_rocm_path()
rocm_lib_path = os.path.join(rocm_path, "lib")
if os.path.exists(rocm_lib_path):
# Check if debug libraries exist
for file in os.listdir(rocm_lib_path):
if "debug" in file.lower() or file.endswith(".debug"):
return "Debug"
# Default to TheRock if no debug indicators
return "TheRock"
except Exception:
pass
return "Unknown"
@staticmethod
def _detect_lib_type() -> Optional[str]:
"""Detect ROCm library type (static, shared, or both)."""
try:
rocm_path = _get_rocm_path()
rocm_lib_path = os.path.join(rocm_path, "lib")
if os.path.exists(rocm_lib_path):
has_static = False
has_shared = False
for file in os.listdir(rocm_lib_path):
if file.endswith(".a"):
has_static = True
if file.endswith(".so") or ".so." in file:
has_shared = True
if has_static and has_shared:
return "both"
elif has_shared:
return "shared"
elif has_static:
return "static"
except Exception:
pass
return None