Files
TheRock/rocm-libraries/projects/hipblaslt/tasks.py
T
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

805 lines
32 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Copyright (C) 2022-2025 Advanced Micro Devices, Inc. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import os
import shutil
import subprocess
import sys
from pathlib import Path
from invoke import task
# ---------------------------------------------------------------------------
# Paths
# ---------------------------------------------------------------------------
ROOT_PATH = Path(__file__).resolve().parent
BUILD_DIR = ROOT_PATH / "build"
# ---------------------------------------------------------------------------
# Helpers – distro detection
# ---------------------------------------------------------------------------
def _os_release() -> dict:
if sys.platform == "win32":
return {}
info = {}
try:
with open("/etc/os-release") as f:
for line in f:
line = line.strip()
if "=" in line:
k, _, v = line.partition("=")
info[k] = v.strip('"')
except FileNotFoundError:
pass
return info
def _distro_id() -> str:
return _os_release().get("ID", "")
def _version_id() -> str:
return _os_release().get("VERSION_ID", "")
def _supported_distros():
if sys.platform == "win32":
return
supported = {"ubuntu", "centos", "almalinux", "rhel", "fedora", "sles",
"opensuse-leap", "mariner", "azurelinux"}
distro = _distro_id()
if distro not in supported:
print(f"Unsupported distro '{distro}'. Supported: {', '.join(sorted(supported))}")
sys.exit(2)
def _setup_msvc_env():
"""Initialize the full MSVC build environment from vcvarsall.bat."""
if "VSINSTALLDIR" in os.environ:
return # Already inside a Developer Command Prompt
# Use vswhere.exe (ships with VS 2017+ Installer) to find the latest VS installation.
vswhere = Path(os.environ.get("ProgramFiles(x86)", "C:/Program Files (x86)")) / \
"Microsoft Visual Studio/Installer/vswhere.exe"
vcvarsall = None
if vswhere.exists():
result = subprocess.run(
[str(vswhere), "-latest", "-property", "installationPath"],
capture_output=True, text=True,
)
vs_path = result.stdout.strip()
if vs_path:
candidate = Path(vs_path) / "VC/Auxiliary/Build/vcvarsall.bat"
if candidate.exists():
vcvarsall = candidate
# Fallback: manual glob for BuildTools installs that vswhere may not index.
if vcvarsall is None:
vs_root = Path("C:/Program Files (x86)/Microsoft Visual Studio")
candidates = sorted(vs_root.glob("*/BuildTools/VC/Auxiliary/Build/vcvarsall.bat"), reverse=True)
if candidates:
vcvarsall = candidates[0]
if vcvarsall is None:
print("Warning: vcvarsall.bat not found. Ensure Visual Studio Build Tools with C++ workload is installed.")
return
result = subprocess.run(
f'"{vcvarsall}" amd64 && set',
capture_output=True, text=True, encoding="mbcs", errors="replace", shell=True,
)
original_lib = os.environ.get("LIB", "")
for line in result.stdout.splitlines():
if "=" in line:
key, _, value = line.partition("=")
os.environ[key] = value
# Restore original LIB entries so vcvarsall doesn't drop existing SDK paths
if original_lib:
existing = os.environ.get("LIB", "")
merged = ";".join(dict.fromkeys(existing.split(";") + original_lib.split(";")))
os.environ["LIB"] = merged
# Ensure Windows SDK lib and include paths are present (vcvarsall.bat may omit them).
prog86 = os.environ.get("ProgramFiles(x86)", "C:/Program Files (x86)")
sdk_lib_root = Path(prog86) / "Windows Kits/10/lib"
sdk_versions = sorted(sdk_lib_root.glob("*/"), reverse=True) if sdk_lib_root.exists() else []
if sdk_versions:
sdk_ver = sdk_versions[0].name
sdk_root = sdk_lib_root.parent
sdk_libs = [
str(sdk_root / "lib" / sdk_ver / "ucrt/x64"),
str(sdk_root / "lib" / sdk_ver / "um/x64"),
]
current_lib = os.environ.get("LIB", "")
missing_libs = [p for p in sdk_libs if p not in current_lib]
if missing_libs:
os.environ["LIB"] = ";".join(missing_libs + ([current_lib] if current_lib else []))
sdk_includes = [
str(p) for p in (sdk_root / "include" / sdk_ver).iterdir() if p.is_dir()
]
current_include = os.environ.get("INCLUDE", "")
missing_includes = [p for p in sdk_includes if p not in current_include]
if missing_includes:
os.environ["INCLUDE"] = ";".join(missing_includes + ([current_include] if current_include else []))
def _setup_clang_shims(build_subdir: Path):
"""Create clang.exe/clang++.exe hardlinks pointing to amdclang equivalents.
Tensile's toolchain validator looks for 'clang.exe' / 'clang++.exe' by name.
The ROCm Windows SDK ships these as 'amdclang.exe' / 'amdclang++.exe'.
We create hardlinks in a shims directory and prepend it to PATH.
"""
aliases = {
"clang.exe": "amdclang.exe",
"clang++.exe": "amdclang++.exe",
# clang-offload-bundler.exe already has the right name in lib/llvm/bin/
}
shims_dir = build_subdir / "clang-shims"
shims_dir.mkdir(parents=True, exist_ok=True)
for alias, real in aliases.items():
target = shutil.which(real)
if not target:
continue
link = shims_dir / alias
if not link.exists():
try:
os.link(target, link) # hardlink — no admin required
except OSError:
shutil.copy2(target, link) # fallback: copy
print(f"clang shim: {alias} -> {target}")
os.environ["PATH"] = str(shims_dir) + os.pathsep + os.environ.get("PATH", "")
def _rmtree(path: Path):
"""Remove a directory tree, handling read-only files on Windows."""
def _on_error(func, path, _exc):
os.chmod(path, 0o666)
func(path)
shutil.rmtree(path, onexc=_on_error)
def _find_rc_exe() -> str:
"""Find rc.exe from PATH or Windows SDK installation directory."""
rc = shutil.which("rc") or shutil.which("rc.exe")
if rc:
return rc
# Search Windows SDK bin directories (installed by Visual Studio Build Tools).
# Use ProgramFiles(x86) env var to handle non-default install locations.
prog_x86 = os.environ.get("ProgramFiles(x86)", "C:/Program Files (x86)")
prog = os.environ.get("ProgramFiles", "C:/Program Files")
for sdk_root in (
Path(prog_x86) / "Windows Kits" / "10" / "bin",
Path(prog) / "Windows Kits" / "10" / "bin",
):
if not sdk_root.exists():
continue
for version_dir in sorted(sdk_root.iterdir(), reverse=True):
for arch in ("x64", "x86"):
candidate = version_dir / arch / "rc.exe"
if candidate.exists():
return candidate.as_posix()
return ""
def _is_root() -> bool:
if sys.platform == "win32":
import ctypes
return ctypes.windll.shell32.IsUserAnAdmin() != 0
return os.getuid() == 0
def _elevate(c, cmd: str):
if _is_root():
c.run(cmd)
else:
c.run(f"sudo {cmd}")
# ---------------------------------------------------------------------------
# Helpers – package installation
# ---------------------------------------------------------------------------
def _apt_install(c, packages: list[str]):
for pkg in packages:
result = subprocess.run(
["dpkg-query", "--show", "--showformat=${db:Status-Abbrev}\n", pkg],
capture_output=True, text=True,
)
if "ii" not in result.stdout:
print(f"\033[32mInstalling \033[33m{pkg}\033[32m via apt\033[0m")
_elevate(c, f"apt install -y --no-install-recommends {pkg}")
def _yum_install(c, packages: list[str], extra_opts: str = ""):
for pkg in packages:
result = subprocess.run(
["yum", "list", "installed", pkg],
capture_output=True, text=True,
)
if result.returncode != 0:
print(f"\033[32mInstalling \033[33m{pkg}\033[32m via yum\033[0m")
opts = ""
if pkg == "openblas-devel" and _distro_id() == "centos":
opts = "--enablerepo=crb"
_elevate(c, f"yum -y --nogpgcheck install {pkg} {opts}")
def _dnf_install(c, packages: list[str]):
for pkg in packages:
result = subprocess.run(
["dnf", "list", "installed", pkg],
capture_output=True, text=True,
)
if result.returncode != 0:
print(f"\033[32mInstalling \033[33m{pkg}\033[32m via dnf\033[0m")
_elevate(c, f"dnf install -y {pkg}")
def _zypper_install(c, packages: list[str]):
for pkg in packages:
result = subprocess.run(
["rpm", "-q", pkg],
capture_output=True, text=True,
)
if result.returncode != 0:
print(f"\033[32mInstalling \033[33m{pkg}\033[32m via zypper\033[0m")
_elevate(c, f"zypper -n --no-gpg-checks install {pkg}")
def _install_msgpack_from_source(c, build_dir: Path, cxx: str, cc: str):
msgpack_dir = build_dir / "deps" / "msgpack-c"
if not msgpack_dir.exists():
deps_dir = build_dir / "deps"
deps_dir.mkdir(parents=True, exist_ok=True)
with c.cd(str(deps_dir)):
c.run("git clone -b cpp-3.1.0 https://github.com/msgpack/msgpack-c.git --depth 1")
with c.cd(str(msgpack_dir)):
c.run("git fetch --unshallow")
c.run(f"CXX={cxx} CC={cc} cmake -DMSGPACK_BUILD_TESTS=OFF -DMSGPACK_BUILD_EXAMPLES=OFF .")
c.run("make")
_elevate(c, "make install")
def _install_blis(c, build_dir: Path):
blis_paths = [
"/opt/AMD/aocl/aocl-linux-gcc-4.2.0/gcc/lib_ILP64/libblis-mt.a",
"/opt/AMD/aocl/aocl-linux-aocc-4.1.0/aocc/lib_ILP64/libblis-mt.a",
"/opt/AMD/aocl/aocl-linux-aocc-4.0/lib_ILP64/libblis-mt.a",
"/usr/local/lib/libblis.a",
]
if any(Path(p).exists() for p in blis_paths):
return
blis_dir = build_dir / "deps" / "blis"
if (blis_dir / "lib" / "libblis.a").exists():
return
distro = _distro_id()
if distro in ("centos", "rhel", "sles", "opensuse-leap", "almalinux"):
url = "https://github.com/amd/blis/releases/download/2.0/aocl-blis-mt-centos-2.0.tar.gz"
else:
url = "https://github.com/amd/blis/releases/download/2.0/aocl-blis-mt-ubuntu-2.0.tar.gz"
deps_dir = build_dir / "deps"
deps_dir.mkdir(parents=True, exist_ok=True)
with c.cd(str(deps_dir)):
c.run(f"wget -nv -O blis.tar.gz {url}")
c.run("tar -xvf blis.tar.gz")
if (deps_dir / "amd-blis-mt").exists():
shutil.rmtree(str(deps_dir / "blis"), ignore_errors=True)
(deps_dir / "amd-blis-mt").rename(deps_dir / "blis")
(deps_dir / "blis.tar.gz").unlink(missing_ok=True)
lib_dir = deps_dir / "blis" / "lib"
symlink = lib_dir / "libblis.a"
if not symlink.exists():
symlink.symlink_to("libblis-mt.a")
# ---------------------------------------------------------------------------
# invoke tasks
# ---------------------------------------------------------------------------
@task(
help={
"install_deps": "Install build dependencies before building.",
"install_pkg": "Install the package after building.",
"clients": "Build library clients.",
"jobs": "Number of parallel build jobs (default: all cores).",
"architecture": "GPU target(s), e.g. 'all' or 'gfx90a:xnack+;gfx90a:xnack-'.",
"cpu_ref_lib": "CPU reference library for testing: 'blis' or 'lapack'.",
"use_system_packages": "Use system-installed msgpack/blas/lapack (requires --install-deps).",
"debug": "Build with CMAKE_BUILD_TYPE=Debug.",
"relwithdebinfo": "Build with CMAKE_BUILD_TYPE=RelWithDebInfo.",
"static": "Build a static library.",
"relocatable": "Create a relocatable ROCm package.",
"address_sanitizer": "Build with AddressSanitizer.",
"codecoverage": "Build with code coverage profiling.",
"gprof": "Enable GNU gprof profiling (requires --static).",
"no_tensile": "Build without the Tensile GEMM backend.",
"tensile_logic": "Path for HIPBLASLT_LIBLOGIC_PATH.",
"tensile_threads": "Parallel build threads for TensileLite (default: nproc).",
"tensile_verbose": "TensileLite verbosity level.",
"no_lazy_load": "Disable lazy library loading.",
"no_msgpack": "Use YAML backend instead of msgpack.",
"no_compress": "Don't compress TensileLite assembly objects.",
"keep_build_tmp": "Keep the temporary build artifacts.",
"experimental": "Include 'Experimental' logic directories.",
"logic_filter": "Logic YAML filter (e.g. 'gfx942/Equality/*').",
"legacy_hipblas_direct": "Enable legacy HIPBLAS_DIRECT mode.",
"disable_marker": "Disable hipBLASLt markers.",
"enable_tensile_marker": "Enable Tensile markers.",
"skip_rocroller": "Skip the rocRoller backend.",
"quiet": "Build without VERBOSE=1.",
"enable_asm_comments": "Enable assembly comments in generated asm.",
"build_dir": "Override the build directory.",
"rocm_path": "Override the ROCm installation path.",
"clean": "Remove the build directory before configuring (default: incremental).",
}
)
def build(
c,
install_deps=False,
install_pkg=False,
clients=False,
jobs=None,
architecture="all",
cpu_ref_lib="blis",
use_system_packages=False,
debug=False,
relwithdebinfo=False,
static=False,
relocatable=False,
address_sanitizer=False,
codecoverage=False,
gprof=False,
no_tensile=False,
tensile_logic="",
tensile_threads=None,
tensile_verbose="",
no_lazy_load=False,
no_msgpack=False,
no_compress=False,
keep_build_tmp=False,
experimental=False,
logic_filter="",
legacy_hipblas_direct=False,
disable_marker=False,
enable_tensile_marker=False,
skip_rocroller=False,
quiet=False,
enable_asm_comments=False,
build_dir=None,
rocm_path=None,
clean=False,
):
_supported_distros()
distro = _distro_id()
version_id = _version_id()
version_major = int(version_id.split(".")[0]) if version_id else 0
bld = Path(build_dir).resolve() if build_dir else BUILD_DIR
if rocm_path:
rocm = Path(rocm_path)
elif "ROCM_PATH" in os.environ:
rocm = Path(os.environ["ROCM_PATH"])
elif sys.platform == "win32":
try:
rocm = Path(subprocess.check_output(["rocm-sdk", "path", "--root"], text=True).strip())
except (subprocess.CalledProcessError, FileNotFoundError):
print("Could not find ROCm SDK. Run: pip install rocm-sdk && rocm-sdk initDevel")
sys.exit(1)
else:
rocm = Path("/opt/rocm")
# CMake requires forward slashes even on Windows to avoid backslash escape issues.
rocm_s = rocm.as_posix()
if tensile_threads is None:
tensile_threads = os.cpu_count()
jobs = jobs or os.cpu_count()
# Determine build type
if debug:
build_type = "Debug"
build_subdir = bld / "debug"
elif relwithdebinfo:
build_type = "RelWithDebInfo"
build_subdir = bld / "release-debug"
else:
build_type = "Release"
build_subdir = bld / "release"
# Clean previous build (opt-in; skipping preserves FetchContent/CMake cache)
if clean and build_subdir.exists():
_rmtree(build_subdir)
# Validate options
if cpu_ref_lib not in ("blis", "lapack"):
print("--cpu-ref-lib must be 'blis' or 'lapack'")
sys.exit(2)
if codecoverage and build_type == "Release":
print("Code coverage requires Debug or RelWithDebInfo build type.")
sys.exit(1)
if gprof and not static:
print("--gprof requires --static.")
sys.exit(2)
# PATH setup — use os.pathsep (';' on Windows, ':' on Linux)
# lib/llvm/bin is Windows-only: the ROCm Windows SDK stores tools there
sep = os.pathsep
rocm_bin_dirs = [f"{rocm_s}/bin", f"{rocm_s}/hip/bin", f"{rocm_s}/llvm/bin"]
if sys.platform == "win32":
rocm_bin_dirs.append(f"{rocm_s}/lib/llvm/bin")
os.environ["PATH"] = sep.join(rocm_bin_dirs) + sep + os.environ.get("PATH", "")
# ROCm env vars are set after _setup_msvc_env() below, since vcvarsall.bat
# would overwrite any vars we set here.
# RocRoller
use_rocroller = not skip_rocroller and not (distro == "rhel" and version_id == "9.1")
# ---------------------------------------------------------------------------
# Dependencies
# ---------------------------------------------------------------------------
if install_deps:
_install_system_deps(
c, distro, version_major, clients, use_system_packages,
no_msgpack, use_rocroller, legacy_hipblas_direct, bld,
)
# ---------------------------------------------------------------------------
# cmake options assembly
# ---------------------------------------------------------------------------
deps_prefix = bld / "deps" / "install"
cmake_opts = [
f'"-DGPU_TARGETS={architecture}"',
f"-DCMAKE_BUILD_TYPE={build_type}",
f"-DCMAKE_PREFIX_PATH={deps_prefix.as_posix()}",
"-DCMAKE_POLICY_VERSION_MINIMUM=3.5",
"-DMSGPACK_USE_BOOST=OFF",
]
if legacy_hipblas_direct:
cmake_opts.append("-DHIPBLASLT_ENABLE_HIPBLAS_DIRECT=ON")
if address_sanitizer:
cmake_opts.append("-DHIPBLASLT_ENABLE_ASAN=ON")
if codecoverage:
cmake_opts.append("-DHIPBLASLT_ENABLE_COVERAGE=ON")
if static:
cmake_opts.append("-DHIPBLASLT_BUILD_SHARED_LIBS=OFF")
if gprof:
cmake_opts += ["-DCMAKE_CXX_FLAGS=-pg", "-DCMAKE_C_FLAGS=-pg"]
if not use_rocroller:
cmake_opts.append("-DHIPBLASLT_ENABLE_ROCROLLER=OFF")
if sys.platform == "win32":
cmake_opts.append("-DHIPBLASLT_ENABLE_OPENMP=OFF")
# Suppress Windows SDK min/max macros that break std::numeric_limits<T>::max().
# Pass --rocm-path explicitly: amdclang++ on Windows does not read ROCM_PATH
# from the environment — it requires the flag on the command line to find HIP headers.
# Append to avoid clobbering flags set earlier (e.g. -pg from --gprof).
existing_cxx_flags = next((o.split("=", 1)[1] for o in cmake_opts if o.startswith("-DCMAKE_CXX_FLAGS=")), "")
cmake_opts = [o for o in cmake_opts if not o.startswith("-DCMAKE_CXX_FLAGS=")]
new_cxx_flags = f"{(existing_cxx_flags + ' ').lstrip()}-DNOMINMAX --rocm-path={rocm_s}"
cmake_opts.append(f'"-DCMAKE_CXX_FLAGS={new_cxx_flags}"')
# Tensile options
if no_tensile:
cmake_opts.append("-DHIPBLASLT_ENABLE_DEVICE=OFF")
else:
if tensile_logic:
logic_path = tensile_logic if Path(tensile_logic).is_absolute() else str(ROOT_PATH / tensile_logic)
cmake_opts.append(f"-DHIPBLASLT_LIBLOGIC_PATH={logic_path}")
if tensile_threads != os.cpu_count():
cmake_opts.append(f"-DTENSILELITE_BUILD_PARALLEL_LEVEL={tensile_threads}")
cmake_opts.append(f"-DHIPBLASLT_ENABLE_YAML={'OFF' if not no_msgpack else 'ON'}")
if build_type != "Release":
cmake_opts.append("-DTENSILELITE_ASM_DEBUG=ON")
if logic_filter:
cmake_opts.append(f"-DTENSILELITE_LOGIC_FILTER={logic_filter}")
if keep_build_tmp:
cmake_opts.append("-DTENSILELITE_KEEP_BUILD_TMP=ON")
if no_compress:
cmake_opts.append("-DTENSILELITE_NO_COMPRESS=ON")
if experimental:
cmake_opts.append("-DTENSILELITE_EXPERIMENTAL=ON")
if disable_marker:
cmake_opts.append("-DHIPBLASLT_ENABLE_MARKER=OFF")
if not enable_asm_comments:
cmake_opts.append("-DTENSILELITE_ENABLE_ASM_COMMENTS=OFF")
if no_lazy_load:
cmake_opts.append("-DHIPBLASLT_ENABLE_LAZY_LOAD=OFF")
# Client options
if not clients:
client_opts = ["-DHIPBLASLT_ENABLE_CLIENT=OFF"]
else:
if cpu_ref_lib == "blis":
if sys.platform == "win32":
print("Warning: BLIS is not available on Windows. Disabling BLIS for clients.")
client_opts = ["-DHIPBLASLT_ENABLE_BLIS=OFF"]
else:
client_opts = ["-DHIPBLASLT_ENABLE_BLIS=ON"]
_install_blis(c, bld)
else:
client_opts = ["-DHIPBLASLT_ENABLE_BLIS=OFF"]
if not use_system_packages and install_deps:
# LAPACK superbuild installs into deps_prefix (see _install_system_deps), not /usr/local.
deps_lib = deps_prefix / "lib"
blas_a = (deps_lib / "libblas.a").as_posix()
lapack_a = (deps_lib / "liblapack.a").as_posix()
client_opts += [
f"-DBLAS_LIBRARIES={blas_a}",
f'"-DLAPACK_LIBRARIES={lapack_a};{blas_a}"',
"-DBLA_STATIC=ON",
]
if sys.platform == "win32":
# Use absolute paths so CMake cache stays stable regardless of PATH ordering.
_cxx = shutil.which("amdclang++") or shutil.which("amdclang++.exe")
_cc = shutil.which("amdclang") or shutil.which("amdclang.exe")
if not _cxx or not _cc:
print("Could not find amdclang++/amdclang on PATH. Ensure ROCm SDK is installed.")
sys.exit(1)
# Lowercase the filename: shutil.which returns the filesystem case (e.g. .EXE),
# but Tensile's validator compares against lowercase extensions (.exe).
def _normalized(p: str) -> str:
pp = Path(p)
return (pp.parent / pp.name.lower()).as_posix()
compiler = _normalized(_cxx)
ccompiler = _normalized(_cc)
else:
compiler = f"{rocm_s}/bin/amdclang++"
ccompiler = f"{rocm_s}/bin/amdclang"
# Build subdir
(build_subdir / "clients").mkdir(parents=True, exist_ok=True)
if sys.platform == "win32":
cmake_prefix = subprocess.check_output(["rocm-sdk", "path", "--cmake"], text=True).strip()
prefix_path = Path(cmake_prefix).as_posix()
else:
prefix_path = f"{rocm_s};{rocm_s}/hcc;{rocm_s}/hip"
module_path = f"{rocm_s}/hip/cmake"
if relocatable:
rocm_rpath = os.environ.get("ROCM_RPATH", "/opt/rocm/lib:/opt/rocm/lib64")
extra = [
"-DCPACK_SET_DESTDIR=OFF",
f"-DCMAKE_INSTALL_PREFIX={rocm_s}",
f"-DCPACK_PACKAGING_INSTALL_PREFIX={rocm_s}",
f'"-DCMAKE_SHARED_LINKER_FLAGS=-Wl,--enable-new-dtags -Wl,--rpath,{rocm_rpath}"',
f'"-DCMAKE_PREFIX_PATH={prefix_path}"',
f"-DCMAKE_MODULE_PATH={module_path}",
"-DROCM_DISABLE_LDCONFIG=ON",
"-DCMAKE_INSTALL_LIBDIR=lib",
f"-DROCM_PATH={rocm_s}",
f"-DROCM_HOME={rocm_s}",
]
else:
install_prefix = (ROOT_PATH / "hipblaslt-install").as_posix()
extra = [
f'"-DCMAKE_PREFIX_PATH={prefix_path}"',
f"-DCMAKE_MODULE_PATH={module_path}",
"-DCPACK_SET_DESTDIR=OFF",
f"-DCMAKE_INSTALL_PREFIX={install_prefix}",
f"-DCPACK_PACKAGING_INSTALL_PREFIX={rocm_s}",
"-DCMAKE_INSTALL_LIBDIR=lib",
f"-DROCM_PATH={rocm_s}",
f"-DROCM_HOME={rocm_s}",
]
compiler_opts = [
f"-DCMAKE_CXX_COMPILER={compiler}",
f"-DCMAKE_C_COMPILER={ccompiler}",
]
if clients:
compiler_opts.append("-DCMAKE_Fortran_COMPILER=gfortran")
if sys.platform == "win32":
_setup_msvc_env()
# Set ROCm env vars AFTER vcvarsall so it doesn't overwrite them.
# amdclang++ reads ROCM_PATH for HIP headers and HIP_DEVICE_LIB_PATH for bitcode.
# The pip-installed SDK stores device libs under lib/llvm/amdgcn/bitcode/ (not amdgcn/bitcode/).
os.environ["ROCM_PATH"] = rocm_s
os.environ["ROCM_HOME"] = rocm_s
# The pip-installed SDK has no separate hip/ subdirectory; HIP headers
# are at {rocm}/include/hip/. Set HIP_PATH to rocm root so amdclang++
# finds them via {HIP_PATH}/include/hip/hip_version.h.
os.environ["HIP_PATH"] = rocm_s
os.environ["HIP_DEVICE_LIB_PATH"] = (rocm / "lib" / "llvm" / "amdgcn" / "bitcode").as_posix()
_setup_clang_shims(build_subdir)
rc = _find_rc_exe()
if not rc:
print("Could not find rc.exe. Ensure the Windows SDK is installed via Visual Studio Build Tools.")
sys.exit(1)
compiler_opts.append(f'"-DCMAKE_RC_COMPILER={rc}"')
# CMake uses ar-style syntax (qc flags) by default. On Windows without
# llvm-ar, use lib.exe with MSVC-style archive rules via a toolchain file
# (angle-bracket placeholders can't be passed cleanly as -D flags).
lib_exe = shutil.which("lib") or shutil.which("lib.exe")
if lib_exe:
lib_posix = Path(lib_exe).as_posix()
toolchain_file = build_subdir / "windows_toolchain.cmake"
toolchain_file.parent.mkdir(parents=True, exist_ok=True)
toolchain_file.write_text(
f'set(CMAKE_AR "{lib_posix}" CACHE FILEPATH "" FORCE)\n'
'set(CMAKE_CXX_CREATE_STATIC_LIBRARY "<CMAKE_AR> /OUT:<TARGET> <LINK_FLAGS> <OBJECTS>")\n'
'set(CMAKE_C_CREATE_STATIC_LIBRARY "<CMAKE_AR> /OUT:<TARGET> <LINK_FLAGS> <OBJECTS>")\n'
)
cmake_opts.append(f"-DCMAKE_TOOLCHAIN_FILE={toolchain_file.as_posix()}")
print(f"lib.exe archiver: {lib_posix}")
else:
print("Warning: lib.exe not found. Static library archiving will fail.")
generator_opts = ['-G "NMake Makefiles"'] if sys.platform == "win32" else []
all_opts = " ".join(generator_opts + cmake_opts + client_opts + extra + compiler_opts)
cmake_cmd = f"cmake {all_opts} {ROOT_PATH}"
print(f"cmake command: {cmake_cmd}")
with c.cd(str(build_subdir)):
c.run(cmake_cmd)
verbose_flag = "" if quiet else " --verbose"
c.run(f"cmake --build . --target install -j {jobs}{verbose_flag}")
# ---------------------------------------------------------------------------
# Package install
# ---------------------------------------------------------------------------
if install_pkg:
with c.cd(str(build_subdir)):
c.run("make package")
if distro == "ubuntu":
_elevate(c, f"dpkg -i {build_subdir}/hipblaslt[-_]*.deb")
elif distro in ("centos", "rhel", "almalinux"):
_elevate(c, f"rpm --nodeps -U {build_subdir}/hipblaslt-*.rpm")
elif distro == "fedora":
_elevate(c, f"dnf install {build_subdir}/hipblaslt-*.rpm")
elif distro in ("sles", "opensuse-leap"):
_elevate(c, f"zypper -n --no-gpg-checks install {build_subdir}/hipblaslt-*.rpm")
# ---------------------------------------------------------------------------
# Dependency installation (split out for clarity)
# ---------------------------------------------------------------------------
def _install_system_deps(
c, distro, version_major, build_clients, use_system_packages,
no_msgpack, use_rocroller, legacy_hipblas_direct, bld: Path,
):
tensile_msgpack_backend = not no_msgpack
lib_ubuntu = ["make", "pkg-config", "libnuma1", "git", "libmsgpack-dev", "libgmock-dev", "libgtest-dev"]
lib_centos = ["epel-release", "make", "gcc-c++", "rpm-build"]
lib_centos8 = ["epel-release", "make", "gcc-c++", "rpm-build", "numactl-libs"]
lib_fedora = ["make", "gcc-c++", "libcxx-devel", "rpm-build", "numactl-libs"]
lib_sles = ["make", "gcc-c++", "libcxxtools9", "rpm-build"]
lib_mariner = ["make", "rpm-build"]
cli_ubuntu = ["python3", "python3-yaml", "libopenblas-dev"]
cli_centos = ["python36", "python3-pip"]
cli_centos8 = ["python39", "python3-virtualenv"]
cli_fedora = ["python36", "PyYAML", "python3-pip"]
cli_sles = ["pkg-config", "dpkg", "python3-pip"]
cli_mariner = ["python3", "python3-yaml"]
if use_system_packages:
cli_ubuntu.append("libopenblas-dev")
cli_centos.append("openblas-devel")
cli_centos8.append("openblas-devel")
cli_fedora.append("openblas-devel")
cli_sles = ["openblas-devel"]
cli_mariner.append("openblas-devel")
if tensile_msgpack_backend:
lib_centos.append("msgpack-devel")
lib_centos8.append("msgpack-devel")
lib_fedora.append("msgpack-devel")
if build_clients:
lib_ubuntu.append("gfortran")
lib_centos.append("devtoolset-7-gcc-gfortran")
lib_centos8.append("gcc-gfortran")
lib_fedora.append("gcc-gfortran")
lib_sles += ["gcc-fortran", "pkg-config", "dpkg"]
if use_rocroller:
lib_ubuntu += ["rocm-llvm-dev", "libzstd-dev"]
lib_centos8 += ["rocm-llvm-devel", "zstd"]
lib_sles += ["rocm-llvm-devel", "zstd"]
if not legacy_hipblas_direct:
lib_ubuntu.append("hipblas-common-dev")
lib_centos8.append("hipblas-common-devel")
if distro in ("centos", "rhel", "almalinux"):
lib_centos.append("numactl" if version_major < 7 else "numactl-libs")
cli_centos8.append("python3-pyyaml" if version_major >= 8 else "PyYAML")
if distro == "ubuntu":
_elevate(c, "apt update")
_apt_install(c, lib_ubuntu)
if build_clients:
_apt_install(c, cli_ubuntu)
c.run("pip3 install wheel")
elif distro in ("centos", "rhel", "almalinux"):
if version_major >= 8:
_yum_install(c, lib_centos8)
if build_clients:
_yum_install(c, cli_centos8)
c.run("pip3 install pyyaml")
else:
_yum_install(c, lib_centos)
if build_clients:
_yum_install(c, cli_centos)
c.run("pip3 install pyyaml")
elif distro == "mariner":
_yum_install(c, lib_mariner)
if build_clients:
_yum_install(c, cli_mariner)
c.run("pip3 install pyyaml")
elif distro == "azurelinux":
_dnf_install(c, lib_mariner)
if build_clients:
_dnf_install(c, cli_mariner)
c.run("pip3 install pyyaml")
elif distro == "fedora":
_dnf_install(c, lib_fedora)
if build_clients:
_dnf_install(c, cli_fedora)
c.run("pip3 install pyyaml")
elif distro in ("sles", "opensuse-leap"):
_zypper_install(c, lib_sles)
if build_clients:
_zypper_install(c, cli_sles)
c.run("pip3 install pyyaml")
# msgpack from source for non-Ubuntu RPM distros
if distro in ("centos", "rhel", "sles", "opensuse-leap", "almalinux"):
if tensile_msgpack_backend and not use_system_packages:
_install_msgpack_from_source(
c, bld,
cxx=f"{os.environ.get('ROCM_PATH', '/opt/rocm')}/bin/amdclang++",
cc=f"{os.environ.get('ROCM_PATH', '/opt/rocm')}/bin/amdclang",
)
# googletest + optional lapack
build_lapack = "OFF" if use_system_packages else "ON"
deps_dir = bld / "deps"
deps_prefix = deps_dir / "install"
deps_dir.mkdir(parents=True, exist_ok=True)
deps_prefix.mkdir(parents=True, exist_ok=True)
print(f"\033[32mBuilding \033[33mgoogletest\033[32m from source into {deps_prefix}\033[0m")
with c.cd(str(deps_dir)):
c.run(
f"cmake -DCMAKE_INSTALL_PREFIX={deps_prefix.as_posix()}"
f" -DCMAKE_INSTALL_LIBDIR=lib"
f" -DBUILD_LAPACK={build_lapack}"
f" {ROOT_PATH}/deps"
)
c.run(f"make -j{os.cpu_count()}")
c.run("make install")