1# Copyright 2022 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15"""Repository rule that tries to find system provided LLVM packages.""" 16 17load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") 18 19SYSTEM_LLVM_BAZEL_TEMPLATE = """package(default_visibility = ["//visibility:public"]) 20# Create one hidden library with all LLVM headers that depends on all its 21# static library archives. This will be used to provide individual library 22# targets named the same as the upstream Bazel files. 23cc_library( 24 name = "llvm", 25 hdrs = glob([ 26 "llvm-project-include/clang-c/**/*.h", 27 "llvm-project-include/clang/**/*.def", 28 "llvm-project-include/clang/**/*.h", 29 "llvm-project-include/clang/**/*.inc", 30 "llvm-project-include/llvm-c/**/*.h", 31 "llvm-project-include/llvm/**/*.def", 32 "llvm-project-include/llvm/**/*.h", 33 "llvm-project-include/llvm/**/*.inc", 34 ]), 35 includes = ["llvm-project-include"], 36 linkopts = [ 37 "-lncurses", 38 "-lz", 39 "-L%{llvm_lib_dir}", 40 "-Wl,--start-group", 41 %{llvm_libs} 42 "-Wl,--end-group", 43 ], 44 visibility = ["@llvm-project//clang:__pkg__"], 45) 46# Fake support library 47cc_library(name = "Support", deps = ["@llvm-project//llvm:llvm"]) 48""" 49 50SYSTEM_CLANG_BAZEL = """package(default_visibility = ["//visibility:public"]) 51# Fake libraries that just depend on a big library with all files. 52cc_library(name = "ast", deps = ["@llvm-project//llvm:llvm"]) 53cc_library(name = "basic", deps = ["@llvm-project//llvm:llvm"]) 54cc_library(name = "driver", deps = ["@llvm-project//llvm:llvm"]) 55cc_library(name = "format", deps = ["@llvm-project//llvm:llvm"]) 56cc_library(name = "frontend", deps = ["@llvm-project//llvm:llvm"]) 57cc_library(name = "lex", deps = ["@llvm-project//llvm:llvm"]) 58cc_library(name = "tooling", deps = ["@llvm-project//llvm:llvm"]) 59""" 60 61def _use_system_llvm(ctx): 62 found = False 63 64 # Look for LLVM in known places 65 llvm_dirs = ctx.execute( 66 ["ls", "-1f"] + 67 [ 68 "/usr/lib/llvm-{}/include/llvm/Support/InitLLVM.h".format(ver) 69 for ver in [16, 15, 14, 13, 12, 11] # Debian 70 ] + [ 71 "/usr/include/llvm/Support/InitLLVM.h", # Fedora and others 72 ], 73 ).stdout.splitlines() 74 if llvm_dirs: 75 llvm_dir = llvm_dirs[0].split("/include/llvm/")[0] 76 for suffix in ["llvm", "llvm-c", "clang", "clang-c"]: 77 ctx.symlink( 78 llvm_dir + "/include/" + suffix, 79 "llvm/llvm-project-include/" + suffix, 80 ) 81 82 # Try to find the lib directory 83 lib_dirs = ctx.execute( 84 ["ls", "-d1f"] + 85 [llvm_dir + "/lib64", llvm_dir + "/lib"], 86 ).stdout.splitlines() 87 if lib_dirs: 88 found = True 89 90 if found: 91 # Create stub targets in sub-packages 92 lib_dir = lib_dirs[0] # buildifier: disable=uninitialized 93 archives = ctx.execute( 94 ["find", ".", "-maxdepth", "1"] + 95 ["(", "-name", "libLLVM*.a", "-o", "-name", "libclang*.a", ")"], 96 working_directory = lib_dir, 97 ).stdout.splitlines() 98 lib_strs = sorted(["\"-l{}\",".format(a[5:-2]) for a in archives]) 99 100 ctx.file( 101 "llvm/BUILD.bazel", 102 SYSTEM_LLVM_BAZEL_TEMPLATE 103 .replace("%{llvm_lib_dir}", lib_dir) 104 .replace("%{llvm_libs}", "\n".join(lib_strs)), 105 ) 106 ctx.file("clang/BUILD.bazel", SYSTEM_CLANG_BAZEL) 107 return found 108 109def _overlay_directories(ctx, src_path, target_path): 110 bazel_path = src_path.get_child("utils").get_child("bazel") 111 overlay_path = bazel_path.get_child("llvm-project-overlay") 112 script_path = bazel_path.get_child("overlay_directories.py") 113 114 python_bin = ctx.which("python3") 115 if not python_bin: 116 python_bin = ctx.which("python") 117 118 if not python_bin: 119 fail("Failed to find python3 binary") 120 121 cmd = [ 122 python_bin, 123 script_path, 124 "--src", 125 src_path, 126 "--overlay", 127 overlay_path, 128 "--target", 129 target_path, 130 ] 131 exec_result = ctx.execute(cmd, timeout = 20) 132 133 if exec_result.return_code != 0: 134 fail(("Failed to execute overlay script: '{cmd}'\n" + 135 "Exited with code {return_code}\n" + 136 "stdout:\n{stdout}\n" + 137 "stderr:\n{stderr}\n").format( 138 cmd = " ".join([str(arg) for arg in cmd]), 139 return_code = exec_result.return_code, 140 stdout = exec_result.stdout, 141 stderr = exec_result.stderr, 142 )) 143 144DEFAULT_LLVM_COMMIT = "2c494f094123562275ae688bd9e946ae2a0b4f8b" # 2022-03-31 145DEFAULT_LLVM_SHA256 = "59b9431ae22f0ea5f2ce880925c0242b32a9e4f1ae8147deb2bb0fc19b53fa0d" 146 147def _llvm_configure_impl(ctx): 148 commit = ctx.attr.commit 149 sha256 = ctx.attr.sha256 150 151 if ctx.attr.system_libraries: 152 if _use_system_llvm(ctx): 153 return 154 if not commit: 155 fail(( 156 "Failed to find LLVM and clang system libraries\n\n" + 157 "Note: You may have to install llvm-13-dev and libclang-13-dev\n" + 158 " packages (or later versions) first.\n" 159 )) 160 161 if not commit: 162 commit = DEFAULT_LLVM_COMMIT 163 sha256 = DEFAULT_LLVM_SHA256 164 165 ctx.download_and_extract( 166 ["https://github.com/llvm/llvm-project/archive/{commit}.tar.gz".format(commit = commit)], 167 "llvm-raw", 168 sha256, 169 "", 170 "llvm-project-" + commit, 171 ) 172 173 target_path = ctx.path("llvm-raw").dirname 174 src_path = target_path.get_child("llvm-raw") 175 _overlay_directories(ctx, src_path, target_path) 176 177 # Create a starlark file with the requested LLVM targets 178 ctx.file( 179 "llvm/targets.bzl", 180 "llvm_targets = " + str(ctx.attr.targets), 181 executable = False, 182 ) 183 184 # Set up C++ toolchain options. LLVM requires at least C++ 14. 185 ctx.file( 186 ".bazelrc", 187 "build --cxxopt=-std=c++17 --host_cxxopt=-std=c++17", 188 executable = False, 189 ) 190 191DEFAULT_TARGETS = ["AArch64", "ARM", "PowerPC", "X86"] 192 193llvm_configure = repository_rule( 194 implementation = _llvm_configure_impl, 195 local = True, 196 configure = True, 197 attrs = { 198 "system_libraries": attr.bool(default = True), 199 "commit": attr.string(), 200 "sha256": attr.string(), 201 "targets": attr.string_list(default = DEFAULT_TARGETS), 202 }, 203) 204 205def _llvm_zlib_disable_impl(ctx): 206 ctx.file( 207 "BUILD.bazel", 208 """cc_library(name = "zlib", visibility = ["//visibility:public"])""", 209 executable = False, 210 ) 211 212llvm_zlib_disable = repository_rule( 213 implementation = _llvm_zlib_disable_impl, 214) 215 216def _llvm_terminfo_disable(ctx): 217 ctx.file( 218 "BUILD.bazel", 219 """cc_library(name = "terminfo", visibility = ["//visibility:public"])""", 220 executable = False, 221 ) 222 223llvm_terminfo_disable = repository_rule( 224 implementation = _llvm_terminfo_disable, 225) 226 227def llvm_disable_optional_support_deps(): 228 maybe(llvm_zlib_disable, name = "llvm_zlib") 229 maybe(llvm_terminfo_disable, name = "llvm_terminfo") 230