def main(args):
    args = build_utils.ExpandFileArgs(args)
    options = _ParseArgs(args)

    debug_temp_resources_dir = os.environ.get(_ENV_DEBUG_VARIABLE)
    if debug_temp_resources_dir:
        debug_temp_resources_dir = os.path.join(
            debug_temp_resources_dir, os.path.basename(options.apk_path))
        build_utils.DeleteDirectory(debug_temp_resources_dir)
        build_utils.MakeDirectory(debug_temp_resources_dir)

    with resource_utils.BuildContext(debug_temp_resources_dir) as build:
        dep_subdirs = resource_utils.ExtractDeps(options.dependencies_res_zips,
                                                 build.deps_dir)

        _PackageApk(options, dep_subdirs, build.temp_dir, build.gen_dir,
                    build.r_txt_path)

        r_txt_path = _WriteFinalRTxtFile(options, build.r_txt_path)

        # If --shared-resources-whitelist is used, the all resources listed in
        # the corresponding R.txt file will be non-final, and an onResourcesLoaded()
        # will be generated to adjust them at runtime.
        #
        # Otherwise, if --shared-resources is used, the all resources will be
        # non-final, and an onResourcesLoaded() method will be generated too.
        #
        # Otherwise, all resources will be final, and no method will be generated.
        #
        rjava_build_options = resource_utils.RJavaBuildOptions()
        if options.shared_resources_whitelist:
            rjava_build_options.ExportSomeResources(
                options.shared_resources_whitelist)
            rjava_build_options.GenerateOnResourcesLoaded()
        elif options.shared_resources or options.app_as_shared_lib:
            rjava_build_options.ExportAllResources()
            rjava_build_options.GenerateOnResourcesLoaded()

        resource_utils.CreateRJavaFiles(build.srcjar_dir, None, r_txt_path,
                                        options.extra_res_packages,
                                        options.extra_r_text_files,
                                        rjava_build_options)

        if options.srcjar_out:
            build_utils.ZipDir(options.srcjar_out, build.srcjar_dir)

        if options.check_resources_pkg_id is not None:
            expected_id = options.check_resources_pkg_id
            package_id = _ExtractPackageIdFromApk(options.apk_path,
                                                  options.aapt_path)
            if package_id != expected_id:
                raise Exception('Invalid package ID 0x%x (expected 0x%x)' %
                                (package_id, expected_id))

    if options.depfile:
        build_utils.WriteDepfile(options.depfile,
                                 options.apk_path,
                                 inputs=options.dependencies_res_zips +
                                 options.extra_r_text_files,
                                 add_pydeps=False)
Exemple #2
0
def _OnStaleMd5(options):
    with resource_utils.BuildContext() as build:
        if options.sources:
            _CheckAllFilesListed(options.sources, options.resource_dirs)
        if options.r_text_in:
            r_txt_path = options.r_text_in
        else:
            # Extract dependencies to resolve @foo/type references into
            # dependent packages.
            dep_subdirs = resource_utils.ExtractDeps(
                options.dependencies_res_zips, build.deps_dir)

            _GenerateRTxt(options, dep_subdirs, build.gen_dir)
            r_txt_path = build.r_txt_path

            # 'aapt' doesn't generate any R.txt file if res/ was empty.
            if not os.path.exists(r_txt_path):
                build_utils.Touch(r_txt_path)

        if options.r_text_out:
            shutil.copyfile(r_txt_path, options.r_text_out)

        if options.srcjar_out:
            package = options.custom_package
            if not package and options.android_manifest:
                _, manifest_node, _ = manifest_utils.ParseManifest(
                    options.android_manifest)
                package = manifest_utils.GetPackage(manifest_node)

            # Don't create a .java file for the current resource target when no
            # package name was provided (either by manifest or build rules).
            if package:
                # All resource IDs should be non-final here, but the
                # onResourcesLoaded() method should only be generated if
                # --shared-resources is used.
                rjava_build_options = resource_utils.RJavaBuildOptions()
                rjava_build_options.ExportAllResources()
                rjava_build_options.ExportAllStyleables()
                if options.shared_resources:
                    rjava_build_options.GenerateOnResourcesLoaded()

                # Not passing in custom_root_package_name or parent to keep
                # file names unique.
                resource_utils.CreateRJavaFiles(build.srcjar_dir, package,
                                                r_txt_path,
                                                options.extra_res_packages,
                                                options.extra_r_text_files,
                                                rjava_build_options,
                                                options.srcjar_out)

            build_utils.ZipDir(options.srcjar_out, build.srcjar_dir)

        if options.resource_zip_out:
            ignore_pattern = resource_utils.AAPT_IGNORE_PATTERN
            if options.strip_drawables:
                ignore_pattern += ':*drawable*'
            _ZipResources(options.resource_dirs, options.resource_zip_out,
                          ignore_pattern)
