• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2019 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 ResourcesBusyBox Commands."""
16
17load(":java.bzl", _java = "java")
18
19_ANDROID_RESOURCES_STRICT_DEPS = "android_resources_strict_deps"
20
21def _sanitize_assets_dir(assets_dir):
22    sanitized_assets_dir = "/".join(
23        [
24            part
25            for part in assets_dir.split("/")
26            if part != "" and part != "."
27        ],
28    )
29
30    return "/" + sanitized_assets_dir if assets_dir.startswith("/") else sanitized_assets_dir
31
32def _get_unique_assets_dirs(assets, assets_dir):
33    """Find the unique assets directories, partitioned by assets_dir.
34
35    Args:
36      assets: A list of Files. List of asset files to process.
37      assets_dir: String. String giving the path to the files in assets.
38
39    Returns:
40      A list of short_paths representing unique asset dirs.
41    """
42    if not assets:
43        return []
44
45    dirs = dict()
46
47    assets_dir = _sanitize_assets_dir(assets_dir)
48    if assets_dir:
49        partition_by = "/%s/" % assets_dir.strip("/")
50        for f in assets:
51            if f.is_directory and f.path.endswith(partition_by[:-1]):
52                # If f is a directory, check if its path ends with the assets_dir.
53                dirs[f.path] = True
54            elif f.is_directory and "_aar/unzipped" in f.path:
55                # Assets from an aar_import rule are extracted in a
56                # "assets" subdirectory of the given path
57                dirs["%s/assets" % f.path] = True
58            else:
59                # Partition to remove subdirectories beneath assets_dir
60                # Also removes the trailing /
61                dirs["".join(f.path.rpartition(partition_by)[:2])[:-1]] = True
62    else:
63        # Use the dirname of the generating target if no assets_dir.
64        for f in assets:
65            if f.is_source:
66                dirs[f.owner.package] = True
67            else:
68                # Prepend the root path for generated files.
69                dirs[f.root.path + "/" + f.owner.package] = True
70    return dirs.keys()
71
72def _get_unique_res_dirs(resource_files):
73    """Find the unique res dirs.
74
75    Args:
76      resource_files: A list of Files. A list of resource_files.
77
78    Returns:
79      A list of short_paths representing unique res dirs from the given resource files.
80    """
81    dirs = dict()
82    for f in resource_files:
83        if f.is_directory:
84            dirs[f.path] = True
85        else:
86            dirs[f.dirname.rpartition("/" + f.dirname.split("/")[-1])[0]] = True
87    return dirs.keys()
88
89def _make_serialized_resources_flag(
90        assets = [],
91        assets_dir = None,
92        resource_files = [],
93        label = "",
94        symbols = None):
95    return ";".join(
96        [
97            "#".join(_get_unique_res_dirs(resource_files)),
98            "#".join(_get_unique_assets_dirs(assets, assets_dir)),
99            label,
100            symbols.path if symbols else "",
101        ],
102    ).rstrip(":")
103
104def _make_resources_flag(
105        assets = [],
106        assets_dir = None,
107        resource_files = [],
108        manifest = None,
109        r_txt = None,
110        symbols = None):
111    return ":".join(
112        [
113            "#".join(_get_unique_res_dirs(resource_files)),
114            "#".join(_get_unique_assets_dirs(assets, assets_dir)),
115            manifest.path if manifest else "",
116            r_txt.path if r_txt else "",
117            symbols.path if symbols else "",
118        ],
119    )
120
121def _path(f):
122    return f.path
123
124def _make_package_resources_flags(resources_node):
125    if not (resources_node.manifest and resources_node.r_txt and resources_node.compiled_resources):
126        return None
127    flag = _make_resources_flag(
128        resource_files = resources_node.resource_files.to_list(),
129        assets = resources_node.assets.to_list(),
130        assets_dir = resources_node.assets_dir,
131        manifest = resources_node.manifest,
132        r_txt = resources_node.r_txt,
133        symbols = resources_node.compiled_resources,
134    )
135    return flag
136
137def _make_package_assets_flags(resources_node):
138    assets = resources_node.assets.to_list()
139    if not assets:
140        return None
141    return _make_serialized_resources_flag(
142        assets = assets,
143        assets_dir = resources_node.assets_dir,
144        label = str(resources_node.label),
145        symbols = resources_node.compiled_assets,
146    )
147
148def _extract_filters(
149        raw_list):
150    """Extract densities and resource_configuration filters from raw string lists.
151
152    In BUILD files, string lists can be represented as a list of strings, a single comma-separated
153    string, or a combination of both. This method outputs a single list of individual string values,
154    which can then be passed directly to resource processing actions. Empty strings are removed and
155    the final list is sorted.
156
157    Args:
158      raw_list: List of strings. The raw densities or resource configuration filters.
159
160    Returns:
161      List of strings extracted from the raw list.
162    """
163    out_filters = []
164    for item in raw_list:
165        if "," in item:
166            item_list = item.split(",")
167            for entry in item_list:
168                stripped_entry = entry.strip()
169                if stripped_entry:
170                    out_filters.append(stripped_entry)
171        elif item:
172            out_filters.append(item)
173    return sorted(out_filters)
174
175def _package(
176        ctx,
177        out_r_src_jar = None,
178        out_r_txt = None,
179        out_symbols = None,
180        out_manifest = None,
181        out_proguard_cfg = None,
182        out_main_dex_proguard_cfg = None,
183        out_resource_files_zip = None,
184        out_file = None,
185        package_type = None,
186        java_package = None,
187        manifest = None,
188        assets = [],
189        assets_dir = None,
190        resource_files = [],
191        resource_configs = None,
192        densities = [],
193        application_id = None,
194        package_id = None,
195        direct_resources_nodes = [],
196        transitive_resources_nodes = [],
197        transitive_manifests = [],
198        transitive_assets = [],
199        transitive_compiled_assets = [],
200        transitive_resource_files = [],
201        transitive_compiled_resources = [],
202        transitive_r_txts = [],
203        additional_apks_to_link_against = [],
204        nocompress_extensions = [],
205        proto_format = False,
206        shrink_resource_cycles = False,
207        version_name = None,
208        version_code = None,
209        android_jar = None,
210        aapt = None,
211        busybox = None,
212        host_javabase = None,
213        should_throw_on_conflict = True,  # TODO: read this from allowlist at caller
214        debug = True):  # TODO: we will set this to false in prod builds
215    """Packages the compiled Android Resources with AAPT.
216
217    Args:
218      ctx: The context.
219      out_r_src_jar: A File. The R.java outputted by linking resources in a srcjar.
220      out_r_txt: A File. The resource IDs outputted by linking resources in text.
221      out_symbols: A File. The output zip containing compiled resources.
222      out_manifest: A File. The output processed manifest.
223      out_proguard_cfg: A File. The proguard config to be generated.
224      out_main_dex_proguard_cfg: A File. The main dex proguard config to be generated.
225      out_resource_files_zip: A File. The resource files zipped by linking resources.
226      out_file: A File. The Resource APK outputted by linking resources.
227      package_type: A string. The configuration type to use when packaging.
228      java_package: A string. The Java package for the generated R.java.
229      manifest: A File. The AndroidManifest.xml.
230      assets: sequence of Files. A list of Android assets files to be processed.
231      assets_dir: String. The name of the assets directory.
232      resource_files: A list of Files. The resource files.
233      resource_configs: A list of strings. The list of resource configuration
234        filters.
235      densities: A list of strings. The list of screen densities to filter for when
236        building the apk.
237      application_id: An optional string. The applicationId set in manifest values.
238      package_id: An optional integer in [2,255]. This is the prefix byte for
239        all generated resource IDs, defaults to 0x7F (127). 1 is reserved by the
240        framework, and some builds are known to crash when given IDs > 127.
241        Shared libraries are also assigned monotonically increasing IDs in
242        [2,126], so care should be taken that there is room at the lower end.
243      direct_resources_nodes: Depset of ResourcesNodeInfo providers. The set of
244        ResourcesNodeInfo from direct dependencies.
245      transitive_resources_nodes: Depset of ResourcesNodeInfo providers. The set
246        of ResourcesNodeInfo from transitive dependencies (not including directs).
247      transitive_manifests: List of Depsets. Depsets contain all transitive manifests.
248      transitive_assets: List of Depsets. Depsets contain all transitive assets.
249      transitive_compiled_assets: List of Depsets. Depsets contain all transitive
250        compiled_assets.
251      transitive_resource_files: List of Depsets. Depsets contain all transitive
252        resource files.
253      transitive_compiled_resources: List of Depsets. Depsets contain all transitive
254        compiled_resources.
255      transitive_r_txts: List of Depsets. Depsets contain all transitive R txt files.
256      additional_apks_to_link_against: A list of Files. Additional APKs to link
257        against. Optional.
258      nocompress_extensions: A list of strings. File extension to leave uncompressed
259        in the apk.
260      proto_format: Boolean, whether to generate the resource table in proto format.
261      shrink_resource_cycles: Boolean, flag that enables more shrinking of
262        code and resources by instructing AAPT2 to emit conditional Proguard keep rules.
263      version_name: A string. The version name to stamp the generated manifest with. Optional.
264      version_code: A string. The version code to stamp the generated manifest with. Optional.
265      android_jar: A File. The Android Jar.
266      aapt: A FilesToRunProvider. The AAPT executable.
267      busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable.
268      host_javabase: Target. The host javabase.
269      should_throw_on_conflict: A boolean. Determines whether an error should be thrown
270        when a resource conflict occurs.
271      debug: A boolean. Determines whether to enable debugging.
272    """
273    if not manifest:
274        fail("No manifest given, the manifest is mandatory.")
275
276    direct_data_flag = []
277    direct_compiled_resources = []
278
279    output_files = []
280    input_files = []
281    transitive_input_files = []
282
283    args = ctx.actions.args()
284    args.use_param_file("@%s")
285    args.add("--tool", "AAPT2_PACKAGE")
286    args.add("--")
287    args.add("--aapt2", aapt.executable)
288    args.add_joined(
289        "--data",
290        transitive_resources_nodes,
291        map_each = _make_package_resources_flags,
292        join_with = ",",
293    )
294    args.add_joined(
295        "--directData",
296        direct_resources_nodes,
297        map_each = _make_package_resources_flags,
298        join_with = ",",
299    )
300    args.add_joined(
301        "--directAssets",
302        direct_resources_nodes,
303        map_each = _make_package_assets_flags,
304        join_with = "&",
305        omit_if_empty = True,
306    )
307    args.add_joined(
308        "--assets",
309        transitive_resources_nodes,
310        map_each = _make_package_assets_flags,
311        join_with = "&",
312        omit_if_empty = True,
313    )
314    transitive_input_files.extend(transitive_resource_files)
315    transitive_input_files.extend(transitive_assets)
316    transitive_input_files.extend(transitive_compiled_assets)
317    transitive_input_files.extend(transitive_compiled_resources)
318    transitive_input_files.extend(transitive_manifests)
319    transitive_input_files.extend(transitive_r_txts)
320    args.add(
321        "--primaryData",
322        _make_resources_flag(
323            manifest = manifest,
324            assets = assets,
325            assets_dir = assets_dir,
326            resource_files = resource_files,
327        ),
328    )
329    input_files.append(manifest)
330    input_files.extend(resource_files)
331    input_files.extend(assets)
332    args.add("--androidJar", android_jar)
333    input_files.append(android_jar)
334    args.add("--rOutput", out_r_txt)
335    output_files.append(out_r_txt)
336    if out_symbols:
337        args.add("--symbolsOut", out_symbols)
338        output_files.append(out_symbols)
339    args.add("--srcJarOutput", out_r_src_jar)
340    output_files.append(out_r_src_jar)
341    if out_proguard_cfg:
342        args.add("--proguardOutput", out_proguard_cfg)
343        output_files.append(out_proguard_cfg)
344    if out_main_dex_proguard_cfg:
345        args.add("--mainDexProguardOutput", out_main_dex_proguard_cfg)
346        output_files.append(out_main_dex_proguard_cfg)
347    args.add("--manifestOutput", out_manifest)
348    output_files.append(out_manifest)
349    if out_resource_files_zip:
350        args.add("--resourcesOutput", out_resource_files_zip)
351        output_files.append(out_resource_files_zip)
352    if out_file:
353        args.add("--packagePath", out_file)
354        output_files.append(out_file)
355    args.add("--useAaptCruncher=no")  # Unnecessary, used for AAPT1 only but added here to minimize diffs.
356    if package_type:
357        args.add("--packageType", package_type)
358    if debug:
359        args.add("--debug")
360    if should_throw_on_conflict:
361        args.add("--throwOnResourceConflict")
362    if resource_configs:
363        args.add_joined("--resourceConfigs", _extract_filters(resource_configs), join_with = ",")
364    if densities:
365        args.add_joined("--densities", _extract_filters(densities), join_with = ",")
366    if application_id:
367        args.add("--applicationId", application_id)
368    if package_id:
369        args.add("--packageId", package_id)
370    if additional_apks_to_link_against:
371        args.add_joined(
372            "--additionalApksToLinkAgainst",
373            additional_apks_to_link_against,
374            join_with = ",",
375            map_each = _path,
376        )
377        input_files.extend(additional_apks_to_link_against)
378    if nocompress_extensions:
379        args.add_joined("--uncompressedExtensions", nocompress_extensions, join_with = ",")
380    if proto_format:
381        args.add("--resourceTableAsProto")
382    if shrink_resource_cycles:
383        args.add("--conditionalKeepRules=yes")
384    if version_name:
385        args.add("--versionName", version_name)
386    if version_code:
387        args.add("--versionCode", version_code)
388    if java_package:
389        args.add("--packageForR", java_package)
390
391    _java.run(
392        ctx = ctx,
393        host_javabase = host_javabase,
394        executable = busybox,
395        tools = [aapt],
396        arguments = [args],
397        inputs = depset(input_files, transitive = transitive_input_files),
398        outputs = output_files,
399        mnemonic = "PackageAndroidResources",
400        progress_message = "Packaging Android Resources in %s" % ctx.label,
401    )
402
403def _parse(
404        ctx,
405        out_symbols = None,
406        assets = [],
407        assets_dir = None,
408        busybox = None,
409        host_javabase = None):
410    """Parses Android assets.
411
412    Args:
413      ctx: The context.
414      out_symbols: A File. The output bin containing parsed assets.
415      assets: sequence of Files. A list of Android assets files to be processed.
416      assets_dir: String. The name of the assets directory.
417      busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable.
418      host_javabase: Target. The host javabase.
419    """
420    args = ctx.actions.args()
421    args.use_param_file("@%s")
422    args.add("--tool", "PARSE")
423    args.add("--")
424    args.add(
425        "--primaryData",
426        _make_resources_flag(
427            assets = assets,
428            assets_dir = assets_dir,
429        ),
430    )
431    args.add("--output", out_symbols)
432
433    _java.run(
434        ctx = ctx,
435        host_javabase = host_javabase,
436        executable = busybox,
437        arguments = [args],
438        inputs = assets,
439        outputs = [out_symbols],
440        mnemonic = "ParseAndroidResources",
441        progress_message = "Parsing Android Resources in %s" % out_symbols.short_path,
442    )
443
444def _make_merge_assets_flags(resources_node):
445    assets = resources_node.assets.to_list()
446    if not (assets or resources_node.assets_dir):
447        return None
448    return _make_serialized_resources_flag(
449        assets = assets,
450        assets_dir = resources_node.assets_dir,
451        label = str(resources_node.label),
452        symbols = resources_node.assets_symbols,
453    )
454
455def _merge_assets(
456        ctx,
457        out_assets_zip = None,
458        assets = [],
459        assets_dir = None,
460        symbols = None,
461        transitive_assets = [],
462        transitive_assets_symbols = [],
463        direct_resources_nodes = [],
464        transitive_resources_nodes = [],
465        busybox = None,
466        host_javabase = None):
467    """Merges Android assets.
468
469    Args:
470      ctx: The context.
471      out_assets_zip: A File.
472      assets: sequence of Files. A list of Android assets files to be processed.
473      assets_dir: String. The name of the assets directory.
474      symbols: A File. The parsed assets.
475      transitive_assets: Sequence of Depsets. The list of transitive
476        assets from deps.
477      transitive_assets_symbols: Sequence of Depsets. The list of
478        transitive assets_symbols files from deps.
479      direct_resources_nodes: Sequence of ResourcesNodeInfo providers. The list
480        of ResourcesNodeInfo providers that are direct depencies.
481      transitive_resources_nodes: Sequence of ResourcesNodeInfo providers. The
482        list of ResourcesNodeInfo providers that are transitive depencies.
483      busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable.
484      host_javabase: Target. The host javabase.
485    """
486    args = ctx.actions.args()
487    args.use_param_file("@%s")
488    args.add("--tool", "MERGE_ASSETS")
489    args.add("--")
490    args.add("--assetsOutput", out_assets_zip)
491    args.add(
492        "--primaryData",
493        _make_serialized_resources_flag(
494            assets = assets,
495            assets_dir = assets_dir,
496            label = str(ctx.label),
497            symbols = symbols,
498        ),
499    )
500    args.add_joined(
501        "--directData",
502        direct_resources_nodes,
503        map_each = _make_merge_assets_flags,
504        join_with = "&",
505    )
506    args.add_joined(
507        "--data",
508        transitive_resources_nodes,
509        map_each = _make_merge_assets_flags,
510        join_with = "&",
511    )
512
513    _java.run(
514        ctx = ctx,
515        host_javabase = host_javabase,
516        executable = busybox,
517        arguments = [args],
518        inputs = depset(
519            assets + [symbols],
520            transitive = transitive_assets + transitive_assets_symbols,
521        ),
522        outputs = [out_assets_zip],
523        mnemonic = "MergeAndroidAssets",
524        progress_message =
525            "Merging Android Assets in %s" % out_assets_zip.short_path,
526    )
527
528def _validate_and_link(
529        ctx,
530        out_r_src_jar = None,
531        out_r_txt = None,
532        out_file = None,
533        compiled_resources = None,
534        transitive_compiled_resources = depset(),
535        java_package = None,
536        manifest = None,
537        android_jar = None,
538        busybox = None,
539        host_javabase = None,
540        aapt = None):
541    """Links compiled Android Resources with AAPT.
542
543    Args:
544      ctx: The context.
545      out_r_src_jar: A File. The R.java outputted by linking resources in a srcjar.
546      out_r_txt: A File. The resource IDs outputted by linking resources in text.
547      out_file: A File. The Resource APK outputted by linking resources.
548      compiled_resources: A File. The symbols.zip of compiled resources for
549        this target.
550      transitive_compiled_resources: Depset of Files. The symbols.zip of the
551        compiled resources from the transitive dependencies of this target.
552      java_package: A string. The Java package for the generated R.java.
553      manifest: A File. The AndroidManifest.xml.
554      android_jar: A File. The Android Jar.
555      busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable.
556      host_javabase: Target. The host javabase.
557      aapt: A FilesToRunProvider. The AAPT executable.
558    """
559    output_files = []
560    input_files = [android_jar]
561    transitive_input_files = []
562
563    # Retrieves the list of files at runtime when a directory is passed.
564    args = ctx.actions.args()
565    args.use_param_file("@%s")
566    args.add("--tool", "LINK_STATIC_LIBRARY")
567    args.add("--")
568    args.add("--aapt2", aapt.executable)
569    args.add("--libraries", android_jar)
570    if compiled_resources:
571        args.add("--compiled", compiled_resources)
572        input_files.append(compiled_resources)
573    args.add_joined(
574        "--compiledDep",
575        transitive_compiled_resources,
576        join_with = ":",
577    )
578    transitive_input_files.append(transitive_compiled_resources)
579    args.add("--manifest", manifest)
580    input_files.append(manifest)
581    if java_package:
582        args.add("--packageForR", java_package)
583    args.add("--sourceJarOut", out_r_src_jar)
584    output_files.append(out_r_src_jar)
585    args.add("--rTxtOut", out_r_txt)
586    output_files.append(out_r_txt)
587    args.add("--staticLibraryOut", out_file)
588    output_files.append(out_file)
589
590    _java.run(
591        ctx = ctx,
592        host_javabase = host_javabase,
593        executable = busybox,
594        tools = [aapt],
595        arguments = [args],
596        inputs = depset(input_files, transitive = transitive_input_files),
597        outputs = output_files,
598        mnemonic = "LinkAndroidResources",
599        progress_message =
600            "Linking Android Resources in " + out_file.short_path,
601    )
602
603def _compile(
604        ctx,
605        out_file = None,
606        assets = [],
607        assets_dir = None,
608        resource_files = [],
609        busybox = None,
610        aapt = None,
611        host_javabase = None):
612    """Compile and store resources in a single archive.
613
614    Args:
615      ctx: The context.
616      out_file: File. The output zip containing compiled resources.
617      resource_files: A list of Files. The list of resource files or directories
618      assets: A list of Files. The list of assets files or directories
619        to process.
620      assets_dir: String. The name of the assets directory.
621      busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable.
622      aapt: AAPT. Tool for compiling resources.
623      host_javabase: Target. The host javabase.
624    """
625    if not out_file:
626        fail("No output directory specified.")
627
628    # Retrieves the list of files at runtime when a directory is passed.
629    args = ctx.actions.args()
630    args.use_param_file("@%s")
631    args.add("--tool", "COMPILE_LIBRARY_RESOURCES")
632    args.add("--")
633    args.add("--aapt2", aapt.executable)
634    args.add(
635        "--resources",
636        _make_resources_flag(
637            resource_files = resource_files,
638            assets = assets,
639            assets_dir = assets_dir,
640        ),
641    )
642    args.add("--output", out_file)
643
644    _java.run(
645        ctx = ctx,
646        host_javabase = host_javabase,
647        executable = busybox,
648        tools = [aapt],
649        arguments = [args],
650        inputs = resource_files + assets,
651        outputs = [out_file],
652        mnemonic = "CompileAndroidResources",
653        progress_message = "Compiling Android Resources in %s" % out_file.short_path,
654    )
655
656def _make_merge_compiled_flags(resources_node_info):
657    if not resources_node_info.compiled_resources:
658        return None
659    return _make_serialized_resources_flag(
660        label = str(resources_node_info.label),
661        symbols = resources_node_info.compiled_resources,
662    )
663
664def _merge_compiled(
665        ctx,
666        out_class_jar = None,
667        out_manifest = None,
668        out_aapt2_r_txt = None,
669        java_package = None,
670        manifest = None,
671        compiled_resources = None,
672        direct_resources_nodes = [],
673        transitive_resources_nodes = [],
674        direct_compiled_resources = depset(),
675        transitive_compiled_resources = depset(),
676        android_jar = None,
677        busybox = None,
678        host_javabase = None):
679    """Merges the compile resources.
680
681    Args:
682      ctx: The context.
683      out_class_jar: A File. The compiled R.java outputted by linking resources.
684      out_manifest: A File. The list of resource files or directories
685      out_aapt2_r_txt: A File. The resource IDs outputted by linking resources in text.
686      java_package: A string. The Java package for the generated R.java.
687      manifest: A File. The AndroidManifest.xml.
688      compiled_resources: A File. The symbols.zip of compiled resources for this target.
689      direct_resources_nodes: Sequence of ResourcesNodeInfo providers. The list
690        of ResourcesNodeInfo providers that are direct depencies.
691      transitive_resources_nodes: Sequence of ResourcesNodeInfo providers. The
692        list of ResourcesNodeInfo providers that are transitive depencies.
693      direct_compiled_resources: Depset of Files. A depset of symbols.zip of
694        compiled resources from direct dependencies.
695      transitive_compiled_resources: Depset of Files. A depset of symbols.zip of
696        compiled resources from transitive dependencies.
697      android_jar: A File. The Android Jar.
698      busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable.
699      host_javabase: Target. The host javabase.
700    """
701    output_files = []
702    input_files = [android_jar]
703    transitive_input_files = []
704
705    args = ctx.actions.args()
706    args.use_param_file("@%s")
707    args.add("--tool", "MERGE_COMPILED")
708    args.add("--")
709    args.add("--classJarOutput", out_class_jar)
710    output_files.append(out_class_jar)
711    args.add("--targetLabel", ctx.label)
712    args.add("--manifestOutput", out_manifest)
713    output_files.append(out_manifest)
714    args.add("--rTxtOut", out_aapt2_r_txt)
715    output_files.append(out_aapt2_r_txt)
716    args.add("--androidJar", android_jar)
717    args.add("--primaryManifest", manifest)
718    input_files.append(manifest)
719    if java_package:
720        args.add("--packageForR", java_package)
721    args.add(
722        "--primaryData",
723        _make_serialized_resources_flag(
724            label = str(ctx.label),
725            symbols = compiled_resources,
726        ),
727    )
728    input_files.append(compiled_resources)
729    args.add_joined(
730        "--directData",
731        direct_resources_nodes,
732        map_each = _make_merge_compiled_flags,
733        join_with = "&",
734    )
735    transitive_input_files.append(direct_compiled_resources)
736    if _ANDROID_RESOURCES_STRICT_DEPS in ctx.disabled_features:
737        args.add_joined(
738            "--data",
739            transitive_resources_nodes,
740            map_each = _make_merge_compiled_flags,
741            join_with = "&",
742        )
743        transitive_input_files.append(transitive_compiled_resources)
744
745    _java.run(
746        ctx = ctx,
747        host_javabase = host_javabase,
748        executable = busybox,
749        arguments = [args],
750        inputs = depset(input_files, transitive = transitive_input_files),
751        outputs = output_files,
752        mnemonic = "StarlarkMergeCompiledAndroidResources",
753        progress_message =
754            "Merging compiled Android Resources in " + out_class_jar.short_path,
755    )
756
757def _escape_mv(s):
758    """Escapes `:` and `,` in manifest values so they can be used as a busybox flag."""
759    return s.replace(":", "\\:").replace(",", "\\,")
760
761def _owner_label(file):
762    return "//" + file.owner.package + ":" + file.owner.name
763
764# We need to remove the "/_migrated/" path segment from file paths in order for sorting to
765# match the order of the native manifest merging action.
766def _manifest_short_path(manifest):
767    return manifest.short_path.replace("/_migrated/", "/")
768
769def _mergee_manifests_flag(manifests):
770    ordered_manifests = sorted(manifests.to_list(), key = _manifest_short_path)
771    entries = []
772    for manifest in ordered_manifests:
773        label = _owner_label(manifest).replace(":", "\\:")
774        entries.append((manifest.path + ":" + label).replace(",", "\\,"))
775    flag_entry = ",".join(entries)
776    if not flag_entry:
777        return None
778    return flag_entry
779
780def _merge_manifests(
781        ctx,
782        out_file = None,
783        out_log_file = None,
784        merge_type = "APPLICATION",
785        manifest = None,
786        mergee_manifests = depset(),
787        manifest_values = None,
788        java_package = None,
789        busybox = None,
790        host_javabase = None):
791    """Merge multiple AndroidManifest.xml files into a single one.
792
793    Args:
794      ctx: The context.
795      out_file: A File. The output merged manifest.
796      out_log_file: A File. The output log from the merge tool.
797      merge_type: A string, either APPLICATION or LIBRARY. Type of merging.
798      manifest: A File. The primary AndroidManifest.xml.
799      mergee_manifests: A depset of Files. All transitive manifests to be merged.
800      manifest_values: A dictionary. Manifest values to substitute.
801      java_package: A string. Custom java package to insert in manifest package attribute.
802      busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable.
803      host_javabase: Target. The host javabase.
804    """
805    if merge_type not in ["APPLICATION", "LIBRARY"]:
806        fail("Unexpected manifest merge type: " + merge_type)
807
808    outputs = [out_file]
809    directs = [manifest] if manifest else []
810    transitives = [mergee_manifests]
811
812    # Args for busybox
813    args = ctx.actions.args()
814    args.use_param_file("@%s", use_always = True)
815    args.add("--tool", "MERGE_MANIFEST")
816    args.add("--")
817    if manifest:
818        args.add("--manifest", manifest)
819    args.add_all(
820        "--mergeeManifests",
821        [mergee_manifests],
822        map_each = _mergee_manifests_flag,
823    )
824    if manifest_values:
825        args.add(
826            "--manifestValues",
827            ",".join(["%s:%s" % (_escape_mv(k), _escape_mv(v)) for k, v in manifest_values.items()]),
828        )
829    args.add("--mergeType", merge_type)
830    if java_package:
831        args.add("--customPackage", java_package)
832    args.add("--manifestOutput", out_file)
833    if out_log_file:
834        args.add("--log", out_log_file)
835        outputs.append(out_log_file)
836
837    _java.run(
838        ctx = ctx,
839        host_javabase = host_javabase,
840        executable = busybox,
841        arguments = [args],
842        inputs = depset(directs, transitive = transitives),
843        outputs = outputs,
844        mnemonic = "MergeManifests",
845        progress_message = "Merging Android Manifests in %s" % out_file.short_path,
846    )
847
848def _process_databinding(
849        ctx,
850        out_databinding_info = None,
851        out_databinding_processed_resources = None,
852        databinding_resources_dirname = None,
853        resource_files = None,
854        java_package = None,
855        busybox = None,
856        host_javabase = None):
857    """Processes databinding for android_binary.
858
859    Processes databinding declarations over resources, populates the databinding layout
860    info file, and generates new resources with databinding expressions stripped out.
861
862    Args:
863      ctx: The context.
864      out_databinding_info: File. The output databinding layout info zip file.
865      out_databinding_processed_resources: List of Files. The generated databinding
866        processed resource files.
867      databinding_resources_dirname: String. The execution path to the directory where
868      the out_databinding_processed_resources are generated.
869      resource_files: List of Files. The resource files to be processed.
870      java_package: String. Java package for which java sources will be
871        generated. By default the package is inferred from the directory where
872        the BUILD file containing the rule is.
873      busybox: FilesToRunProvider. The ResourceBusyBox executable or
874        FilesToRunprovider
875      host_javabase: A Target. The host javabase.
876    """
877    res_dirs = _get_unique_res_dirs(resource_files)
878
879    args = ctx.actions.args()
880    args.add("--tool", "PROCESS_DATABINDING")
881    args.add("--")
882    args.add("--output_resource_directory", databinding_resources_dirname)
883    args.add_all(res_dirs, before_each = "--resource_root")
884    args.add("--dataBindingInfoOut", out_databinding_info)
885    args.add("--appId", java_package)
886
887    _java.run(
888        ctx = ctx,
889        host_javabase = host_javabase,
890        executable = busybox,
891        arguments = [args],
892        inputs = resource_files,
893        outputs = [out_databinding_info] + out_databinding_processed_resources,
894        mnemonic = "StarlarkProcessDatabinding",
895        progress_message = "Processing data binding",
896    )
897
898def _make_generate_binay_r_flags(resources_node):
899    if not (resources_node.r_txt or resources_node.manifest):
900        return None
901    return ",".join(
902        [
903            resources_node.r_txt.path if resources_node.r_txt else "",
904            resources_node.manifest.path if resources_node.manifest else "",
905        ],
906    )
907
908def _generate_binary_r(
909        ctx,
910        out_class_jar = None,
911        r_txt = None,
912        manifest = None,
913        package_for_r = None,
914        final_fields = None,
915        resources_nodes = depset(),
916        transitive_r_txts = [],
917        transitive_manifests = [],
918        busybox = None,
919        host_javabase = None):
920    """Generate compiled resources class jar.
921
922    Args:
923      ctx: The context.
924      out_class_jar: File. The output class jar file.
925      r_txt: File. The resource IDs outputted by linking resources in text.
926      manifest: File. The primary AndroidManifest.xml.
927      package_for_r: String. The Java package for the generated R class files.
928      final_fields: Bool. Whether fields get declared as final.
929      busybox: FilesToRunProvider. The ResourceBusyBox executable or
930        FilesToRunprovider
931      host_javabase: A Target. The host javabase.
932    """
933    args = ctx.actions.args()
934    args.add("--tool", "GENERATE_BINARY_R")
935    args.add("--")
936    args.add("--primaryRTxt", r_txt)
937    args.add("--primaryManifest", manifest)
938    if package_for_r:
939        args.add("--packageForR", package_for_r)
940    args.add_all(
941        resources_nodes,
942        map_each = _make_generate_binay_r_flags,
943        before_each = "--library",
944    )
945    if final_fields:
946        args.add("--finalFields")
947    else:
948        args.add("--nofinalFields")
949
950    # TODO(b/154003916): support transitive "--library transitive_r_txt_path,transitive_manifest_path" flags
951    args.add("--classJarOutput", out_class_jar)
952    args.add("--targetLabel", str(ctx.label))
953    args.use_param_file("@%s")
954
955    _java.run(
956        ctx = ctx,
957        host_javabase = host_javabase,
958        executable = busybox,
959        arguments = [args],
960        inputs = depset([r_txt, manifest], transitive = transitive_r_txts + transitive_manifests),
961        outputs = [out_class_jar],
962        mnemonic = "StarlarkRClassGenerator",
963        progress_message = "Generating R classes",
964    )
965
966def _make_aar(
967        ctx,
968        out_aar = None,
969        assets = [],
970        assets_dir = None,
971        resource_files = [],
972        class_jar = None,
973        r_txt = None,
974        manifest = None,
975        proguard_specs = [],
976        should_throw_on_conflict = False,
977        busybox = None,
978        host_javabase = None):
979    """Generate an android archive file.
980
981    Args:
982      ctx: The context.
983      out_aar: File. The output AAR file.
984      assets: sequence of Files. A list of Android assets files to be processed.
985      assets_dir: String. The name of the assets directory.
986      resource_files: A list of Files. The resource files.
987      class_jar: File. The class jar file.
988      r_txt: File. The resource IDs outputted by linking resources in text.
989      manifest: File. The primary AndroidManifest.xml.
990      proguard_specs: List of File. The proguard spec files.
991      busybox: FilesToRunProvider. The ResourceBusyBox executable or
992        FilesToRunprovider
993      host_javabase: A Target. The host javabase.
994      should_throw_on_conflict: A boolean. Determines whether an error should be thrown
995        when a resource conflict occurs.
996    """
997    args = ctx.actions.args()
998    args.add("--tool", "GENERATE_AAR")
999    args.add("--")
1000    args.add(
1001        "--mainData",
1002        _make_resources_flag(
1003            manifest = manifest,
1004            assets = assets,
1005            assets_dir = assets_dir,
1006            resource_files = resource_files,
1007        ),
1008    )
1009    args.add("--manifest", manifest)
1010    args.add("--rtxt", r_txt)
1011    args.add("--classes", class_jar)
1012    args.add("--aarOutput", out_aar)
1013    args.add_all(proguard_specs, before_each = "--proguardSpec")
1014    if should_throw_on_conflict:
1015        args.add("--throwOnResourceConflict")
1016
1017    _java.run(
1018        ctx = ctx,
1019        host_javabase = host_javabase,
1020        executable = busybox,
1021        arguments = [args],
1022        inputs = (
1023            resource_files +
1024            assets +
1025            proguard_specs +
1026            [r_txt, manifest, class_jar]
1027        ),
1028        outputs = [out_aar],
1029        mnemonic = "StarlarkAARGenerator",
1030        progress_message = "Generating AAR package for %s" % ctx.label,
1031    )
1032
1033busybox = struct(
1034    compile = _compile,
1035    merge_compiled = _merge_compiled,
1036    validate_and_link = _validate_and_link,
1037    merge_manifests = _merge_manifests,
1038    package = _package,
1039    parse = _parse,
1040    merge_assets = _merge_assets,
1041    make_resources_flag = _make_resources_flag,
1042    process_databinding = _process_databinding,
1043    generate_binary_r = _generate_binary_r,
1044    make_aar = _make_aar,
1045
1046    # Exposed for testing
1047    mergee_manifests_flag = _mergee_manifests_flag,
1048    get_unique_res_dirs = _get_unique_res_dirs,
1049    sanitize_assets_dir = _sanitize_assets_dir,
1050    extract_filters = _extract_filters,
1051)
1052