| # Copyright 2026 The IREE Authors |
| # |
| # Licensed under the Apache License v2.0 with LLVM Exceptions. |
| # See https://llvm.org/LICENSE.txt for license information. |
| # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| |
| """Repository rule exposing optional AMDGPU builtin device compiler tools.""" |
| |
| _STUB_BUILD = """\ |
| # AUTO-GENERATED by amdgpu_device_toolchain_repo.bzl - do not edit. |
| |
| package(default_visibility = ["//visibility:public"]) |
| |
| filegroup( |
| name = "unused", |
| srcs = [], |
| ) |
| """ |
| |
| _STUB_PATHS = """\ |
| # AUTO-GENERATED by amdgpu_device_toolchain_repo.bzl - do not edit. |
| |
| AMDGPU_DEVICE_TOOLCHAIN_AVAILABLE = False |
| AMDGPU_DEVICE_TOOLCHAIN_ERROR = "AMDGPU device binary source mode requires --repo_env=IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN=rocm, llvm-tools, llvm-project, or auto." |
| AMDGPU_CLANG_TOOL = "" |
| AMDGPU_LLVM_LINK_TOOL = "" |
| AMDGPU_LLD_TOOL = "" |
| AMDGPU_LLVM_OBJCOPY_TOOL = "" |
| AMDGPU_CLANG_RESOURCE_HEADERS = "" |
| AMDGPU_CLANG_RESOURCE_MARKER = "" |
| AMDGPU_CLANG_RESOURCE_INCLUDE_ARGS = [] |
| """ |
| |
| _PREBUILT_TOOL_BZL = """\ |
| \"\"\"Rule for exposing pre-built binaries as executable Bazel targets.\"\"\" |
| |
| def _prebuilt_tool_impl(ctx): |
| out = ctx.actions.declare_file(ctx.label.name) |
| ctx.actions.symlink( |
| output = out, |
| target_file = ctx.file.src, |
| is_executable = True, |
| ) |
| runfiles = ctx.runfiles(files = ctx.files.data + [ctx.file.src]) |
| return [DefaultInfo( |
| executable = out, |
| files = depset([out]), |
| runfiles = runfiles, |
| )] |
| |
| prebuilt_tool = rule( |
| implementation = _prebuilt_tool_impl, |
| executable = True, |
| attrs = { |
| "src": attr.label( |
| allow_single_file = True, |
| mandatory = True, |
| doc = "The pre-built binary file.", |
| ), |
| "data": attr.label_list( |
| allow_files = True, |
| doc = "Additional files the tool needs at runtime.", |
| ), |
| }, |
| doc = "Wraps a pre-built binary as an executable Bazel target.", |
| ) |
| """ |
| |
| _LOCAL_BUILD_TEMPLATE = """\ |
| # AUTO-GENERATED by amdgpu_device_toolchain_repo.bzl - do not edit. |
| |
| load(":prebuilt_tool.bzl", "prebuilt_tool") |
| |
| package(default_visibility = ["//visibility:public"]) |
| |
| prebuilt_tool( |
| name = "clang", |
| src = "bin/clang", |
| ) |
| |
| prebuilt_tool( |
| name = "llvm-link", |
| src = "bin/llvm-link", |
| ) |
| |
| prebuilt_tool( |
| name = "lld", |
| src = "bin/lld", |
| ) |
| |
| prebuilt_tool( |
| name = "llvm-objcopy", |
| src = "bin/llvm-objcopy", |
| ) |
| |
| filegroup( |
| name = "clang_resource_headers", |
| srcs = glob(["clang_resource_include/**"]), |
| ) |
| |
| filegroup( |
| name = "clang_resource_include_marker", |
| srcs = ["clang_resource_include/stddef.h"], |
| ) |
| """ |
| |
| _LOCAL_PATHS_TEMPLATE = """\ |
| # AUTO-GENERATED by amdgpu_device_toolchain_repo.bzl - do not edit. |
| |
| AMDGPU_DEVICE_TOOLCHAIN_AVAILABLE = True |
| AMDGPU_DEVICE_TOOLCHAIN_ERROR = "" |
| AMDGPU_CLANG_TOOL = "@iree_amdgpu_device_toolchain//:clang" |
| AMDGPU_LLVM_LINK_TOOL = "@iree_amdgpu_device_toolchain//:llvm-link" |
| AMDGPU_LLD_TOOL = "@iree_amdgpu_device_toolchain//:lld" |
| AMDGPU_LLVM_OBJCOPY_TOOL = "@iree_amdgpu_device_toolchain//:llvm-objcopy" |
| AMDGPU_CLANG_RESOURCE_HEADERS = "@iree_amdgpu_device_toolchain//:clang_resource_headers" |
| AMDGPU_CLANG_RESOURCE_MARKER = "@iree_amdgpu_device_toolchain//:clang_resource_include_marker" |
| AMDGPU_CLANG_RESOURCE_INCLUDE_ARGS = [ |
| "--clang-resource-include", |
| "$$(dirname $(location @iree_amdgpu_device_toolchain//:clang_resource_include_marker))", |
| ] |
| """ |
| |
| _LLVM_PROJECT_BUILD = """\ |
| # AUTO-GENERATED by amdgpu_device_toolchain_repo.bzl - do not edit. |
| |
| package(default_visibility = ["//visibility:public"]) |
| |
| alias( |
| name = "clang", |
| actual = "@llvm-project//clang:clang", |
| ) |
| |
| alias( |
| name = "llvm-link", |
| actual = "@llvm-project//llvm:llvm-link", |
| ) |
| |
| alias( |
| name = "lld", |
| actual = "@llvm-project//lld:lld", |
| ) |
| |
| alias( |
| name = "llvm-objcopy", |
| actual = "@llvm-project//llvm:llvm-objcopy", |
| ) |
| """ |
| |
| _LLVM_PROJECT_PATHS = """\ |
| # AUTO-GENERATED by amdgpu_device_toolchain_repo.bzl - do not edit. |
| |
| AMDGPU_DEVICE_TOOLCHAIN_AVAILABLE = True |
| AMDGPU_DEVICE_TOOLCHAIN_ERROR = "" |
| AMDGPU_CLANG_TOOL = "@iree_amdgpu_device_toolchain//:clang" |
| AMDGPU_LLVM_LINK_TOOL = "@iree_amdgpu_device_toolchain//:llvm-link" |
| AMDGPU_LLD_TOOL = "@iree_amdgpu_device_toolchain//:lld" |
| AMDGPU_LLVM_OBJCOPY_TOOL = "@iree_amdgpu_device_toolchain//:llvm-objcopy" |
| AMDGPU_CLANG_RESOURCE_HEADERS = "@llvm-project//clang:builtin_headers_gen" |
| AMDGPU_CLANG_RESOURCE_MARKER = "" |
| AMDGPU_CLANG_RESOURCE_INCLUDE_ARGS = [ |
| "--clang-resource-include", |
| "$(BINDIR)/external/+_repo_rules+llvm-project/clang/staging/include/", |
| ] |
| """ |
| |
| def _split_env_list(value): |
| if not value: |
| return [] |
| normalized = value |
| for separator in [",", ";", "\n", "\t", " "]: |
| normalized = normalized.replace(separator, ":") |
| return [part for part in normalized.split(":") if part] |
| |
| def _join_path(left, right): |
| if left.endswith("/"): |
| return left + right |
| return left + "/" + right |
| |
| def _dirname(path): |
| if "/" not in path: |
| return "." |
| return path.rsplit("/", 1)[0] |
| |
| def _basename(path): |
| if "/" not in path: |
| return path |
| return path.rsplit("/", 1)[1] |
| |
| def _append_unique(values, new_values): |
| for value in new_values: |
| if value not in values: |
| values.append(value) |
| |
| def _is_executable(repository_ctx, path): |
| result = repository_ctx.execute(["test", "-x", path], quiet = True) |
| return result.return_code == 0 |
| |
| def _find_tool( |
| repository_ctx, |
| names, |
| directories, |
| explicit_env_vars, |
| description, |
| additional_candidates = ()): |
| candidates = [] |
| for env_var in explicit_env_vars: |
| value = repository_ctx.getenv(env_var) |
| if value: |
| candidates.append(value) |
| candidates.extend(additional_candidates) |
| for directory in directories: |
| for name in names: |
| candidates.append(_join_path(directory, name)) |
| |
| for candidate in candidates: |
| if _is_executable(repository_ctx, candidate): |
| return str(repository_ctx.path(candidate)) |
| |
| fail("Could not find {}. Tried env vars [{}] and candidate directories [{}].".format( |
| description, |
| ", ".join(explicit_env_vars), |
| ", ".join(directories), |
| )) |
| |
| def _rocm_tool_dirs(root): |
| return [ |
| _join_path(root, "llvm/bin"), |
| _join_path(root, "lib/llvm/bin"), |
| _join_path(root, "bin"), |
| ] |
| |
| def _candidate_rocm_roots(repository_ctx): |
| roots = [] |
| for env_var in [ |
| "IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_ROCM_PATH", |
| "IREE_ROCM_PATH", |
| "ROCM_PATH", |
| "ROCM_ROOT", |
| "ROCM_HOME", |
| "HIP_PATH", |
| ]: |
| for value in _split_env_list(repository_ctx.getenv(env_var)): |
| _append_unique(roots, [value]) |
| if env_var == "HIP_PATH" and _basename(value) == "hip": |
| _append_unique(roots, [_dirname(value)]) |
| |
| hipcc = repository_ctx.which("hipcc") |
| if hipcc: |
| _append_unique(roots, [_dirname(_dirname(str(hipcc)))]) |
| |
| if repository_ctx.path("/opt/rocm").exists: |
| _append_unique(roots, ["/opt/rocm"]) |
| return roots |
| |
| def _candidate_tool_dirs(repository_ctx, mode): |
| dirs = [] |
| for env_var in [ |
| "IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_LLVM_TOOLS_DIR", |
| "IREE_HOST_BIN_DIR", |
| "IREE_HOST_TOOLS", |
| "IREE_HOST_TOOLS_DIR", |
| "IREE_BINARY_DIR", |
| "IREE_LLVM_TOOLS_DIR", |
| "IREE_LLVM_TOOL_DIR", |
| "LLVM_TOOLS_BINARY_DIR", |
| "LLVM_BINARY_DIR", |
| ]: |
| for value in _split_env_list(repository_ctx.getenv(env_var)): |
| _append_unique(dirs, [value]) |
| |
| expanded_dirs = [] |
| for directory in dirs: |
| expanded_dirs.extend([ |
| directory, |
| _join_path(directory, "bin"), |
| _join_path(directory, "llvm-project/bin"), |
| _join_path(directory, "llvm/bin"), |
| _join_path(directory, "lib/llvm/bin"), |
| ]) |
| dirs = [] |
| _append_unique(dirs, expanded_dirs) |
| |
| if mode in ["auto", "rocm"]: |
| for root in _candidate_rocm_roots(repository_ctx): |
| _append_unique(dirs, _rocm_tool_dirs(root)) |
| return dirs |
| |
| def _clang_tool_candidates(repository_ctx, clang, names): |
| candidates = [] |
| clang_dir = _dirname(clang) |
| for name in names: |
| result = repository_ctx.execute( |
| [clang, "--print-prog-name=" + name], |
| quiet = True, |
| ) |
| if result.return_code != 0: |
| continue |
| printed_name = result.stdout.strip() |
| if not printed_name: |
| continue |
| if printed_name.startswith("/"): |
| _append_unique(candidates, [printed_name]) |
| else: |
| _append_unique(candidates, [ |
| _join_path(clang_dir, printed_name), |
| printed_name, |
| ]) |
| return candidates |
| |
| def _detect_clang_resource_include(repository_ctx, clang): |
| explicit = repository_ctx.getenv("IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_CLANG_RESOURCE_INCLUDE") |
| if not explicit: |
| explicit = repository_ctx.getenv("IREE_CLANG_BUILTIN_HEADERS_PATH") |
| if explicit: |
| path = repository_ctx.path(explicit) |
| if not path.exists: |
| fail("Configured clang resource include directory does not exist: {}".format(explicit)) |
| return str(path) |
| |
| result = repository_ctx.execute([clang, "-print-resource-dir"], quiet = True) |
| if result.return_code != 0: |
| fail("Failed to query clang resource directory from {}:\n{}".format( |
| clang, |
| result.stderr, |
| )) |
| include_dir = _join_path(result.stdout.strip(), "include") |
| path = repository_ctx.path(include_dir) |
| if not path.exists: |
| fail("clang resource include directory does not exist: {}".format(include_dir)) |
| return str(path) |
| |
| def _write_local_toolchain_repo(repository_ctx, tools, clang_resource_include): |
| repository_ctx.file("prebuilt_tool.bzl", _PREBUILT_TOOL_BZL) |
| repository_ctx.file("bin/.keep", "") |
| for name, path in tools.items(): |
| repository_ctx.symlink(repository_ctx.path(path), "bin/" + name) |
| repository_ctx.symlink( |
| repository_ctx.path(clang_resource_include), |
| "clang_resource_include", |
| ) |
| repository_ctx.file("BUILD.bazel", _LOCAL_BUILD_TEMPLATE) |
| repository_ctx.file("paths.bzl", _LOCAL_PATHS_TEMPLATE) |
| |
| def _amdgpu_device_toolchain_repo_impl(repository_ctx): |
| mode = repository_ctx.getenv("IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN") or "none" |
| if mode not in ["none", "auto", "rocm", "llvm-tools", "llvm-project"]: |
| fail("Unknown IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN '{}'. Available: none, auto, rocm, llvm-tools, llvm-project.".format(mode)) |
| |
| if mode == "none": |
| repository_ctx.file("BUILD.bazel", _STUB_BUILD) |
| repository_ctx.file("paths.bzl", _STUB_PATHS) |
| return |
| |
| if mode == "llvm-project": |
| repository_ctx.file("BUILD.bazel", _LLVM_PROJECT_BUILD) |
| repository_ctx.file("paths.bzl", _LLVM_PROJECT_PATHS) |
| return |
| |
| tool_dirs = _candidate_tool_dirs(repository_ctx, mode) |
| if mode == "rocm" and not tool_dirs: |
| fail("IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN=rocm requires a ROCm/TheRock root such as IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_ROCM_PATH, IREE_ROCM_PATH, ROCM_PATH, ROCM_ROOT, ROCM_HOME, HIP_PATH, hipcc on PATH, or /opt/rocm.") |
| |
| clang = _find_tool( |
| repository_ctx, |
| ["clang", "amdclang", "clang-22"], |
| tool_dirs, |
| ["IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_CLANG_BINARY", "IREE_CLANG_BINARY", "CLANG"], |
| "clang with AMDGPU support", |
| ) |
| llvm_link = _find_tool( |
| repository_ctx, |
| ["llvm-link"], |
| tool_dirs, |
| ["IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_LLVM_LINK_BINARY", "IREE_LLVM_LINK_BINARY", "LLVM_LINK"], |
| "llvm-link", |
| additional_candidates = _clang_tool_candidates(repository_ctx, clang, ["llvm-link"]), |
| ) |
| lld = _find_tool( |
| repository_ctx, |
| ["lld", "ld.lld"], |
| tool_dirs, |
| ["IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_LLD_BINARY", "IREE_LLD_BINARY", "LLD", "LD_LLD"], |
| "lld", |
| additional_candidates = _clang_tool_candidates(repository_ctx, clang, ["lld", "ld.lld"]), |
| ) |
| llvm_objcopy = _find_tool( |
| repository_ctx, |
| ["llvm-objcopy"], |
| tool_dirs, |
| ["IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_LLVM_OBJCOPY_BINARY", "IREE_LLVM_OBJCOPY_BINARY", "LLVM_OBJCOPY"], |
| "llvm-objcopy", |
| additional_candidates = _clang_tool_candidates(repository_ctx, clang, ["llvm-objcopy"]), |
| ) |
| clang_resource_include = _detect_clang_resource_include(repository_ctx, clang) |
| |
| _write_local_toolchain_repo( |
| repository_ctx, |
| { |
| "clang": clang, |
| "llvm-link": llvm_link, |
| "lld": lld, |
| "llvm-objcopy": llvm_objcopy, |
| }, |
| clang_resource_include, |
| ) |
| |
| amdgpu_device_toolchain_repo = repository_rule( |
| implementation = _amdgpu_device_toolchain_repo_impl, |
| environ = [ |
| "CLANG", |
| "HIP_PATH", |
| "IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN", |
| "IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_CLANG_BINARY", |
| "IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_CLANG_RESOURCE_INCLUDE", |
| "IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_LLD_BINARY", |
| "IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_LLVM_LINK_BINARY", |
| "IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_LLVM_OBJCOPY_BINARY", |
| "IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_LLVM_TOOLS_DIR", |
| "IREE_HAL_AMDGPU_DEVICE_TOOLCHAIN_ROCM_PATH", |
| "IREE_CLANG_BINARY", |
| "IREE_CLANG_BUILTIN_HEADERS_PATH", |
| "IREE_HOST_BIN_DIR", |
| "IREE_HOST_TOOLS", |
| "IREE_HOST_TOOLS_DIR", |
| "IREE_BINARY_DIR", |
| "IREE_LLD_BINARY", |
| "IREE_LLVM_LINK_BINARY", |
| "IREE_LLVM_OBJCOPY_BINARY", |
| "IREE_LLVM_TOOL_DIR", |
| "IREE_LLVM_TOOLS_DIR", |
| "IREE_ROCM_PATH", |
| "LD_LLD", |
| "LLD", |
| "LLVM_BINARY_DIR", |
| "LLVM_LINK", |
| "LLVM_OBJCOPY", |
| "LLVM_TOOLS_BINARY_DIR", |
| "ROCM_HOME", |
| "ROCM_PATH", |
| "ROCM_ROOT", |
| ], |
| doc = "Exposes optional AMDGPU builtin device compiler tools.", |
| ) |