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 args = ctx.actions.args() 52 args.add("-b") 53 args.add_all(transitive_idl_import_roots, format_each = "-I%s") 54 args.add(aidl_framework, format = "-p%s") 55 args.add_all(transitive_idl_preprocessed, format_each = "-p%s") 56 args.add(idl_src) 57 args.add(out_idl_java_src) 58 59 ctx.actions.run( 60 executable = aidl, 61 arguments = [args], 62 inputs = depset( 63 [aidl_framework], 64 transitive = [ 65 aidl_lib.files, 66 transitive_idl_imports, 67 transitive_idl_preprocessed, 68 ], 69 ), 70 outputs = [out_idl_java_src], 71 mnemonic = "AndroidIDLGenerate", 72 progress_message = "Android IDL generation %s" % idl_src.path, 73 ) 74 75def _get_idl_import_root_path( 76 package, 77 idl_import_root, 78 idl_file_root_path): 79 package_path = _path.relative( 80 idl_file_root_path, 81 package, 82 ) 83 return _path.relative( 84 package_path, 85 idl_import_root, 86 ) 87 88def _collect_unique_idl_import_root_paths( 89 package, 90 idl_import_root, 91 idl_imports): 92 idl_import_roots = dict() 93 for idl_import in idl_imports: 94 idl_import_roots[_get_idl_import_root_path( 95 package, 96 idl_import_root, 97 idl_import.root.path, 98 )] = True 99 return sorted(idl_import_roots.keys()) 100 101def _collect_unique_java_roots(idl_imports): 102 idl_import_roots = dict() 103 for idl_import in idl_imports: 104 java_root = _java.root(idl_import.path) 105 if not java_root: 106 _log.error(_AIDL_JAVA_ROOT_UNDETERMINABLE_ERROR % idl_import.path) 107 idl_import_roots[java_root] = True 108 return sorted(idl_import_roots.keys()) 109 110def _determine_idl_import_roots( 111 package, 112 idl_import_root = None, 113 idl_imports = []): 114 if idl_import_root == None: 115 return _collect_unique_java_roots(idl_imports) 116 return _collect_unique_idl_import_root_paths( 117 package, 118 idl_import_root, 119 idl_imports, 120 ) 121 122def _process( 123 ctx, 124 idl_srcs = [], 125 idl_parcelables = [], 126 idl_import_root = None, 127 idl_preprocessed = [], 128 deps = [], 129 exports = [], 130 aidl = None, 131 aidl_lib = None, 132 aidl_framework = None): 133 """Processes Android IDL. 134 135 Args: 136 ctx: The context. 137 idl_srcs: sequence of Files. A list of the aidl source files to be 138 processed into Java source files and then compiled. Optional. 139 idl_parcelables: sequence of Files. A list of Android IDL definitions to 140 supply as imports. These files will be made available as imports for any 141 android_library target that depends on this library, directly or via its 142 transitive closure, but will not be translated to Java or compiled. 143 144 Only .aidl files that correspond directly to .java sources in this library 145 should be included (e.g. custom implementations of Parcelable), otherwise 146 idl_srcs should be used. 147 148 These files must be placed appropriately for the aidl compiler to find 149 them. See the description of idl_import_root for information about what 150 this means. Optional. 151 idl_import_root: string. Package-relative path to the root of the java 152 package tree containing idl sources included in this library. This path 153 will be used as the import root when processing idl sources that depend on 154 this library. 155 156 When idl_import_root is specified, both idl_parcelables and idl_srcs must 157 be at the path specified by the java package of the object they represent 158 under idl_import_root. When idl_import_root is not specified, both 159 idl_parcelables and idl_srcs must be at the path specified by their 160 package under a Java root. Optional. 161 idl_preprocessed: sequence of Files. A list of preprocessed Android IDL 162 definitions to supply as imports. These files will be made available as 163 imports for any android_library target that depends on this library, 164 directly or via its transitive closure, but will not be translated to 165 Java or compiled. 166 167 Only preprocessed .aidl files that correspond directly to .java sources 168 in this library should be included (e.g. custom implementations of 169 Parcelable), otherwise use idl_srcs for Android IDL definitions that 170 need to be translated to Java interfaces and use idl_parcelable for 171 non-preprcessed AIDL files. Optional. 172 deps: sequence of Targets. A list of dependencies. Optional. 173 exports: sequence of Targets. A list of exports. Optional. 174 aidl: Target. A target pointing to the aidl executable to be used for 175 Java code generation from *.idl source files. Optional, unless idl_srcs 176 are supplied. 177 aidl_lib: Target. A target pointing to the aidl_lib library required 178 during Java compilation when Java code is generated from idl sources. 179 Optional, unless idl_srcs are supplied. 180 aidl_framework: Target. A target pointing to the aidl framework. Optional, 181 unless idl_srcs are supplied. 182 183 Returns: 184 A IDLContextInfo provider. 185 """ 186 if idl_srcs and not (aidl and aidl_lib and aidl_framework): 187 _log.error(_AIDL_TOOLCHAIN_MISSING_ERROR) 188 189 transitive_idl_import_roots = [] 190 transitive_idl_imports = [] 191 transitive_idl_preprocessed = [] 192 for dep in deps + exports: 193 transitive_idl_import_roots.append(dep.transitive_idl_import_roots) 194 transitive_idl_imports.append(dep.transitive_idl_imports) 195 transitive_idl_preprocessed.append(dep.transitive_idl_preprocessed) 196 197 idl_java_srcs = [] 198 for idl_src in idl_srcs: 199 idl_java_src = ctx.actions.declare_file( 200 ctx.label.name + "_aidl/" + idl_src.path.replace(".aidl", ".java"), 201 ) 202 idl_java_srcs.append(idl_java_src) 203 _gen_java_from_idl( 204 ctx, 205 out_idl_java_src = idl_java_src, 206 idl_src = idl_src, 207 transitive_idl_import_roots = depset( 208 _determine_idl_import_roots( 209 ctx.label.package, 210 idl_import_root, 211 idl_parcelables + idl_srcs, 212 ), 213 transitive = transitive_idl_import_roots, 214 order = "preorder", 215 ), 216 transitive_idl_imports = depset( 217 idl_parcelables + idl_srcs, 218 transitive = transitive_idl_imports, 219 order = "preorder", 220 ), 221 transitive_idl_preprocessed = depset( 222 transitive = transitive_idl_preprocessed, 223 ), 224 aidl = aidl, 225 aidl_lib = aidl_lib, 226 aidl_framework = aidl_framework, 227 ) 228 229 return IDLContextInfo( 230 idl_srcs = idl_srcs, 231 idl_import_root = idl_import_root, 232 idl_java_srcs = idl_java_srcs, 233 idl_deps = [aidl_lib] if idl_java_srcs else [], 234 providers = [ 235 # TODO(b/146216105): Make this a Starlark provider. 236 AndroidIdlInfo( 237 depset( 238 _determine_idl_import_roots( 239 ctx.label.package, 240 idl_import_root, 241 idl_parcelables + idl_srcs + idl_preprocessed, 242 ), 243 transitive = transitive_idl_import_roots, 244 order = "preorder", 245 ), 246 depset( 247 idl_parcelables + idl_srcs + idl_preprocessed, 248 transitive = transitive_idl_imports, 249 order = "preorder", 250 ), 251 depset(), # TODO(b/146216105): Delete this field once in Starlark. 252 depset(idl_preprocessed, transitive = transitive_idl_preprocessed), 253 ), 254 ], 255 ) 256 257idl = struct( 258 process = _process, 259) 260 261# Visible for testing. 262testing = struct( 263 get_idl_import_root_path = _get_idl_import_root_path, 264) 265