| # 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", "ANDROID_TOOLCHAIN_TYPE", _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 |
| ), |
| toolchain = ANDROID_TOOLCHAIN_TYPE, |
| ) |
| 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, |
| ) |