|  | # 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.""" | 
|  |  | 
|  | # 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", | 
|  | "SPIRV", | 
|  | "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, | 
|  | "LLVM_VERSION_SUFFIX": 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"], | 
|  | ) | 
|  |  | 
|  | c["PACKAGE_VERSION"] = "{}.{}.{}{}".format( | 
|  | c["LLVM_VERSION_MAJOR"], | 
|  | c["LLVM_VERSION_MINOR"], | 
|  | c["LLVM_VERSION_PATCH"], | 
|  | c["LLVM_VERSION_SUFFIX"], | 
|  | ) | 
|  |  | 
|  | 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, | 
|  | ) | 
|  |  | 
|  | # Grab version info and merge it with the other vars | 
|  | version = _extract_cmake_settings( | 
|  | repository_ctx, | 
|  | "cmake/Modules/LLVMVersion.cmake", | 
|  | ) | 
|  | version = {k: v for k, v in version.items() if v != None} | 
|  | vars.update(version) | 
|  |  | 
|  | _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. | 
|  | llvm_targets = repository_ctx.attr.targets | 
|  | repository_ctx.file( | 
|  | "llvm/targets.bzl", | 
|  | content = "llvm_targets = " + str(llvm_targets), | 
|  | executable = False, | 
|  | ) | 
|  |  | 
|  | # Create a starlark file with the requested BOLT targets. | 
|  | bolt_targets = ["AArch64","X86","RISCV"]  # Supported targets. | 
|  | bolt_targets = [t for t in llvm_targets if t in bolt_targets] | 
|  | repository_ctx.file( | 
|  | "bolt/targets.bzl", | 
|  | content = "bolt_targets = " + str(bolt_targets), | 
|  | executable = False, | 
|  | ) | 
|  |  | 
|  | llvm_configure = repository_rule( | 
|  | implementation = _llvm_configure_impl, | 
|  | local = True, | 
|  | configure = True, | 
|  | attrs = { | 
|  | "targets": attr.string_list(default = DEFAULT_TARGETS), | 
|  | }, | 
|  | ) |