• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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