Create option to build against a CMake build of LLVM
Set `PREBUILT_LLVM_PATH` to the root of an LLVM tree built with CMake
where the build output directory is named `build`.
The path can be provided relative to the root of the Crubit tree.
PiperOrigin-RevId: 450711879
diff --git a/.bazelrc b/.bazelrc
index c1881c2..301c00f 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -5,5 +5,12 @@
build:generic_clang --cxxopt=-Wno-range-loop-analysis --host_cxxopt=-Wno-range-loop-analysis
build:generic_clang --copt=-Wno-deprecated --host_copt=-Wno-deprecated
+# enable linking against prebuilt LLVM with no RTTI
+build:generic_clang --copt=-fno-rtti --host_copt=-fno-rtti
+
+# new warning in LLVM 15, fires when building LLVM 14.0.0
+# ref: https://github.com/llvm/llvm-project/commit/48285c20
+build:generic_clang --copt=-Wno-unused-but-set-variable --host_copt=-Wno-unused-but-set-variable
+
build --config=generic_clang
build --sandbox_base=/dev/shm
diff --git a/README.md b/README.md
index 55b3068..671119b 100644
--- a/README.md
+++ b/README.md
@@ -13,4 +13,16 @@
$ git clone git@github.com:google/crubit.git
$ cd crubit
$ bazel build --linkopt=-fuse-ld=/usr/bin/ld.lld //rs_bindings_from_cc:rs_bindings_from_cc_impl
+```
+
+### Using a prebuilt LLVM tree
+
+```
+$ git clone https://github.com/llvm/llvm-project
+$ cd llvm-project
+$ CC=clang CXX=clang++ cmake -S llvm -B build -DLLVM_ENABLE_PROJECTS='clang' -DCMAKE_BUILD_TYPE=Release
+$ cmake --build build -j
+$ # wait...
+$ cd ../crubit
+$ PREBUILT_LLVM_PATH=../llvm-project bazel build //rs_bindings_from_cc:rs_bindings_from_cc_impl
```
\ No newline at end of file
diff --git a/WORKSPACE b/WORKSPACE
index 060b2c5..e4e4928 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -102,21 +102,12 @@
urls = ["https://github.com/abseil/abseil-cpp/archive/refs/tags/20211102.0.zip"],
)
-# https://github.com/llvm/llvm-project/blob/main/utils/bazel/examples/http_archive/WORKSPACE
-# https://github.com/llvm/llvm-project/releases/tag/llvmorg-14.0.0
+# Create the "loader" repository, then use it to configure the desired LLVM
+# repository. For more details, see the comment in bazel/llvm.bzl.
-http_archive(
- name = "llvm-raw",
- build_file_content = "# empty",
- sha256 = "eb7437b60a6f78e7910d08911975f100e99e9c714f069a5487119c7eadc79171",
- strip_prefix = "llvm-project-llvmorg-14.0.0",
- urls = ["https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-14.0.0.zip"],
-)
+load("//bazel:llvm.bzl", "llvm_loader_repository_dependencies", "llvm_loader_repository")
+llvm_loader_repository_dependencies()
+llvm_loader_repository(name = "llvm-loader")
-load("@llvm-raw//utils/bazel:configure.bzl", "llvm_configure", "llvm_disable_optional_support_deps")
-
-# this *must* be llvm-project, it's hardcoded in the Bazel build
-# e.g. https://github.com/llvm/llvm-project/blob/aaddfbf9/utils/bazel/llvm-project-overlay/clang/BUILD.bazel#L1473
-llvm_configure(name = "llvm-project")
-
-llvm_disable_optional_support_deps()
+load("@llvm-loader//:llvm.bzl", "llvm_repository")
+llvm_repository(name = "llvm-project")
diff --git a/bazel/BUILD b/bazel/BUILD
new file mode 100644
index 0000000..1bb8bf6
--- /dev/null
+++ b/bazel/BUILD
@@ -0,0 +1 @@
+# empty
diff --git a/bazel/llvm.bzl b/bazel/llvm.bzl
new file mode 100644
index 0000000..bcfd0e4
--- /dev/null
+++ b/bazel/llvm.bzl
@@ -0,0 +1,78 @@
+# Part of the Crubit project, under the Apache License v2.0 with LLVM
+# Exceptions. See /LICENSE for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+# Create a loader/trampoline repository that we can call into to load LLVM.
+#
+# Our real goal is to choose between two different sources for LLVM binaries:
+# - if `PREBUILT_LLVM_PATH` is in the environment, we treat it as the root of
+# an LLVM tree that's been built with CMake and try to use headers and
+# libraries from there
+# - otherwise, we build LLVM from source
+#
+# We *could* implement this choice directly as an if/else between `http_archive`
+# or `new_local_repository`. However, all Bazel `load`s are unconditional, so we
+# would always end up cloning the (very large) LLVM project repository to load
+# its Bazel configuration even if we aren't going to use it.
+#
+# To avoid that, we add the extra indirection of the "loader" repository. We
+# populate the loader repository with one of two templated .bzl files depending
+# on whether we want "local" or "remote" LLVM. Then our caller activates that
+# .bzl file and gets the desired effect.
+def _llvm_loader_repository(repository_ctx):
+ # The final repository is required to have a `BUILD` file at the root.
+ repository_ctx.file("BUILD")
+
+ # Create `llvm.bzl` from one of `llvm_{remote|local}.bzl.tmpl`.
+ if "PREBUILT_LLVM_PATH" in repository_ctx.os.environ:
+ # Use prebuilt LLVM
+ path = repository_ctx.os.environ["PREBUILT_LLVM_PATH"]
+
+ # If needed, resolve relative to root of *calling* repository
+ if not path.startswith("/"):
+ root_path = repository_ctx.path(
+ repository_ctx.attr.file_at_root,
+ ).dirname
+ path = repository_ctx.path(str(root_path) + "/" + path)
+
+ repository_ctx.template(
+ "llvm.bzl",
+ Label("//bazel:llvm_local.bzl.tmpl"),
+ substitutions = {
+ "${PREBUILT_LLVM_PATH}": str(path),
+ "${CMAKE_BUILD_DIR}": "build",
+ },
+ executable = False,
+ )
+ else:
+ # Use downloaded LLVM built with Bazel
+ repository_ctx.template(
+ "llvm.bzl",
+ Label("//bazel:llvm_remote.bzl.tmpl"),
+ substitutions = {},
+ executable = False,
+ )
+
+def llvm_loader_repository_dependencies():
+ # This *declares* the dependency, but it won't actually be *downloaded*
+ # unless it's used.
+ http_archive(
+ name = "llvm-raw",
+ build_file_content = "# empty",
+ sha256 = "eb7437b60a6f78e7910d08911975f100e99e9c714f069a5487119c7eadc79171",
+ strip_prefix = "llvm-project-llvmorg-14.0.0",
+ urls = ["https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-14.0.0.zip"],
+ )
+
+llvm_loader_repository = repository_rule(
+ implementation = _llvm_loader_repository,
+ attrs = {
+ # We need a file from the root in order to get the workspace path
+ "file_at_root": attr.label(default = "//:BUILD"),
+ },
+ environ = [
+ "PREBUILT_LLVM_PATH",
+ ],
+)
diff --git a/bazel/llvm_local.bzl.tmpl b/bazel/llvm_local.bzl.tmpl
new file mode 100644
index 0000000..b78d7ad
--- /dev/null
+++ b/bazel/llvm_local.bzl.tmpl
@@ -0,0 +1,48 @@
+# Part of the Crubit project, under the Apache License v2.0 with LLVM
+# Exceptions. See /LICENSE for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+# Provide LLVM from a local tree built with CMake.
+
+prebuilt_llvm_tree_build_file_contents = """
+package(default_visibility = ["//visibility:public"])
+
+# An extremely coarse-grained target that provides *all* headers and libraries.
+cc_library(
+ name = "all",
+ srcs = glob([
+ "${CMAKE_BUILD_DIR}/lib/*.a",
+ ], exclude = [
+ "**/*.i386.a",
+ ]),
+ hdrs = glob([
+ "${CMAKE_BUILD_DIR}/include/**",
+ "${CMAKE_BUILD_DIR}/tools/clang/include/**",
+ "clang/include/**",
+ "llvm/include/**",
+ ]),
+ includes = [
+ "${CMAKE_BUILD_DIR}/include/",
+ "${CMAKE_BUILD_DIR}/tools/clang/include/",
+ "clang/include/",
+ "llvm/include/",
+ ],
+ linkopts = ["-lncurses", "-lz"],
+)
+"""
+
+def llvm_repository(name):
+ # First, create an intermediate repo that overlays the BUILD file onto the
+ # prebuilt LLVM tree, yielding the single ":all" target.
+ native.new_local_repository(
+ name = "prebuilt-llvm",
+ path = "${PREBUILT_LLVM_PATH}", # template value
+ build_file_content = prebuilt_llvm_tree_build_file_contents,
+ )
+
+ # Next, create the final repo that emulates the layout of Bazel targets in
+ # llvm-project.
+ native.local_repository(
+ name = name,
+ path = "bazel/llvm_project",
+ )
diff --git a/bazel/llvm_project/README.md b/bazel/llvm_project/README.md
new file mode 100644
index 0000000..a189d4b
--- /dev/null
+++ b/bazel/llvm_project/README.md
@@ -0,0 +1,3 @@
+# `llvm-project` shim
+
+This directory exists to provide a set of Bazel targets that match [LLVM's Bazel build](https://github.com/llvm/llvm-project/tree/main/utils/bazel) but all forward to a single "all LLVM" target.
diff --git a/bazel/llvm_project/WORKSPACE b/bazel/llvm_project/WORKSPACE
new file mode 100644
index 0000000..90aec77
--- /dev/null
+++ b/bazel/llvm_project/WORKSPACE
@@ -0,0 +1,5 @@
+# Part of the Crubit project, under the Apache License v2.0 with LLVM
+# Exceptions. See /LICENSE for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+# empty, dependencies are handled by the repo that loads us
diff --git a/bazel/llvm_project/clang/BUILD b/bazel/llvm_project/clang/BUILD
new file mode 100644
index 0000000..159d84b
--- /dev/null
+++ b/bazel/llvm_project/clang/BUILD
@@ -0,0 +1,14 @@
+package(default_visibility = ["//visibility:public"])
+
+[cc_library(
+ name = n,
+ deps = ["@prebuilt-llvm//:all"],
+) for n in [
+ "ast",
+ "basic",
+ "format",
+ "frontend",
+ "lex",
+ "sema",
+ "tooling",
+]]
diff --git a/bazel/llvm_project/llvm/BUILD b/bazel/llvm_project/llvm/BUILD
new file mode 100644
index 0000000..04760b1
--- /dev/null
+++ b/bazel/llvm_project/llvm/BUILD
@@ -0,0 +1,6 @@
+package(default_visibility = ["//visibility:public"])
+
+cc_library(
+ name = "Support",
+ deps = ["@prebuilt-llvm//:all"],
+)
diff --git a/bazel/llvm_remote.bzl.tmpl b/bazel/llvm_remote.bzl.tmpl
new file mode 100644
index 0000000..061a0be
--- /dev/null
+++ b/bazel/llvm_remote.bzl.tmpl
@@ -0,0 +1,21 @@
+# Part of the Crubit project, under the Apache License v2.0 with LLVM
+# Exceptions. See /LICENSE for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+# Provide LLVM with upstream's Bazel build support.
+#
+# refs:
+# https://github.com/llvm/llvm-project/blob/main/utils/bazel/examples/http_archive/WORKSPACE
+# https://github.com/llvm/llvm-project/releases/tag/llvmorg-14.0.0
+
+load("@llvm-raw//utils/bazel:configure.bzl", "llvm_configure", "llvm_disable_optional_support_deps")
+
+# Pass through to LLVM's Bazel configuration to create the repository.
+def llvm_repository(name):
+ if name != "llvm-project":
+ # this *must* be llvm-project, it's hardcoded in the Bazel build
+ # e.g. https://github.com/llvm/llvm-project/blob/aaddfbf9/utils/bazel/llvm-project-overlay/clang/BUILD.bazel#L1473
+ fail("""name must be llvm-project""")
+
+ llvm_configure(name = "llvm-project")
+ llvm_disable_optional_support_deps()