def main(raw_args): options = _ParseArgs(raw_args) arsc_package, _ = resource_utils.ExtractArscPackage( options.aapt2_path, options.in_apk) # Extract version from the compiled manifest since it might have been set # via aapt, and not exist in the manifest's text form. version_code, version_name, manifest_package = ( resource_utils.ExtractBinaryManifestValues(options.aapt2_path, options.in_apk)) new_manifest_data = _ProcessManifest(options.src_manifest, arsc_package, options.disable_isolated_processes) with tempfile.NamedTemporaryFile() as tmp_manifest, \ tempfile.NamedTemporaryFile() as tmp_apk: tmp_manifest.write(new_manifest_data) tmp_manifest.flush() cmd = [ options.aapt2_path, 'link', '-o', tmp_apk.name, '--manifest', tmp_manifest.name, '-I', options.in_apk, '--replace-version', '--version-code', version_code, '--version-name', version_name, '--rename-manifest-package', manifest_package, '--debug-mode' ] for j in options.android_sdk_jars: cmd += ['-I', j] subprocess.check_call(cmd) with zipfile.ZipFile(options.out_apk, 'w') as z: path_transform = lambda p: None if p != 'AndroidManifest.xml' else p build_utils.MergeZips(z, [tmp_apk.name], path_transform=path_transform) path_transform = lambda p: None if p == 'AndroidManifest.xml' else p build_utils.MergeZips(z, [options.in_apk], path_transform=path_transform)
def main(args): build_utils.InitLogging('RESOURCE_DEBUG') args = build_utils.ExpandFileArgs(args) options = _ParseArgs(args) path = options.arsc_path or options.proto_path debug_temp_resources_dir = os.environ.get('TEMP_RESOURCES_DIR') if debug_temp_resources_dir: path = os.path.join(debug_temp_resources_dir, os.path.basename(path)) else: # Use a deterministic temp directory since .pb files embed the absolute # path of resources: crbug.com/939984 path = path + '.tmpdir' build_utils.DeleteDirectory(path) build_utils.MakeDirectory(path) with resource_utils.BuildContext( temp_dir=path, keep_files=bool(debug_temp_resources_dir)) as build: manifest_package_name = _PackageApk(options, build) # 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() custom_root_package_name = options.r_java_root_package_name grandparent_custom_package_name = None if options.package_name and not options.arsc_package_name: # Feature modules have their own custom root package name and should # inherit from the appropriate base module package. This behaviour should # not be present for test apks with an apk under test. Thus, # arsc_package_name is used as it is only defined for test apks with an # apk under test. custom_root_package_name = options.package_name grandparent_custom_package_name = options.r_java_root_package_name if options.shared_resources or options.app_as_shared_lib: package_for_library = manifest_package_name else: package_for_library = None logging.debug('Creating R.srcjar') resource_utils.CreateRJavaFiles( build.srcjar_dir, package_for_library, build.r_txt_path, options.extra_res_packages, options.extra_r_text_files, rjava_build_options, options.srcjar_out, custom_root_package_name, grandparent_custom_package_name, options.extra_main_r_text_files) build_utils.ZipDir(build.srcjar_path, build.srcjar_dir) # Sanity check that the created resources have the expected package ID. logging.debug('Performing sanity check') if options.package_id: expected_id = options.package_id elif options.shared_resources: expected_id = 0 else: expected_id = 127 # == '0x7f'. _, package_id = resource_utils.ExtractArscPackage( options.aapt2_path, build.arsc_path if options.arsc_path else build.proto_path) if package_id != expected_id: raise Exception('Invalid package ID 0x%x (expected 0x%x)' % (package_id, expected_id)) logging.debug('Copying outputs') _WriteOutputs(options, build) if options.depfile: build_utils.WriteDepfile(options.depfile, options.srcjar_out, inputs=options.dependencies_res_zips + options.extra_r_text_files, add_pydeps=False)
def main(args): build_utils.InitLogging('RESOURCE_DEBUG') args = build_utils.ExpandFileArgs(args) options = _ParseArgs(args) if options.expected_file: actual_data = _CreateNormalizedManifestForVerification(options) diff_utils.CheckExpectations(actual_data, options) if options.only_verify_expectations: return path = options.arsc_path or options.proto_path debug_temp_resources_dir = os.environ.get('TEMP_RESOURCES_DIR') if debug_temp_resources_dir: path = os.path.join(debug_temp_resources_dir, os.path.basename(path)) else: # Use a deterministic temp directory since .pb files embed the absolute # path of resources: crbug.com/939984 path = path + '.tmpdir' build_utils.DeleteDirectory(path) with resource_utils.BuildContext( temp_dir=path, keep_files=bool(debug_temp_resources_dir)) as build: manifest_package_name = _PackageApk(options, build) # If --shared-resources-allowlist is used, all the 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_allowlist: rjava_build_options.ExportSomeResources( options.shared_resources_allowlist) rjava_build_options.GenerateOnResourcesLoaded() if options.shared_resources: # The final resources will only be used in WebLayer, so hardcode the # package ID to be what WebLayer expects. rjava_build_options.SetFinalPackageId( protoresources.SHARED_LIBRARY_HARDCODED_ID) elif options.shared_resources or options.app_as_shared_lib: rjava_build_options.ExportAllResources() rjava_build_options.GenerateOnResourcesLoaded() custom_root_package_name = options.r_java_root_package_name grandparent_custom_package_name = None # Always generate an R.java file for the package listed in # AndroidManifest.xml because this is where Android framework looks to find # onResourcesLoaded() for shared library apks. While not actually necessary # for application apks, it also doesn't hurt. apk_package_name = manifest_package_name if options.package_name and not options.arsc_package_name: # Feature modules have their own custom root package name and should # inherit from the appropriate base module package. This behaviour should # not be present for test apks with an apk under test. Thus, # arsc_package_name is used as it is only defined for test apks with an # apk under test. custom_root_package_name = options.package_name grandparent_custom_package_name = options.r_java_root_package_name # Feature modules have the same manifest package as the base module but # they should not create an R.java for said manifest package because it # will be created in the base module. apk_package_name = None logging.debug('Creating R.srcjar') resource_utils.CreateRJavaFiles( build.srcjar_dir, apk_package_name, build.r_txt_path, options.extra_res_packages, rjava_build_options, options.srcjar_out, custom_root_package_name, grandparent_custom_package_name, options.extra_main_r_text_files) build_utils.ZipDir(build.srcjar_path, build.srcjar_dir) # Sanity check that the created resources have the expected package ID. logging.debug('Performing sanity check') if options.package_id: expected_id = options.package_id elif options.shared_resources: expected_id = 0 else: expected_id = 127 # == '0x7f'. _, package_id = resource_utils.ExtractArscPackage( options.aapt2_path, build.arsc_path if options.arsc_path else build.proto_path) if package_id != expected_id: raise Exception('Invalid package ID 0x%x (expected 0x%x)' % (package_id, expected_id)) logging.debug('Copying outputs') _WriteOutputs(options, build) if options.depfile: depfile_deps = (options.dependencies_res_zips + options.dependencies_res_zip_overlays + options.extra_main_r_text_files + options.include_resources) build_utils.WriteDepfile(options.depfile, options.srcjar_out, depfile_deps)
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.arsc_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: _PackageApk(options, build) # 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() custom_root_package_name = None grandparent_custom_package_name = None if options.arsc_package_name: # This is for test apks with an apk under test. If we have an apk under # test we don't want to name the resources for both the test apk and the # under test apk "base". This special case lets us name the test apk's # resources "test". custom_root_package_name = 'test' elif options.package_name: # If there exists a custom package name such as vr for a feature module, # pass the name and base for the parent_custom_root_package_name. custom_root_package_name = options.package_name grandparent_custom_package_name = 'base' else: # No grandparent_custom_package_name for base module custom_root_package_name = 'base' resource_utils.CreateRJavaFiles( build.srcjar_dir, None, build.r_txt_path, options.extra_res_packages, options.extra_r_text_files, rjava_build_options, options.srcjar_out, custom_root_package_name, grandparent_custom_package_name) build_utils.ZipDir(build.srcjar_path, build.srcjar_dir) # Sanity check that the created resources have the expected package ID. expected_id = _PackageIdFromOptions(options) if expected_id is None: expected_id = '0x00' if options.shared_resources else '0x7f' expected_id = int(expected_id, 16) _, package_id = resource_utils.ExtractArscPackage( options.aapt2_path, build.arsc_path if options.arsc_path else build.proto_path) if package_id != expected_id: raise Exception('Invalid package ID 0x%x (expected 0x%x)' % (package_id, expected_id)) _WriteOutputs(options, build) if options.depfile: build_utils.WriteDepfile(options.depfile, options.srcjar_out, inputs=options.dependencies_res_zips + options.extra_r_text_files, add_pydeps=False)
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) # Sanity check that the created resources have the expected package ID. expected_id = _PackageIdFromOptions(options) if expected_id is None: expected_id = '0x00' if options.shared_resources else '0x7f' expected_id = int(expected_id, 16) _, package_id = resource_utils.ExtractArscPackage( options.aapt2_path, options.apk_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 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.arsc_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: _PackageApk(options, build) # 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() custom_root_package_name = options.r_java_root_package_name grandparent_custom_package_name = None if options.package_name and not options.arsc_package_name: # Feature modules have their own custom root package name and should # inherit from the appropriate base module package. This behaviour should # not be present for test apks with an apk under test. Thus, # arsc_package_name is used as it is only defined for test apks with an # apk under test. custom_root_package_name = options.package_name grandparent_custom_package_name = options.r_java_root_package_name resource_utils.CreateRJavaFiles( build.srcjar_dir, None, build.r_txt_path, options.extra_res_packages, options.extra_r_text_files, rjava_build_options, options.srcjar_out, custom_root_package_name, grandparent_custom_package_name, options.extra_main_r_text_files) build_utils.ZipDir(build.srcjar_path, build.srcjar_dir) # Sanity check that the created resources have the expected package ID. expected_id = _PackageIdFromOptions(options) if expected_id is None: expected_id = '0x00' if options.shared_resources else '0x7f' expected_id = int(expected_id, 16) _, package_id = resource_utils.ExtractArscPackage( options.aapt2_path, build.arsc_path if options.arsc_path else build.proto_path) if package_id != expected_id: raise Exception('Invalid package ID 0x%x (expected 0x%x)' % (package_id, expected_id)) _WriteOutputs(options, build) if options.depfile: build_utils.WriteDepfile(options.depfile, options.srcjar_out, inputs=options.dependencies_res_zips + options.extra_r_text_files, add_pydeps=False)
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', '0x%02x' % 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 ] if options.package_id is not None: package_id = options.package_id elif options.shared_resources: package_id = 0 else: package_id = 0x7f _CreateStableIdsFile(options.use_resource_ids_path, build.stable_ids_path, fixed_manifest_package, package_id) 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() assert exit_code == 0, f'aapt2 link cmd failed with {exit_code=}' logging.debug('Finished: aapt2 link') if options.shared_resources: logging.debug('Resolving styleables in R.txt') # Need to resolve references because unused resource removal tool does not # support references in R.txt files. resource_utils.ResolveStyleableReferences(build.r_txt_path) 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 ]) # Sanity check that the created resources have the expected package ID. logging.debug('Performing sanity check') _, actual_package_id = resource_utils.ExtractArscPackage( options.aapt2_path, build.arsc_path if options.arsc_path else build.proto_path) # When there are no resources, ExtractArscPackage returns (None, None), in # this case there is no need to check for matching package ID. if actual_package_id is not None and actual_package_id != package_id: raise Exception('Invalid package ID 0x%x (expected 0x%x)' % (actual_package_id, package_id)) return desired_manifest_package_name