xref: /aosp_15_r20/external/skia/toolchain/clang_layering_check.bzl (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker"""
2*c8dee2aaSAndroid Build Coastguard WorkerThis file contains logic related to enforcing public API relationships, also known as
3*c8dee2aaSAndroid Build Coastguard Workerlayering checks.
4*c8dee2aaSAndroid Build Coastguard Worker
5*c8dee2aaSAndroid Build Coastguard WorkerSee also https://maskray.me/blog/2022-09-25-layering-check-with-clang and go/layering_check
6*c8dee2aaSAndroid Build Coastguard Worker
7*c8dee2aaSAndroid Build Coastguard Worker"""
8*c8dee2aaSAndroid Build Coastguard Worker
9*c8dee2aaSAndroid Build Coastguard Worker# https://github.com/bazelbuild/bazel/blob/master/tools/build_defs/cc/action_names.bzl
10*c8dee2aaSAndroid Build Coastguard Workerload("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")
11*c8dee2aaSAndroid Build Coastguard Worker
12*c8dee2aaSAndroid Build Coastguard Worker# https://github.com/bazelbuild/bazel/blob/master/tools/cpp/cc_toolchain_config_lib.bzl
13*c8dee2aaSAndroid Build Coastguard Workerload(
14*c8dee2aaSAndroid Build Coastguard Worker    "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
15*c8dee2aaSAndroid Build Coastguard Worker    "feature",
16*c8dee2aaSAndroid Build Coastguard Worker    "feature_set",
17*c8dee2aaSAndroid Build Coastguard Worker    "flag_group",
18*c8dee2aaSAndroid Build Coastguard Worker    "flag_set",
19*c8dee2aaSAndroid Build Coastguard Worker)
20*c8dee2aaSAndroid Build Coastguard Worker
21*c8dee2aaSAndroid Build Coastguard Workerdef make_layering_check_features():
22*c8dee2aaSAndroid Build Coastguard Worker    """Returns a list of features which enforce "layering checks".
23*c8dee2aaSAndroid Build Coastguard Worker
24*c8dee2aaSAndroid Build Coastguard Worker    Layering checks catch two types of problems:
25*c8dee2aaSAndroid Build Coastguard Worker      1) A cc_library using private headers from another cc_library.
26*c8dee2aaSAndroid Build Coastguard Worker      2) A cc_library using public headers from a transitive dependency instead of
27*c8dee2aaSAndroid Build Coastguard Worker         directly depending on that library.
28*c8dee2aaSAndroid Build Coastguard Worker
29*c8dee2aaSAndroid Build Coastguard Worker    This is implemented using Clang module maps, which are generated for each cc_library
30*c8dee2aaSAndroid Build Coastguard Worker    as it is being built.
31*c8dee2aaSAndroid Build Coastguard Worker
32*c8dee2aaSAndroid Build Coastguard Worker    This implementation is very similar to the one in the default Bazel C++ toolchain
33*c8dee2aaSAndroid Build Coastguard Worker    (which is not inherited by custom toolchains).
34*c8dee2aaSAndroid Build Coastguard Worker    https://github.com/bazelbuild/bazel/commit/8b9f74649512ee17ac52815468bf3d7e5e71c9fa
35*c8dee2aaSAndroid Build Coastguard Worker
36*c8dee2aaSAndroid Build Coastguard Worker    Returns:
37*c8dee2aaSAndroid Build Coastguard Worker        A list of Bazel "features", the primary one being one called "layering_check".
38*c8dee2aaSAndroid Build Coastguard Worker    """
39*c8dee2aaSAndroid Build Coastguard Worker    return [
40*c8dee2aaSAndroid Build Coastguard Worker        feature(
41*c8dee2aaSAndroid Build Coastguard Worker            name = "use_module_maps",
42*c8dee2aaSAndroid Build Coastguard Worker            enabled = False,
43*c8dee2aaSAndroid Build Coastguard Worker            requires = [feature_set(features = ["module_maps"])],
44*c8dee2aaSAndroid Build Coastguard Worker            flag_sets = [
45*c8dee2aaSAndroid Build Coastguard Worker                flag_set(
46*c8dee2aaSAndroid Build Coastguard Worker                    actions = [
47*c8dee2aaSAndroid Build Coastguard Worker                        ACTION_NAMES.c_compile,
48*c8dee2aaSAndroid Build Coastguard Worker                        ACTION_NAMES.cpp_compile,
49*c8dee2aaSAndroid Build Coastguard Worker                    ],
50*c8dee2aaSAndroid Build Coastguard Worker                    flag_groups = [
51*c8dee2aaSAndroid Build Coastguard Worker                        flag_group(
52*c8dee2aaSAndroid Build Coastguard Worker                            flags = [
53*c8dee2aaSAndroid Build Coastguard Worker                                "-fmodule-name=%{module_name}",
54*c8dee2aaSAndroid Build Coastguard Worker                                "-fmodule-map-file=%{module_map_file}",
55*c8dee2aaSAndroid Build Coastguard Worker                            ],
56*c8dee2aaSAndroid Build Coastguard Worker                        ),
57*c8dee2aaSAndroid Build Coastguard Worker                    ],
58*c8dee2aaSAndroid Build Coastguard Worker                ),
59*c8dee2aaSAndroid Build Coastguard Worker            ],
60*c8dee2aaSAndroid Build Coastguard Worker        ),
61*c8dee2aaSAndroid Build Coastguard Worker        # This feature name is baked into Bazel
62*c8dee2aaSAndroid Build Coastguard Worker        # https://github.com/bazelbuild/bazel/blob/8f5b626acea0086be8a314d5efbf6bc6d3473cd2/src/main/java/com/google/devtools/build/lib/rules/cpp/CompileBuildVariables.java#L471
63*c8dee2aaSAndroid Build Coastguard Worker        feature(name = "module_maps", enabled = True),
64*c8dee2aaSAndroid Build Coastguard Worker        feature(
65*c8dee2aaSAndroid Build Coastguard Worker            name = "layering_check",
66*c8dee2aaSAndroid Build Coastguard Worker            # This is currently disabled by default (although we aim to enable it by default)
67*c8dee2aaSAndroid Build Coastguard Worker            # because a previous build didn't support passing a flag along.
68*c8dee2aaSAndroid Build Coastguard Worker            # TODO(kjlubick): enable this by default.
69*c8dee2aaSAndroid Build Coastguard Worker            enabled = False,
70*c8dee2aaSAndroid Build Coastguard Worker            implies = ["use_module_maps"],
71*c8dee2aaSAndroid Build Coastguard Worker            flag_sets = [
72*c8dee2aaSAndroid Build Coastguard Worker                flag_set(
73*c8dee2aaSAndroid Build Coastguard Worker                    actions = [
74*c8dee2aaSAndroid Build Coastguard Worker                        ACTION_NAMES.c_compile,
75*c8dee2aaSAndroid Build Coastguard Worker                        ACTION_NAMES.cpp_compile,
76*c8dee2aaSAndroid Build Coastguard Worker                    ],
77*c8dee2aaSAndroid Build Coastguard Worker                    flag_groups = [
78*c8dee2aaSAndroid Build Coastguard Worker                        flag_group(flags = [
79*c8dee2aaSAndroid Build Coastguard Worker                            # Identify issue #1 (see docstring)
80*c8dee2aaSAndroid Build Coastguard Worker                            "-Wprivate-header",
81*c8dee2aaSAndroid Build Coastguard Worker                            # Identify issue #2
82*c8dee2aaSAndroid Build Coastguard Worker                            "-fmodules-strict-decluse",
83*c8dee2aaSAndroid Build Coastguard Worker                        ]),
84*c8dee2aaSAndroid Build Coastguard Worker                        flag_group(
85*c8dee2aaSAndroid Build Coastguard Worker                            iterate_over = "dependent_module_map_files",
86*c8dee2aaSAndroid Build Coastguard Worker                            flags = [
87*c8dee2aaSAndroid Build Coastguard Worker                                "-fmodule-map-file=%{dependent_module_map_files}",
88*c8dee2aaSAndroid Build Coastguard Worker                            ],
89*c8dee2aaSAndroid Build Coastguard Worker                        ),
90*c8dee2aaSAndroid Build Coastguard Worker                    ],
91*c8dee2aaSAndroid Build Coastguard Worker                ),
92*c8dee2aaSAndroid Build Coastguard Worker            ],
93*c8dee2aaSAndroid Build Coastguard Worker        ),
94*c8dee2aaSAndroid Build Coastguard Worker    ]
95*c8dee2aaSAndroid Build Coastguard Worker
96*c8dee2aaSAndroid Build Coastguard Workerdef generate_system_module_map(ctx, module_file, folders):
97*c8dee2aaSAndroid Build Coastguard Worker    """Generates a module map [1] for all the "system" headers in the toolchain.
98*c8dee2aaSAndroid Build Coastguard Worker
99*c8dee2aaSAndroid Build Coastguard Worker    The generated map looks something like:
100*c8dee2aaSAndroid Build Coastguard Worker        module "crosstool" [system] {
101*c8dee2aaSAndroid Build Coastguard Worker            textual header "lib/clang/15.0.1/include/__clang_cuda_builtin_vars.h"
102*c8dee2aaSAndroid Build Coastguard Worker            textual header "lib/clang/15.0.1/include/__clang_cuda_cmath.h"
103*c8dee2aaSAndroid Build Coastguard Worker            ...
104*c8dee2aaSAndroid Build Coastguard Worker            textual header "include/c++/v1/climits"
105*c8dee2aaSAndroid Build Coastguard Worker            textual header "include/c++/v1/clocale"
106*c8dee2aaSAndroid Build Coastguard Worker            textual header "include/c++/v1/cmath"
107*c8dee2aaSAndroid Build Coastguard Worker            textual header "symlinks/xcode/MacSDK/usr/share/man/mann/zip.n"
108*c8dee2aaSAndroid Build Coastguard Worker        }
109*c8dee2aaSAndroid Build Coastguard Worker    Notice how all the file paths are relative to *this* directory, where
110*c8dee2aaSAndroid Build Coastguard Worker    the toolchain_system_headers.modulemap. Annoyingly, Clang will silently
111*c8dee2aaSAndroid Build Coastguard Worker    ignore a file that is declared if it does not actually exist on disk.
112*c8dee2aaSAndroid Build Coastguard Worker
113*c8dee2aaSAndroid Build Coastguard Worker    [1] https://clang.llvm.org/docs/Modules.html#module-map-language
114*c8dee2aaSAndroid Build Coastguard Worker
115*c8dee2aaSAndroid Build Coastguard Worker    Args:
116*c8dee2aaSAndroid Build Coastguard Worker        ctx: A repository_ctx (https://bazel.build/rules/lib/repository_ctx)
117*c8dee2aaSAndroid Build Coastguard Worker        module_file: The name of the modulemap file to create.
118*c8dee2aaSAndroid Build Coastguard Worker        folders: List of strings corresponding to paths in the toolchain with system headers.
119*c8dee2aaSAndroid Build Coastguard Worker
120*c8dee2aaSAndroid Build Coastguard Worker    """
121*c8dee2aaSAndroid Build Coastguard Worker
122*c8dee2aaSAndroid Build Coastguard Worker    # https://github.com/bazelbuild/bazel/blob/8f5b626acea0086be8a314d5efbf6bc6d3473cd2/tools/cpp/generate_system_module_map.sh
123*c8dee2aaSAndroid Build Coastguard Worker    script_path = ctx.path(Label("@bazel_tools//tools/cpp:generate_system_module_map.sh"))
124*c8dee2aaSAndroid Build Coastguard Worker
125*c8dee2aaSAndroid Build Coastguard Worker    # https://bazel.build/rules/lib/repository_ctx#execute
126*c8dee2aaSAndroid Build Coastguard Worker    res = ctx.execute([script_path] + folders)
127*c8dee2aaSAndroid Build Coastguard Worker    if res.return_code != 0:
128*c8dee2aaSAndroid Build Coastguard Worker        fail("Could not generate module map")
129*c8dee2aaSAndroid Build Coastguard Worker
130*c8dee2aaSAndroid Build Coastguard Worker    # https://bazel.build/rules/lib/repository_ctx#file
131*c8dee2aaSAndroid Build Coastguard Worker    ctx.file(
132*c8dee2aaSAndroid Build Coastguard Worker        module_file,
133*c8dee2aaSAndroid Build Coastguard Worker        content = res.stdout,
134*c8dee2aaSAndroid Build Coastguard Worker        executable = False,
135*c8dee2aaSAndroid Build Coastguard Worker    )
136