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", _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 ) 96 return srcjar, class_info 97 98def _setup_dependent_lib_artifacts(ctx, output_dir, deps): 99 # DataBinding requires files in very specific locations. 100 # The following expand_template (copy actions) are moving the files 101 # to the correct locations. 102 dep_lib_artifacts = [] 103 for info in deps: 104 # Yes, DataBinding requires depsets iterations. 105 for artifact in (info.transitive_br_files.to_list() + 106 _utils.list_or_depset_to_list(info.setter_stores) + 107 _utils.list_or_depset_to_list(info.class_infos)): 108 # short_path might contain a parent directory reference if the 109 # databinding artifact is from an external repository (e.g. an aar 110 # from Maven). If that's the case, just remove the parent directory 111 # reference, otherwise the "dependent-lib-artifacts" directory will 112 # get removed by the "..". 113 path = artifact.short_path 114 if path.startswith("../"): 115 path = path[3:] 116 dep_lib_artifact = ctx.actions.declare_file( 117 output_dir + "dependent-lib-artifacts/" + path, 118 ) 119 120 # Copy file to a location required by the DataBinding annotation 121 # processor. 122 # TODO(djwhang): Look into SymlinkAction. 123 if artifact.is_directory: 124 _utils.copy_dir(ctx, artifact, dep_lib_artifact) 125 else: 126 _utils.copy_file(ctx, artifact, dep_lib_artifact) 127 dep_lib_artifacts.append(dep_lib_artifact) 128 return dep_lib_artifacts 129 130def _get_javac_opts( 131 ctx, 132 java_package, 133 dependency_artifacts_dir, 134 aar_out_dir, 135 class_info_path, 136 layout_info_path, 137 deps): 138 java_packages = [] 139 for info in deps: 140 for label_and_java_package in info.label_and_java_packages: 141 java_packages.append(label_and_java_package.java_package) 142 143 javac_opts = [] 144 javac_opts.append("-Aandroid.databinding.dependencyArtifactsDir=" + 145 dependency_artifacts_dir) 146 javac_opts.append("-Aandroid.databinding.aarOutDir=" + aar_out_dir) 147 javac_opts.append("-Aandroid.databinding.sdkDir=/not/used") 148 javac_opts.append("-Aandroid.databinding.artifactType=LIBRARY") 149 javac_opts.append("-Aandroid.databinding.exportClassListOutFile=" + 150 "/tmp/exported_classes") 151 javac_opts.append("-Aandroid.databinding.modulePackage=" + java_package) 152 javac_opts.append("-Aandroid.databinding.directDependencyPkgs=[%s]" % 153 ",".join(java_packages)) 154 155 # The minimum Android SDK compatible with this rule. 156 # TODO(djwhang): This probably should be based on the actual min-sdk from 157 # the manifest, or an appropriate rule attribute. 158 javac_opts.append("-Aandroid.databinding.minApi=14") 159 javac_opts.append("-Aandroid.databinding.enableV2=1") 160 161 javac_opts.append("-Aandroid.databinding.classLogDir=" + class_info_path) 162 javac_opts.append("-Aandroid.databinding.layoutInfoDir=" + layout_info_path) 163 return javac_opts 164 165def _process( 166 ctx, 167 resources_ctx = None, 168 defines_resources = False, 169 enable_data_binding = False, 170 java_package = None, 171 layout_info = None, 172 deps = [], 173 exports = [], 174 data_binding_exec = None, 175 data_binding_annotation_processor = None, 176 data_binding_annotation_template = None): 177 """Processes Android Data Binding. 178 179 Args: 180 ctx: The context. 181 resources_ctx: The Android Resources context. 182 defines_resources: boolean. Determines whether resources were defined. 183 enable_data_binding: boolean. Determines whether Data Binding should be 184 enabled. 185 java_package: String. The Java package. 186 deps: sequence of DataBindingV2Info providers. A list of deps. Optional. 187 exports: sequence of DataBindingV2Info providers. A list of exports. 188 Optional. 189 layout_info: A file. The layout-info zip file. 190 data_binding_exec: The DataBinding executable. 191 data_binding_annotation_processor: JavaInfo. The JavaInfo for the 192 annotation processor. 193 data_binding_annotation_template: A file. Used to generate data binding 194 classes. 195 196 Returns: 197 A DataBindingContextInfo provider. 198 """ 199 200 # TODO(b/154513292): Clean up bad usages of context objects. 201 if resources_ctx: 202 defines_resources = resources_ctx.defines_resources 203 204 # The Android Data Binding context object. 205 db_info = { 206 _JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS: [], 207 _JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_OUTPUTS: [], 208 _JAVA_PLUGINS: [], 209 _JAVA_SRCS: [], 210 _JAVAC_OPTS: [], 211 _PROVIDERS: [], 212 } 213 214 if not enable_data_binding: 215 db_info[_PROVIDERS] = [ 216 DataBindingV2Info( 217 databinding_v2_providers_in_deps = deps, 218 databinding_v2_providers_in_exports = exports, 219 ), 220 ] 221 return struct(**db_info) 222 223 output_dir = "databinding/%s/" % ctx.label.name 224 225 db_info[_JAVA_SRCS].append(_copy_annotation_file( 226 ctx, 227 output_dir, 228 data_binding_annotation_template, 229 )) 230 db_info[_JAVA_PLUGINS].append(data_binding_annotation_processor) 231 232 br_out = None 233 setter_store_out = None 234 class_info = None 235 if defines_resources: 236 # Outputs of the Data Binding annotation processor. 237 br_out = ctx.actions.declare_file( 238 output_dir + "bin-files/%s-br.bin" % java_package, 239 ) 240 db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_OUTPUTS].append(br_out) 241 setter_store_out = ctx.actions.declare_file( 242 output_dir + "bin-files/%s-setter_store.json" % java_package, 243 ) 244 db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_OUTPUTS].append( 245 setter_store_out, 246 ) 247 248 srcjar, class_info = _gen_sources( 249 ctx, 250 output_dir, 251 java_package, 252 deps, 253 layout_info, 254 data_binding_exec, 255 ) 256 db_info[_JAVA_SRCS].append(srcjar) 257 db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS].append(class_info) 258 db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS].append( 259 layout_info, 260 ) 261 262 dep_lib_artifacts = _setup_dependent_lib_artifacts(ctx, output_dir, deps) 263 db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS].extend( 264 dep_lib_artifacts, 265 ) 266 267 db_info[_JAVAC_OPTS] = _get_javac_opts( 268 ctx, 269 java_package, 270 ( 271 br_out.path.rpartition(br_out.short_path)[0] + 272 ctx.label.package + 273 "/" + 274 output_dir + 275 "dependent-lib-artifacts" 276 ), 277 br_out.dirname, 278 class_info.path if class_info else _NO_RESOURCES_PATH, 279 layout_info.path if layout_info else _NO_RESOURCES_PATH, 280 deps, 281 ) 282 283 db_info[_PROVIDERS] = [ 284 DataBindingV2Info( 285 setter_store_file = setter_store_out, 286 class_info_file = class_info, 287 br_file = br_out, 288 label = str(ctx.label), 289 java_package = java_package, 290 databinding_v2_providers_in_deps = deps, 291 databinding_v2_providers_in_exports = exports, 292 ), 293 ] 294 295 return DataBindingContextInfo(**db_info) 296 297data_binding = struct( 298 process = _process, 299) 300