Exemple #3
0
def main(args):
    parser = argparse.ArgumentParser()

    build_utils.AddDepfileOption(parser)
    parser.add_argument('--script',
                        required=True,
                        help='Path to the unused resources detector script.')
    parser.add_argument(
        '--dependencies-res-zips',
        required=True,
        help='Resources zip archives to investigate for unused resources.')
    parser.add_argument('--dex',
                        required=True,
                        help='Path to dex file, or zip with dex files.')
    parser.add_argument(
        '--proguard-mapping',
        required=True,
        help='Path to proguard mapping file for the optimized dex.')
    parser.add_argument('--r-text', required=True, help='Path to R.txt')
    parser.add_argument('--android-manifest',
                        required=True,
                        help='Path to AndroidManifest')
    parser.add_argument('--output-config',
                        required=True,
                        help='Path to output the aapt2 config to.')
    args = build_utils.ExpandFileArgs(args)
    options = parser.parse_args(args)
    options.dependencies_res_zips = (build_utils.ParseGnList(
        options.dependencies_res_zips))

    # in case of no resources, short circuit early.
    if not options.dependencies_res_zips:
        build_utils.Touch(options.output_config)
        return

    with build_utils.TempDir() as temp_dir:
        dep_subdirs = []
        for dependency_res_zip in options.dependencies_res_zips:
            dep_subdirs += resource_utils.ExtractDeps([dependency_res_zip],
                                                      temp_dir)

        build_utils.CheckOutput([
            options.script, '--rtxts', options.r_text, '--manifests',
            options.android_manifest, '--resourceDirs', ':'.join(dep_subdirs),
            '--dex', options.dex, '--mapping', options.proguard_mapping,
            '--outputConfig', options.output_config
        ])

    if options.depfile:
        depfile_deps = options.dependencies_res_zips + [
            options.r_text,
            options.android_manifest,
            options.dex,
            options.proguard_mapping,
        ]
        build_utils.WriteDepfile(options.depfile, options.output_config,
                                 depfile_deps)
Exemple #4
0
def _OnStaleMd5(options):
    with resource_utils.BuildContext() as build:
        if options.r_text_in:
            r_txt_path = options.r_text_in
        else:
            # Extract dependencies to resolve @foo/type references into
            # dependent packages.
            dep_subdirs = resource_utils.ExtractDeps(
                options.dependencies_res_zips, build.deps_dir)

            _GenerateRTxt(options, dep_subdirs, build.gen_dir)
            r_txt_path = build.r_txt_path

            # 'aapt' doesn't generate any R.txt file if res/ was empty.
            if not os.path.exists(r_txt_path):
                build_utils.Touch(r_txt_path)

        if options.r_text_out:
            shutil.copyfile(r_txt_path, options.r_text_out)

        if options.srcjar_out:
            package = options.custom_package
            if not package and options.android_manifest:
                package = resource_utils.ExtractPackageFromManifest(
                    options.android_manifest)

            # Don't create a .java file for the current resource target when no
            # package name was provided (either by manifest or build rules).
            if package:
                # All resource IDs should be non-final here, but the
                # onResourcesLoaded() method should only be generated if
                # --shared-resources is used.
                rjava_build_options = resource_utils.RJavaBuildOptions()
                rjava_build_options.ExportAllResources()
                rjava_build_options.ExportAllStyleables()
                if options.shared_resources:
                    rjava_build_options.GenerateOnResourcesLoaded()

                resource_utils.CreateRJavaFiles(build.srcjar_dir, package,
                                                r_txt_path,
                                                options.extra_res_packages,
                                                options.extra_r_text_files,
                                                rjava_build_options,
                                                options.srcjar_out)

            build_utils.ZipDir(options.srcjar_out, build.srcjar_dir)

        if options.resource_zip_out:
            _GenerateResourcesZip(options.resource_zip_out,
                                  options.resource_dirs, options.v14_skip,
                                  options.strip_drawables, build.temp_dir)
Exemple #5
0
def main(args):
    parser = argparse.ArgumentParser(
        description='Create an R.txt from resources.')
    parser.add_argument('--resources-zip-path',
                        required=True,
                        help='Path to input resources zip.')
    parser.add_argument('--rtxt-path',
                        required=True,
                        help='Path to output R.txt file.')
    options = parser.parse_args(build_utils.ExpandFileArgs(args))
    with build_utils.TempDir() as temp:
        dep_subdirs = resource_utils.ExtractDeps([options.resources_zip_path],
                                                 temp)
        resources_parser.RTxtGenerator(dep_subdirs).WriteRTxtFile(
            options.rtxt_path)
