1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
|
# This file is 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
"""Helper macros to configure the LLVM overlay project."""
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
# Directory of overlay files relative to WORKSPACE
DEFAULT_OVERLAY_PATH = "llvm-project-overlay"
DEFAULT_TARGETS = [
"AArch64",
"AMDGPU",
"ARM",
"AVR",
"BPF",
"Hexagon",
"Lanai",
"LoongArch",
"Mips",
"MSP430",
"NVPTX",
"PowerPC",
"RISCV",
"Sparc",
"SystemZ",
"VE",
"WebAssembly",
"X86",
"XCore",
]
def _overlay_directories(repository_ctx):
src_path = repository_ctx.path(Label("@llvm-raw//:WORKSPACE")).dirname
bazel_path = src_path.get_child("utils").get_child("bazel")
overlay_path = bazel_path.get_child("llvm-project-overlay")
script_path = bazel_path.get_child("overlay_directories.py")
python_bin = repository_ctx.which("python3")
if not python_bin:
# Windows typically just defines "python" as python3. The script itself
# contains a check to ensure python3.
python_bin = repository_ctx.which("python")
if not python_bin:
fail("Failed to find python3 binary")
cmd = [
python_bin,
script_path,
"--src",
src_path,
"--overlay",
overlay_path,
"--target",
".",
]
exec_result = repository_ctx.execute(cmd, timeout = 20)
if exec_result.return_code != 0:
fail(("Failed to execute overlay script: '{cmd}'\n" +
"Exited with code {return_code}\n" +
"stdout:\n{stdout}\n" +
"stderr:\n{stderr}\n").format(
cmd = " ".join([str(arg) for arg in cmd]),
return_code = exec_result.return_code,
stdout = exec_result.stdout,
stderr = exec_result.stderr,
))
def _extract_cmake_settings(repository_ctx, llvm_cmake):
# The list to be written to vars.bzl
# `CMAKE_CXX_STANDARD` may be used from WORKSPACE for the toolchain.
c = {
"CMAKE_CXX_STANDARD": None,
"LLVM_VERSION_MAJOR": None,
"LLVM_VERSION_MINOR": None,
"LLVM_VERSION_PATCH": None,
}
# It would be easier to use external commands like sed(1) and python.
# For portability, the parser should run on Starlark.
llvm_cmake_path = repository_ctx.path(Label("//:" + llvm_cmake))
for line in repository_ctx.read(llvm_cmake_path).splitlines():
# Extract "set ( FOO bar ... "
setfoo = line.partition("(")
if setfoo[1] != "(":
continue
if setfoo[0].strip().lower() != "set":
continue
# `kv` is assumed as \s*KEY\s+VAL\s*\).*
# Typical case is like
# LLVM_REQUIRED_CXX_STANDARD 17)
# Possible case -- It should be ignored.
# CMAKE_CXX_STANDARD ${...} CACHE STRING "...")
kv = setfoo[2].strip()
i = kv.find(" ")
if i < 0:
continue
k = kv[:i]
# Prefer LLVM_REQUIRED_CXX_STANDARD instead of CMAKE_CXX_STANDARD
if k == "LLVM_REQUIRED_CXX_STANDARD":
k = "CMAKE_CXX_STANDARD"
c[k] = None
if k not in c:
continue
# Skip if `CMAKE_CXX_STANDARD` is set with
# `LLVM_REQUIRED_CXX_STANDARD`.
# Then `v` will not be desired form, like "${...} CACHE"
if c[k] != None:
continue
# Pick up 1st word as the value.
# Note: It assumes unquoted word.
v = kv[i:].strip().partition(")")[0].partition(" ")[0]
c[k] = v
# Synthesize `LLVM_VERSION` for convenience.
c["LLVM_VERSION"] = "{}.{}.{}".format(
c["LLVM_VERSION_MAJOR"],
c["LLVM_VERSION_MINOR"],
c["LLVM_VERSION_PATCH"],
)
return c
def _write_dict_to_file(repository_ctx, filepath, header, vars):
# (fci + individual vars) + (fcd + dict items) + (fct)
fci = header
fcd = "\nllvm_vars={\n"
fct = "}\n"
for k, v in vars.items():
fci += '{} = "{}"\n'.format(k, v)
fcd += ' "{}": "{}",\n'.format(k, v)
repository_ctx.file(filepath, content = fci + fcd + fct)
def _llvm_configure_impl(repository_ctx):
_overlay_directories(repository_ctx)
llvm_cmake = "llvm/CMakeLists.txt"
vars = _extract_cmake_settings(
repository_ctx,
llvm_cmake,
)
_write_dict_to_file(
repository_ctx,
filepath = "vars.bzl",
header = "# Generated from {}\n\n".format(llvm_cmake),
vars = vars,
)
# Create a starlark file with the requested LLVM targets.
targets = repository_ctx.attr.targets
repository_ctx.file(
"llvm/targets.bzl",
content = "llvm_targets = " + str(targets),
executable = False,
)
llvm_configure = repository_rule(
implementation = _llvm_configure_impl,
local = True,
configure = True,
attrs = {
"targets": attr.string_list(default = DEFAULT_TARGETS),
},
)
|