1# Copyright 2018 The Bazel Authors. All rights reserved. 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 15"""Bazel Android IDL library for the Android rules.""" 16 17load(":java.bzl", _java = "java") 18load(":path.bzl", _path = "path") 19load(":utils.bzl", "ANDROID_TOOLCHAIN_TYPE", _log = "log") 20 21_AIDL_TOOLCHAIN_MISSING_ERROR = ( 22 "IDL sources provided without the Android IDL toolchain." 23) 24 25_AIDL_JAVA_ROOT_UNDETERMINABLE_ERROR = ( 26 "Cannot determine java/javatests root for import %s." 27) 28 29IDLContextInfo = provider( 30 doc = "Contains data from processing Android IDL.", 31 fields = dict( 32 idl_srcs = "List of IDL sources", 33 idl_import_root = "IDL import root", 34 idl_java_srcs = "List of IDL Java sources", 35 idl_deps = 36 "List of IDL targets required for Java compilation, Proguard, etc.", 37 providers = "The list of all providers to propagate.", 38 ), 39) 40 41def _gen_java_from_idl( 42 ctx, 43 out_idl_java_src = None, 44 idl_src = None, 45 transitive_idl_import_roots = [], 46 transitive_idl_imports = [], 47 transitive_idl_preprocessed = [], 48 aidl = None, 49 aidl_lib = None, 50 aidl_framework = None, 51 uses_aosp_compiler = False, 52 idlopts = []): 53 args = ctx.actions.args() 54 55 # Note: at the moment (2022/11/07), the flags that the AOSP compiler accepts is a superset of 56 # the Google3 compiler, but that might not be true in the future. 57 if uses_aosp_compiler: 58 args.add("--use-aosp-compiler") 59 60 for opt in idlopts: 61 args.add(opt) 62 63 args.add("-b") # fail on parcelable 64 args.add_all(transitive_idl_import_roots, format_each = "-I%s") 65 args.add(aidl_framework, format = "-p%s") 66 args.add_all(transitive_idl_preprocessed, format_each = "-p%s") 67 args.add(idl_src) 68 args.add(out_idl_java_src) 69 70 aidl_lib_files = [aidl_lib.files] if aidl_lib else [] 71 72 ctx.actions.run( 73 executable = aidl, 74 arguments = [args], 75 inputs = depset( 76 [aidl_framework], 77 transitive = aidl_lib_files + [ 78 transitive_idl_imports, 79 transitive_idl_preprocessed, 80 ], 81 ), 82 outputs = [out_idl_java_src], 83 mnemonic = "AndroidIDLGenerate", 84 progress_message = "Android IDL generation %s" % idl_src.path, 85 toolchain = ANDROID_TOOLCHAIN_TYPE, 86 ) 87 88def _get_idl_import_root_path( 89 package, 90 idl_import_root, 91 idl_file_root_path): 92 package_path = _path.relative( 93 idl_file_root_path, 94 package, 95 ) 96 return _path.relative( 97 package_path, 98 idl_import_root, 99 ) 100 101def _collect_unique_idl_import_root_paths( 102 package, 103 idl_import_root, 104 idl_imports): 105 idl_import_roots = dict() 106 for idl_import in idl_imports: 107 idl_import_roots[_get_idl_import_root_path( 108 package, 109 idl_import_root, 110 idl_import.root.path, 111 )] = True 112 return sorted(idl_import_roots.keys()) 113 114def _collect_unique_java_roots(idl_imports): 115 idl_import_roots = dict() 116 for idl_import in idl_imports: 117 java_root = _java.root(idl_import.path) 118 if not java_root: 119 _log.error(_AIDL_JAVA_ROOT_UNDETERMINABLE_ERROR % idl_import.path) 120 idl_import_roots[java_root] = True 121 return sorted(idl_import_roots.keys()) 122 123def _determine_idl_import_roots( 124 package, 125 idl_import_root = None, 126 idl_imports = []): 127 if idl_import_root == None: 128 return _collect_unique_java_roots(idl_imports) 129 return _collect_unique_idl_import_root_paths( 130 package, 131 idl_import_root, 132 idl_imports, 133 ) 134 135def _process( 136 ctx, 137 idl_srcs = [], 138 idl_parcelables = [], 139 idl_import_root = None, 140 idl_preprocessed = [], 141 deps = [], 142 exports = [], 143 aidl = None, 144 aidl_lib = None, 145 aidl_framework = None, 146 uses_aosp_compiler = False, 147 idlopts = []): 148 """Processes Android IDL. 149 150 Args: 151 ctx: The context. 152 idl_srcs: sequence of Files. A list of the aidl source files to be 153 processed into Java source files and then compiled. Optional. 154 idl_parcelables: sequence of Files. A list of Android IDL definitions to 155 supply as imports. These files will be made available as imports for any 156 android_library target that depends on this library, directly or via its 157 transitive closure, but will not be translated to Java or compiled. 158 159 Only .aidl files that correspond directly to .java sources in this library 160 should be included (e.g. custom implementations of Parcelable), otherwise 161 idl_srcs should be used. 162 163 These files must be placed appropriately for the aidl compiler to find 164 them. See the description of idl_import_root for information about what 165 this means. Optional. 166 idl_import_root: string. Package-relative path to the root of the java 167 package tree containing idl sources included in this library. This path 168 will be used as the import root when processing idl sources that depend on 169 this library. 170 171 When idl_import_root is specified, both idl_parcelables and idl_srcs must 172 be at the path specified by the java package of the object they represent 173 under idl_import_root. When idl_import_root is not specified, both 174 idl_parcelables and idl_srcs must be at the path specified by their 175 package under a Java root. Optional. 176 idl_preprocessed: sequence of Files. A list of preprocessed Android IDL 177 definitions to supply as imports. These files will be made available as 178 imports for any android_library target that depends on this library, 179 directly or via its transitive closure, but will not be translated to 180 Java or compiled. 181 182 Only preprocessed .aidl files that correspond directly to .java sources 183 in this library should be included (e.g. custom implementations of 184 Parcelable), otherwise use idl_srcs for Android IDL definitions that 185 need to be translated to Java interfaces and use idl_parcelable for 186 non-preprcessed AIDL files. Optional. 187 deps: sequence of Targets. A list of dependencies. Optional. 188 exports: sequence of Targets. A list of exports. Optional. 189 aidl: Target. A target pointing to the aidl executable to be used for 190 Java code generation from *.idl source files. Optional, unless idl_srcs 191 are supplied. 192 aidl_lib: Target. A target pointing to the aidl_lib library required 193 during Java compilation when Java code is generated from idl sources. 194 Optional. 195 aidl_framework: Target. A target pointing to the aidl framework. Optional, 196 unless idl_srcs are supplied. 197 uses_aosp_compiler: boolean. If True, the upstream AOSP AIDL compiler is 198 used instead of the Google3-only AIDL compiler. This allows wider range 199 of AIDL language features including the structured parcelable, enum, 200 union, and many more. On the other hand, using this may cause noticeable 201 regression in terms of code size and performance as the compiler doesn't 202 implement several optimization techniques that the Google3 compiler has. 203 idlopts: list of string. Additional flags to add to the AOSP AIDL compiler 204 invocation. 205 206 Returns: 207 A IDLContextInfo provider. 208 """ 209 if idl_srcs and not (aidl and aidl_framework): 210 _log.error(_AIDL_TOOLCHAIN_MISSING_ERROR) 211 212 transitive_idl_import_roots = [] 213 transitive_idl_imports = [] 214 transitive_idl_preprocessed = [] 215 for dep in deps + exports: 216 transitive_idl_import_roots.append(dep.transitive_idl_import_roots) 217 transitive_idl_imports.append(dep.transitive_idl_imports) 218 transitive_idl_preprocessed.append(dep.transitive_idl_preprocessed) 219 220 idl_java_srcs = [] 221 for idl_src in idl_srcs: 222 idl_java_src = ctx.actions.declare_file( 223 ctx.label.name + "_aidl/" + idl_src.path.replace(".aidl", ".java"), 224 ) 225 idl_java_srcs.append(idl_java_src) 226 _gen_java_from_idl( 227 ctx, 228 out_idl_java_src = idl_java_src, 229 idl_src = idl_src, 230 transitive_idl_import_roots = depset( 231 _determine_idl_import_roots( 232 ctx.label.package, 233 idl_import_root, 234 idl_parcelables + idl_srcs, 235 ), 236 transitive = transitive_idl_import_roots, 237 order = "preorder", 238 ), 239 transitive_idl_imports = depset( 240 idl_parcelables + idl_srcs, 241 transitive = transitive_idl_imports, 242 order = "preorder", 243 ), 244 transitive_idl_preprocessed = depset( 245 transitive = transitive_idl_preprocessed, 246 ), 247 aidl = aidl, 248 aidl_lib = aidl_lib, 249 aidl_framework = aidl_framework, 250 uses_aosp_compiler = uses_aosp_compiler, 251 idlopts = idlopts, 252 ) 253 254 return IDLContextInfo( 255 idl_srcs = idl_srcs, 256 idl_import_root = idl_import_root, 257 idl_java_srcs = idl_java_srcs, 258 idl_deps = [aidl_lib] if (idl_java_srcs and aidl_lib) else [], 259 providers = [ 260 # TODO(b/146216105): Make this a Starlark provider. 261 AndroidIdlInfo( 262 depset( 263 _determine_idl_import_roots( 264 ctx.label.package, 265 idl_import_root, 266 idl_parcelables + idl_srcs + idl_preprocessed, 267 ), 268 transitive = transitive_idl_import_roots, 269 order = "preorder", 270 ), 271 depset( 272 idl_parcelables + idl_srcs + idl_preprocessed, 273 transitive = transitive_idl_imports, 274 order = "preorder", 275 ), 276 depset(), # TODO(b/146216105): Delete this field once in Starlark. 277 depset(idl_preprocessed, transitive = transitive_idl_preprocessed), 278 ), 279 ], 280 ) 281 282idl = struct( 283 process = _process, 284) 285 286# Visible for testing. 287testing = struct( 288 get_idl_import_root_path = _get_idl_import_root_path, 289) 290