1# Copyright (C) 2022 The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15load("@bazel_skylib//lib:paths.bzl", "paths") 16load("//build/bazel/rules/aidl:aidl_library.bzl", "AidlGenInfo") 17load(":cc_library_common.bzl", "create_ccinfo_for_includes") 18 19_SOURCES = "sources" 20_HEADERS = "headers" 21_INCLUDE_DIR = "include_dir" 22 23def _cc_aidl_code_gen_impl(ctx): 24 """ 25 Generate stub C++ code from direct aidl srcs using transitive deps 26 27 Args: 28 ctx: (RuleContext) 29 Returns: 30 (DefaultInfo) Generated .cpp and .cpp.d files 31 (CcInfo) Generated headers and their include dirs 32 """ 33 generated_srcs, generated_hdrs, include_dirs = [], [], [] 34 35 for aidl_info in [d[AidlGenInfo] for d in ctx.attr.deps]: 36 stub = _compile_aidl_srcs(ctx, aidl_info, ctx.attr.lang) 37 generated_srcs.extend(stub[_SOURCES]) 38 generated_hdrs.extend(stub[_HEADERS]) 39 include_dirs.extend([stub[_INCLUDE_DIR]]) 40 41 return [ 42 DefaultInfo(files = depset(direct = generated_srcs + generated_hdrs)), 43 create_ccinfo_for_includes( 44 ctx, 45 hdrs = generated_hdrs, 46 includes = include_dirs, 47 ), 48 ] 49 50def _declare_stub_files(ctx, aidl_file, direct_include_dir, lang): 51 """ 52 Declare stub files that AIDL compiles to for cc 53 54 Args: 55 ctx: (Context) Used to register declare_file actions. 56 aidl_file: (File) The aidl file 57 direct_include_dir: (String) The path to given aidl file minus the aidl package namespace 58 lang: (String) AIDL backend 59 Returns: 60 (list[File]) List of declared stub files 61 """ 62 ret = {} 63 ret[_SOURCES], ret[_HEADERS] = [], [] 64 short_basename = paths.replace_extension(aidl_file.basename, "") 65 66 # aidl file path relative to direct include dir 67 short_path = paths.relativize(aidl_file.path, direct_include_dir) 68 69 ret[_SOURCES] = [ 70 ctx.actions.declare_file( 71 paths.join( 72 ctx.label.name, 73 paths.dirname(short_path), 74 short_basename + ".cpp", 75 ), 76 ), 77 ] 78 79 headers = [short_basename + ".h"] 80 81 # https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/gen.go;bpv=1;bpt=1?q=cc%2Fgen.go 82 # Strip prefix I before creating basenames for bp and bn headers 83 if len(short_basename) > 2 and short_basename.startswith("I") and short_basename[1].upper() == short_basename[1]: 84 short_basename = short_basename.removeprefix("I") 85 86 headers.extend([ 87 "Bp" + short_basename + ".h", 88 "Bn" + short_basename + ".h", 89 ]) 90 91 # Headers for ndk backend are nested under aidl directory to prevent 92 # c++ namespaces collision with cpp backend 93 # Context: https://android.googlesource.com/platform/system/tools/aidl/+/7c93337add97ce36f0a35c6705f3a67a441f2ae7 94 out_dir_prefix = "" 95 if lang == "ndk": 96 out_dir_prefix = "aidl" 97 98 for basename in headers: 99 ret[_HEADERS].append(ctx.actions.declare_file( 100 paths.join(ctx.label.name, out_dir_prefix, paths.dirname(short_path), basename), 101 )) 102 103 return ret 104 105def _compile_aidl_srcs(ctx, aidl_info, lang): 106 """ 107 Compile AIDL stub code for direct AIDL srcs 108 109 Args: 110 ctx: (Context) Used to register declare_file actions 111 aidl_info: (AidlGenInfo) aidl_info from an aidl library 112 lang: (String) AIDL backend 113 114 Returns: 115 (Dict) A dict of where the the values are generated headers (.h) and their boilerplate implementation (.cpp) 116 """ 117 118 ret = {} 119 ret[_SOURCES], ret[_HEADERS] = [], [] 120 121 # transitive_include_dirs is traversed in preorder 122 direct_include_dir = aidl_info.transitive_include_dirs.to_list()[0] 123 124 # Given AIDL file a/b/c/d/Foo.aidl with direct_include_dir a/b 125 # The outputs paths are 126 # cpp backend: 127 # <package-dir>/<target-name>/c/d/*Foo.h 128 # <package-dir>/<target-name>/c/d/Foo.cpp 129 # ndk backend: 130 # <package-dir>/<target-name>/aidl/c/d/*Foo.h 131 # <package-dir>/<target-name>/c/d/Foo.cpp 132 # 133 # where <package-dir> is bazel-bin/<path-to-cc_aidl_library-target> 134 # and <target-name> is <cc_aidl_library-name>_aidl_code_gen 135 # cpp and ndk are created in separate cc_aidl-library targets, so 136 # <target-name> are unique among cpp and ndk backends 137 138 # include dir, relative to package dir, to the generated headers 139 ret[_INCLUDE_DIR] = ctx.label.name 140 141 # AIDL needs to know the full path to outputs 142 # <bazel-bin>/<package-dir>/<target-name> 143 out_dir = paths.join( 144 ctx.bin_dir.path, 145 ctx.label.package, 146 ret[_INCLUDE_DIR], 147 ) 148 149 outputs = [] 150 for aidl_file in aidl_info.srcs.to_list(): 151 files = _declare_stub_files(ctx, aidl_file, direct_include_dir, lang) 152 outputs.extend(files[_SOURCES] + files[_HEADERS]) 153 ret[_SOURCES].extend(files[_SOURCES]) 154 ret[_HEADERS].extend(files[_HEADERS]) 155 156 args = ctx.actions.args() 157 158 # Add flags from lang-agnostic aidl_library target 159 args.add_all(aidl_info.flags) 160 161 # Add flags specific for cpp and ndk lang 162 if ctx.attr.min_sdk_version != "": 163 args.add("--min_sdk_version={}".format(ctx.attr.min_sdk_version)) 164 165 if aidl_info.hash_file == None: 166 args.add("--hash=notfrozen") 167 else: 168 args.add("--hash=$(tail -1 {})".format(aidl_info.hash_file)) 169 170 args.add_all([ 171 "--ninja", 172 "--lang={}".format(lang), 173 "--out={}".format(out_dir), 174 "--header_out={}".format(out_dir), 175 ]) 176 args.add_all(["-I {}".format(i) for i in aidl_info.transitive_include_dirs.to_list()]) 177 args.add_all(["{}".format(aidl_file.path) for aidl_file in aidl_info.srcs.to_list()]) 178 179 ctx.actions.run( 180 inputs = aidl_info.transitive_srcs, 181 outputs = outputs, 182 executable = ctx.executable._aidl, 183 arguments = [args], 184 progress_message = "Compiling AIDL binding", 185 mnemonic = "CcAidlCodeGen", 186 ) 187 188 return ret 189 190cc_aidl_code_gen = rule( 191 implementation = _cc_aidl_code_gen_impl, 192 doc = "This rule generates AIDL stub C++ code for the direct srcs in each " + 193 "AidlGenInfo in deps attribute using transitive srcs and transitive_include_dirs.", 194 attrs = { 195 "deps": attr.label_list( 196 providers = [AidlGenInfo], 197 ), 198 "lang": attr.string( 199 mandatory = True, 200 values = ["cpp", "ndk"], 201 ), 202 "min_sdk_version": attr.string(), 203 "_aidl": attr.label( 204 allow_single_file = True, 205 executable = True, 206 cfg = "exec", 207 default = Label("//system/tools/aidl"), 208 ), 209 }, 210 provides = [CcInfo], 211) 212