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", _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 ) 86 87def _get_idl_import_root_path( 88 package, 89 idl_import_root, 90 idl_file_root_path): 91 package_path = _path.relative( 92 idl_file_root_path, 93 package, 94 ) 95 return _path.relative( 96 package_path, 97 idl_import_root, 98 ) 99 100def _collect_unique_idl_import_root_paths( 101 package, 102 idl_import_root, 103 idl_imports): 104 idl_import_roots = dict() 105 for idl_import in idl_imports: 106 idl_import_roots[_get_idl_import_root_path( 107 package, 108 idl_import_root, 109 idl_import.root.path, 110 )] = True 111 return sorted(idl_import_roots.keys()) 112 113def _collect_unique_java_roots(idl_imports): 114 idl_import_roots = dict() 115 for idl_import in idl_imports: 116 java_root = _java.root(idl_import.path) 117 if not java_root: 118 _log.error(_AIDL_JAVA_ROOT_UNDETERMINABLE_ERROR % idl_import.path) 119 idl_import_roots[java_root] = True 120 return sorted(idl_import_roots.keys()) 121 122def _determine_idl_import_roots( 123 package, 124 idl_import_root = None, 125 idl_imports = []): 126 if idl_import_root == None: 127 return _collect_unique_java_roots(idl_imports) 128 return _collect_unique_idl_import_root_paths( 129 package, 130 idl_import_root, 131 idl_imports, 132 ) 133 134def _process( 135 ctx, 136 idl_srcs = [], 137 idl_parcelables = [], 138 idl_import_root = None, 139 idl_preprocessed = [], 140 deps = [], 141 exports = [], 142 aidl = None, 143 aidl_lib = None, 144 aidl_framework = None, 145 uses_aosp_compiler = False, 146 idlopts = []): 147 """Processes Android IDL. 148 149 Args: 150 ctx: The context. 151 idl_srcs: sequence of Files. A list of the aidl source files to be 152 processed into Java source files and then compiled. Optional. 153 idl_parcelables: sequence of Files. A list of Android IDL definitions to 154 supply as imports. These files will be made available as imports for any 155 android_library target that depends on this library, directly or via its 156 transitive closure, but will not be translated to Java or compiled. 157 158 Only .aidl files that correspond directly to .java sources in this library 159 should be included (e.g. custom implementations of Parcelable), otherwise 160 idl_srcs should be used. 161 162 These files must be placed appropriately for the aidl compiler to find 163 them. See the description of idl_import_root for information about what 164 this means. Optional. 165 idl_import_root: string. Package-relative path to the root of the java 166 package tree containing idl sources included in this library. This path 167 will be used as the import root when processing idl sources that depend on 168 this library. 169 170 When idl_import_root is specified, both idl_parcelables and idl_srcs must 171 be at the path specified by the java package of the object they represent 172 under idl_import_root. When idl_import_root is not specified, both 173 idl_parcelables and idl_srcs must be at the path specified by their 174 package under a Java root. Optional. 175 idl_preprocessed: sequence of Files. A list of preprocessed Android IDL 176 definitions to supply as imports. These files will be made available as 177 imports for any android_library target that depends on this library, 178 directly or via its transitive closure, but will not be translated to 179 Java or compiled. 180 181 Only preprocessed .aidl files that correspond directly to .java sources 182 in this library should be included (e.g. custom implementations of 183 Parcelable), otherwise use idl_srcs for Android IDL definitions that 184 need to be translated to Java interfaces and use idl_parcelable for 185 non-preprcessed AIDL files. Optional. 186 deps: sequence of Targets. A list of dependencies. Optional. 187 exports: sequence of Targets. A list of exports. Optional. 188 aidl: Target. A target pointing to the aidl executable to be used for 189 Java code generation from *.idl source files. Optional, unless idl_srcs 190 are supplied. 191 aidl_lib: Target. A target pointing to the aidl_lib library required 192 during Java compilation when Java code is generated from idl sources. 193 Optional. 194 aidl_framework: Target. A target pointing to the aidl framework. Optional, 195 unless idl_srcs are supplied. 196 uses_aosp_compiler: boolean. If True, the upstream AOSP AIDL compiler is 197 used instead of the Google3-only AIDL compiler. This allows wider range 198 of AIDL language features including the structured parcelable, enum, 199 union, and many more. On the other hand, using this may cause noticeable 200 regression in terms of code size and performance as the compiler doesn't 201 implement several optimization techniques that the Google3 compiler has. 202 idlopts: list of string. Additional flags to add to the AOSP AIDL compiler 203 invocation. 204 205 Returns: 206 A IDLContextInfo provider. 207 """ 208 if idl_srcs and not (aidl and aidl_framework): 209 _log.error(_AIDL_TOOLCHAIN_MISSING_ERROR) 210 211 transitive_idl_import_roots = [] 212 transitive_idl_imports = [] 213 transitive_idl_preprocessed = [] 214 for dep in deps + exports: 215 transitive_idl_import_roots.append(dep.transitive_idl_import_roots) 216 transitive_idl_imports.append(dep.transitive_idl_imports) 217 transitive_idl_preprocessed.append(dep.transitive_idl_preprocessed) 218 219 idl_java_srcs = [] 220 for idl_src in idl_srcs: 221 idl_java_src = ctx.actions.declare_file( 222 ctx.label.name + "_aidl/" + idl_src.path.replace(".aidl", ".java"), 223 ) 224 idl_java_srcs.append(idl_java_src) 225 _gen_java_from_idl( 226 ctx, 227 out_idl_java_src = idl_java_src, 228 idl_src = idl_src, 229 transitive_idl_import_roots = depset( 230 _determine_idl_import_roots( 231 ctx.label.package, 232 idl_import_root, 233 idl_parcelables + idl_srcs, 234 ), 235 transitive = transitive_idl_import_roots, 236 order = "preorder", 237 ), 238 transitive_idl_imports = depset( 239 idl_parcelables + idl_srcs, 240 transitive = transitive_idl_imports, 241 order = "preorder", 242 ), 243 transitive_idl_preprocessed = depset( 244 transitive = transitive_idl_preprocessed, 245 ), 246 aidl = aidl, 247 aidl_lib = aidl_lib, 248 aidl_framework = aidl_framework, 249 uses_aosp_compiler = uses_aosp_compiler, 250 idlopts = idlopts, 251 ) 252 253 return IDLContextInfo( 254 idl_srcs = idl_srcs, 255 idl_import_root = idl_import_root, 256 idl_java_srcs = idl_java_srcs, 257 idl_deps = [aidl_lib] if (idl_java_srcs and aidl_lib) else [], 258 providers = [ 259 # TODO(b/146216105): Make this a Starlark provider. 260 AndroidIdlInfo( 261 depset( 262 _determine_idl_import_roots( 263 ctx.label.package, 264 idl_import_root, 265 idl_parcelables + idl_srcs + idl_preprocessed, 266 ), 267 transitive = transitive_idl_import_roots, 268 order = "preorder", 269 ), 270 depset( 271 idl_parcelables + idl_srcs + idl_preprocessed, 272 transitive = transitive_idl_imports, 273 order = "preorder", 274 ), 275 depset(), # TODO(b/146216105): Delete this field once in Starlark. 276 depset(idl_preprocessed, transitive = transitive_idl_preprocessed), 277 ), 278 ], 279 ) 280 281idl = struct( 282 process = _process, 283) 284 285# Visible for testing. 286testing = struct( 287 get_idl_import_root_path = _get_idl_import_root_path, 288) 289