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