# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Repository rule that tries to find system provided LLVM packages.""" load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") SYSTEM_LLVM_BAZEL_TEMPLATE = """package(default_visibility = ["//visibility:public"]) # Create one hidden library with all LLVM headers that depends on all its # static library archives. This will be used to provide individual library # targets named the same as the upstream Bazel files. cc_library( name = "llvm", hdrs = glob([ "llvm-project-include/clang-c/**/*.h", "llvm-project-include/clang/**/*.def", "llvm-project-include/clang/**/*.h", "llvm-project-include/clang/**/*.inc", "llvm-project-include/llvm-c/**/*.h", "llvm-project-include/llvm/**/*.def", "llvm-project-include/llvm/**/*.h", "llvm-project-include/llvm/**/*.inc", ]), includes = ["llvm-project-include"], linkopts = [ "-lncurses", "-lz", "-L%{llvm_lib_dir}", "-Wl,--start-group", %{llvm_libs} "-Wl,--end-group", ], visibility = ["@llvm-project//clang:__pkg__"], ) # Fake support library cc_library(name = "Support", deps = ["@llvm-project//llvm:llvm"]) """ SYSTEM_CLANG_BAZEL = """package(default_visibility = ["//visibility:public"]) # Fake libraries that just depend on a big library with all files. cc_library(name = "ast", deps = ["@llvm-project//llvm:llvm"]) cc_library(name = "basic", deps = ["@llvm-project//llvm:llvm"]) cc_library(name = "driver", deps = ["@llvm-project//llvm:llvm"]) cc_library(name = "format", deps = ["@llvm-project//llvm:llvm"]) cc_library(name = "frontend", deps = ["@llvm-project//llvm:llvm"]) cc_library(name = "lex", deps = ["@llvm-project//llvm:llvm"]) cc_library(name = "tooling", deps = ["@llvm-project//llvm:llvm"]) """ def _use_system_llvm(ctx): found = False # Look for LLVM in known places llvm_dirs = ctx.execute( ["ls", "-1f"] + [ "/usr/lib/llvm-{}/include/llvm/Support/InitLLVM.h".format(ver) for ver in [16, 15, 14, 13, 12, 11] # Debian ] + [ "/usr/include/llvm/Support/InitLLVM.h", # Fedora and others ], ).stdout.splitlines() if llvm_dirs: llvm_dir = llvm_dirs[0].split("/include/llvm/")[0] for suffix in ["llvm", "llvm-c", "clang", "clang-c"]: ctx.symlink( llvm_dir + "/include/" + suffix, "llvm/llvm-project-include/" + suffix, ) # Try to find the lib directory lib_dirs = ctx.execute( ["ls", "-d1f"] + [llvm_dir + "/lib64", llvm_dir + "/lib"], ).stdout.splitlines() if lib_dirs: found = True if found: # Create stub targets in sub-packages lib_dir = lib_dirs[0] # buildifier: disable=uninitialized archives = ctx.execute( ["find", ".", "-maxdepth", "1"] + ["(", "-name", "libLLVM*.a", "-o", "-name", "libclang*.a", ")"], working_directory = lib_dir, ).stdout.splitlines() lib_strs = sorted(["\"-l{}\",".format(a[5:-2]) for a in archives]) ctx.file( "llvm/BUILD.bazel", SYSTEM_LLVM_BAZEL_TEMPLATE .replace("%{llvm_lib_dir}", lib_dir) .replace("%{llvm_libs}", "\n".join(lib_strs)), ) ctx.file("clang/BUILD.bazel", SYSTEM_CLANG_BAZEL) return found def _overlay_directories(ctx, src_path, target_path): 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 = ctx.which("python3") if not python_bin: python_bin = 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", target_path, ] exec_result = 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, )) DEFAULT_LLVM_COMMIT = "2c494f094123562275ae688bd9e946ae2a0b4f8b" # 2022-03-31 DEFAULT_LLVM_SHA256 = "59b9431ae22f0ea5f2ce880925c0242b32a9e4f1ae8147deb2bb0fc19b53fa0d" def _llvm_configure_impl(ctx): commit = ctx.attr.commit sha256 = ctx.attr.sha256 if ctx.attr.system_libraries: if _use_system_llvm(ctx): return if not commit: fail(( "Failed to find LLVM and clang system libraries\n\n" + "Note: You may have to install llvm-13-dev and libclang-13-dev\n" + " packages (or later versions) first.\n" )) if not commit: commit = DEFAULT_LLVM_COMMIT sha256 = DEFAULT_LLVM_SHA256 ctx.download_and_extract( ["https://github.com/llvm/llvm-project/archive/{commit}.tar.gz".format(commit = commit)], "llvm-raw", sha256, "", "llvm-project-" + commit, ) target_path = ctx.path("llvm-raw").dirname src_path = target_path.get_child("llvm-raw") _overlay_directories(ctx, src_path, target_path) # Create a starlark file with the requested LLVM targets ctx.file( "llvm/targets.bzl", "llvm_targets = " + str(ctx.attr.targets), executable = False, ) # Set up C++ toolchain options. LLVM requires at least C++ 14. ctx.file( ".bazelrc", "build --cxxopt=-std=c++17 --host_cxxopt=-std=c++17", executable = False, ) DEFAULT_TARGETS = ["AArch64", "ARM", "PowerPC", "X86"] llvm_configure = repository_rule( implementation = _llvm_configure_impl, local = True, configure = True, attrs = { "system_libraries": attr.bool(default = True), "commit": attr.string(), "sha256": attr.string(), "targets": attr.string_list(default = DEFAULT_TARGETS), }, ) def _llvm_zlib_disable_impl(ctx): ctx.file( "BUILD.bazel", """cc_library(name = "zlib", visibility = ["//visibility:public"])""", executable = False, ) llvm_zlib_disable = repository_rule( implementation = _llvm_zlib_disable_impl, ) def _llvm_terminfo_disable(ctx): ctx.file( "BUILD.bazel", """cc_library(name = "terminfo", visibility = ["//visibility:public"])""", executable = False, ) llvm_terminfo_disable = repository_rule( implementation = _llvm_terminfo_disable, ) def llvm_disable_optional_support_deps(): maybe(llvm_zlib_disable, name = "llvm_zlib") maybe(llvm_terminfo_disable, name = "llvm_terminfo")