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