Exemple #6
0
def _OnStaleMd5(options):
    with resource_utils.BuildContext() as build:
        dep_subdirs = resource_utils.ExtractDeps(options.dependencies_res_zips,
                                                 build.deps_dir)

        _PackageApk(options, dep_subdirs, build.temp_dir, build.gen_dir,
                    build.r_txt_path)

        r_txt_path = _WriteFinalRTxtFile(options, build.r_txt_path)

        package = resource_utils.ExtractPackageFromManifest(
            options.android_manifest)

        # If --shared-resources-whitelist is used, the all resources listed in
        # the corresponding R.txt file will be non-final, and an onResourcesLoaded()
        # will be generated to adjust them at runtime.
        #
        # Otherwise, if --shared-resources is used, the all resources will be
        # non-final, and an onResourcesLoaded() method will be generated too.
        #
        # Otherwise, all resources will be final, and no method will be generated.
        #
        rjava_build_options = resource_utils.RJavaBuildOptions()
        if options.shared_resources_whitelist:
            rjava_build_options.ExportSomeResources(
                options.shared_resources_whitelist)
            rjava_build_options.GenerateOnResourcesLoaded()
        elif options.shared_resources or options.app_as_shared_lib:
            rjava_build_options.ExportAllResources()
            rjava_build_options.GenerateOnResourcesLoaded()

        resource_utils.CreateRJavaFiles(build.srcjar_dir, package, r_txt_path,
                                        options.extra_res_packages,
                                        options.extra_r_text_files,
                                        rjava_build_options)

        if options.srcjar_out:
            build_utils.ZipDir(options.srcjar_out, build.srcjar_dir)

        if options.check_resources_pkg_id is not None:
            expected_id = options.check_resources_pkg_id
            package_id = _ExtractPackageIdFromApk(options.apk_path,
                                                  options.aapt_path)
            if package_id != expected_id:
                raise Exception('Invalid package ID 0x%x (expected 0x%x)' %
                                (package_id, expected_id))
def main(args):
    args = build_utils.ExpandFileArgs(args)
    options = _ParseArgs(args)

    # Resource files aren't explicitly listed in GN. Listing them in the depfile
    # ensures the target will be marked stale when resource files are removed.
    depfile_deps = []
    for resource_dir in options.resource_dirs:
        for resource_file in build_utils.FindInDirectory(resource_dir, '*'):
            # Don't list the empty .keep file in depfile. Since it doesn't end up
            # included in the .zip, it can lead to -w 'dupbuild=err' ninja errors
            # if ever moved.
            if not resource_file.endswith(os.path.join('empty', '.keep')):
                depfile_deps.append(resource_file)

    with resource_utils.BuildContext() as build:
        if options.sources:
            _CheckAllFilesListed(options.sources, options.resource_dirs)
        if options.r_text_in:
            r_txt_path = options.r_text_in
        else:
            # Extract dependencies to resolve @foo/type references into
            # dependent packages.
            dep_subdirs = resource_utils.ExtractDeps(
                options.dependencies_res_zips, build.deps_dir)

            _GenerateRTxt(options, dep_subdirs, build.gen_dir)
            r_txt_path = build.r_txt_path

        if options.r_text_out:
            shutil.copyfile(r_txt_path, options.r_text_out)

        if options.resource_zip_out:
            ignore_pattern = resource_utils.AAPT_IGNORE_PATTERN
            if options.strip_drawables:
                ignore_pattern += ':*drawable*'
            _ZipResources(options.resource_dirs, options.resource_zip_out,
                          ignore_pattern)

    if options.depfile:
        # Order of output must match order specified in GN so that the correct one
        # appears first in the depfile.
        build_utils.WriteDepfile(
            options.depfile, options.resource_zip_out or options.r_text_out,
            depfile_deps)
Exemple #8
0
def _CreateRJava(resource_zips, package_name, srcjar_out, rtxt_out):
    with resource_utils.BuildContext() as build:
        # TODO(mheikal): maybe we can rewrite resources_parser to just read out of a
        # zipfile without needing to extract first.
        dep_subdirs = resource_utils.ExtractDeps(resource_zips, build.deps_dir)
        resources_parser.RTxtGenerator(dep_subdirs).WriteRTxtFile(rtxt_out)

        rjava_build_options = resource_utils.RJavaBuildOptions()
        rjava_build_options.ExportAllResources()
        rjava_build_options.ExportAllStyleables()
        resource_utils.CreateRJavaFiles(
            build.srcjar_dir,
            package_name,
            rtxt_out,
            extra_res_packages=[],
            rjava_build_options=rjava_build_options,
            srcjar_out=srcjar_out)
        build_utils.ZipDir(srcjar_out, build.srcjar_dir)
