Skip to content

Commit

Permalink
Add support for layering_check
Browse files Browse the repository at this point in the history
The general support for this feature is inherited from the
Bazel-provided Unix C++ toolchain, except that a module map for the
toolchain and sysroot headers has to be supplied to the `cc_toolchain`.
  • Loading branch information
fmeum committed Jan 14, 2024
1 parent 214fc1c commit 6973046
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 12 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,14 @@ then they can be referenced as:
- `@llvm_toolchain//:clang-format`
- `@llvm_toolchain//:llvm-cov`

### Strict header deps (Linux only)

The toolchain supports Bazel's `layering_check` feature, which relies on
[Clang modules](https://clang.llvm.org/docs/Modules.html) to implement strict
deps (also known as "depend on what you use") for `cc_*` rules. This features
can be enabled by enabling the `layering_check` feature on a per-target,
per-package or global basis.

## Prior Art

Other examples of toolchain configuration:
Expand Down
1 change: 1 addition & 0 deletions tests/.bazelrc
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
build --incompatible_enable_cc_toolchain_resolution
build --features=layering_check
34 changes: 22 additions & 12 deletions toolchain/cc_toolchain_config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ load(
_host_tools = "host_tools",
_os_arch_pair = "os_arch_pair",
)
load(
"//toolchain/internal:system_module_map.bzl",
"system_module_map",
)

# Bazel 4.* doesn't support nested starlark functions, so we cannot simplify
# _fmt_flags() by defining it as a nested function.
Expand All @@ -41,6 +45,7 @@ def cc_toolchain_config(
wrapper_bin_prefix,
compiler_configuration,
llvm_version,
all_files,
host_tools_info = {}):
host_os_arch_key = _os_arch_pair(host_os, host_arch)
target_os_arch_key = _os_arch_pair(target_os, target_arch)
Expand Down Expand Up @@ -262,18 +267,16 @@ def cc_toolchain_config(
## doesn't seem to have a feature for this.

# C++ built-in include directories:
cxx_builtin_include_directories = []
if toolchain_path_prefix.startswith("/"):
cxx_builtin_include_directories.extend([
toolchain_path_prefix + "include/c++/v1",
toolchain_path_prefix + "include/{}/c++/v1".format(target_system_name),
toolchain_path_prefix + "lib/clang/{}/include".format(llvm_version),
toolchain_path_prefix + "lib/clang/{}/share".format(llvm_version),
toolchain_path_prefix + "lib64/clang/{}/include".format(llvm_version),
toolchain_path_prefix + "lib/clang/{}/include".format(major_llvm_version),
toolchain_path_prefix + "lib/clang/{}/share".format(major_llvm_version),
toolchain_path_prefix + "lib64/clang/{}/include".format(major_llvm_version),
])
cxx_builtin_include_directories = [
toolchain_path_prefix + "include/c++/v1",
toolchain_path_prefix + "include/{}/c++/v1".format(target_system_name),
toolchain_path_prefix + "lib/clang/{}/include".format(llvm_version),
toolchain_path_prefix + "lib/clang/{}/share".format(llvm_version),
toolchain_path_prefix + "lib64/clang/{}/include".format(llvm_version),
toolchain_path_prefix + "lib/clang/{}/include".format(major_llvm_version),
toolchain_path_prefix + "lib/clang/{}/share".format(major_llvm_version),
toolchain_path_prefix + "lib64/clang/{}/include".format(major_llvm_version),
]

sysroot_path = compiler_configuration["sysroot_path"]
sysroot_prefix = ""
Expand Down Expand Up @@ -371,3 +374,10 @@ def cc_toolchain_config(
supports_start_end_lib = supports_start_end_lib,
builtin_sysroot = sysroot_path,
)

system_module_map(
name = name + "-module",
toolchain = all_files,
cxx_builtin_include_directories = cxx_builtin_include_directories,
sysroot_path = sysroot_path,
)
2 changes: 2 additions & 0 deletions toolchain/internal/configure.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ cc_toolchain_config(
}},
llvm_version = "{llvm_version}",
host_tools_info = {host_tools_info},
all_files = "all-files-{suffix}",
)
toolchain(
Expand Down Expand Up @@ -436,6 +437,7 @@ cc_toolchain(
objcopy_files = "objcopy-files-{suffix}",
strip_files = "strip-files-{suffix}",
toolchain_config = "local-{suffix}",
module_map = "local-{suffix}-module",
)
"""

Expand Down
75 changes: 75 additions & 0 deletions toolchain/internal/system_module_map.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Copyright 2024 The Bazel Authors.
#
# 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
#
# http://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.

def _system_module_map(ctx):
module_map = ctx.actions.declare_file(ctx.attr.name + ".modulemap")

# The builtin include directories are relative to the execroot, but the
# paths in the module map must be relative to the directory that contains
# the module map.
to_execroot = (module_map.dirname.count("/") + 1) * "../"

dirs = []
non_hermetic = False
for dir in ctx.attr.cxx_builtin_include_directories:
if ctx.attr.sysroot_path and dir.startswith("%sysroot%"):
dir = ctx.attr.sysroot_path + dir[len("%sysroot%"):]
if dir.startswith("/"):
non_hermetic = True
else:
dir = to_execroot + dir
dir = dir.replace("//", "/")
dirs.append(dir)

# If the action references a file outside of the execroot, it isn't safe to
# cache or run remotely.
execution_requirements = {}
if non_hermetic:
execution_requirements = {
"no-cache": "",
"no-remote": "",
}

ctx.actions.run_shell(
outputs = [module_map],
inputs = ctx.attr.toolchain[DefaultInfo].files,
arguments = [
ctx.executable._generate_system_module_map.path,
module_map.path,
] + dirs,
tools = [ctx.executable._generate_system_module_map],
command = """
"$1" "${@:3}" > "$2"
""",
execution_requirements = execution_requirements,
mnemonic = "LlvmSystemModuleMap",
progress_message = "Generating system module map",
)
return DefaultInfo(files = depset([module_map]))

system_module_map = rule(
doc = """Generates a Clang module map for the toolchain and sysroot headers.""",
implementation = _system_module_map,
attrs = {
"toolchain": attr.label(mandatory = True),
"cxx_builtin_include_directories": attr.string_list(mandatory = True),
"sysroot_path": attr.string(),
"_generate_system_module_map": attr.label(
default = "@bazel_tools//tools/cpp:generate_system_module_map.sh",
allow_single_file = True,
cfg = "exec",
executable = True,
),
},
)

0 comments on commit 6973046

Please sign in to comment.