예제 #1
0
def _XMLTransform(source_pairs, outputs_zip):
    """
  Apply module codes to input sources.
  """
    with build_utils.TempDir() as temp_dir:
        path_info = resource_utils.ResourceInfoFile()
        for source, module in source_pairs:
            with codecs.open(source, 'r', 'utf-8') as f:
                xml_content = f.read()
            loaded_module = _ImportModuleByPath(module)

            if not xml_content or not loaded_module:
                continue

            root = ET.XML(xml_content)
            result = loaded_module._ProcessXML(root)
            output = ET.tostring(result,
                                 encoding='utf-8',
                                 xml_declaration=True)

            # Parse output path
            # For simplicity, we assume input path will always has java/res in it
            if not (relpath := _UnderJavaRes(os.path.abspath(source))):
                raise Exception('input file %s is not under java/res' % source)

            # resource_overlay doesn't seem to work from android_generated_resources
            relpath = _AddBravePrefix(relpath)
            output_filename = os.path.join(temp_dir, relpath)
            parent_dir = os.path.dirname(output_filename)
            build_utils.MakeDirectory(parent_dir)
            _ProcessFile(output_filename, output)
            path_info.AddMapping(relpath, source)

        path_info.Write(outputs_zip + '.info')
        build_utils.ZipDir(outputs_zip, temp_dir)
예제 #2
0
def _ProcessFiles(processor, input_filenames, inputs_base_dir, outputs_zip):
    with build_utils.TempDir() as temp_dir:
        path_info = resource_utils.ResourceInfoFile()
        for input_filename in input_filenames:
            relpath = os.path.relpath(os.path.abspath(input_filename),
                                      os.path.abspath(inputs_base_dir))
            if relpath.startswith(os.pardir):
                raise Exception(
                    'input file %s is not contained in inputs base dir %s' %
                    (input_filename, inputs_base_dir))

            output_filename = os.path.join(temp_dir, relpath)
            parent_dir = os.path.dirname(output_filename)
            build_utils.MakeDirectory(parent_dir)
            _ProcessFile(processor, input_filename, output_filename)
            path_info.AddMapping(relpath, input_filename)

        path_info.Write(outputs_zip + '.info')
        build_utils.ZipDir(outputs_zip, temp_dir)
예제 #3
0
def _ZipResources(resource_dirs, zip_path, ignore_pattern):
    # ignore_pattern is a string of ':' delimited list of globs used to ignore
    # files that should not be part of the final resource zip.
    files_to_zip = []
    path_info = resource_utils.ResourceInfoFile()
    for index, resource_dir in enumerate(resource_dirs):
        attributed_aar = None
        if not resource_dir.startswith('..'):
            aar_source_info_path = os.path.join(os.path.dirname(resource_dir),
                                                'source.info')
            if os.path.exists(aar_source_info_path):
                attributed_aar = jar_info_utils.ReadAarSourceInfo(
                    aar_source_info_path)

        for path, archive_path in resource_utils.IterResourceFilesInDirectories(
            [resource_dir], ignore_pattern):
            attributed_path = path
            if attributed_aar:
                attributed_path = os.path.join(attributed_aar, 'res',
                                               path[len(resource_dir) + 1:])
            # Use the non-prefixed archive_path in the .info file.
            path_info.AddMapping(archive_path, attributed_path)

            resource_dir_name = os.path.basename(resource_dir)
            archive_path = '{}_{}/{}'.format(index, resource_dir_name,
                                             archive_path)
            files_to_zip.append((archive_path, path))

    path_info.Write(zip_path + '.info')

    with zipfile.ZipFile(zip_path, 'w') as z:
        # This magic comment signals to resource_utils.ExtractDeps that this zip is
        # not just the contents of a single res dir, without the encapsulating res/
        # (like the outputs of android_generated_resources targets), but instead has
        # the contents of possibly multiple res/ dirs each within an encapsulating
        # directory within the zip.
        z.comment = resource_utils.MULTIPLE_RES_MAGIC_STRING
        build_utils.DoZip(files_to_zip, z)
예제 #4
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
예제 #5
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 = []
    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