def _OnStaleMd5(options):
    with resource_utils.BuildContext() as build:
        if options.sources:
            _CheckAllFilesListed(options.sources, options.resource_dirs)
        if options.r_text_in:
            r_txt_path = options.r_text_in
        else:
            # Extract dependencies to resolve @foo/type references into
            # dependent packages.
            dep_subdirs = resource_utils.ExtractDeps(
                options.dependencies_res_zips, build.deps_dir)

            _GenerateRTxt(options, dep_subdirs, build.gen_dir)
            r_txt_path = build.r_txt_path

        if options.r_text_out:
            shutil.copyfile(r_txt_path, options.r_text_out)

        if options.resource_zip_out:
            ignore_pattern = resource_utils.AAPT_IGNORE_PATTERN
            if options.strip_drawables:
                ignore_pattern += ':*drawable*'
            _ZipResources(options.resource_dirs, options.resource_zip_out,
                          ignore_pattern)
Exemple #10
0
def _PackageApk(options, build):
    """Compile and link resources with aapt2.

  Args:
    options: The command-line options.
    build: BuildContext object.
  Returns:
    The manifest package name for the APK.
  """
    logging.debug('Extracting resource .zips')
    dep_subdirs = resource_utils.ExtractDeps(options.dependencies_res_zips,
                                             build.deps_dir)
    logging.debug('Applying locale transformations')
    path_info = resource_utils.ResourceInfoFile()
    if options.support_zh_hk:
        _DuplicateZhResources(dep_subdirs, path_info)
    _RenameLocaleResourceDirs(dep_subdirs, path_info)

    _RemoveUnwantedLocalizedStrings(dep_subdirs, options)

    # Create a function that selects which resource files should be packaged
    # into the final output. Any file that does not pass the predicate will
    # be removed below.
    logging.debug('Applying file-based blacklist')
    keep_predicate = _CreateKeepPredicate(
        options.resource_blacklist_regex,
        options.resource_blacklist_exceptions)
    png_paths = []
    for directory in dep_subdirs:
        for f in _IterFiles(directory):
            if not keep_predicate(f):
                os.remove(f)
            elif f.endswith('.png'):
                png_paths.append((f, directory))
    if png_paths and options.png_to_webp:
        logging.debug('Converting png->webp')
        _ConvertToWebP(options.webp_binary, png_paths, path_info)
    logging.debug('Applying drawable transformations')
    for directory in dep_subdirs:
        _MoveImagesToNonMdpiFolders(directory, path_info)
        _RemoveImageExtensions(directory, path_info)

    link_command = [
        options.aapt2_path,
        'link',
        '--auto-add-overlay',
        '--no-version-vectors',
        # Set SDK versions in case they are not set in the Android manifest.
        '--min-sdk-version',
        options.min_sdk_version,
        '--target-sdk-version',
        options.target_sdk_version,
    ]

    for j in options.include_resources:
        link_command += ['-I', j]
    if options.version_code:
        link_command += ['--version-code', options.version_code]
    if options.version_name:
        link_command += ['--version-name', options.version_name]
    if options.proguard_file:
        link_command += ['--proguard', build.proguard_path]
        link_command += ['--proguard-minimal-keep-rules']
    if options.proguard_file_main_dex:
        link_command += ['--proguard-main-dex', build.proguard_main_dex_path]
    if options.emit_ids_out:
        link_command += ['--emit-ids', build.emit_ids_path]
    if options.r_text_in:
        shutil.copyfile(options.r_text_in, build.r_txt_path)
    else:
        link_command += ['--output-text-symbols', build.r_txt_path]

    # Note: only one of --proto-format, --shared-lib or --app-as-shared-lib
    #       can be used with recent versions of aapt2.
    if options.shared_resources:
        link_command.append('--shared-lib')

    if options.no_xml_namespaces:
        link_command.append('--no-xml-namespaces')

    if options.package_id:
        link_command += [
            '--package-id',
            hex(options.package_id),
            '--allow-reserved-package-id',
        ]

    fixed_manifest, desired_manifest_package_name = _FixManifest(
        options, build.temp_dir)
    if options.rename_manifest_package:
        desired_manifest_package_name = options.rename_manifest_package
    if options.android_manifest_expected:
        _VerifyManifest(fixed_manifest, options.android_manifest_expected,
                        options.android_manifest_normalized,
                        options.android_manifest_expectations_failure_file)

    link_command += [
        '--manifest', fixed_manifest, '--rename-manifest-package',
        desired_manifest_package_name
    ]

    # Creates a .zip with AndroidManifest.xml, resources.arsc, res/*
    # Also creates R.txt
    if options.use_resource_ids_path:
        _CreateStableIdsFile(options.use_resource_ids_path,
                             build.stable_ids_path,
                             desired_manifest_package_name)
        link_command += ['--stable-ids', build.stable_ids_path]

    partials = _CompileDeps(options.aapt2_path, dep_subdirs, build.temp_dir)
    for partial in partials:
        link_command += ['-R', partial]

    # We always create a binary arsc file first, then convert to proto, so flags
    # such as --shared-lib can be supported.
    arsc_path = build.arsc_path
    if arsc_path is None:
        _, arsc_path = tempfile.mkstmp()
    link_command += ['-o', build.arsc_path]

    logging.debug('Starting: aapt2 link')
    link_proc = subprocess.Popen(link_command)

    # Create .res.info file in parallel.
    _CreateResourceInfoFile(path_info, build.info_path,
                            options.dependencies_res_zips)
    logging.debug('Created .res.info file')

    exit_code = link_proc.wait()
    logging.debug('Finished: aapt2 link')
    if exit_code:
        raise subprocess.CalledProcessError(exit_code, link_command)

    if options.proguard_file and (options.shared_resources
                                  or options.app_as_shared_lib):
        # Make sure the R class associated with the manifest package does not have
        # its onResourcesLoaded method obfuscated or removed, so that the framework
        # can call it in the case where the APK is being loaded as a library.
        with open(build.proguard_path, 'a') as proguard_file:
            keep_rule = '''
                  -keep class {package}.R {{
                    public static void onResourcesLoaded(int);
                  }}
                  '''.format(package=desired_manifest_package_name)
            proguard_file.write(textwrap.dedent(keep_rule))

    logging.debug('Running aapt2 convert')
    build_utils.CheckOutput([
        options.aapt2_path, 'convert', '--output-format', 'proto', '-o',
        build.proto_path, build.arsc_path
    ])

    # Workaround for b/147674078. This is only needed for WebLayer and does not
    # affect WebView usage, since WebView does not used dynamic attributes.
    if options.shared_resources:
        logging.debug('Hardcoding dynamic attributes')
        _HardcodeSharedLibraryDynamicAttributes(build.proto_path)
        build_utils.CheckOutput([
            options.aapt2_path, 'convert', '--output-format', 'binary', '-o',
            build.arsc_path, build.proto_path
        ])

    if build.arsc_path is None:
        os.remove(arsc_path)

    if options.optimized_proto_path:
        _OptimizeApk(build.optimized_proto_path, options, build.temp_dir,
                     build.proto_path, build.r_txt_path)
    elif options.optimized_arsc_path:
        _OptimizeApk(build.optimized_arsc_path, options, build.temp_dir,
                     build.arsc_path, build.r_txt_path)

    return desired_manifest_package_name
