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)
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)
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)
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)
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)
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)
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)
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
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