xref: /aosp_15_r20/external/bazelbuild-rules_android/rules/aar_import/impl.bzl (revision 9e965d6fece27a77de5377433c2f7e6999b8cc0b)
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"""Implementation."""
16
17load(
18    "//rules:acls.bzl",
19    _acls = "acls",
20)
21load(
22    "//rules:common.bzl",
23    _common = "common",
24)
25load("//rules:intellij.bzl", "intellij")
26load(
27    "//rules:java.bzl",
28    _java = "java",
29)
30load("//rules:providers.bzl", "AndroidLintRulesInfo")
31load(
32    "//rules:resources.bzl",
33    _resources = "resources",
34)
35load(
36    "//rules:utils.bzl",
37    "ANDROID_TOOLCHAIN_TYPE",
38    _get_android_toolchain = "get_android_toolchain",
39    _utils = "utils",
40)
41
42RULE_PREFIX = "_aar"
43ANDROID_MANIFEST = "AndroidManifest.xml"
44LINT_JAR = "lint.jar"
45
46# Resources context dict fields.
47_PROVIDERS = "providers"
48_VALIDATION_RESULTS = "validation_results"
49
50def _create_aar_tree_artifact(ctx, name):
51    return ctx.actions.declare_directory("%s/unzipped/%s/%s" % (RULE_PREFIX, name, ctx.label.name))
52
53def create_aar_artifact(ctx, name):
54    return ctx.actions.declare_file("%s/%s/%s" % (RULE_PREFIX, ctx.label.name, name))
55
56# Create an action to extract a file (specified by the parameter filename) from an AAR file.
57# Will optionally create an empty output if the requested file does not exist..
58def extract_single_file(
59        ctx,
60        out_file,
61        aar,
62        filename,
63        unzip_tool,
64        create_empty_file = False):
65    ctx.actions.run_shell(
66        tools = [unzip_tool],
67        inputs = [aar],
68        outputs = [out_file],
69        command =
70            """
71            if ! {create_empty_file} || {unzip_tool} -l {aar} | grep -q {file}; then
72              {unzip_tool} -q {aar} {file} -d {dirname};
73           else
74               touch {dirname}/{file};
75           fi
76        """.format(
77                unzip_tool = unzip_tool.executable.path,
78                aar = aar.path,
79                file = out_file.basename,
80                dirname = out_file.dirname,
81                create_empty_file = str(create_empty_file).lower(),
82            ),
83        mnemonic = "AarFileExtractor",
84        progress_message = "Extracting %s from %s" % (filename, aar.basename),
85        toolchain = ANDROID_TOOLCHAIN_TYPE,
86    )
87
88def _extract_resources(
89        ctx,
90        out_resources_dir,
91        out_assets_dir,
92        aar,
93        aar_resources_extractor_tool):
94    args = ctx.actions.args()
95    args.add("--input_aar", aar)
96    args.add("--output_res_dir", out_resources_dir.path)
97    args.add("--output_assets_dir", out_assets_dir.path)
98    ctx.actions.run(
99        executable = aar_resources_extractor_tool,
100        arguments = [args],
101        inputs = [aar],
102        outputs = [out_resources_dir, out_assets_dir],
103        mnemonic = "AarResourcesExtractor",
104        progress_message = "Extracting resources and assets from %s" % aar.basename,
105        toolchain = None,
106    )
107
108def _extract_native_libs(
109        ctx,
110        output_zip,
111        aar,
112        android_cpu,
113        aar_native_libs_zip_creator_tool):
114    args = ctx.actions.args()
115    args.add("--input_aar", aar)
116    args.add("--cpu", android_cpu)
117    args.add("--output_zip", output_zip)
118    ctx.actions.run(
119        executable = aar_native_libs_zip_creator_tool,
120        arguments = [args],
121        inputs = [aar],
122        outputs = [output_zip],
123        mnemonic = "AarNativeLibsFilter",
124        progress_message = "Filtering AAR native libs by architecture",
125        toolchain = None,
126    )
127
128def _process_resources(
129        ctx,
130        aar,
131        package,
132        manifest,
133        deps,
134        aar_resources_extractor_tool,
135        unzip_tool):
136    # Extract resources and assets, if they exist.
137    resources = _create_aar_tree_artifact(ctx, "resources")
138    assets = _create_aar_tree_artifact(ctx, "assets")
139    _extract_resources(
140        ctx,
141        resources,
142        assets,
143        aar,
144        aar_resources_extractor_tool,
145    )
146
147    resources_ctx = _resources.process_starlark(
148        ctx,
149        manifest = manifest,
150        assets = [assets],
151        assets_dir = assets.path,
152        resource_files = [resources],
153        stamp_manifest = False,
154        deps = ctx.attr.deps,
155        exports = ctx.attr.exports,
156        exports_manifest = getattr(ctx.attr, "exports_manifest", True),
157        propagate_resources = _acls.in_aar_propagate_resources(str(ctx.label)),
158
159        # Tool and Processing related inputs
160        aapt = _get_android_toolchain(ctx).aapt2.files_to_run,
161        android_jar = ctx.attr._android_sdk[AndroidSdkInfo].android_jar,
162        android_kit = _get_android_toolchain(ctx).android_kit.files_to_run,
163        busybox = _get_android_toolchain(ctx).android_resources_busybox.files_to_run,
164        java_toolchain = _common.get_java_toolchain(ctx),
165        host_javabase = _common.get_host_javabase(ctx),
166        instrument_xslt = _utils.only(_get_android_toolchain(ctx).add_g3itr_xslt.files.to_list()),
167        xsltproc = _get_android_toolchain(ctx).xsltproc_tool.files_to_run,
168    )
169
170    native_android_manifest = manifest
171    if not getattr(ctx.attr, "exports_manifest", True):
172        # Write an empty manifest, for the native pipeline.
173        native_android_manifest = ctx.actions.declare_file(ctx.label.name + "_aar/AndroidManifest.xml")
174        ctx.actions.write(native_android_manifest, content = """<?xml version="1.0" encoding="utf-8"?>
175<manifest package="%s">
176</manifest>
177""" % package)
178
179
180    return struct(**resources_ctx)
181
182def _extract_jars(
183        ctx,
184        out_jars_tree_artifact,
185        out_jars_params_file,
186        aar,
187        aar_embedded_jars_extractor_tool):
188    args = ctx.actions.args()
189    args.add("--input_aar", aar)
190    args.add("--output_dir", out_jars_tree_artifact.path)
191    args.add("--output_singlejar_param_file", out_jars_params_file)
192    ctx.actions.run(
193        executable = aar_embedded_jars_extractor_tool,
194        arguments = [args],
195        inputs = [aar],
196        outputs = [out_jars_tree_artifact, out_jars_params_file],
197        mnemonic = "AarEmbeddedJarsExtractor",
198        progress_message = "Extracting classes.jar and libs/*.jar from %s" % aar.basename,
199        toolchain = None,
200    )
201
202def _merge_jars(
203        ctx,
204        out_jar,
205        jars_tree_artifact,
206        jars_param_file,
207        single_jar_tool):
208    args = ctx.actions.args()
209    args.add("--output", out_jar)
210    args.add("--dont_change_compression")
211    args.add("--normalize")
212    args.add("@" + jars_param_file.path)
213    ctx.actions.run(
214        executable = single_jar_tool,
215        arguments = [args],
216        inputs = [jars_tree_artifact, jars_param_file],
217        outputs = [out_jar],
218        mnemonic = "AarJarsMerger",
219        progress_message = "Merging AAR embedded jars",
220        toolchain = None,
221    )
222
223def _extract_and_merge_jars(
224        ctx,
225        out_jar,
226        aar,
227        aar_embedded_jars_extractor_tool,
228        single_jar_tool):
229    """Extracts all the Jars within the AAR and produces a single jar.
230
231    An AAR may have multiple Jar files embedded within it. This method
232    extracts and merges all Jars.
233    """
234    jars_tree_artifact = _create_aar_tree_artifact(ctx, "jars")
235    jars_params_file = create_aar_artifact(ctx, "jar_merging_params")
236    _extract_jars(
237        ctx,
238        jars_tree_artifact,
239        jars_params_file,
240        aar,
241        aar_embedded_jars_extractor_tool,
242    )
243    _merge_jars(
244        ctx,
245        out_jar,
246        jars_tree_artifact,
247        jars_params_file,
248        single_jar_tool,
249    )
250
251def _create_import_deps_check(
252        ctx,
253        jars_to_check,
254        declared_deps,
255        transitive_deps,
256        bootclasspath,
257        jdeps_output,
258        import_deps_checker_tool,
259        host_javabase):
260    args = ctx.actions.args()
261    args.add_all(jars_to_check, before_each = "--input")
262    args.add_all(declared_deps, before_each = "--directdep")
263    args.add_all(
264        depset(order = "preorder", transitive = [declared_deps, transitive_deps]),
265        before_each = "--classpath_entry",
266    )
267    args.add_all(bootclasspath, before_each = "--bootclasspath_entry")
268    args.add("--checking_mode=error")
269    args.add("--jdeps_output", jdeps_output)
270    args.add("--rule_label", ctx.label)
271
272    _java.run(
273        ctx = ctx,
274        host_javabase = host_javabase,
275        executable = import_deps_checker_tool,
276        arguments = [args],
277        inputs = depset(
278            jars_to_check,
279            transitive = [
280                declared_deps,
281                transitive_deps,
282                bootclasspath,
283            ],
284        ),
285        outputs = [jdeps_output],
286        mnemonic = "ImportDepsChecker",
287        progress_message = "Checking the completeness of the deps for %s" % jars_to_check,
288    )
289
290def _process_jars(
291        ctx,
292        out_jar,
293        aar,
294        source_jar,
295        r_java,
296        deps,
297        exports,
298        enable_desugar_java8,
299        enable_imports_deps_check,
300        bootclasspath,
301        desugar_java8_extra_bootclasspath,
302        aar_embedded_jars_extractor_tool,
303        import_deps_checker_tool,
304        single_jar_tool,
305        java_toolchain,
306        host_javabase):
307    providers = []
308    validation_results = []
309    r_java_info = [r_java] if r_java else []
310
311    # An aar may have multple Jar files, extract and merge into a single jar.
312    _extract_and_merge_jars(
313        ctx,
314        out_jar,
315        aar,
316        aar_embedded_jars_extractor_tool,
317        single_jar_tool,
318    )
319
320    java_infos = deps + exports
321
322    if enable_desugar_java8:
323        bootclasspath = depset(transitive = [
324            desugar_java8_extra_bootclasspath,
325            bootclasspath,
326        ])
327
328    merged_java_info = java_common.merge(java_infos + r_java_info)
329    jdeps_artifact = create_aar_artifact(ctx, "jdeps.proto")
330    _create_import_deps_check(
331        ctx,
332        [out_jar],
333        merged_java_info.compile_jars,
334        merged_java_info.transitive_compile_time_jars,
335        bootclasspath,
336        jdeps_artifact,
337        import_deps_checker_tool,
338        host_javabase,
339    )
340    if enable_imports_deps_check:
341        validation_results.append(jdeps_artifact)
342
343    java_info = JavaInfo(
344        out_jar,
345        compile_jar = java_common.stamp_jar(
346            actions = ctx.actions,
347            jar = out_jar,
348            target_label = ctx.label,
349            java_toolchain = java_toolchain,
350        ),
351        source_jar = source_jar,
352        neverlink = False,
353        deps = r_java_info + java_infos,  # TODO(djwhang): Exports are not deps.
354        exports =
355            (r_java_info if _acls.in_aar_import_exports_r_java(str(ctx.label)) else []) +
356            java_infos,  # TODO(djwhang): Deps are not exports.
357        # TODO(djwhang): AarImportTest is not expecting jdeps, enable or remove it completely
358        # jdeps = jdeps_artifact,
359    )
360    providers.append(java_info)
361
362    return struct(
363        java_info = java_info,
364        providers = providers,
365        validation_results = validation_results,
366    )
367
368def _validate_rule(
369        ctx,
370        aar,
371        package,
372        manifest,
373        checks):
374    validation_output = ctx.actions.declare_file("%s_validation_output" % ctx.label.name)
375
376    args = ctx.actions.args()
377    args.add("-aar", aar)
378    args.add("-label", str(ctx.label))
379    args.add("-pkg", package)
380    args.add("-manifest", manifest)
381    if ctx.attr.has_lint_jar:
382        args.add("-has_lint_jar")
383    args.add("-output", validation_output)
384
385    ctx.actions.run(
386        executable = checks,
387        arguments = [args],
388        inputs = [aar, manifest],
389        outputs = [validation_output],
390        mnemonic = "ValidateAAR",
391        progress_message = "Validating aar_import %s" % str(ctx.label),
392        toolchain = None,
393    )
394    return validation_output
395
396def _process_lint_rules(
397        ctx,
398        aar,
399        unzip_tool):
400    transitive_lint_jars = [info.lint_jars for info in _utils.collect_providers(
401        AndroidLintRulesInfo,
402        ctx.attr.exports,
403    )]
404
405    if ctx.attr.has_lint_jar:
406        lint_jar = create_aar_artifact(ctx, LINT_JAR)
407        extract_single_file(
408            ctx,
409            lint_jar,
410            aar,
411            LINT_JAR,
412            unzip_tool,
413        )
414        return [
415            AndroidLintRulesInfo(
416                lint_jars = depset(direct = [lint_jar], transitive = transitive_lint_jars),
417            ),
418        ]
419    elif transitive_lint_jars:
420        return [
421            AndroidLintRulesInfo(lint_jars = depset(transitive = transitive_lint_jars)),
422        ]
423    else:
424        return []
425
426def _collect_proguard(
427        ctx,
428        out_proguard,
429        aar,
430        aar_embedded_proguard_extractor):
431    args = ctx.actions.args()
432    args.add("--input_aar", aar)
433    args.add("--output_proguard_file", out_proguard)
434    ctx.actions.run(
435        executable = aar_embedded_proguard_extractor,
436        arguments = [args],
437        inputs = [aar],
438        outputs = [out_proguard],
439        mnemonic = "AarEmbeddedProguardExtractor",
440        progress_message = "Extracting proguard spec from %s" % aar.basename,
441        toolchain = None,
442    )
443    transitive_proguard_specs = []
444    for p in _utils.collect_providers(ProguardSpecProvider, ctx.attr.deps, ctx.attr.exports):
445        transitive_proguard_specs.append(p.specs)
446    return ProguardSpecProvider(depset([out_proguard], transitive = transitive_proguard_specs))
447
448def impl(ctx):
449    """The rule implementation.
450
451    Args:
452      ctx: The context.
453
454    Returns:
455      A tuple of (list of providers, JavaInfo)
456    """
457    providers = []
458    validation_outputs = []
459
460    aar = _utils.only(ctx.files.aar)
461    unzip_tool = _get_android_toolchain(ctx).unzip_tool.files_to_run
462    package = _java.resolve_package_from_label(ctx.label, ctx.attr.package)
463
464    # Extract the AndroidManifest.xml from the AAR.
465    android_manifest = create_aar_artifact(ctx, ANDROID_MANIFEST)
466    extract_single_file(
467        ctx,
468        android_manifest,
469        aar,
470        ANDROID_MANIFEST,
471        unzip_tool,
472    )
473
474    # Bump min SDK to floor
475    manifest_ctx = _resources.bump_min_sdk(
476        ctx,
477        manifest = android_manifest,
478        floor = _acls.get_min_sdk_floor(str(ctx.label)),
479        enforce_min_sdk_floor_tool = _get_android_toolchain(ctx).enforce_min_sdk_floor_tool.files_to_run,
480    )
481
482    resources_ctx = _process_resources(
483        ctx,
484        aar = aar,
485        package = package,
486        manifest = manifest_ctx.processed_manifest,
487        deps = ctx.attr.deps,
488        aar_resources_extractor_tool =
489            _get_android_toolchain(ctx).aar_resources_extractor.files_to_run,
490        unzip_tool = unzip_tool,
491    )
492    providers.extend(resources_ctx.providers)
493
494    merged_jar = create_aar_artifact(ctx, "classes_and_libs_merged.jar")
495    jvm_ctx = _process_jars(
496        ctx,
497        out_jar = merged_jar,
498        aar = aar,
499        source_jar = ctx.file.srcjar,
500        deps = _utils.collect_providers(JavaInfo, ctx.attr.deps),
501        r_java = resources_ctx.r_java,
502        exports = _utils.collect_providers(JavaInfo, ctx.attr.exports),
503        enable_desugar_java8 = ctx.fragments.android.desugar_java8,
504        enable_imports_deps_check =
505            _acls.in_aar_import_deps_checker(str(ctx.label)),
506        aar_embedded_jars_extractor_tool =
507            _get_android_toolchain(ctx).aar_embedded_jars_extractor.files_to_run,
508        bootclasspath =
509            ctx.attr._java_toolchain[java_common.JavaToolchainInfo].bootclasspath,
510        desugar_java8_extra_bootclasspath =
511            _get_android_toolchain(ctx).desugar_java8_extra_bootclasspath.files,
512        import_deps_checker_tool =
513            _get_android_toolchain(ctx).import_deps_checker.files_to_run,
514        single_jar_tool =
515            ctx.attr._java_toolchain[java_common.JavaToolchainInfo].single_jar,
516        java_toolchain =
517            ctx.attr._java_toolchain[java_common.JavaToolchainInfo],
518        host_javabase = ctx.attr._host_javabase,
519    )
520    providers.extend(jvm_ctx.providers)
521    validation_outputs.extend(jvm_ctx.validation_results)
522
523    native_libs = create_aar_artifact(ctx, "native_libs.zip")
524    _extract_native_libs(
525        ctx,
526        native_libs,
527        aar = aar,
528        android_cpu = ctx.fragments.android.android_cpu,
529        aar_native_libs_zip_creator_tool =
530            _get_android_toolchain(ctx).aar_native_libs_zip_creator.files_to_run,
531    )
532    native_libs_infos = _utils.collect_providers(
533        AndroidNativeLibsInfo,
534        ctx.attr.deps,
535        ctx.attr.exports,
536    )
537    providers.append(
538        AndroidNativeLibsInfo(
539            depset(
540                [native_libs],
541                transitive = [info.native_libs for info in native_libs_infos],
542            ),
543        ),
544    )
545
546    # Will be empty if there's no proguard.txt file in the aar
547    proguard_spec = create_aar_artifact(ctx, "proguard.txt")
548    providers.append(_collect_proguard(
549        ctx,
550        proguard_spec,
551        aar,
552        _get_android_toolchain(ctx).aar_embedded_proguard_extractor.files_to_run,
553    ))
554
555    lint_providers = _process_lint_rules(
556        ctx,
557        aar = aar,
558        unzip_tool = unzip_tool,
559    )
560    providers.extend(lint_providers)
561
562    validation_outputs.append(_validate_rule(
563        ctx,
564        aar = aar,
565        package = package,
566        manifest = manifest_ctx.processed_manifest,
567        checks = _get_android_toolchain(ctx).aar_import_checks.files_to_run,
568    ))
569
570    providers.append(
571        intellij.make_android_ide_info(
572            ctx,
573            java_package = _java.resolve_package_from_label(ctx.label, ctx.attr.package),
574            manifest = resources_ctx.merged_manifest,
575            defines_resources = resources_ctx.defines_resources,
576            merged_manifest = resources_ctx.merged_manifest,
577            resources_apk = resources_ctx.resources_apk,
578            r_jar = _utils.only(resources_ctx.r_java.outputs.jars) if resources_ctx.r_java else None,
579            java_info = jvm_ctx.java_info,
580            signed_apk = None,  # signed_apk, always empty for aar_import
581            apks_under_test = [],  # apks_under_test, always empty for aar_import
582            native_libs = dict(),  # nativelibs, always empty for aar_import
583            idlclass = _get_android_toolchain(ctx).idlclass.files_to_run,
584            host_javabase = _common.get_host_javabase(ctx),
585        ),
586    )
587
588    providers.append(OutputGroupInfo(_validation = depset(validation_outputs)))
589
590    # There isn't really any use case for building an aar_import target on its own, so the files to
591    # build could be empty. The R class JAR and merged JARs are added here as a sanity check for
592    # Bazel developers so that `bazel build java/com/my_aar_import` will fail if the resource
593    # processing or JAR merging steps fail.
594    files_to_build = []
595    files_to_build.extend(resources_ctx.validation_results)  # TODO(djwhang): This should be validation.
596    files_to_build.append(merged_jar)
597
598    providers.append(
599        DefaultInfo(
600            files = depset(files_to_build),
601            runfiles = ctx.runfiles(),
602        ),
603    )
604
605    return providers, jvm_ctx.java_info
606