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