xref: /aosp_15_r20/external/bazelbuild-rules_android/rules/dex.bzl (revision 9e965d6fece27a77de5377433c2f7e6999b8cc0b)
1# Copyright 2023 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 Dex Commands."""
16
17load("//rules:attrs.bzl", _attrs = "attrs")
18load("//rules:common.bzl", _common = "common")
19load("//rules:java.bzl", _java = "java")
20load("@bazel_skylib//lib:collections.bzl", "collections")
21load(":providers.bzl", "StarlarkAndroidDexInfo")
22load(":utils.bzl", "ANDROID_TOOLCHAIN_TYPE", "get_android_toolchain", "utils")
23
24_DEX_MEMORY = 4096
25_DEX_THREADS = 5
26
27_tristate = _attrs.tristate
28
29def _resource_set_for_monolithic_dexing():
30    return {"cpu": _DEX_THREADS, "memory": _DEX_MEMORY}
31
32def _process_incremental_dexing(
33        ctx,
34        output,
35        deps = [],
36        runtime_jars = [],
37        dexopts = [],
38        main_dex_list = None,
39        min_sdk_version = 0,
40        proguarded_jar = None,
41        java_info = None,
42        desugar_dict = {},
43        shuffle_jars = None,
44        dexbuilder = None,
45        dexbuilder_after_proguard = None,
46        dexmerger = None,
47        dexsharder = None,
48        toolchain_type = None):
49    info = _merge_infos(utils.collect_providers(StarlarkAndroidDexInfo, deps))
50    incremental_dexopts = _filter_dexopts(dexopts, ctx.fragments.android.get_dexopts_supported_in_incremental_dexing)
51    inclusion_filter_jar = proguarded_jar
52    if not proguarded_jar:
53        dex_archives = []
54        for jar in runtime_jars:
55            dex_archive = _get_dx_artifact(ctx, jar.basename + ".dex.zip")
56            _dex(
57                ctx,
58                input = desugar_dict[jar] if jar in desugar_dict else jar,
59                output = dex_archive,
60                incremental_dexopts = incremental_dexopts,
61                min_sdk_version = min_sdk_version,
62                dex_exec = dexbuilder,
63                toolchain_type = toolchain_type,
64            )
65            dex_archives.append(dex_archive)
66        dex_archives += _to_dexed_classpath(
67            dex_archives_dict = {d.jar: d.dex for d in info.dex_archives_dict.get("".join(incremental_dexopts), depset()).to_list()},
68            classpath = _filter(java_info.transitive_runtime_jars.to_list(), excludes = _get_library_r_jars(deps)),
69            runtime_jars = runtime_jars,
70        )
71    else:
72        java_resource_jar = ctx.actions.declare_file(ctx.label.name + "_files/java_resources.jar")
73        if ctx.fragments.android.incremental_dexing_shards_after_proguard > 1:
74            dex_archives = _shard_proguarded_jar_and_dex(
75                ctx,
76                java_resource_jar = java_resource_jar,
77                num_shards = ctx.fragments.android.incremental_dexing_shards_after_proguard,
78                dexopts = incremental_dexopts,
79                proguarded_jar = proguarded_jar,
80                main_dex_list = main_dex_list,
81                min_sdk_version = min_sdk_version,
82                shuffle_jars = shuffle_jars,
83                dexbuilder_after_proguard = dexbuilder_after_proguard,
84                toolchain_type = toolchain_type,
85            )
86            inclusion_filter_jar = None
87        else:
88            # No need to shuffle if there is only one shard
89            dex_archive = _get_dx_artifact(ctx, "classes.jar")
90            _dex(
91                ctx,
92                input = proguarded_jar,
93                output = dex_archive,
94                incremental_dexopts = incremental_dexopts,
95                min_sdk_version = min_sdk_version,
96                dex_exec = dexbuilder_after_proguard,
97                toolchain_type = toolchain_type,
98            )
99            dex_archives = [dex_archive]
100
101    if len(dex_archives) == 1:
102        _dex_merge(
103            ctx,
104            output = output,
105            inputs = dex_archives,
106            multidex_strategy = "minimal",
107            main_dex_list = main_dex_list,
108            dexopts = _filter_dexopts(dexopts, ctx.fragments.android.get_dexopts_supported_in_dex_merger),
109            dexmerger = dexmerger,
110            toolchain_type = toolchain_type,
111        )
112    else:
113        shards = ctx.actions.declare_directory("dexsplits/" + ctx.label.name)
114        dexes = ctx.actions.declare_directory("dexfiles/" + ctx.label.name)
115        _shard_dexes(
116            ctx,
117            output = shards,
118            inputs = dex_archives,
119            dexopts = _filter_dexopts(dexopts, ctx.fragments.android.get_dexopts_supported_in_dex_sharder),
120            main_dex_list = main_dex_list,
121            inclusion_filter_jar = inclusion_filter_jar,
122            dexsharder = dexsharder,
123            toolchain_type = toolchain_type,
124        )
125
126        # TODO(b/130571505): Implement this after SpawnActionTemplate is supported in Starlark
127        android_common.create_dex_merger_actions(
128            ctx,
129            output = dexes,
130            input = shards,
131            dexopts = dexopts,
132            dexmerger = dexmerger,
133        )
134        _java.singlejar(
135            ctx,
136            output = output,
137            inputs = [dexes],
138            mnemonic = "MergeDexZips",
139            progress_message = "Merging dex shards for %s." % ctx.label,
140            java_toolchain = _common.get_java_toolchain(ctx),
141        )
142
143def _process_optimized_dexing(
144        ctx,
145        output,
146        input = None,
147        proguard_output_map = None,
148        postprocessing_output_map = None,
149        dexopts = [],
150        native_multidex = True,
151        min_sdk_version = 0,
152        main_dex_list = None,
153        library_jar = None,
154        startup_profile = None,
155        optimizing_dexer = None,
156        toolchain_type = None):
157    inputs = [input]
158    outputs = [output]
159
160    args = ctx.actions.args()
161    args.add(input)
162    args.add("--release")
163    args.add("--no-desugaring")
164    args.add("--output", output)
165    args.add_all(dexopts)
166
167    if proguard_output_map:
168        args.add("--pg-map", proguard_output_map)
169        args.add("--pg-map-output", postprocessing_output_map)
170        inputs.append(proguard_output_map)
171        outputs.append(postprocessing_output_map)
172
173    if startup_profile and native_multidex:
174        args.add("--startup-profile", startup_profile)
175        inputs.append(startup_profile)
176
177    # TODO(b/261110876): Pass min SDK through here based on the value in the merged manifest. The
178    # current value is statically defined for the entire depot.
179    # We currently set the minimum SDK version to 21 if you are doing native multidex as that is
180    # required for native multidex to work in the first place and as a result is required for
181    # correct behavior from the dexer.
182    sdk = max(min_sdk_version, 21) if native_multidex else min_sdk_version
183    if sdk != 0:
184        args.add("--min-api", sdk)
185    if main_dex_list:
186        args.add("--main-dex-list", main_dex_list)
187        inputs.append(main_dex_list)
188    if library_jar:
189        args.add("--lib", library_jar)
190        inputs.append(library_jar)
191
192    ctx.actions.run(
193        outputs = outputs,
194        executable = optimizing_dexer,
195        inputs = inputs,
196        arguments = [args],
197        mnemonic = "OptimizingDex",
198        progress_message = "Optimized dexing for " + str(ctx.label),
199        use_default_shell_env = True,
200        toolchain = toolchain_type,
201    )
202
203def _process_monolithic_dexing(
204        ctx,
205        output,
206        input,
207        dexopts = [],
208        min_sdk_version = 0,
209        main_dex_list = None,
210        dexbuilder = None,
211        toolchain_type = None):
212    # Create an artifact for the intermediate zip output generated by AndroidDexer that includes
213    # non-.dex files. A subsequent TrimDexZip action will filter out all non-.dex files.
214    classes_dex_intermediate = _get_dx_artifact(ctx, "intermediate_classes.dex.zip")
215    inputs = [input]
216
217    args = ctx.actions.args()
218    args.add("--dex")
219    args.add_all(dexopts)
220    if min_sdk_version > 0:
221        args.add("--min_sdk_version", min_sdk_version)
222    args.add("--multi-dex")
223    if main_dex_list:
224        args.add(main_dex_list, format = "--main-dex-list=%s")
225        inputs.append(main_dex_list)
226    args.add(classes_dex_intermediate, format = "--output=%s")
227    args.add(input)
228
229    ctx.actions.run(
230        executable = dexbuilder,
231        inputs = inputs,
232        outputs = [classes_dex_intermediate],
233        arguments = [args],
234        progress_message = "Converting %s to dex format" % input.short_path,
235        mnemonic = "AndroidDexer",
236        use_default_shell_env = True,
237        resource_set = _resource_set_for_monolithic_dexing,
238        toolchain = toolchain_type,
239    )
240
241    # Because the dexer also places resources into this zip, we also need to create a cleanup
242    # action that removes all non-.dex files before staging for apk building.
243    _java.singlejar(
244        ctx,
245        inputs = [classes_dex_intermediate],
246        output = output,
247        include_prefixes = ["classes"],
248        java_toolchain = _common.get_java_toolchain(ctx),
249        mnemonic = "TrimDexZip",
250        progress_message = "Trimming %s." % classes_dex_intermediate.short_path,
251    )
252
253def _shard_proguarded_jar_and_dex(
254        ctx,
255        java_resource_jar,
256        num_shards = 50,
257        dexopts = [],
258        proguarded_jar = None,
259        main_dex_list = None,
260        min_sdk_version = 0,
261        shuffle_jars = None,
262        dexbuilder_after_proguard = None,
263        toolchain_type = None):
264    if num_shards <= 1:
265        fail("num_shards expects to be larger than 1.")
266
267    shards = _make_shard_artifacts(ctx, num_shards, ".jar.dex.zip")
268    shuffle_outputs = _make_shard_artifacts(ctx, num_shards, ".jar")
269    inputs = []
270    args = ctx.actions.args()
271    args.add_all(shuffle_outputs, before_each = "--output_jar")
272    args.add("--output_resources", java_resource_jar)
273
274    if main_dex_list:
275        args.add("--main_dex_filter", main_dex_list)
276        inputs.append(main_dex_list)
277
278    # If we need to run Proguard, all the class files will be in the Proguarded jar, which has to
279    # be converted to dex.
280    args.add("--input_jar", proguarded_jar)
281    inputs.append(proguarded_jar)
282
283    ctx.actions.run(
284        executable = shuffle_jars,
285        outputs = shuffle_outputs + [java_resource_jar],
286        inputs = inputs,
287        arguments = [args],
288        mnemonic = "ShardClassesToDex",
289        progress_message = "Sharding classes for dexing for " + str(ctx.label),
290        use_default_shell_env = True,
291        toolchain = toolchain_type,
292    )
293
294    for i in range(len(shards)):
295        _dex(
296            ctx,
297            input = shuffle_outputs[i],
298            output = shards[i],
299            incremental_dexopts = dexopts,
300            min_sdk_version = min_sdk_version,
301            dex_exec = dexbuilder_after_proguard,
302            toolchain_type = toolchain_type,
303        )
304    return shards
305
306def _make_shard_artifacts(ctx, n, suffix):
307    return [_get_dx_artifact(ctx, "shard" + str(i) + suffix) for i in range(1, n + 1)]
308
309def _shard_dexes(
310        ctx,
311        output,
312        inputs = [],
313        dexopts = [],
314        main_dex_list = None,
315        inclusion_filter_jar = None,
316        dexsharder = None,
317        toolchain_type = None):
318    args = ctx.actions.args().use_param_file(param_file_arg = "@%s")
319    args.add_all(inputs, before_each = "--input")
320    args.add("--output", output.path)
321    if main_dex_list:
322        inputs.append(main_dex_list)
323        args.add("--main-dex-list", main_dex_list)
324    if inclusion_filter_jar:
325        inputs.append(inclusion_filter_jar)
326        args.add("--inclusion_filter_jar", inclusion_filter_jar)
327
328    args.add_all(dexopts)
329
330    ctx.actions.run(
331        executable = dexsharder,
332        outputs = [output],
333        inputs = inputs,
334        arguments = [args],
335        mnemonic = "ShardForMultidex",
336        progress_message = "Assembling dex files for " + ctx.label.name,
337        use_default_shell_env = True,
338        toolchain = toolchain_type,
339    )
340
341    return output
342
343def _append_java8_legacy_dex(
344        ctx,
345        output = None,
346        input = None,
347        java8_legacy_dex = None,
348        dex_zips_merger = None):
349    args = ctx.actions.args()
350
351    # Order matters here: we want java8_legacy_dex to be the highest-numbered classesN.dex
352    args.add("--input_zip", input)
353    args.add("--input_zip", java8_legacy_dex)
354    args.add("--output_zip", output)
355
356    ctx.actions.run(
357        executable = dex_zips_merger,
358        inputs = [input, java8_legacy_dex],
359        outputs = [output],
360        arguments = [args],
361        mnemonic = "AppendJava8LegacyDex",
362        use_default_shell_env = True,
363        progress_message = "Adding Java8 legacy library for %s" % ctx.label,
364        toolchain = ANDROID_TOOLCHAIN_TYPE,
365    )
366
367def _to_dexed_classpath(dex_archives_dict = {}, classpath = [], runtime_jars = []):
368    dexed_classpath = []
369    for jar in classpath:
370        if jar not in dex_archives_dict:
371            if jar not in runtime_jars:
372                fail("Dependencies on .jar artifacts are not allowed in Android binaries, please use " +
373                     "a java_import to depend on " + jar.short_path +
374                     ". If this is an implicit dependency then the rule that " +
375                     "introduces it will need to be fixed to account for it correctly.")
376        else:
377            dexed_classpath.append(dex_archives_dict[jar])
378    return dexed_classpath
379
380def _dex(
381        ctx,
382        input,
383        output = None,
384        incremental_dexopts = [],
385        min_sdk_version = 0,
386        dex_exec = None,
387        toolchain_type = None):
388    """Dexes a JAR.
389
390    Args:
391        ctx: The context.
392        input: File. The jar to be dexed.
393        output: File. The archive file containing all of the dexes.
394        incremental_dexopts: List of strings. Additional command-line flags for the dexing tool when building dexes.
395        min_sdk_version: Integer. The minimum targeted sdk version.
396        dex_exec: File. The executable dex builder file.
397    """
398    args = ctx.actions.args()
399    args.use_param_file("@%s", use_always = True)  # Required for workers.
400    args.set_param_file_format("multiline")
401
402    args.add("--input_jar", input)
403    args.add("--output_zip", output)
404    args.add_all(incremental_dexopts)
405
406    if min_sdk_version > 0:
407        args.add("--min_sdk_version", min_sdk_version)
408
409    execution_requirements = {}
410    if ctx.fragments.android.persistent_android_dex_desugar:
411        execution_requirements["supports-workers"] = "1"
412        if ctx.fragments.android.persistent_multiplex_android_dex_desugar:
413            execution_requirements["supports-multiplex-workers"] = "1"
414
415    ctx.actions.run(
416        executable = dex_exec,
417        arguments = [args],
418        inputs = [input],
419        outputs = [output],
420        mnemonic = "DexBuilder",
421        progress_message = "Dexing " + input.path + " with applicable dexopts " + str(incremental_dexopts),
422        execution_requirements = execution_requirements,
423        toolchain = toolchain_type,
424    )
425
426def _get_dx_artifact(ctx, basename):
427    return ctx.actions.declare_file("_dx_migrated/" + ctx.label.name + "/" + basename)
428
429def _get_effective_incremental_dexing(
430        force_incremental_dexing = _tristate.auto,
431        has_forbidden_dexopts = False,
432        incremental_dexing_after_proguard_by_default = True,
433        incremental_dexing_shards_after_proguard = True,
434        is_binary_optimized = False,
435        use_incremental_dexing = True):
436    if (is_binary_optimized and
437        force_incremental_dexing == _tristate.yes and incremental_dexing_shards_after_proguard <= 0):
438        fail("Target cannot be incrementally dexed because it uses Proguard")
439
440    if force_incremental_dexing == _tristate.yes:
441        return True
442
443    if force_incremental_dexing == _tristate.no:
444        return False
445
446    # If there are incompatible dexopts and the incremental_dexing attr is not set, we silently don't run
447    # incremental dexing.
448    if has_forbidden_dexopts or (is_binary_optimized and not incremental_dexing_after_proguard_by_default):
449        return False
450
451    # use_incremental_dexing config flag will take effect if incremental_dexing attr is not set
452    return use_incremental_dexing
453
454def _get_java8_legacy_dex_and_map(ctx, build_customized_files = False, binary_jar = None, android_jar = None):
455    if not build_customized_files:
456        return utils.only(get_android_toolchain(ctx).java8_legacy_dex.files.to_list()), None
457    else:
458        java8_legacy_dex_rules = _get_dx_artifact(ctx, "_java8_legacy.dex.pgcfg")
459        java8_legacy_dex_map = _get_dx_artifact(ctx, "_java8_legacy.dex.map")
460        java8_legacy_dex = _get_dx_artifact(ctx, "_java8_legacy.dex.zip")
461
462        args = ctx.actions.args()
463        args.add("--rules", java8_legacy_dex_rules)
464        args.add("--binary", binary_jar)
465        args.add("--android_jar", android_jar)
466        args.add("--output", java8_legacy_dex)
467        args.add("--output_map", java8_legacy_dex_map)
468
469        ctx.actions.run(
470            executable = get_android_toolchain(ctx).build_java8_legacy_dex.files_to_run,
471            inputs = [binary_jar, android_jar],
472            outputs = [java8_legacy_dex_rules, java8_legacy_dex_map, java8_legacy_dex],
473            arguments = [args],
474            mnemonic = "BuildLegacyDex",
475            progress_message = "Building Java8 legacy library for %s" % ctx.label,
476            toolchain = ANDROID_TOOLCHAIN_TYPE,
477        )
478
479        return java8_legacy_dex, java8_legacy_dex_map
480
481def _get_library_r_jars(deps):
482    transitive_resource_jars = []
483    for dep in utils.collect_providers(AndroidLibraryResourceClassJarProvider, deps):
484        transitive_resource_jars += dep.jars.to_list()
485    return transitive_resource_jars
486
487def _dex_merge(
488        ctx,
489        output = None,
490        inputs = [],
491        multidex_strategy = "minimal",
492        main_dex_list = None,
493        dexopts = [],
494        dexmerger = None,
495        toolchain_type = None):
496    args = ctx.actions.args()
497    args.add("--multidex", multidex_strategy)
498    args.add_all(inputs, before_each = "--input")
499    args.add("--output", output)
500    args.add_all(dexopts)
501
502    if main_dex_list:
503        inputs.append(main_dex_list)
504        args.add("--main-dex-list", main_dex_list)
505
506    ctx.actions.run(
507        executable = dexmerger,
508        arguments = [args],
509        inputs = inputs,
510        outputs = [output],
511        mnemonic = "DexMerger",
512        progress_message = "Assembling dex files into " + output.short_path,
513        toolchain = toolchain_type,
514    )
515
516def _merge_infos(infos):
517    dex_archives_dict = {}
518    for info in infos:
519        for dexopts in info.dex_archives_dict:
520            if dexopts not in dex_archives_dict:
521                dex_archives_dict[dexopts] = [info.dex_archives_dict[dexopts]]
522            else:
523                dex_archives_dict[dexopts].append(info.dex_archives_dict[dexopts])
524    return StarlarkAndroidDexInfo(
525        dex_archives_dict =
526            {dexopts: depset(direct = [], transitive = dex_archives) for dexopts, dex_archives in dex_archives_dict.items()},
527    )
528
529def _filter_dexopts(tokenized_dexopts, includes):
530    return _normalize_dexopts(_filter(tokenized_dexopts, includes = includes))
531
532def _filter(candidates, includes = [], excludes = []):
533    if excludes and includes:
534        fail("Only one of excludes list and includes list can be set.")
535    if includes:
536        return [c for c in candidates if c in includes]
537    if excludes:
538        return [c for c in candidates if c not in excludes]
539    return candidates
540
541def _normalize_dexopts(tokenized_dexopts):
542    def _dx_to_dexbuilder(opt):
543        return opt.replace("--no-", "--no")
544
545    return collections.uniq(sorted([_dx_to_dexbuilder(token) for token in tokenized_dexopts]))
546
547def _generate_main_dex_list(
548        ctx,
549        jar,
550        android_jar = None,
551        desugar_java8_libs = True,
552        main_dex_classes = None,
553        main_dex_list_opts = [],
554        main_dex_proguard_spec = None,
555        proguard_specs = [],
556        legacy_apis = [],
557        shrinked_android_jar = None,
558        toolchain_type = None,
559        main_dex_list_creator = None,
560        legacy_main_dex_list_generator = None,
561        proguard_tool = None):
562    main_dex_list = _get_dx_artifact(ctx, "main_dex_list.txt")
563    if not proguard_specs:
564        proguard_specs.append(main_dex_classes)
565    if main_dex_proguard_spec:
566        proguard_specs.append(main_dex_proguard_spec)
567
568    #  If legacy_main_dex_list_generator is not set by either the SDK or the flag, use ProGuard and
569    #  the main dext list creator specified by the android_sdk rule. If
570    #  legacy_main_dex_list_generator is provided, use that tool instead.
571    #  TODO(b/147692286): Remove the old main-dex list generation that relied on ProGuard.
572    if not legacy_main_dex_list_generator:
573        if not shrinked_android_jar:
574            fail("In \"legacy\" multidex mode, either legacy_main_dex_list_generator or " +
575                 "shrinked_android_jar must be set in the android_sdk.")
576
577        # Process the input jar through Proguard into an intermediate, streamlined jar.
578        stripped_jar = _get_dx_artifact(ctx, "main_dex_intermediate.jar")
579        args = ctx.actions.args()
580        args.add("-forceprocessing")
581        args.add("-injars", jar)
582        args.add("-libraryjars", shrinked_android_jar)
583        args.add("-outjars", stripped_jar)
584        args.add("-dontwarn")
585        args.add("-dontnote")
586        args.add("-dontoptimize")
587        args.add("-dontobfuscate")
588        ctx.actions.run(
589            outputs = [stripped_jar],
590            executable = proguard_tool,
591            args = [args],
592            inputs = [jar, shrinked_android_jar],
593            mnemonic = "MainDexClassesIntermediate",
594            progress_message = "Generating streamlined input jar for main dex classes list",
595            use_default_shell_dev = True,
596            toolchain = toolchain_type,
597        )
598
599        args = ctx.actions.args()
600        args.add_all([main_dex_list, stripped_jar, jar])
601        args.add_all(main_dex_list_opts)
602
603        ctx.actions.run(
604            outputs = [main_dex_list],
605            executable = main_dex_list_creator,
606            arguments = [args],
607            inputs = [jar, stripped_jar],
608            mnemonic = "MainDexClasses",
609            progress_message = "Generating main dex classes list",
610            toolchain = toolchain_type,
611        )
612    else:
613        inputs = [jar, android_jar] + proguard_specs
614
615        args = ctx.actions.args()
616        args.add("--main-dex-list-output", main_dex_list)
617        args.add("--lib", android_jar)
618        if desugar_java8_libs:
619            args.add_all(legacy_apis, before_each = "--lib")
620            inputs += legacy_apis
621        args.add_all(proguard_specs, before_each = "--main-dex-rules")
622        args.add(jar)
623        ctx.actions.run(
624            executable = legacy_main_dex_list_generator,
625            arguments = [args],
626            outputs = [main_dex_list],
627            inputs = inputs,
628            mnemonic = "MainDexClasses",
629            progress_message = "Generating main dex classes list",
630            toolchain = toolchain_type,
631        )
632    return main_dex_list
633
634def _transform_dex_list_through_proguard_map(
635        ctx,
636        proguard_output_map = None,
637        main_dex_list = None,
638        toolchain_type = None,
639        dex_list_obfuscator = None):
640    if not proguard_output_map:
641        return main_dex_list
642
643    obfuscated_main_dex_list = _get_dx_artifact(ctx, "main_dex_list_obfuscated.txt")
644
645    args = ctx.actions.args()
646    args.add("--input", main_dex_list)
647    args.add("--output", obfuscated_main_dex_list)
648    args.add("--obfuscation_map", proguard_output_map)
649    ctx.actions.run(
650        executable = dex_list_obfuscator,
651        arguments = [args],
652        outputs = [obfuscated_main_dex_list],
653        inputs = [main_dex_list],
654        mnemonic = "MainDexProguardClasses",
655        progress_message = "Obfuscating main dex classes list",
656        toolchain = toolchain_type,
657    )
658
659    return obfuscated_main_dex_list
660
661dex = struct(
662    append_java8_legacy_dex = _append_java8_legacy_dex,
663    dex = _dex,
664    dex_merge = _dex_merge,
665    generate_main_dex_list = _generate_main_dex_list,
666    get_dx_artifact = _get_dx_artifact,
667    get_effective_incremental_dexing = _get_effective_incremental_dexing,
668    get_java8_legacy_dex_and_map = _get_java8_legacy_dex_and_map,
669    filter_dexopts = _filter_dexopts,
670    merge_infos = _merge_infos,
671    normalize_dexopts = _normalize_dexopts,
672    process_monolithic_dexing = _process_monolithic_dexing,
673    process_incremental_dexing = _process_incremental_dexing,
674    process_optimized_dexing = _process_optimized_dexing,
675    transform_dex_list_through_proguard_map = _transform_dex_list_through_proguard_map,
676)
677