Exemple #11
0
def _PackageApk(options, build):
    """Compile and link resources with aapt2.

  Args:
    options: The command-line options.
    build: BuildContext object.
  """
    dep_subdirs = resource_utils.ExtractDeps(options.dependencies_res_zips,
                                             build.deps_dir)
    renamed_paths = dict()
    renamed_paths.update(_DuplicateZhResources(dep_subdirs))
    renamed_paths.update(_RenameLocaleResourceDirs(dep_subdirs))

    _RemoveUnwantedLocalizedStrings(dep_subdirs, options)

    # Create a function that selects which resource files should be packaged
    # into the final output. Any file that does not pass the predicate will
    # be removed below.
    keep_predicate = _CreateKeepPredicate(
        dep_subdirs, options.resource_blacklist_regex,
        options.resource_blacklist_exceptions)
    png_paths = []
    for directory in dep_subdirs:
        for f in _IterFiles(directory):
            if not keep_predicate(f):
                os.remove(f)
            elif f.endswith('.png'):
                png_paths.append((f, directory))
    if png_paths and options.png_to_webp:
        renamed_paths.update(_ConvertToWebP(options.webp_binary, png_paths))
    for directory in dep_subdirs:
        renamed_paths.update(_MoveImagesToNonMdpiFolders(directory))

    _CreateResourceInfoFile(renamed_paths, build.info_path,
                            options.dependencies_res_zips)

    link_command = [
        options.aapt2_path,
        'link',
        '--auto-add-overlay',
        '--no-version-vectors',
        '--min-sdk-version',
        options.min_sdk_version,
        '--target-sdk-version',
        options.target_sdk_version,
    ]

    for j in options.include_resources:
        link_command += ['-I', j]
    if options.version_code:
        link_command += ['--version-code', options.version_code]
    if options.version_name:
        link_command += ['--version-name', options.version_name]
    if options.proguard_file:
        link_command += ['--proguard', build.proguard_path]
    if options.proguard_file_main_dex:
        link_command += ['--proguard-main-dex', build.proguard_main_dex_path]
    if options.emit_ids_out:
        link_command += ['--emit-ids', build.emit_ids_path]
    if options.r_text_in:
        shutil.copyfile(options.r_text_in, build.r_txt_path)
    else:
        link_command += ['--output-text-symbols', build.r_txt_path]

    # Note: only one of --proto-format, --shared-lib or --app-as-shared-lib
    #       can be used with recent versions of aapt2.
    if options.shared_resources and not options.proto_path:
        link_command.append('--shared-lib')

    if options.no_xml_namespaces:
        link_command.append('--no-xml-namespaces')

    package_id = _PackageIdFromOptions(options)
    if package_id is not None:
        link_command += [
            '--package-id', package_id, '--allow-reserved-package-id'
        ]

    fixed_manifest, desired_manifest_package_name = _FixManifest(
        options, build.temp_dir)
    if options.rename_manifest_package:
        desired_manifest_package_name = options.rename_manifest_package
    if options.android_manifest_expected:
        _VerifyManifest(fixed_manifest, options.android_manifest_expected,
                        options.android_manifest_normalized,
                        options.fail_if_unexpected_android_manifest)

    link_command += [
        '--manifest', fixed_manifest, '--rename-manifest-package',
        desired_manifest_package_name
    ]

    # Creates a .zip with AndroidManifest.xml, resources.arsc, res/*
    # Also creates R.txt
    if options.use_resource_ids_path:
        _CreateStableIdsFile(options.use_resource_ids_path,
                             build.stable_ids_path,
                             desired_manifest_package_name)
        link_command += ['--stable-ids', build.stable_ids_path]

    partials = _CompileDeps(options.aapt2_path, dep_subdirs, build.temp_dir)
    for partial in partials:
        link_command += ['-R', partial]

    if options.proto_path:
        link_command += ['--proto-format', '-o', build.proto_path]
    else:
        link_command += ['-o', build.arsc_path]

    build_utils.CheckOutput(link_command,
                            print_stdout=False,
                            print_stderr=False)

    if options.proto_path and options.arsc_path:
        build_utils.CheckOutput([
            options.aapt2_path, 'convert', '-o', build.arsc_path,
            build.proto_path
        ])

    if options.optimized_proto_path:
        _OptimizeApk(build.optimized_proto_path, options, build.temp_dir,
                     build.proto_path, build.r_txt_path)
    elif options.optimized_arsc_path:
        _OptimizeApk(build.optimized_arsc_path, options, build.temp_dir,
                     build.arsc_path, build.r_txt_path)
