| # 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", | 
 |     "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. | 
 |     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), | 
 |     }, | 
 | ) |