| # Copyright 2023 The Bazel Authors. All rights reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| """ |
| Defines baseline profiles processing methods in Android Rules. |
| """ |
| |
| load("//providers:providers.bzl", "ArtProfileInfo") |
| load("//rules:common.bzl", _common = "common") |
| load("//rules:java.bzl", _java = "java") |
| load("//rules:visibility.bzl", "PROJECT_VISIBILITY") |
| |
| visibility(PROJECT_VISIBILITY) |
| _BASELINE_PROFILE_DIR_SUFFIX = "-baseline-profile/" |
| |
| def _process( |
| ctx, |
| transitive_profiles = depset(), |
| startup_profiles = [], |
| deploy_jar = None, |
| has_proguard_specs = False, |
| enable_optimizer_integration = False, |
| merge_tool = None, |
| profgen = None, |
| toolchain_type = None): |
| """Processes all the transitive baseline profiles. |
| |
| Baseline profiles propagated from libraries will be merged, and if optimizer integration is |
| enabled, startup profiles will be merged as well, and wildcards in baseline profiles will be |
| expanded. |
| |
| Args: |
| ctx: The context. |
| transitive_profiles: Depset. The transitive baseline profiles propagated from android_library |
| and aar_import. |
| startup_profiles: List. The startup profiles. |
| deploy_jar: File. The deploy jar. |
| has_proguard_specs: Boolean. Whether to have proguard specs. |
| enable_optimizer_integration: Boolean. Whether to use startup profile and baseline profiles in optimization. |
| merge_tool: FilesToRunProvider. An executable for merging baseline profiles. |
| profgen: FilesToRunProvider: An executable for compiling baseline profiles. |
| toolchain_type: Label or String. Toolchain type of the executable used in actions. |
| Returns: |
| A struct containing all the outputs from processing baseline profiles. |
| """ |
| |
| baseline_profile = None |
| startup_profile = None |
| if transitive_profiles: |
| baseline_profile = _get_profile_artifact(ctx, "static-prof.txt") |
| _merge( |
| ctx, |
| baseline_profile, |
| transitive_profiles, |
| "MergeBaselineProfiles", |
| merge_tool = merge_tool, |
| toolchain_type = toolchain_type, |
| ) |
| |
| if has_proguard_specs and enable_optimizer_integration: |
| # This is only needed for optimized builds since otherwise the dexer doesn't process this. |
| if startup_profiles: |
| startup_profile = _get_profile_artifact(ctx, "static-startup-prof.txt") |
| _merge( |
| ctx, |
| output = startup_profile, |
| inputs = ctx.files.startup_profiles, |
| mnemonic = "MergeStartupProfiles", |
| merge_tool = merge_tool, |
| toolchain_type = toolchain_type, |
| ) |
| |
| # Wildcards only need to be expanded for optimized builds since if these aren't consumed by |
| # the optimizer, they can just be expanded during profile compilation instead. |
| # Start-up profiles are not expanded because it shouldn't be necessary as these should |
| # contain profiles generated by devices on start-up. |
| if baseline_profile: |
| expanded_baseline_profile = _get_profile_artifact(ctx, "expanded-static-prof.txt") |
| _expand_wildcards( |
| ctx, |
| output = expanded_baseline_profile, |
| deploy_jar = deploy_jar, |
| profile = baseline_profile, |
| profgen = profgen, |
| toolchain_type = toolchain_type, |
| ) |
| baseline_profile = expanded_baseline_profile |
| return struct( |
| baseline_profile = baseline_profile, |
| startup_profile = startup_profile, |
| ) |
| |
| def _process_art_profile( |
| ctx, |
| final_classes_dex, |
| merged_profile, |
| output_primary_profile, |
| proguard_output_map = None, |
| profgen = None, |
| toolchain_type = None): |
| """ Compiles the merged baseline profile. |
| |
| Profiles are compiled with profgen into binary ART profiles. The binary |
| profiles will be bundled into the final APK and used at installation time to speed up app |
| startup and reduce jank. |
| |
| Args: |
| ctx: The context. |
| final_classes_dex: File. Final classes zip artifact. |
| merged_profile: File. The merged profile from transitive baseline profile files. |
| proguard_output_map: File. Optional. The proguard output mapping file. |
| profgen: FilesToRunProvider. The profgen executable for profile compilation. |
| toolchain_type: Label or String. Toolchain type of the executable used in actions. |
| Returns: |
| ArtProfileInfo containing the generated profile, the metadata, and the combined zip file. |
| """ |
| |
| output_profile = _get_profile_artifact(ctx, "baseline.prof") |
| output_profile_meta = _get_profile_artifact(ctx, "baseline.profm") |
| _generate_profile( |
| ctx, |
| output_profile, |
| output_profile_meta, |
| final_classes_dex, |
| merged_profile, |
| proguard_output_map, |
| profgen, |
| toolchain_type, |
| ) |
| |
| # Zip ART profiles |
| output_profile_zip = _get_profile_artifact(ctx, "art_profile.zip") |
| |
| _java.singlejar( |
| ctx, |
| compression = False, |
| inputs = [], |
| output = output_profile_zip, |
| mnemonic = "ZipARTProfiles", |
| progress_message = "Zip ART Profiles for %{label}", |
| resource_paths = [output_profile.path + ":assets/dexopt/baseline.prof", output_profile_meta.path + ":assets/dexopt/baseline.profm"], |
| resources = [output_profile, output_profile_meta], |
| java_toolchain = _common.get_java_toolchain(ctx), |
| ) |
| |
| _dump_profile( |
| ctx, |
| output_primary_profile, |
| output_profile, |
| final_classes_dex, |
| profgen, |
| toolchain_type, |
| ) |
| |
| return ArtProfileInfo( |
| art_profile_zip = output_profile_zip, |
| baseline_profile = output_profile, |
| baseline_profile_metadata = output_profile_meta, |
| primary_profile = output_primary_profile, |
| ) |
| |
| def _get_profile_dir(ctx): |
| return ctx.label.name + _BASELINE_PROFILE_DIR_SUFFIX |
| |
| def _get_profile_artifact(ctx, name): |
| return ctx.actions.declare_file(_get_profile_dir(ctx) + name) |
| |
| def _merge( |
| ctx, |
| output, |
| inputs = [], |
| mnemonic = "MergeBaselineProfiles", |
| merge_tool = None, |
| toolchain_type = None): |
| args = ctx.actions.args() |
| args.add_all(inputs, before_each = "--input") |
| args.add("--output", output) |
| |
| ctx.actions.run( |
| executable = merge_tool, |
| mnemonic = mnemonic, |
| arguments = [args], |
| inputs = inputs, |
| outputs = [output], |
| use_default_shell_env = True, |
| toolchain = toolchain_type, |
| ) |
| |
| def _expand_wildcards( |
| ctx, |
| output, |
| deploy_jar = None, |
| profile = None, |
| profgen = None, |
| toolchain_type = None): |
| args = ctx.actions.args() |
| args.add("expandWildcards", deploy_jar) |
| args.add("--profile", profile) |
| args.add("--output", output) |
| |
| ctx.actions.run( |
| executable = profgen, |
| outputs = [output], |
| inputs = [deploy_jar, profile], |
| arguments = [args], |
| mnemonic = "ExpandBaselineProfileWildcards", |
| progress_message = "Expanding baseline profile wildcards for %{label} APK", |
| toolchain = toolchain_type, |
| ) |
| |
| def _generate_profile( |
| ctx, |
| output_profile, |
| output_profile_meta, |
| final_classes_dex, |
| merged_profile, |
| proguard_output_map = None, |
| profgen = None, |
| toolchain_type = None): |
| profgen_inputs = [final_classes_dex, merged_profile] |
| args = ctx.actions.args() |
| args.add("bin", merged_profile) |
| args.add("--apk", final_classes_dex) |
| args.add("--output", output_profile) |
| args.add("--output-meta", output_profile_meta) |
| if proguard_output_map: |
| args.add("--map", proguard_output_map) |
| profgen_inputs.append(proguard_output_map) |
| |
| ctx.actions.run( |
| mnemonic = "GenerateARTProfile", |
| executable = profgen, |
| progress_message = "Generating Android P-R ART profile for %{label} APK", |
| arguments = [args], |
| inputs = profgen_inputs, |
| outputs = [output_profile, output_profile_meta], |
| use_default_shell_env = True, |
| toolchain = toolchain_type, |
| ) |
| |
| def _dump_profile( |
| ctx, |
| output, |
| binary_profile, |
| final_classes_dex, |
| profgen = None, |
| toolchain_type = None): |
| args = ctx.actions.args() |
| args.add("dumpProfile") |
| args.add("--profile", binary_profile) |
| args.add("--apk", final_classes_dex) |
| args.add("--output", output) |
| |
| ctx.actions.run( |
| mnemonic = "DumpProfile", |
| executable = profgen, |
| progress_message = "Dumping Obfuscated-HRP profile for %{label} APK", |
| arguments = [args], |
| inputs = [binary_profile, final_classes_dex], |
| outputs = [output], |
| use_default_shell_env = True, |
| toolchain = toolchain_type, |
| ) |
| |
| baseline_profiles = struct( |
| expand_wildcards = _expand_wildcards, |
| get_profile_artifact = _get_profile_artifact, |
| process = _process, |
| process_art_profile = _process_art_profile, |
| ) |