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 Data Binding.""" 16 17load(":utils.bzl", "ANDROID_TOOLCHAIN_TYPE", _utils = "utils") 18 19# Data Binding context attributes. 20_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS = \ 21 "java_annotation_processor_additional_inputs" 22_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_OUTPUTS = \ 23 "java_annotation_processor_additional_outputs" 24_JAVA_PLUGINS = "java_plugins" 25_JAVA_SRCS = "java_srcs" 26_JAVAC_OPTS = "javac_opts" 27_PROVIDERS = "providers" 28 29DataBindingContextInfo = provider( 30 doc = "Contains data from processing Android Data Binding.", 31 fields = { 32 _JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS: ( 33 "Additional inputs required by the Java annotation processor." 34 ), 35 _JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_OUTPUTS: ( 36 "Additional outputs produced by the Java annotation processor." 37 ), 38 _JAVA_PLUGINS: "Data Binding Java annotation processor", 39 _JAVA_SRCS: "Java sources required by the Java annotation processor.", 40 _JAVAC_OPTS: ( 41 "Additional Javac opts required by the Java annotation processor." 42 ), 43 _PROVIDERS: "The list of all providers to propagate.", 44 }, 45) 46 47# Path used when resources have not been defined. 48_NO_RESOURCES_PATH = "/tmp/no_resources" 49 50def _copy_annotation_file(ctx, output_dir, annotation_template): 51 annotation_out = ctx.actions.declare_file( 52 output_dir + "/android/databinding/layouts/DataBindingInfo.java", 53 ) 54 _utils.copy_file(ctx, annotation_template, annotation_out) 55 return annotation_out 56 57def _gen_sources(ctx, output_dir, java_package, deps, layout_info, data_binding_exec): 58 class_info = ctx.actions.declare_file(output_dir + "class-info.zip") 59 srcjar = ctx.actions.declare_file(output_dir + "baseClassSrc.srcjar") 60 61 args = ctx.actions.args() 62 args.add("-layoutInfoFiles", layout_info) 63 args.add("-package", java_package) 64 args.add("-classInfoOut", class_info) 65 args.add("-sourceOut", srcjar) 66 args.add("-zipSourceOutput", "true") 67 args.add("-useAndroidX", "false") 68 69 if deps: 70 if type(deps[0].class_infos) == "depset": 71 class_infos = depset(transitive = [info.class_infos for info in deps]) 72 inputs = depset(direct = [layout_info], transitive = [class_infos]) 73 elif type(deps[0].class_infos) == "list": 74 class_infos = [] 75 for info in deps: 76 class_infos.extend(info.class_infos) 77 inputs = class_infos + [layout_info] 78 else: 79 fail("Expected list or depset. Got %s" % type(deps[0].class_infos)) 80 else: 81 class_infos = [] 82 inputs = [layout_info] 83 84 args.add_all(class_infos, before_each = "-dependencyClassInfoList") 85 86 ctx.actions.run( 87 executable = data_binding_exec, 88 arguments = ["GEN_BASE_CLASSES", args], 89 inputs = inputs, 90 outputs = [class_info, srcjar], 91 mnemonic = "GenerateDataBindingBaseClasses", 92 progress_message = ( 93 "GenerateDataBindingBaseClasses %s" % class_info.short_path 94 ), 95 toolchain = ANDROID_TOOLCHAIN_TYPE, 96 ) 97 return srcjar, class_info 98 99def _setup_dependent_lib_artifacts(ctx, output_dir, deps): 100 # DataBinding requires files in very specific locations. 101 # The following expand_template (copy actions) are moving the files 102 # to the correct locations. 103 dep_lib_artifacts = [] 104 for info in deps: 105 # Yes, DataBinding requires depsets iterations. 106 for artifact in (info.transitive_br_files.to_list() + 107 _utils.list_or_depset_to_list(info.setter_stores) + 108 _utils.list_or_depset_to_list(info.class_infos)): 109 # short_path might contain a parent directory reference if the 110 # databinding artifact is from an external repository (e.g. an aar 111 # from Maven). If that's the case, just remove the parent directory 112 # reference, otherwise the "dependent-lib-artifacts" directory will 113 # get removed by the "..". 114 path = artifact.short_path 115 if path.startswith("../"): 116 path = path[3:] 117 dep_lib_artifact = ctx.actions.declare_file( 118 output_dir + "dependent-lib-artifacts/" + path, 119 ) 120 121 # Copy file to a location required by the DataBinding annotation 122 # processor. 123 # TODO(djwhang): Look into SymlinkAction. 124 if artifact.is_directory: 125 _utils.copy_dir(ctx, artifact, dep_lib_artifact) 126 else: 127 _utils.copy_file(ctx, artifact, dep_lib_artifact) 128 dep_lib_artifacts.append(dep_lib_artifact) 129 return dep_lib_artifacts 130 131def _get_javac_opts( 132 ctx, 133 java_package, 134 artifact_type, 135 dependency_artifacts_dir, 136 aar_out_dir, 137 class_info_path, 138 layout_info_path, 139 deps): 140 java_packages = [] 141 for info in deps: 142 for label_and_java_package in info.label_and_java_packages: 143 java_packages.append(label_and_java_package.java_package) 144 145 javac_opts = [] 146 javac_opts.append("-Aandroid.databinding.dependencyArtifactsDir=" + 147 dependency_artifacts_dir) 148 javac_opts.append("-Aandroid.databinding.aarOutDir=" + aar_out_dir) 149 javac_opts.append("-Aandroid.databinding.sdkDir=/not/used") 150 javac_opts.append("-Aandroid.databinding.artifactType=" + artifact_type) 151 javac_opts.append("-Aandroid.databinding.exportClassListOutFile=" + 152 "/tmp/exported_classes") 153 javac_opts.append("-Aandroid.databinding.modulePackage=" + java_package) 154 javac_opts.append("-Aandroid.databinding.directDependencyPkgs=[%s]" % 155 ",".join(java_packages)) 156 157 # The minimum Android SDK compatible with this rule. 158 # TODO(djwhang): This probably should be based on the actual min-sdk from 159 # the manifest, or an appropriate rule attribute. 160 javac_opts.append("-Aandroid.databinding.minApi=14") 161 javac_opts.append("-Aandroid.databinding.enableV2=1") 162 163 javac_opts.append("-Aandroid.databinding.classLogDir=" + class_info_path) 164 javac_opts.append("-Aandroid.databinding.layoutInfoDir=" + layout_info_path) 165 return javac_opts 166 167def _process( 168 ctx, 169 resources_ctx = None, 170 defines_resources = False, 171 enable_data_binding = False, 172 java_package = None, 173 layout_info = None, 174 artifact_type = "LIBRARY", 175 deps = [], 176 exports = [], 177 data_binding_exec = None, 178 data_binding_annotation_processor = None, 179 data_binding_annotation_template = None): 180 """Processes Android Data Binding. 181 182 Args: 183 ctx: The context. 184 resources_ctx: The Android Resources context. 185 defines_resources: boolean. Determines whether resources were defined. 186 enable_data_binding: boolean. Determines whether Data Binding should be 187 enabled. 188 java_package: String. The Java package. 189 layout_info: A file. The layout-info zip file. 190 artifact_type: String. Either LIBRARY or APPLICATION. 191 deps: sequence of DataBindingV2Info providers. A list of deps. Optional. 192 exports: sequence of DataBindingV2Info providers. A list of exports. 193 Optional. 194 data_binding_exec: The DataBinding executable. 195 data_binding_annotation_processor: JavaInfo. The JavaInfo for the 196 annotation processor. 197 data_binding_annotation_template: A file. Used to generate data binding 198 classes. 199 200 Returns: 201 A DataBindingContextInfo provider. 202 """ 203 204 if artifact_type not in ["LIBRARY", "APPLICATION"]: 205 fail("Unexpected artifact type: " + artifact_type) 206 207 # TODO(b/154513292): Clean up bad usages of context objects. 208 if resources_ctx: 209 defines_resources = resources_ctx.defines_resources 210 211 # The Android Data Binding context object. 212 db_info = { 213 _JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS: [], 214 _JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_OUTPUTS: [], 215 _JAVA_PLUGINS: [], 216 _JAVA_SRCS: [], 217 _JAVAC_OPTS: [], 218 _PROVIDERS: [], 219 } 220 221 if not enable_data_binding: 222 db_info[_PROVIDERS] = [ 223 DataBindingV2Info( 224 databinding_v2_providers_in_deps = deps, 225 databinding_v2_providers_in_exports = exports, 226 ), 227 ] 228 return struct(**db_info) 229 230 output_dir = "databinding/%s/" % ctx.label.name 231 232 db_info[_JAVA_SRCS].append(_copy_annotation_file( 233 ctx, 234 output_dir, 235 data_binding_annotation_template, 236 )) 237 db_info[_JAVA_PLUGINS].append(data_binding_annotation_processor) 238 239 br_out = None 240 setter_store_out = None 241 class_info = None 242 if defines_resources: 243 # Outputs of the Data Binding annotation processor. 244 br_out = ctx.actions.declare_file( 245 output_dir + "bin-files/%s-br.bin" % java_package, 246 ) 247 db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_OUTPUTS].append(br_out) 248 setter_store_out = ctx.actions.declare_file( 249 output_dir + "bin-files/%s-setter_store.json" % java_package, 250 ) 251 db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_OUTPUTS].append( 252 setter_store_out, 253 ) 254 255 srcjar, class_info = _gen_sources( 256 ctx, 257 output_dir, 258 java_package, 259 deps, 260 layout_info, 261 data_binding_exec, 262 ) 263 db_info[_JAVA_SRCS].append(srcjar) 264 db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS].append(class_info) 265 db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS].append( 266 layout_info, 267 ) 268 269 dep_lib_artifacts = _setup_dependent_lib_artifacts(ctx, output_dir, deps) 270 db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS].extend( 271 dep_lib_artifacts, 272 ) 273 274 db_info[_JAVAC_OPTS] = _get_javac_opts( 275 ctx, 276 java_package, 277 artifact_type, 278 ( 279 br_out.path.rpartition(br_out.short_path)[0] + 280 ctx.label.package + 281 "/" + 282 output_dir + 283 "dependent-lib-artifacts" 284 ), 285 br_out.dirname, 286 class_info.path if class_info else _NO_RESOURCES_PATH, 287 layout_info.path if layout_info else _NO_RESOURCES_PATH, 288 deps, 289 ) 290 291 db_info[_PROVIDERS] = [ 292 DataBindingV2Info( 293 setter_store_file = setter_store_out, 294 class_info_file = class_info, 295 br_file = br_out, 296 label = str(ctx.label), 297 java_package = java_package, 298 databinding_v2_providers_in_deps = deps, 299 databinding_v2_providers_in_exports = exports, 300 ), 301 ] 302 303 return DataBindingContextInfo(**db_info) 304 305data_binding = struct( 306 process = _process, 307) 308