def _PackageApk(options, build):
    """Compile and link resources with aapt2.

  Args:
    options: The command-line options.
    build: BuildContext object.
  Returns:
    The manifest package name for the APK.
  """
    dep_subdirs = resource_utils.ExtractDeps(options.dependencies_res_zips,
                                             build.deps_dir)
    renamed_paths = dict()
    renamed_paths.update(_DuplicateZhResources(dep_subdirs))
    renamed_paths.update(_RenameLocaleResourceDirs(dep_subdirs))

    _RemoveUnwantedLocalizedStrings(dep_subdirs, options)

    # Create a function that selects which resource files should be packaged
    # into the final output. Any file that does not pass the predicate will
    # be removed below.
    keep_predicate = _CreateKeepPredicate(
        options.resource_blacklist_regex,
        options.resource_blacklist_exceptions)
    png_paths = []
    for directory in dep_subdirs:
        for f in _IterFiles(directory):
            if not keep_predicate(f):
                os.remove(f)
            elif f.endswith('.png'):
                png_paths.append((f, directory))
    if png_paths and options.png_to_webp:
        renamed_paths.update(_ConvertToWebP(options.webp_binary, png_paths))
    for directory in dep_subdirs:
        renamed_paths.update(_MoveImagesToNonMdpiFolders(directory))

    _CreateResourceInfoFile(renamed_paths, build.info_path,
                            options.dependencies_res_zips)

    link_command = [
        options.aapt2_path,
        'link',
        '--auto-add-overlay',
        '--no-version-vectors',
        # Set SDK versions in case they are not set in the Android manifest.
        '--min-sdk-version',
        options.min_sdk_version,
        '--target-sdk-version',
        options.target_sdk_version,
    ]

    for j in options.include_resources:
        link_command += ['-I', j]
    if options.version_code:
        link_command += ['--version-code', options.version_code]
    if options.version_name:
        link_command += ['--version-name', options.version_name]
    if options.proguard_file:
        link_command += ['--proguard', build.proguard_path]
        link_command += ['--proguard-minimal-keep-rules']
    if options.proguard_file_main_dex:
        link_command += ['--proguard-main-dex', build.proguard_main_dex_path]
    if options.emit_ids_out:
        link_command += ['--emit-ids', build.emit_ids_path]
    if options.r_text_in:
        shutil.copyfile(options.r_text_in, build.r_txt_path)
    else:
        link_command += ['--output-text-symbols', build.r_txt_path]

    # Note: only one of --proto-format, --shared-lib or --app-as-shared-lib
    #       can be used with recent versions of aapt2.
    if options.shared_resources and not options.proto_path:
        link_command.append('--shared-lib')

    if options.no_xml_namespaces:
        link_command.append('--no-xml-namespaces')

    if options.package_id:
        link_command += [
            '--package-id',
            hex(options.package_id),
            '--allow-reserved-package-id',
        ]

    fixed_manifest, desired_manifest_package_name = _FixManifest(
        options, build.temp_dir)
    if options.rename_manifest_package:
        desired_manifest_package_name = options.rename_manifest_package
    if options.android_manifest_expected:
        _VerifyManifest(fixed_manifest, options.android_manifest_expected,
                        options.android_manifest_normalized,
                        options.android_manifest_expectations_failure_file)

    link_command += [
        '--manifest', fixed_manifest, '--rename-manifest-package',
        desired_manifest_package_name
    ]

    # Creates a .zip with AndroidManifest.xml, resources.arsc, res/*
    # Also creates R.txt
    if options.use_resource_ids_path:
        _CreateStableIdsFile(options.use_resource_ids_path,
                             build.stable_ids_path,
                             desired_manifest_package_name)
        link_command += ['--stable-ids', build.stable_ids_path]

    partials = _CompileDeps(options.aapt2_path, dep_subdirs, build.temp_dir)
    for partial in partials:
        link_command += ['-R', partial]

    if options.proto_path:
        link_command += ['--proto-format', '-o', build.proto_path]
    else:
        link_command += ['-o', build.arsc_path]

    build_utils.CheckOutput(link_command,
                            print_stdout=False,
                            print_stderr=False)

    if options.proguard_file and (options.shared_resources
                                  or options.app_as_shared_lib):
        # Make sure the R class associated with the manifest package does not have
        # its onResourcesLoaded method obfuscated or removed, so that the framework
        # can call it in the case where the APK is being loaded as a library.
        with open(build.proguard_path, 'a') as proguard_file:
            keep_rule = '''
                  -keep class {package}.R {{
                    public static void onResourcesLoaded(int);
                  }}
                  '''.format(package=desired_manifest_package_name)
            proguard_file.write(textwrap.dedent(keep_rule))

    if options.proto_path and options.arsc_path:
        build_utils.CheckOutput([
            options.aapt2_path, 'convert', '-o', build.arsc_path,
            build.proto_path
        ])

    if options.optimized_proto_path:
        _OptimizeApk(build.optimized_proto_path, options, build.temp_dir,
                     build.proto_path, build.r_txt_path)
    elif options.optimized_arsc_path:
        _OptimizeApk(build.optimized_arsc_path, options, build.temp_dir,
                     build.arsc_path, build.r_txt_path)

    return desired_manifest_package_name
