| # Copyright 2018 The Bazel Authors. All rights reserved. | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| """Bazel Android Data Binding.""" | |
| load(":utils.bzl", _utils = "utils") | |
| # Data Binding context attributes. | |
| _JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS = \ | |
| "java_annotation_processor_additional_inputs" | |
| _JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_OUTPUTS = \ | |
| "java_annotation_processor_additional_outputs" | |
| _JAVA_PLUGINS = "java_plugins" | |
| _JAVA_SRCS = "java_srcs" | |
| _JAVAC_OPTS = "javac_opts" | |
| _PROVIDERS = "providers" | |
| DataBindingContextInfo = provider( | |
| doc = "Contains data from processing Android Data Binding.", | |
| fields = { | |
| _JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS: ( | |
| "Additional inputs required by the Java annotation processor." | |
| ), | |
| _JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_OUTPUTS: ( | |
| "Additional outputs produced by the Java annotation processor." | |
| ), | |
| _JAVA_PLUGINS: "Data Binding Java annotation processor", | |
| _JAVA_SRCS: "Java sources required by the Java annotation processor.", | |
| _JAVAC_OPTS: ( | |
| "Additional Javac opts required by the Java annotation processor." | |
| ), | |
| _PROVIDERS: "The list of all providers to propagate.", | |
| }, | |
| ) | |
| # Path used when resources have not been defined. | |
| _NO_RESOURCES_PATH = "/tmp/no_resources" | |
| def _copy_annotation_file(ctx, output_dir, annotation_template): | |
| annotation_out = ctx.actions.declare_file( | |
| output_dir + "/android/databinding/layouts/DataBindingInfo.java", | |
| ) | |
| _utils.copy_file(ctx, annotation_template, annotation_out) | |
| return annotation_out | |
| def _gen_sources(ctx, output_dir, java_package, deps, layout_info, data_binding_exec): | |
| class_info = ctx.actions.declare_file(output_dir + "class-info.zip") | |
| srcjar = ctx.actions.declare_file(output_dir + "baseClassSrc.srcjar") | |
| args = ctx.actions.args() | |
| args.add("-layoutInfoFiles", layout_info) | |
| args.add("-package", java_package) | |
| args.add("-classInfoOut", class_info) | |
| args.add("-sourceOut", srcjar) | |
| args.add("-zipSourceOutput", "true") | |
| args.add("-useAndroidX", "false") | |
| if deps: | |
| if type(deps[0].class_infos) == "depset": | |
| class_infos = depset(transitive = [info.class_infos for info in deps]) | |
| inputs = depset(direct = [layout_info], transitive = [class_infos]) | |
| elif type(deps[0].class_infos) == "list": | |
| class_infos = [] | |
| for info in deps: | |
| class_infos.extend(info.class_infos) | |
| inputs = class_infos + [layout_info] | |
| else: | |
| fail("Expected list or depset. Got %s" % type(deps[0].class_infos)) | |
| else: | |
| class_infos = [] | |
| inputs = [layout_info] | |
| args.add_all(class_infos, before_each = "-dependencyClassInfoList") | |
| ctx.actions.run( | |
| executable = data_binding_exec, | |
| arguments = ["GEN_BASE_CLASSES", args], | |
| inputs = inputs, | |
| outputs = [class_info, srcjar], | |
| mnemonic = "GenerateDataBindingBaseClasses", | |
| progress_message = ( | |
| "GenerateDataBindingBaseClasses %s" % class_info.short_path | |
| ), | |
| ) | |
| return srcjar, class_info | |
| def _setup_dependent_lib_artifacts(ctx, output_dir, deps): | |
| # DataBinding requires files in very specific locations. | |
| # The following expand_template (copy actions) are moving the files | |
| # to the correct locations. | |
| dep_lib_artifacts = [] | |
| for info in deps: | |
| # Yes, DataBinding requires depsets iterations. | |
| for artifact in (info.transitive_br_files.to_list() + | |
| _utils.list_or_depset_to_list(info.setter_stores) + | |
| _utils.list_or_depset_to_list(info.class_infos)): | |
| # short_path might contain a parent directory reference if the | |
| # databinding artifact is from an external repository (e.g. an aar | |
| # from Maven). If that's the case, just remove the parent directory | |
| # reference, otherwise the "dependent-lib-artifacts" directory will | |
| # get removed by the "..". | |
| path = artifact.short_path | |
| if path.startswith("../"): | |
| path = path[3:] | |
| dep_lib_artifact = ctx.actions.declare_file( | |
| output_dir + "dependent-lib-artifacts/" + path, | |
| ) | |
| # Copy file to a location required by the DataBinding annotation | |
| # processor. | |
| # TODO(djwhang): Look into SymlinkAction. | |
| if artifact.is_directory: | |
| _utils.copy_dir(ctx, artifact, dep_lib_artifact) | |
| else: | |
| _utils.copy_file(ctx, artifact, dep_lib_artifact) | |
| dep_lib_artifacts.append(dep_lib_artifact) | |
| return dep_lib_artifacts | |
| def _get_javac_opts( | |
| ctx, | |
| java_package, | |
| artifact_type, | |
| dependency_artifacts_dir, | |
| aar_out_dir, | |
| class_info_path, | |
| layout_info_path, | |
| deps): | |
| java_packages = [] | |
| for info in deps: | |
| for label_and_java_package in info.label_and_java_packages: | |
| java_packages.append(label_and_java_package.java_package) | |
| javac_opts = [] | |
| javac_opts.append("-Aandroid.databinding.dependencyArtifactsDir=" + | |
| dependency_artifacts_dir) | |
| javac_opts.append("-Aandroid.databinding.aarOutDir=" + aar_out_dir) | |
| javac_opts.append("-Aandroid.databinding.sdkDir=/not/used") | |
| javac_opts.append("-Aandroid.databinding.artifactType=" + artifact_type) | |
| javac_opts.append("-Aandroid.databinding.exportClassListOutFile=" + | |
| "/tmp/exported_classes") | |
| javac_opts.append("-Aandroid.databinding.modulePackage=" + java_package) | |
| javac_opts.append("-Aandroid.databinding.directDependencyPkgs=[%s]" % | |
| ",".join(java_packages)) | |
| # The minimum Android SDK compatible with this rule. | |
| # TODO(djwhang): This probably should be based on the actual min-sdk from | |
| # the manifest, or an appropriate rule attribute. | |
| javac_opts.append("-Aandroid.databinding.minApi=14") | |
| javac_opts.append("-Aandroid.databinding.enableV2=1") | |
| javac_opts.append("-Aandroid.databinding.classLogDir=" + class_info_path) | |
| javac_opts.append("-Aandroid.databinding.layoutInfoDir=" + layout_info_path) | |
| return javac_opts | |
| def _process( | |
| ctx, | |
| resources_ctx = None, | |
| defines_resources = False, | |
| enable_data_binding = False, | |
| java_package = None, | |
| layout_info = None, | |
| artifact_type = "LIBRARY", | |
| deps = [], | |
| exports = [], | |
| data_binding_exec = None, | |
| data_binding_annotation_processor = None, | |
| data_binding_annotation_template = None): | |
| """Processes Android Data Binding. | |
| Args: | |
| ctx: The context. | |
| resources_ctx: The Android Resources context. | |
| defines_resources: boolean. Determines whether resources were defined. | |
| enable_data_binding: boolean. Determines whether Data Binding should be | |
| enabled. | |
| java_package: String. The Java package. | |
| layout_info: A file. The layout-info zip file. | |
| artifact_type: String. Either LIBRARY or APPLICATION. | |
| deps: sequence of DataBindingV2Info providers. A list of deps. Optional. | |
| exports: sequence of DataBindingV2Info providers. A list of exports. | |
| Optional. | |
| data_binding_exec: The DataBinding executable. | |
| data_binding_annotation_processor: JavaInfo. The JavaInfo for the | |
| annotation processor. | |
| data_binding_annotation_template: A file. Used to generate data binding | |
| classes. | |
| Returns: | |
| A DataBindingContextInfo provider. | |
| """ | |
| if artifact_type not in ["LIBRARY", "APPLICATION"]: | |
| fail("Unexpected artifact type: " + artifact_type) | |
| # TODO(b/154513292): Clean up bad usages of context objects. | |
| if resources_ctx: | |
| defines_resources = resources_ctx.defines_resources | |
| # The Android Data Binding context object. | |
| db_info = { | |
| _JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS: [], | |
| _JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_OUTPUTS: [], | |
| _JAVA_PLUGINS: [], | |
| _JAVA_SRCS: [], | |
| _JAVAC_OPTS: [], | |
| _PROVIDERS: [], | |
| } | |
| if not enable_data_binding: | |
| db_info[_PROVIDERS] = [ | |
| DataBindingV2Info( | |
| databinding_v2_providers_in_deps = deps, | |
| databinding_v2_providers_in_exports = exports, | |
| ), | |
| ] | |
| return struct(**db_info) | |
| output_dir = "databinding/%s/" % ctx.label.name | |
| db_info[_JAVA_SRCS].append(_copy_annotation_file( | |
| ctx, | |
| output_dir, | |
| data_binding_annotation_template, | |
| )) | |
| db_info[_JAVA_PLUGINS].append(data_binding_annotation_processor) | |
| br_out = None | |
| setter_store_out = None | |
| class_info = None | |
| if defines_resources: | |
| # Outputs of the Data Binding annotation processor. | |
| br_out = ctx.actions.declare_file( | |
| output_dir + "bin-files/%s-br.bin" % java_package, | |
| ) | |
| db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_OUTPUTS].append(br_out) | |
| setter_store_out = ctx.actions.declare_file( | |
| output_dir + "bin-files/%s-setter_store.json" % java_package, | |
| ) | |
| db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_OUTPUTS].append( | |
| setter_store_out, | |
| ) | |
| srcjar, class_info = _gen_sources( | |
| ctx, | |
| output_dir, | |
| java_package, | |
| deps, | |
| layout_info, | |
| data_binding_exec, | |
| ) | |
| db_info[_JAVA_SRCS].append(srcjar) | |
| db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS].append(class_info) | |
| db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS].append( | |
| layout_info, | |
| ) | |
| dep_lib_artifacts = _setup_dependent_lib_artifacts(ctx, output_dir, deps) | |
| db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS].extend( | |
| dep_lib_artifacts, | |
| ) | |
| db_info[_JAVAC_OPTS] = _get_javac_opts( | |
| ctx, | |
| java_package, | |
| artifact_type, | |
| ( | |
| br_out.path.rpartition(br_out.short_path)[0] + | |
| ctx.label.package + | |
| "/" + | |
| output_dir + | |
| "dependent-lib-artifacts" | |
| ), | |
| br_out.dirname, | |
| class_info.path if class_info else _NO_RESOURCES_PATH, | |
| layout_info.path if layout_info else _NO_RESOURCES_PATH, | |
| deps, | |
| ) | |
| db_info[_PROVIDERS] = [ | |
| DataBindingV2Info( | |
| setter_store_file = setter_store_out, | |
| class_info_file = class_info, | |
| br_file = br_out, | |
| label = str(ctx.label), | |
| java_package = java_package, | |
| databinding_v2_providers_in_deps = deps, | |
| databinding_v2_providers_in_exports = exports, | |
| ), | |
| ] | |
| return DataBindingContextInfo(**db_info) | |
| data_binding = struct( | |
| process = _process, | |
| ) |