def _PackageApk(options, build):
    """Compile and link resources with aapt2.

  Args:
    options: The command-line options.
    build: BuildContext object.
  Returns:
    The manifest package name for the APK.
  """
    logging.debug('Extracting resource .zips')
    dep_subdirs = []
    dep_subdir_overlay_set = set()
    for dependency_res_zip in options.dependencies_res_zips:
        extracted_dep_subdirs = resource_utils.ExtractDeps(
            [dependency_res_zip], build.deps_dir)
        dep_subdirs += extracted_dep_subdirs
        if dependency_res_zip in options.dependencies_res_zip_overlays:
            dep_subdir_overlay_set.update(extracted_dep_subdirs)

    logging.debug('Applying locale transformations')
    path_info = resource_utils.ResourceInfoFile()
    _RenameLocaleResourceDirs(dep_subdirs, path_info)

    logging.debug('Applying file-based exclusions')
    keep_predicate = _CreateKeepPredicate(
        options.resource_exclusion_regex,
        options.resource_exclusion_exceptions)
    png_paths = _FilterResourceFiles(dep_subdirs, keep_predicate)

    if options.locale_allowlist or options.shared_resources_allowlist_locales:
        logging.debug('Applying locale-based string exclusions')
        _RemoveUnwantedLocalizedStrings(dep_subdirs, options)

    if png_paths and options.png_to_webp:
        logging.debug('Converting png->webp')
        _ConvertToWebP(options.webp_binary, png_paths, path_info,
                       options.webp_cache_dir)
    logging.debug('Applying drawable transformations')
    for directory in dep_subdirs:
        _MoveImagesToNonMdpiFolders(directory, path_info)
        _RemoveImageExtensions(directory, path_info)

    logging.debug('Running aapt2 compile')
    exclusion_rules = [x.split(':', 1) for x in options.values_filter_rules]
    partials = _CompileDeps(options.aapt2_path, dep_subdirs,
                            dep_subdir_overlay_set, build.temp_dir,
                            exclusion_rules)

    link_command = [
        options.aapt2_path,
        'link',
        '--auto-add-overlay',
        '--no-version-vectors',
        # Set SDK versions in case they are not set in the Android manifest.
        '--min-sdk-version',
        options.min_sdk_version,
        '--target-sdk-version',
        options.target_sdk_version,
        '--output-text-symbols',
        build.r_txt_path,
    ]

    for j in options.include_resources:
        link_command += ['-I', j]
    if options.version_code:
        link_command += ['--version-code', options.version_code]
    if options.version_name:
        link_command += ['--version-name', options.version_name]
    if options.proguard_file:
        link_command += ['--proguard', build.proguard_path]
        link_command += ['--proguard-minimal-keep-rules']
    if options.proguard_file_main_dex:
        link_command += ['--proguard-main-dex', build.proguard_main_dex_path]
    if options.emit_ids_out:
        link_command += ['--emit-ids', build.emit_ids_path]

    # Note: only one of --proto-format, --shared-lib or --app-as-shared-lib
    #       can be used with recent versions of aapt2.
    if options.shared_resources:
        link_command.append('--shared-lib')

    if options.no_xml_namespaces:
        link_command.append('--no-xml-namespaces')

    if options.package_id:
        link_command += [
            '--package-id',
            hex(options.package_id),
            '--allow-reserved-package-id',
        ]

    fixed_manifest, desired_manifest_package_name, fixed_manifest_package = (
        _FixManifest(options, build.temp_dir))
    if options.rename_manifest_package:
        desired_manifest_package_name = options.rename_manifest_package

    link_command += [
        '--manifest', fixed_manifest, '--rename-manifest-package',
        desired_manifest_package_name
    ]

    # Creates a .zip with AndroidManifest.xml, resources.arsc, res/*
    # Also creates R.txt
    if options.use_resource_ids_path:
        _CreateStableIdsFile(options.use_resource_ids_path,
                             build.stable_ids_path, fixed_manifest_package)
        link_command += ['--stable-ids', build.stable_ids_path]

    link_command += partials

    # We always create a binary arsc file first, then convert to proto, so flags
    # such as --shared-lib can be supported.
    link_command += ['-o', build.arsc_path]

    logging.debug('Starting: aapt2 link')
    link_proc = subprocess.Popen(link_command)

    # Create .res.info file in parallel.
    _CreateResourceInfoFile(path_info, build.info_path,
                            options.dependencies_res_zips)
    logging.debug('Created .res.info file')

    exit_code = link_proc.wait()
    logging.debug('Finished: aapt2 link')
    if exit_code:
        raise subprocess.CalledProcessError(exit_code, link_command)

    if options.proguard_file and (options.shared_resources
                                  or options.app_as_shared_lib):
        # Make sure the R class associated with the manifest package does not have
        # its onResourcesLoaded method obfuscated or removed, so that the framework
        # can call it in the case where the APK is being loaded as a library.
        with open(build.proguard_path, 'a') as proguard_file:
            keep_rule = '''
                  -keep,allowoptimization class {package}.R {{
                    public static void onResourcesLoaded(int);
                  }}
                  '''.format(package=desired_manifest_package_name)
            proguard_file.write(textwrap.dedent(keep_rule))

    logging.debug('Running aapt2 convert')
    build_utils.CheckOutput([
        options.aapt2_path, 'convert', '--output-format', 'proto', '-o',
        build.proto_path, build.arsc_path
    ])

    # Workaround for b/147674078. This is only needed for WebLayer and does not
    # affect WebView usage, since WebView does not used dynamic attributes.
    if options.shared_resources:
        logging.debug('Hardcoding dynamic attributes')
        protoresources.HardcodeSharedLibraryDynamicAttributes(
            build.proto_path, options.is_bundle_module,
            options.shared_resources_allowlist)

        build_utils.CheckOutput([
            options.aapt2_path, 'convert', '--output-format', 'binary', '-o',
            build.arsc_path, build.proto_path
        ])

    return desired_manifest_package_name