1*8975f5c5SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*8975f5c5SAndroid Build Coastguard Worker# 3*8975f5c5SAndroid Build Coastguard Worker# Copyright 2012 The Chromium Authors 4*8975f5c5SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 5*8975f5c5SAndroid Build Coastguard Worker# found in the LICENSE file. 6*8975f5c5SAndroid Build Coastguard Worker 7*8975f5c5SAndroid Build Coastguard Worker"""Compile Android resources into an intermediate APK. 8*8975f5c5SAndroid Build Coastguard Worker 9*8975f5c5SAndroid Build Coastguard WorkerThis can also generate an R.txt, and an .srcjar file containing the proper 10*8975f5c5SAndroid Build Coastguard Workerfinal R.java class for all resource packages the APK depends on. 11*8975f5c5SAndroid Build Coastguard Worker 12*8975f5c5SAndroid Build Coastguard WorkerThis will crunch images with aapt2. 13*8975f5c5SAndroid Build Coastguard Worker""" 14*8975f5c5SAndroid Build Coastguard Worker 15*8975f5c5SAndroid Build Coastguard Workerimport argparse 16*8975f5c5SAndroid Build Coastguard Workerimport collections 17*8975f5c5SAndroid Build Coastguard Workerimport contextlib 18*8975f5c5SAndroid Build Coastguard Workerimport filecmp 19*8975f5c5SAndroid Build Coastguard Workerimport hashlib 20*8975f5c5SAndroid Build Coastguard Workerimport logging 21*8975f5c5SAndroid Build Coastguard Workerimport os 22*8975f5c5SAndroid Build Coastguard Workerimport pathlib 23*8975f5c5SAndroid Build Coastguard Workerimport re 24*8975f5c5SAndroid Build Coastguard Workerimport shutil 25*8975f5c5SAndroid Build Coastguard Workerimport subprocess 26*8975f5c5SAndroid Build Coastguard Workerimport sys 27*8975f5c5SAndroid Build Coastguard Workerimport textwrap 28*8975f5c5SAndroid Build Coastguard Workerfrom xml.etree import ElementTree 29*8975f5c5SAndroid Build Coastguard Worker 30*8975f5c5SAndroid Build Coastguard Workerfrom util import build_utils 31*8975f5c5SAndroid Build Coastguard Workerfrom util import diff_utils 32*8975f5c5SAndroid Build Coastguard Workerfrom util import manifest_utils 33*8975f5c5SAndroid Build Coastguard Workerfrom util import parallel 34*8975f5c5SAndroid Build Coastguard Workerfrom util import protoresources 35*8975f5c5SAndroid Build Coastguard Workerfrom util import resource_utils 36*8975f5c5SAndroid Build Coastguard Workerimport action_helpers # build_utils adds //build to sys.path. 37*8975f5c5SAndroid Build Coastguard Workerimport zip_helpers 38*8975f5c5SAndroid Build Coastguard Worker 39*8975f5c5SAndroid Build Coastguard Worker 40*8975f5c5SAndroid Build Coastguard Worker# Pngs that we shouldn't convert to webp. Please add rationale when updating. 41*8975f5c5SAndroid Build Coastguard Worker_PNG_WEBP_EXCLUSION_PATTERN = re.compile('|'.join([ 42*8975f5c5SAndroid Build Coastguard Worker # Android requires pngs for 9-patch images. 43*8975f5c5SAndroid Build Coastguard Worker r'.*\.9\.png', 44*8975f5c5SAndroid Build Coastguard Worker # Daydream requires pngs for icon files. 45*8975f5c5SAndroid Build Coastguard Worker r'.*daydream_icon_.*\.png' 46*8975f5c5SAndroid Build Coastguard Worker])) 47*8975f5c5SAndroid Build Coastguard Worker 48*8975f5c5SAndroid Build Coastguard Worker 49*8975f5c5SAndroid Build Coastguard Workerdef _ParseArgs(args): 50*8975f5c5SAndroid Build Coastguard Worker """Parses command line options. 51*8975f5c5SAndroid Build Coastguard Worker 52*8975f5c5SAndroid Build Coastguard Worker Returns: 53*8975f5c5SAndroid Build Coastguard Worker An options object as from argparse.ArgumentParser.parse_args() 54*8975f5c5SAndroid Build Coastguard Worker """ 55*8975f5c5SAndroid Build Coastguard Worker parser = argparse.ArgumentParser(description=__doc__) 56*8975f5c5SAndroid Build Coastguard Worker 57*8975f5c5SAndroid Build Coastguard Worker input_opts = parser.add_argument_group('Input options') 58*8975f5c5SAndroid Build Coastguard Worker output_opts = parser.add_argument_group('Output options') 59*8975f5c5SAndroid Build Coastguard Worker 60*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument('--include-resources', 61*8975f5c5SAndroid Build Coastguard Worker action='append', 62*8975f5c5SAndroid Build Coastguard Worker required=True, 63*8975f5c5SAndroid Build Coastguard Worker help='Paths to arsc resource files used to link ' 64*8975f5c5SAndroid Build Coastguard Worker 'against. Can be specified multiple times.') 65*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 66*8975f5c5SAndroid Build Coastguard Worker '--dependencies-res-zips', 67*8975f5c5SAndroid Build Coastguard Worker default=[], 68*8975f5c5SAndroid Build Coastguard Worker help='Resources zip archives from dependents. Required to ' 69*8975f5c5SAndroid Build Coastguard Worker 'resolve @type/foo references into dependent libraries.') 70*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 71*8975f5c5SAndroid Build Coastguard Worker '--extra-res-packages', 72*8975f5c5SAndroid Build Coastguard Worker help='Additional package names to generate R.java files for.') 73*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 74*8975f5c5SAndroid Build Coastguard Worker '--aapt2-path', required=True, help='Path to the Android aapt2 tool.') 75*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 76*8975f5c5SAndroid Build Coastguard Worker '--android-manifest', required=True, help='AndroidManifest.xml path.') 77*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 78*8975f5c5SAndroid Build Coastguard Worker '--r-java-root-package-name', 79*8975f5c5SAndroid Build Coastguard Worker default='base', 80*8975f5c5SAndroid Build Coastguard Worker help='Short package name for this target\'s root R java file (ex. ' 81*8975f5c5SAndroid Build Coastguard Worker 'input of "base" would become gen.base_module). Defaults to "base".') 82*8975f5c5SAndroid Build Coastguard Worker group = input_opts.add_mutually_exclusive_group() 83*8975f5c5SAndroid Build Coastguard Worker group.add_argument( 84*8975f5c5SAndroid Build Coastguard Worker '--shared-resources', 85*8975f5c5SAndroid Build Coastguard Worker action='store_true', 86*8975f5c5SAndroid Build Coastguard Worker help='Make all resources in R.java non-final and allow the resource IDs ' 87*8975f5c5SAndroid Build Coastguard Worker 'to be reset to a different package index when the apk is loaded by ' 88*8975f5c5SAndroid Build Coastguard Worker 'another application at runtime.') 89*8975f5c5SAndroid Build Coastguard Worker group.add_argument( 90*8975f5c5SAndroid Build Coastguard Worker '--app-as-shared-lib', 91*8975f5c5SAndroid Build Coastguard Worker action='store_true', 92*8975f5c5SAndroid Build Coastguard Worker help='Same as --shared-resources, but also ensures all resource IDs are ' 93*8975f5c5SAndroid Build Coastguard Worker 'directly usable from the APK loaded as an application.') 94*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 95*8975f5c5SAndroid Build Coastguard Worker '--package-id', 96*8975f5c5SAndroid Build Coastguard Worker type=int, 97*8975f5c5SAndroid Build Coastguard Worker help='Decimal integer representing custom package ID for resources ' 98*8975f5c5SAndroid Build Coastguard Worker '(instead of 127==0x7f). Cannot be used with --shared-resources.') 99*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 100*8975f5c5SAndroid Build Coastguard Worker '--package-name', 101*8975f5c5SAndroid Build Coastguard Worker help='Package name that will be used to create R class.') 102*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 103*8975f5c5SAndroid Build Coastguard Worker '--rename-manifest-package', help='Package name to force AAPT to use.') 104*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 105*8975f5c5SAndroid Build Coastguard Worker '--arsc-package-name', 106*8975f5c5SAndroid Build Coastguard Worker help='Package name to set in manifest of resources.arsc file. This is ' 107*8975f5c5SAndroid Build Coastguard Worker 'only used for apks under test.') 108*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 109*8975f5c5SAndroid Build Coastguard Worker '--shared-resources-allowlist', 110*8975f5c5SAndroid Build Coastguard Worker help='An R.txt file acting as a allowlist for resources that should be ' 111*8975f5c5SAndroid Build Coastguard Worker 'non-final and have their package ID changed at runtime in R.java. ' 112*8975f5c5SAndroid Build Coastguard Worker 'Implies and overrides --shared-resources.') 113*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 114*8975f5c5SAndroid Build Coastguard Worker '--shared-resources-allowlist-locales', 115*8975f5c5SAndroid Build Coastguard Worker default='[]', 116*8975f5c5SAndroid Build Coastguard Worker help='Optional GN-list of locales. If provided, all strings corresponding' 117*8975f5c5SAndroid Build Coastguard Worker ' to this locale list will be kept in the final output for the ' 118*8975f5c5SAndroid Build Coastguard Worker 'resources identified through --shared-resources-allowlist, even ' 119*8975f5c5SAndroid Build Coastguard Worker 'if --locale-allowlist is being used.') 120*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 121*8975f5c5SAndroid Build Coastguard Worker '--use-resource-ids-path', 122*8975f5c5SAndroid Build Coastguard Worker help='Use resource IDs generated by aapt --emit-ids.') 123*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 124*8975f5c5SAndroid Build Coastguard Worker '--debuggable', 125*8975f5c5SAndroid Build Coastguard Worker action='store_true', 126*8975f5c5SAndroid Build Coastguard Worker help='Whether to add android:debuggable="true".') 127*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument('--static-library-version', 128*8975f5c5SAndroid Build Coastguard Worker help='Version code for static library.') 129*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument('--version-code', help='Version code for apk.') 130*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument('--version-name', help='Version name for apk.') 131*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 132*8975f5c5SAndroid Build Coastguard Worker '--min-sdk-version', required=True, help='android:minSdkVersion for APK.') 133*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 134*8975f5c5SAndroid Build Coastguard Worker '--target-sdk-version', 135*8975f5c5SAndroid Build Coastguard Worker required=True, 136*8975f5c5SAndroid Build Coastguard Worker help="android:targetSdkVersion for APK.") 137*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 138*8975f5c5SAndroid Build Coastguard Worker '--max-sdk-version', 139*8975f5c5SAndroid Build Coastguard Worker help="android:maxSdkVersion expected in AndroidManifest.xml.") 140*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 141*8975f5c5SAndroid Build Coastguard Worker '--manifest-package', help='Package name of the AndroidManifest.xml.') 142*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 143*8975f5c5SAndroid Build Coastguard Worker '--locale-allowlist', 144*8975f5c5SAndroid Build Coastguard Worker default='[]', 145*8975f5c5SAndroid Build Coastguard Worker help='GN list of languages to include. All other language configs will ' 146*8975f5c5SAndroid Build Coastguard Worker 'be stripped out. List may include a combination of Android locales ' 147*8975f5c5SAndroid Build Coastguard Worker 'or Chrome locales.') 148*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 149*8975f5c5SAndroid Build Coastguard Worker '--resource-exclusion-regex', 150*8975f5c5SAndroid Build Coastguard Worker default='', 151*8975f5c5SAndroid Build Coastguard Worker help='File-based filter for resources (applied before compiling)') 152*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 153*8975f5c5SAndroid Build Coastguard Worker '--resource-exclusion-exceptions', 154*8975f5c5SAndroid Build Coastguard Worker default='[]', 155*8975f5c5SAndroid Build Coastguard Worker help='GN list of globs that say which files to include even ' 156*8975f5c5SAndroid Build Coastguard Worker 'when --resource-exclusion-regex is set.') 157*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 158*8975f5c5SAndroid Build Coastguard Worker '--dependencies-res-zip-overlays', 159*8975f5c5SAndroid Build Coastguard Worker help='GN list with subset of --dependencies-res-zips to use overlay ' 160*8975f5c5SAndroid Build Coastguard Worker 'semantics for.') 161*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 162*8975f5c5SAndroid Build Coastguard Worker '--values-filter-rules', 163*8975f5c5SAndroid Build Coastguard Worker help='GN list of source_glob:regex for filtering resources after they ' 164*8975f5c5SAndroid Build Coastguard Worker 'are compiled. Use this to filter out entries within values/ files.') 165*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument('--png-to-webp', action='store_true', 166*8975f5c5SAndroid Build Coastguard Worker help='Convert png files to webp format.') 167*8975f5c5SAndroid Build Coastguard Worker 168*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument('--webp-binary', default='', 169*8975f5c5SAndroid Build Coastguard Worker help='Path to the cwebp binary.') 170*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 171*8975f5c5SAndroid Build Coastguard Worker '--webp-cache-dir', help='The directory to store webp image cache.') 172*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 173*8975f5c5SAndroid Build Coastguard Worker '--is-bundle-module', 174*8975f5c5SAndroid Build Coastguard Worker action='store_true', 175*8975f5c5SAndroid Build Coastguard Worker help='Whether resources are being generated for a bundle module.') 176*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 177*8975f5c5SAndroid Build Coastguard Worker '--uses-split', 178*8975f5c5SAndroid Build Coastguard Worker help='Value to set uses-split to in the AndroidManifest.xml.') 179*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 180*8975f5c5SAndroid Build Coastguard Worker '--verification-version-code-offset', 181*8975f5c5SAndroid Build Coastguard Worker help='Subtract this from versionCode for expectation files') 182*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument( 183*8975f5c5SAndroid Build Coastguard Worker '--verification-library-version-offset', 184*8975f5c5SAndroid Build Coastguard Worker help='Subtract this from static-library version for expectation files') 185*8975f5c5SAndroid Build Coastguard Worker input_opts.add_argument('--xml-namespaces', 186*8975f5c5SAndroid Build Coastguard Worker action='store_true', 187*8975f5c5SAndroid Build Coastguard Worker help='Do not pass --no-xml-namespaces') 188*8975f5c5SAndroid Build Coastguard Worker 189*8975f5c5SAndroid Build Coastguard Worker action_helpers.add_depfile_arg(output_opts) 190*8975f5c5SAndroid Build Coastguard Worker output_opts.add_argument('--arsc-path', help='Apk output for arsc format.') 191*8975f5c5SAndroid Build Coastguard Worker output_opts.add_argument('--proto-path', help='Apk output for proto format.') 192*8975f5c5SAndroid Build Coastguard Worker output_opts.add_argument( 193*8975f5c5SAndroid Build Coastguard Worker '--info-path', help='Path to output info file for the partial apk.') 194*8975f5c5SAndroid Build Coastguard Worker output_opts.add_argument( 195*8975f5c5SAndroid Build Coastguard Worker '--srcjar-out', 196*8975f5c5SAndroid Build Coastguard Worker help='Path to srcjar to contain generated R.java.') 197*8975f5c5SAndroid Build Coastguard Worker output_opts.add_argument('--r-text-out', 198*8975f5c5SAndroid Build Coastguard Worker help='Path to store the generated R.txt file.') 199*8975f5c5SAndroid Build Coastguard Worker output_opts.add_argument( 200*8975f5c5SAndroid Build Coastguard Worker '--proguard-file', help='Path to proguard.txt generated file.') 201*8975f5c5SAndroid Build Coastguard Worker output_opts.add_argument( 202*8975f5c5SAndroid Build Coastguard Worker '--emit-ids-out', help='Path to file produced by aapt2 --emit-ids.') 203*8975f5c5SAndroid Build Coastguard Worker 204*8975f5c5SAndroid Build Coastguard Worker diff_utils.AddCommandLineFlags(parser) 205*8975f5c5SAndroid Build Coastguard Worker options = parser.parse_args(args) 206*8975f5c5SAndroid Build Coastguard Worker 207*8975f5c5SAndroid Build Coastguard Worker options.include_resources = action_helpers.parse_gn_list( 208*8975f5c5SAndroid Build Coastguard Worker options.include_resources) 209*8975f5c5SAndroid Build Coastguard Worker options.dependencies_res_zips = action_helpers.parse_gn_list( 210*8975f5c5SAndroid Build Coastguard Worker options.dependencies_res_zips) 211*8975f5c5SAndroid Build Coastguard Worker options.extra_res_packages = action_helpers.parse_gn_list( 212*8975f5c5SAndroid Build Coastguard Worker options.extra_res_packages) 213*8975f5c5SAndroid Build Coastguard Worker options.locale_allowlist = action_helpers.parse_gn_list( 214*8975f5c5SAndroid Build Coastguard Worker options.locale_allowlist) 215*8975f5c5SAndroid Build Coastguard Worker options.shared_resources_allowlist_locales = action_helpers.parse_gn_list( 216*8975f5c5SAndroid Build Coastguard Worker options.shared_resources_allowlist_locales) 217*8975f5c5SAndroid Build Coastguard Worker options.resource_exclusion_exceptions = action_helpers.parse_gn_list( 218*8975f5c5SAndroid Build Coastguard Worker options.resource_exclusion_exceptions) 219*8975f5c5SAndroid Build Coastguard Worker options.dependencies_res_zip_overlays = action_helpers.parse_gn_list( 220*8975f5c5SAndroid Build Coastguard Worker options.dependencies_res_zip_overlays) 221*8975f5c5SAndroid Build Coastguard Worker options.values_filter_rules = action_helpers.parse_gn_list( 222*8975f5c5SAndroid Build Coastguard Worker options.values_filter_rules) 223*8975f5c5SAndroid Build Coastguard Worker 224*8975f5c5SAndroid Build Coastguard Worker if not options.arsc_path and not options.proto_path: 225*8975f5c5SAndroid Build Coastguard Worker parser.error('One of --arsc-path or --proto-path is required.') 226*8975f5c5SAndroid Build Coastguard Worker 227*8975f5c5SAndroid Build Coastguard Worker if options.package_id and options.shared_resources: 228*8975f5c5SAndroid Build Coastguard Worker parser.error('--package-id and --shared-resources are mutually exclusive') 229*8975f5c5SAndroid Build Coastguard Worker 230*8975f5c5SAndroid Build Coastguard Worker if options.static_library_version and (options.static_library_version != 231*8975f5c5SAndroid Build Coastguard Worker options.version_code): 232*8975f5c5SAndroid Build Coastguard Worker assert options.static_library_version == options.version_code, ( 233*8975f5c5SAndroid Build Coastguard Worker f'static_library_version={options.static_library_version} must equal ' 234*8975f5c5SAndroid Build Coastguard Worker f'version_code={options.version_code}. Please verify the version code ' 235*8975f5c5SAndroid Build Coastguard Worker 'map for this target is defined correctly.') 236*8975f5c5SAndroid Build Coastguard Worker 237*8975f5c5SAndroid Build Coastguard Worker return options 238*8975f5c5SAndroid Build Coastguard Worker 239*8975f5c5SAndroid Build Coastguard Worker 240*8975f5c5SAndroid Build Coastguard Workerdef _IterFiles(root_dir): 241*8975f5c5SAndroid Build Coastguard Worker for root, _, files in os.walk(root_dir): 242*8975f5c5SAndroid Build Coastguard Worker for f in files: 243*8975f5c5SAndroid Build Coastguard Worker yield os.path.join(root, f) 244*8975f5c5SAndroid Build Coastguard Worker 245*8975f5c5SAndroid Build Coastguard Worker 246*8975f5c5SAndroid Build Coastguard Workerdef _RenameLocaleResourceDirs(resource_dirs, path_info): 247*8975f5c5SAndroid Build Coastguard Worker """Rename locale resource directories into standard names when necessary. 248*8975f5c5SAndroid Build Coastguard Worker 249*8975f5c5SAndroid Build Coastguard Worker This is necessary to deal with the fact that older Android releases only 250*8975f5c5SAndroid Build Coastguard Worker support ISO 639-1 two-letter codes, and sometimes even obsolete versions 251*8975f5c5SAndroid Build Coastguard Worker of them. 252*8975f5c5SAndroid Build Coastguard Worker 253*8975f5c5SAndroid Build Coastguard Worker In practice it means: 254*8975f5c5SAndroid Build Coastguard Worker * 3-letter ISO 639-2 qualifiers are renamed under a corresponding 255*8975f5c5SAndroid Build Coastguard Worker 2-letter one. E.g. for Filipino, strings under values-fil/ will be moved 256*8975f5c5SAndroid Build Coastguard Worker to a new corresponding values-tl/ sub-directory. 257*8975f5c5SAndroid Build Coastguard Worker 258*8975f5c5SAndroid Build Coastguard Worker * Modern ISO 639-1 codes will be renamed to their obsolete variant 259*8975f5c5SAndroid Build Coastguard Worker for Indonesian, Hebrew and Yiddish (e.g. 'values-in/ -> values-id/). 260*8975f5c5SAndroid Build Coastguard Worker 261*8975f5c5SAndroid Build Coastguard Worker * Norwegian macrolanguage strings will be renamed to Bokmal (main 262*8975f5c5SAndroid Build Coastguard Worker Norway language). See http://crbug.com/920960. In practice this 263*8975f5c5SAndroid Build Coastguard Worker means that 'values-no/ -> values-nb/' unless 'values-nb/' already 264*8975f5c5SAndroid Build Coastguard Worker exists. 265*8975f5c5SAndroid Build Coastguard Worker 266*8975f5c5SAndroid Build Coastguard Worker * BCP 47 langauge tags will be renamed to an equivalent ISO 639-1 267*8975f5c5SAndroid Build Coastguard Worker locale qualifier if possible (e.g. 'values-b+en+US/ -> values-en-rUS'). 268*8975f5c5SAndroid Build Coastguard Worker 269*8975f5c5SAndroid Build Coastguard Worker Args: 270*8975f5c5SAndroid Build Coastguard Worker resource_dirs: list of top-level resource directories. 271*8975f5c5SAndroid Build Coastguard Worker """ 272*8975f5c5SAndroid Build Coastguard Worker for resource_dir in resource_dirs: 273*8975f5c5SAndroid Build Coastguard Worker ignore_dirs = {} 274*8975f5c5SAndroid Build Coastguard Worker for path in _IterFiles(resource_dir): 275*8975f5c5SAndroid Build Coastguard Worker locale = resource_utils.FindLocaleInStringResourceFilePath(path) 276*8975f5c5SAndroid Build Coastguard Worker if not locale: 277*8975f5c5SAndroid Build Coastguard Worker continue 278*8975f5c5SAndroid Build Coastguard Worker cr_locale = resource_utils.ToChromiumLocaleName(locale) 279*8975f5c5SAndroid Build Coastguard Worker if not cr_locale: 280*8975f5c5SAndroid Build Coastguard Worker continue # Unsupported Android locale qualifier!? 281*8975f5c5SAndroid Build Coastguard Worker locale2 = resource_utils.ToAndroidLocaleName(cr_locale) 282*8975f5c5SAndroid Build Coastguard Worker if locale != locale2: 283*8975f5c5SAndroid Build Coastguard Worker path2 = path.replace('/values-%s/' % locale, '/values-%s/' % locale2) 284*8975f5c5SAndroid Build Coastguard Worker if path == path2: 285*8975f5c5SAndroid Build Coastguard Worker raise Exception('Could not substitute locale %s for %s in %s' % 286*8975f5c5SAndroid Build Coastguard Worker (locale, locale2, path)) 287*8975f5c5SAndroid Build Coastguard Worker 288*8975f5c5SAndroid Build Coastguard Worker # Ignore rather than rename when the destination resources config 289*8975f5c5SAndroid Build Coastguard Worker # already exists. 290*8975f5c5SAndroid Build Coastguard Worker # e.g. some libraries provide both values-nb/ and values-no/. 291*8975f5c5SAndroid Build Coastguard Worker # e.g. material design provides: 292*8975f5c5SAndroid Build Coastguard Worker # * res/values-rUS/values-rUS.xml 293*8975f5c5SAndroid Build Coastguard Worker # * res/values-b+es+419/values-b+es+419.xml 294*8975f5c5SAndroid Build Coastguard Worker config_dir = os.path.dirname(path2) 295*8975f5c5SAndroid Build Coastguard Worker already_has_renamed_config = ignore_dirs.get(config_dir) 296*8975f5c5SAndroid Build Coastguard Worker if already_has_renamed_config is None: 297*8975f5c5SAndroid Build Coastguard Worker # Cache the result of the first time the directory is encountered 298*8975f5c5SAndroid Build Coastguard Worker # since subsequent encounters will find the directory already exists 299*8975f5c5SAndroid Build Coastguard Worker # (due to the rename). 300*8975f5c5SAndroid Build Coastguard Worker already_has_renamed_config = os.path.exists(config_dir) 301*8975f5c5SAndroid Build Coastguard Worker ignore_dirs[config_dir] = already_has_renamed_config 302*8975f5c5SAndroid Build Coastguard Worker if already_has_renamed_config: 303*8975f5c5SAndroid Build Coastguard Worker continue 304*8975f5c5SAndroid Build Coastguard Worker 305*8975f5c5SAndroid Build Coastguard Worker build_utils.MakeDirectory(os.path.dirname(path2)) 306*8975f5c5SAndroid Build Coastguard Worker shutil.move(path, path2) 307*8975f5c5SAndroid Build Coastguard Worker path_info.RegisterRename( 308*8975f5c5SAndroid Build Coastguard Worker os.path.relpath(path, resource_dir), 309*8975f5c5SAndroid Build Coastguard Worker os.path.relpath(path2, resource_dir)) 310*8975f5c5SAndroid Build Coastguard Worker 311*8975f5c5SAndroid Build Coastguard Worker 312*8975f5c5SAndroid Build Coastguard Workerdef _ToAndroidLocales(locale_allowlist): 313*8975f5c5SAndroid Build Coastguard Worker """Converts the list of Chrome locales to Android config locale qualifiers. 314*8975f5c5SAndroid Build Coastguard Worker 315*8975f5c5SAndroid Build Coastguard Worker Args: 316*8975f5c5SAndroid Build Coastguard Worker locale_allowlist: A list of Chromium locale names. 317*8975f5c5SAndroid Build Coastguard Worker Returns: 318*8975f5c5SAndroid Build Coastguard Worker A set of matching Android config locale qualifier names. 319*8975f5c5SAndroid Build Coastguard Worker """ 320*8975f5c5SAndroid Build Coastguard Worker ret = set() 321*8975f5c5SAndroid Build Coastguard Worker for locale in locale_allowlist: 322*8975f5c5SAndroid Build Coastguard Worker locale = resource_utils.ToAndroidLocaleName(locale) 323*8975f5c5SAndroid Build Coastguard Worker if locale is None or ('-' in locale and '-r' not in locale): 324*8975f5c5SAndroid Build Coastguard Worker raise Exception('Unsupported Chromium locale name: %s' % locale) 325*8975f5c5SAndroid Build Coastguard Worker ret.add(locale) 326*8975f5c5SAndroid Build Coastguard Worker # Always keep non-regional fall-backs. 327*8975f5c5SAndroid Build Coastguard Worker language = locale.split('-')[0] 328*8975f5c5SAndroid Build Coastguard Worker ret.add(language) 329*8975f5c5SAndroid Build Coastguard Worker 330*8975f5c5SAndroid Build Coastguard Worker return ret 331*8975f5c5SAndroid Build Coastguard Worker 332*8975f5c5SAndroid Build Coastguard Worker 333*8975f5c5SAndroid Build Coastguard Workerdef _MoveImagesToNonMdpiFolders(res_root, path_info): 334*8975f5c5SAndroid Build Coastguard Worker """Move images from drawable-*-mdpi-* folders to drawable-* folders. 335*8975f5c5SAndroid Build Coastguard Worker 336*8975f5c5SAndroid Build Coastguard Worker Why? http://crbug.com/289843 337*8975f5c5SAndroid Build Coastguard Worker """ 338*8975f5c5SAndroid Build Coastguard Worker for src_dir_name in os.listdir(res_root): 339*8975f5c5SAndroid Build Coastguard Worker src_components = src_dir_name.split('-') 340*8975f5c5SAndroid Build Coastguard Worker if src_components[0] != 'drawable' or 'mdpi' not in src_components: 341*8975f5c5SAndroid Build Coastguard Worker continue 342*8975f5c5SAndroid Build Coastguard Worker src_dir = os.path.join(res_root, src_dir_name) 343*8975f5c5SAndroid Build Coastguard Worker if not os.path.isdir(src_dir): 344*8975f5c5SAndroid Build Coastguard Worker continue 345*8975f5c5SAndroid Build Coastguard Worker dst_components = [c for c in src_components if c != 'mdpi'] 346*8975f5c5SAndroid Build Coastguard Worker assert dst_components != src_components 347*8975f5c5SAndroid Build Coastguard Worker dst_dir_name = '-'.join(dst_components) 348*8975f5c5SAndroid Build Coastguard Worker dst_dir = os.path.join(res_root, dst_dir_name) 349*8975f5c5SAndroid Build Coastguard Worker build_utils.MakeDirectory(dst_dir) 350*8975f5c5SAndroid Build Coastguard Worker for src_file_name in os.listdir(src_dir): 351*8975f5c5SAndroid Build Coastguard Worker src_file = os.path.join(src_dir, src_file_name) 352*8975f5c5SAndroid Build Coastguard Worker dst_file = os.path.join(dst_dir, src_file_name) 353*8975f5c5SAndroid Build Coastguard Worker assert not os.path.lexists(dst_file) 354*8975f5c5SAndroid Build Coastguard Worker shutil.move(src_file, dst_file) 355*8975f5c5SAndroid Build Coastguard Worker path_info.RegisterRename( 356*8975f5c5SAndroid Build Coastguard Worker os.path.relpath(src_file, res_root), 357*8975f5c5SAndroid Build Coastguard Worker os.path.relpath(dst_file, res_root)) 358*8975f5c5SAndroid Build Coastguard Worker 359*8975f5c5SAndroid Build Coastguard Worker 360*8975f5c5SAndroid Build Coastguard Workerdef _DeterminePlatformVersion(aapt2_path, jar_candidates): 361*8975f5c5SAndroid Build Coastguard Worker def maybe_extract_version(j): 362*8975f5c5SAndroid Build Coastguard Worker try: 363*8975f5c5SAndroid Build Coastguard Worker return resource_utils.ExtractBinaryManifestValues(aapt2_path, j) 364*8975f5c5SAndroid Build Coastguard Worker except build_utils.CalledProcessError: 365*8975f5c5SAndroid Build Coastguard Worker return None 366*8975f5c5SAndroid Build Coastguard Worker 367*8975f5c5SAndroid Build Coastguard Worker def is_sdk_jar(jar_name): 368*8975f5c5SAndroid Build Coastguard Worker if jar_name in ('android.jar', 'android_system.jar'): 369*8975f5c5SAndroid Build Coastguard Worker return True 370*8975f5c5SAndroid Build Coastguard Worker # Robolectric jar looks a bit different. 371*8975f5c5SAndroid Build Coastguard Worker return 'android-all' in jar_name and 'robolectric' in jar_name 372*8975f5c5SAndroid Build Coastguard Worker 373*8975f5c5SAndroid Build Coastguard Worker android_sdk_jars = [ 374*8975f5c5SAndroid Build Coastguard Worker j for j in jar_candidates if is_sdk_jar(os.path.basename(j)) 375*8975f5c5SAndroid Build Coastguard Worker ] 376*8975f5c5SAndroid Build Coastguard Worker extract_all = [maybe_extract_version(j) for j in android_sdk_jars] 377*8975f5c5SAndroid Build Coastguard Worker extract_all = [x for x in extract_all if x] 378*8975f5c5SAndroid Build Coastguard Worker if len(extract_all) == 0: 379*8975f5c5SAndroid Build Coastguard Worker raise Exception( 380*8975f5c5SAndroid Build Coastguard Worker 'Unable to find android SDK jar among candidates: %s' 381*8975f5c5SAndroid Build Coastguard Worker % ', '.join(android_sdk_jars)) 382*8975f5c5SAndroid Build Coastguard Worker if len(extract_all) > 1: 383*8975f5c5SAndroid Build Coastguard Worker raise Exception( 384*8975f5c5SAndroid Build Coastguard Worker 'Found multiple android SDK jars among candidates: %s' 385*8975f5c5SAndroid Build Coastguard Worker % ', '.join(android_sdk_jars)) 386*8975f5c5SAndroid Build Coastguard Worker platform_version_code, platform_version_name = extract_all.pop()[:2] 387*8975f5c5SAndroid Build Coastguard Worker return platform_version_code, platform_version_name 388*8975f5c5SAndroid Build Coastguard Worker 389*8975f5c5SAndroid Build Coastguard Worker 390*8975f5c5SAndroid Build Coastguard Workerdef _FixManifest(options, temp_dir): 391*8975f5c5SAndroid Build Coastguard Worker """Fix the APK's AndroidManifest.xml. 392*8975f5c5SAndroid Build Coastguard Worker 393*8975f5c5SAndroid Build Coastguard Worker This adds any missing namespaces for 'android' and 'tools', and 394*8975f5c5SAndroid Build Coastguard Worker sets certains elements like 'platformBuildVersionCode' or 395*8975f5c5SAndroid Build Coastguard Worker 'android:debuggable' depending on the content of |options|. 396*8975f5c5SAndroid Build Coastguard Worker 397*8975f5c5SAndroid Build Coastguard Worker Args: 398*8975f5c5SAndroid Build Coastguard Worker options: The command-line arguments tuple. 399*8975f5c5SAndroid Build Coastguard Worker temp_dir: A temporary directory where the fixed manifest will be written to. 400*8975f5c5SAndroid Build Coastguard Worker Returns: 401*8975f5c5SAndroid Build Coastguard Worker Tuple of: 402*8975f5c5SAndroid Build Coastguard Worker * Manifest path within |temp_dir|. 403*8975f5c5SAndroid Build Coastguard Worker * Original package_name. 404*8975f5c5SAndroid Build Coastguard Worker * Manifest package name. 405*8975f5c5SAndroid Build Coastguard Worker """ 406*8975f5c5SAndroid Build Coastguard Worker doc, manifest_node, app_node = manifest_utils.ParseManifest( 407*8975f5c5SAndroid Build Coastguard Worker options.android_manifest) 408*8975f5c5SAndroid Build Coastguard Worker 409*8975f5c5SAndroid Build Coastguard Worker # merge_manifest.py also sets package & <uses-sdk>. We may want to ensure 410*8975f5c5SAndroid Build Coastguard Worker # manifest merger is always enabled and remove these command-line arguments. 411*8975f5c5SAndroid Build Coastguard Worker manifest_utils.SetUsesSdk(manifest_node, options.target_sdk_version, 412*8975f5c5SAndroid Build Coastguard Worker options.min_sdk_version, options.max_sdk_version) 413*8975f5c5SAndroid Build Coastguard Worker orig_package = manifest_node.get('package') or options.manifest_package 414*8975f5c5SAndroid Build Coastguard Worker fixed_package = (options.arsc_package_name or options.manifest_package 415*8975f5c5SAndroid Build Coastguard Worker or orig_package) 416*8975f5c5SAndroid Build Coastguard Worker manifest_node.set('package', fixed_package) 417*8975f5c5SAndroid Build Coastguard Worker 418*8975f5c5SAndroid Build Coastguard Worker platform_version_code, platform_version_name = _DeterminePlatformVersion( 419*8975f5c5SAndroid Build Coastguard Worker options.aapt2_path, options.include_resources) 420*8975f5c5SAndroid Build Coastguard Worker manifest_node.set('platformBuildVersionCode', platform_version_code) 421*8975f5c5SAndroid Build Coastguard Worker manifest_node.set('platformBuildVersionName', platform_version_name) 422*8975f5c5SAndroid Build Coastguard Worker if options.version_code: 423*8975f5c5SAndroid Build Coastguard Worker manifest_utils.NamespacedSet(manifest_node, 'versionCode', 424*8975f5c5SAndroid Build Coastguard Worker options.version_code) 425*8975f5c5SAndroid Build Coastguard Worker if options.version_name: 426*8975f5c5SAndroid Build Coastguard Worker manifest_utils.NamespacedSet(manifest_node, 'versionName', 427*8975f5c5SAndroid Build Coastguard Worker options.version_name) 428*8975f5c5SAndroid Build Coastguard Worker if options.debuggable: 429*8975f5c5SAndroid Build Coastguard Worker manifest_utils.NamespacedSet(app_node, 'debuggable', 'true') 430*8975f5c5SAndroid Build Coastguard Worker 431*8975f5c5SAndroid Build Coastguard Worker if options.uses_split: 432*8975f5c5SAndroid Build Coastguard Worker uses_split = ElementTree.SubElement(manifest_node, 'uses-split') 433*8975f5c5SAndroid Build Coastguard Worker manifest_utils.NamespacedSet(uses_split, 'name', options.uses_split) 434*8975f5c5SAndroid Build Coastguard Worker 435*8975f5c5SAndroid Build Coastguard Worker # Make sure the min-sdk condition is not less than the min-sdk of the bundle. 436*8975f5c5SAndroid Build Coastguard Worker for min_sdk_node in manifest_node.iter('{%s}min-sdk' % 437*8975f5c5SAndroid Build Coastguard Worker manifest_utils.DIST_NAMESPACE): 438*8975f5c5SAndroid Build Coastguard Worker dist_value = '{%s}value' % manifest_utils.DIST_NAMESPACE 439*8975f5c5SAndroid Build Coastguard Worker if int(min_sdk_node.get(dist_value)) < int(options.min_sdk_version): 440*8975f5c5SAndroid Build Coastguard Worker min_sdk_node.set(dist_value, options.min_sdk_version) 441*8975f5c5SAndroid Build Coastguard Worker 442*8975f5c5SAndroid Build Coastguard Worker debug_manifest_path = os.path.join(temp_dir, 'AndroidManifest.xml') 443*8975f5c5SAndroid Build Coastguard Worker manifest_utils.SaveManifest(doc, debug_manifest_path) 444*8975f5c5SAndroid Build Coastguard Worker return debug_manifest_path, orig_package, fixed_package 445*8975f5c5SAndroid Build Coastguard Worker 446*8975f5c5SAndroid Build Coastguard Worker 447*8975f5c5SAndroid Build Coastguard Workerdef _CreateKeepPredicate(resource_exclusion_regex, 448*8975f5c5SAndroid Build Coastguard Worker resource_exclusion_exceptions): 449*8975f5c5SAndroid Build Coastguard Worker """Return a predicate lambda to determine which resource files to keep. 450*8975f5c5SAndroid Build Coastguard Worker 451*8975f5c5SAndroid Build Coastguard Worker Args: 452*8975f5c5SAndroid Build Coastguard Worker resource_exclusion_regex: A regular expression describing all resources 453*8975f5c5SAndroid Build Coastguard Worker to exclude, except if they are mip-maps, or if they are listed 454*8975f5c5SAndroid Build Coastguard Worker in |resource_exclusion_exceptions|. 455*8975f5c5SAndroid Build Coastguard Worker resource_exclusion_exceptions: A list of glob patterns corresponding 456*8975f5c5SAndroid Build Coastguard Worker to exceptions to the |resource_exclusion_regex|. 457*8975f5c5SAndroid Build Coastguard Worker Returns: 458*8975f5c5SAndroid Build Coastguard Worker A lambda that takes a path, and returns true if the corresponding file 459*8975f5c5SAndroid Build Coastguard Worker must be kept. 460*8975f5c5SAndroid Build Coastguard Worker """ 461*8975f5c5SAndroid Build Coastguard Worker predicate = lambda path: os.path.basename(path)[0] != '.' 462*8975f5c5SAndroid Build Coastguard Worker if resource_exclusion_regex == '': 463*8975f5c5SAndroid Build Coastguard Worker # Do not extract dotfiles (e.g. ".gitkeep"). aapt ignores them anyways. 464*8975f5c5SAndroid Build Coastguard Worker return predicate 465*8975f5c5SAndroid Build Coastguard Worker 466*8975f5c5SAndroid Build Coastguard Worker # A simple predicate that only removes (returns False for) paths covered by 467*8975f5c5SAndroid Build Coastguard Worker # the exclusion regex or listed as exceptions. 468*8975f5c5SAndroid Build Coastguard Worker return lambda path: ( 469*8975f5c5SAndroid Build Coastguard Worker not re.search(resource_exclusion_regex, path) or 470*8975f5c5SAndroid Build Coastguard Worker build_utils.MatchesGlob(path, resource_exclusion_exceptions)) 471*8975f5c5SAndroid Build Coastguard Worker 472*8975f5c5SAndroid Build Coastguard Worker 473*8975f5c5SAndroid Build Coastguard Workerdef _ComputeSha1(path): 474*8975f5c5SAndroid Build Coastguard Worker with open(path, 'rb') as f: 475*8975f5c5SAndroid Build Coastguard Worker data = f.read() 476*8975f5c5SAndroid Build Coastguard Worker return hashlib.sha1(data).hexdigest() 477*8975f5c5SAndroid Build Coastguard Worker 478*8975f5c5SAndroid Build Coastguard Worker 479*8975f5c5SAndroid Build Coastguard Workerdef _ConvertToWebPSingle(png_path, cwebp_binary, cwebp_version, webp_cache_dir): 480*8975f5c5SAndroid Build Coastguard Worker sha1_hash = _ComputeSha1(png_path) 481*8975f5c5SAndroid Build Coastguard Worker 482*8975f5c5SAndroid Build Coastguard Worker # The set of arguments that will appear in the cache key. 483*8975f5c5SAndroid Build Coastguard Worker quality_args = ['-m', '6', '-q', '100', '-lossless'] 484*8975f5c5SAndroid Build Coastguard Worker 485*8975f5c5SAndroid Build Coastguard Worker webp_cache_path = os.path.join( 486*8975f5c5SAndroid Build Coastguard Worker webp_cache_dir, '{}-{}-{}'.format(sha1_hash, cwebp_version, 487*8975f5c5SAndroid Build Coastguard Worker ''.join(quality_args))) 488*8975f5c5SAndroid Build Coastguard Worker # No need to add .webp. Android can load images fine without them. 489*8975f5c5SAndroid Build Coastguard Worker webp_path = os.path.splitext(png_path)[0] 490*8975f5c5SAndroid Build Coastguard Worker 491*8975f5c5SAndroid Build Coastguard Worker cache_hit = os.path.exists(webp_cache_path) 492*8975f5c5SAndroid Build Coastguard Worker if cache_hit: 493*8975f5c5SAndroid Build Coastguard Worker os.link(webp_cache_path, webp_path) 494*8975f5c5SAndroid Build Coastguard Worker else: 495*8975f5c5SAndroid Build Coastguard Worker # We place the generated webp image to webp_path, instead of in the 496*8975f5c5SAndroid Build Coastguard Worker # webp_cache_dir to avoid concurrency issues. 497*8975f5c5SAndroid Build Coastguard Worker args = [cwebp_binary, png_path, '-o', webp_path, '-quiet'] + quality_args 498*8975f5c5SAndroid Build Coastguard Worker subprocess.check_call(args) 499*8975f5c5SAndroid Build Coastguard Worker 500*8975f5c5SAndroid Build Coastguard Worker try: 501*8975f5c5SAndroid Build Coastguard Worker os.link(webp_path, webp_cache_path) 502*8975f5c5SAndroid Build Coastguard Worker except OSError: 503*8975f5c5SAndroid Build Coastguard Worker # Because of concurrent run, a webp image may already exists in 504*8975f5c5SAndroid Build Coastguard Worker # webp_cache_path. 505*8975f5c5SAndroid Build Coastguard Worker pass 506*8975f5c5SAndroid Build Coastguard Worker 507*8975f5c5SAndroid Build Coastguard Worker os.remove(png_path) 508*8975f5c5SAndroid Build Coastguard Worker original_dir = os.path.dirname(os.path.dirname(png_path)) 509*8975f5c5SAndroid Build Coastguard Worker rename_tuple = (os.path.relpath(png_path, original_dir), 510*8975f5c5SAndroid Build Coastguard Worker os.path.relpath(webp_path, original_dir)) 511*8975f5c5SAndroid Build Coastguard Worker return rename_tuple, cache_hit 512*8975f5c5SAndroid Build Coastguard Worker 513*8975f5c5SAndroid Build Coastguard Worker 514*8975f5c5SAndroid Build Coastguard Workerdef _ConvertToWebP(cwebp_binary, png_paths, path_info, webp_cache_dir): 515*8975f5c5SAndroid Build Coastguard Worker cwebp_version = subprocess.check_output([cwebp_binary, '-version']).rstrip() 516*8975f5c5SAndroid Build Coastguard Worker shard_args = [(f, ) for f in png_paths 517*8975f5c5SAndroid Build Coastguard Worker if not _PNG_WEBP_EXCLUSION_PATTERN.match(f)] 518*8975f5c5SAndroid Build Coastguard Worker 519*8975f5c5SAndroid Build Coastguard Worker build_utils.MakeDirectory(webp_cache_dir) 520*8975f5c5SAndroid Build Coastguard Worker results = parallel.BulkForkAndCall(_ConvertToWebPSingle, 521*8975f5c5SAndroid Build Coastguard Worker shard_args, 522*8975f5c5SAndroid Build Coastguard Worker cwebp_binary=cwebp_binary, 523*8975f5c5SAndroid Build Coastguard Worker cwebp_version=cwebp_version, 524*8975f5c5SAndroid Build Coastguard Worker webp_cache_dir=webp_cache_dir) 525*8975f5c5SAndroid Build Coastguard Worker total_cache_hits = 0 526*8975f5c5SAndroid Build Coastguard Worker for rename_tuple, cache_hit in results: 527*8975f5c5SAndroid Build Coastguard Worker path_info.RegisterRename(*rename_tuple) 528*8975f5c5SAndroid Build Coastguard Worker total_cache_hits += int(cache_hit) 529*8975f5c5SAndroid Build Coastguard Worker 530*8975f5c5SAndroid Build Coastguard Worker logging.debug('png->webp cache: %d/%d', total_cache_hits, len(shard_args)) 531*8975f5c5SAndroid Build Coastguard Worker 532*8975f5c5SAndroid Build Coastguard Worker 533*8975f5c5SAndroid Build Coastguard Workerdef _RemoveImageExtensions(directory, path_info): 534*8975f5c5SAndroid Build Coastguard Worker """Remove extensions from image files in the passed directory. 535*8975f5c5SAndroid Build Coastguard Worker 536*8975f5c5SAndroid Build Coastguard Worker This reduces binary size but does not affect android's ability to load the 537*8975f5c5SAndroid Build Coastguard Worker images. 538*8975f5c5SAndroid Build Coastguard Worker """ 539*8975f5c5SAndroid Build Coastguard Worker for f in _IterFiles(directory): 540*8975f5c5SAndroid Build Coastguard Worker if (f.endswith('.png') or f.endswith('.webp')) and not f.endswith('.9.png'): 541*8975f5c5SAndroid Build Coastguard Worker path_with_extension = f 542*8975f5c5SAndroid Build Coastguard Worker path_no_extension = os.path.splitext(path_with_extension)[0] 543*8975f5c5SAndroid Build Coastguard Worker if path_no_extension != path_with_extension: 544*8975f5c5SAndroid Build Coastguard Worker shutil.move(path_with_extension, path_no_extension) 545*8975f5c5SAndroid Build Coastguard Worker path_info.RegisterRename( 546*8975f5c5SAndroid Build Coastguard Worker os.path.relpath(path_with_extension, directory), 547*8975f5c5SAndroid Build Coastguard Worker os.path.relpath(path_no_extension, directory)) 548*8975f5c5SAndroid Build Coastguard Worker 549*8975f5c5SAndroid Build Coastguard Worker 550*8975f5c5SAndroid Build Coastguard Workerdef _CompileSingleDep(index, dep_subdir, keep_predicate, aapt2_path, 551*8975f5c5SAndroid Build Coastguard Worker partials_dir): 552*8975f5c5SAndroid Build Coastguard Worker unique_name = '{}_{}'.format(index, os.path.basename(dep_subdir)) 553*8975f5c5SAndroid Build Coastguard Worker partial_path = os.path.join(partials_dir, '{}.zip'.format(unique_name)) 554*8975f5c5SAndroid Build Coastguard Worker 555*8975f5c5SAndroid Build Coastguard Worker compile_command = [ 556*8975f5c5SAndroid Build Coastguard Worker aapt2_path, 557*8975f5c5SAndroid Build Coastguard Worker 'compile', 558*8975f5c5SAndroid Build Coastguard Worker # TODO(wnwen): Turn this on once aapt2 forces 9-patch to be crunched. 559*8975f5c5SAndroid Build Coastguard Worker # '--no-crunch', 560*8975f5c5SAndroid Build Coastguard Worker '--dir', 561*8975f5c5SAndroid Build Coastguard Worker dep_subdir, 562*8975f5c5SAndroid Build Coastguard Worker '-o', 563*8975f5c5SAndroid Build Coastguard Worker partial_path 564*8975f5c5SAndroid Build Coastguard Worker ] 565*8975f5c5SAndroid Build Coastguard Worker 566*8975f5c5SAndroid Build Coastguard Worker # There are resources targeting API-versions lower than our minapi. For 567*8975f5c5SAndroid Build Coastguard Worker # various reasons it's easier to let aapt2 ignore these than for us to 568*8975f5c5SAndroid Build Coastguard Worker # remove them from our build (e.g. it's from a 3rd party library). 569*8975f5c5SAndroid Build Coastguard Worker build_utils.CheckOutput( 570*8975f5c5SAndroid Build Coastguard Worker compile_command, 571*8975f5c5SAndroid Build Coastguard Worker stderr_filter=lambda output: build_utils.FilterLines( 572*8975f5c5SAndroid Build Coastguard Worker output, r'ignoring configuration .* for (styleable|attribute)')) 573*8975f5c5SAndroid Build Coastguard Worker 574*8975f5c5SAndroid Build Coastguard Worker # Filtering these files is expensive, so only apply filters to the partials 575*8975f5c5SAndroid Build Coastguard Worker # that have been explicitly targeted. 576*8975f5c5SAndroid Build Coastguard Worker if keep_predicate: 577*8975f5c5SAndroid Build Coastguard Worker logging.debug('Applying .arsc filtering to %s', dep_subdir) 578*8975f5c5SAndroid Build Coastguard Worker protoresources.StripUnwantedResources(partial_path, keep_predicate) 579*8975f5c5SAndroid Build Coastguard Worker return partial_path 580*8975f5c5SAndroid Build Coastguard Worker 581*8975f5c5SAndroid Build Coastguard Worker 582*8975f5c5SAndroid Build Coastguard Workerdef _CreateValuesKeepPredicate(exclusion_rules, dep_subdir): 583*8975f5c5SAndroid Build Coastguard Worker patterns = [ 584*8975f5c5SAndroid Build Coastguard Worker x[1] for x in exclusion_rules 585*8975f5c5SAndroid Build Coastguard Worker if build_utils.MatchesGlob(dep_subdir, [x[0]]) 586*8975f5c5SAndroid Build Coastguard Worker ] 587*8975f5c5SAndroid Build Coastguard Worker if not patterns: 588*8975f5c5SAndroid Build Coastguard Worker return None 589*8975f5c5SAndroid Build Coastguard Worker 590*8975f5c5SAndroid Build Coastguard Worker regexes = [re.compile(p) for p in patterns] 591*8975f5c5SAndroid Build Coastguard Worker return lambda x: not any(r.search(x) for r in regexes) 592*8975f5c5SAndroid Build Coastguard Worker 593*8975f5c5SAndroid Build Coastguard Worker 594*8975f5c5SAndroid Build Coastguard Workerdef _CompileDeps(aapt2_path, dep_subdirs, dep_subdir_overlay_set, temp_dir, 595*8975f5c5SAndroid Build Coastguard Worker exclusion_rules): 596*8975f5c5SAndroid Build Coastguard Worker partials_dir = os.path.join(temp_dir, 'partials') 597*8975f5c5SAndroid Build Coastguard Worker build_utils.MakeDirectory(partials_dir) 598*8975f5c5SAndroid Build Coastguard Worker 599*8975f5c5SAndroid Build Coastguard Worker job_params = [(i, dep_subdir, 600*8975f5c5SAndroid Build Coastguard Worker _CreateValuesKeepPredicate(exclusion_rules, dep_subdir)) 601*8975f5c5SAndroid Build Coastguard Worker for i, dep_subdir in enumerate(dep_subdirs)] 602*8975f5c5SAndroid Build Coastguard Worker 603*8975f5c5SAndroid Build Coastguard Worker # Filtering is slow, so ensure jobs with keep_predicate are started first. 604*8975f5c5SAndroid Build Coastguard Worker job_params.sort(key=lambda x: not x[2]) 605*8975f5c5SAndroid Build Coastguard Worker partials = list( 606*8975f5c5SAndroid Build Coastguard Worker parallel.BulkForkAndCall(_CompileSingleDep, 607*8975f5c5SAndroid Build Coastguard Worker job_params, 608*8975f5c5SAndroid Build Coastguard Worker aapt2_path=aapt2_path, 609*8975f5c5SAndroid Build Coastguard Worker partials_dir=partials_dir)) 610*8975f5c5SAndroid Build Coastguard Worker 611*8975f5c5SAndroid Build Coastguard Worker partials_cmd = list() 612*8975f5c5SAndroid Build Coastguard Worker for i, partial in enumerate(partials): 613*8975f5c5SAndroid Build Coastguard Worker dep_subdir = job_params[i][1] 614*8975f5c5SAndroid Build Coastguard Worker if dep_subdir in dep_subdir_overlay_set: 615*8975f5c5SAndroid Build Coastguard Worker partials_cmd += ['-R'] 616*8975f5c5SAndroid Build Coastguard Worker partials_cmd += [partial] 617*8975f5c5SAndroid Build Coastguard Worker return partials_cmd 618*8975f5c5SAndroid Build Coastguard Worker 619*8975f5c5SAndroid Build Coastguard Worker 620*8975f5c5SAndroid Build Coastguard Workerdef _CreateResourceInfoFile(path_info, info_path, dependencies_res_zips): 621*8975f5c5SAndroid Build Coastguard Worker for zip_file in dependencies_res_zips: 622*8975f5c5SAndroid Build Coastguard Worker zip_info_file_path = zip_file + '.info' 623*8975f5c5SAndroid Build Coastguard Worker if os.path.exists(zip_info_file_path): 624*8975f5c5SAndroid Build Coastguard Worker path_info.MergeInfoFile(zip_info_file_path) 625*8975f5c5SAndroid Build Coastguard Worker path_info.Write(info_path) 626*8975f5c5SAndroid Build Coastguard Worker 627*8975f5c5SAndroid Build Coastguard Worker 628*8975f5c5SAndroid Build Coastguard Workerdef _RemoveUnwantedLocalizedStrings(dep_subdirs, options): 629*8975f5c5SAndroid Build Coastguard Worker """Remove localized strings that should not go into the final output. 630*8975f5c5SAndroid Build Coastguard Worker 631*8975f5c5SAndroid Build Coastguard Worker Args: 632*8975f5c5SAndroid Build Coastguard Worker dep_subdirs: List of resource dependency directories. 633*8975f5c5SAndroid Build Coastguard Worker options: Command-line options namespace. 634*8975f5c5SAndroid Build Coastguard Worker """ 635*8975f5c5SAndroid Build Coastguard Worker # Collect locale and file paths from the existing subdirs. 636*8975f5c5SAndroid Build Coastguard Worker # The following variable maps Android locale names to 637*8975f5c5SAndroid Build Coastguard Worker # sets of corresponding xml file paths. 638*8975f5c5SAndroid Build Coastguard Worker locale_to_files_map = collections.defaultdict(set) 639*8975f5c5SAndroid Build Coastguard Worker for directory in dep_subdirs: 640*8975f5c5SAndroid Build Coastguard Worker for f in _IterFiles(directory): 641*8975f5c5SAndroid Build Coastguard Worker locale = resource_utils.FindLocaleInStringResourceFilePath(f) 642*8975f5c5SAndroid Build Coastguard Worker if locale: 643*8975f5c5SAndroid Build Coastguard Worker locale_to_files_map[locale].add(f) 644*8975f5c5SAndroid Build Coastguard Worker 645*8975f5c5SAndroid Build Coastguard Worker all_locales = set(locale_to_files_map) 646*8975f5c5SAndroid Build Coastguard Worker 647*8975f5c5SAndroid Build Coastguard Worker # Set A: wanted locales, either all of them or the 648*8975f5c5SAndroid Build Coastguard Worker # list provided by --locale-allowlist. 649*8975f5c5SAndroid Build Coastguard Worker wanted_locales = all_locales 650*8975f5c5SAndroid Build Coastguard Worker if options.locale_allowlist: 651*8975f5c5SAndroid Build Coastguard Worker wanted_locales = _ToAndroidLocales(options.locale_allowlist) 652*8975f5c5SAndroid Build Coastguard Worker 653*8975f5c5SAndroid Build Coastguard Worker # Set B: shared resources locales, which is either set A 654*8975f5c5SAndroid Build Coastguard Worker # or the list provided by --shared-resources-allowlist-locales 655*8975f5c5SAndroid Build Coastguard Worker shared_resources_locales = wanted_locales 656*8975f5c5SAndroid Build Coastguard Worker shared_names_allowlist = set() 657*8975f5c5SAndroid Build Coastguard Worker if options.shared_resources_allowlist_locales: 658*8975f5c5SAndroid Build Coastguard Worker shared_names_allowlist = set( 659*8975f5c5SAndroid Build Coastguard Worker resource_utils.GetRTxtStringResourceNames( 660*8975f5c5SAndroid Build Coastguard Worker options.shared_resources_allowlist)) 661*8975f5c5SAndroid Build Coastguard Worker 662*8975f5c5SAndroid Build Coastguard Worker shared_resources_locales = _ToAndroidLocales( 663*8975f5c5SAndroid Build Coastguard Worker options.shared_resources_allowlist_locales) 664*8975f5c5SAndroid Build Coastguard Worker 665*8975f5c5SAndroid Build Coastguard Worker # Remove any file that belongs to a locale not covered by 666*8975f5c5SAndroid Build Coastguard Worker # either A or B. 667*8975f5c5SAndroid Build Coastguard Worker removable_locales = (all_locales - wanted_locales - shared_resources_locales) 668*8975f5c5SAndroid Build Coastguard Worker for locale in removable_locales: 669*8975f5c5SAndroid Build Coastguard Worker for path in locale_to_files_map[locale]: 670*8975f5c5SAndroid Build Coastguard Worker os.remove(path) 671*8975f5c5SAndroid Build Coastguard Worker 672*8975f5c5SAndroid Build Coastguard Worker # For any locale in B but not in A, only keep the shared 673*8975f5c5SAndroid Build Coastguard Worker # resource strings in each file. 674*8975f5c5SAndroid Build Coastguard Worker for locale in shared_resources_locales - wanted_locales: 675*8975f5c5SAndroid Build Coastguard Worker for path in locale_to_files_map[locale]: 676*8975f5c5SAndroid Build Coastguard Worker resource_utils.FilterAndroidResourceStringsXml( 677*8975f5c5SAndroid Build Coastguard Worker path, lambda x: x in shared_names_allowlist) 678*8975f5c5SAndroid Build Coastguard Worker 679*8975f5c5SAndroid Build Coastguard Worker # For any locale in A but not in B, only keep the strings 680*8975f5c5SAndroid Build Coastguard Worker # that are _not_ from shared resources in the file. 681*8975f5c5SAndroid Build Coastguard Worker for locale in wanted_locales - shared_resources_locales: 682*8975f5c5SAndroid Build Coastguard Worker for path in locale_to_files_map[locale]: 683*8975f5c5SAndroid Build Coastguard Worker resource_utils.FilterAndroidResourceStringsXml( 684*8975f5c5SAndroid Build Coastguard Worker path, lambda x: x not in shared_names_allowlist) 685*8975f5c5SAndroid Build Coastguard Worker 686*8975f5c5SAndroid Build Coastguard Worker 687*8975f5c5SAndroid Build Coastguard Workerdef _FilterResourceFiles(dep_subdirs, keep_predicate): 688*8975f5c5SAndroid Build Coastguard Worker # Create a function that selects which resource files should be packaged 689*8975f5c5SAndroid Build Coastguard Worker # into the final output. Any file that does not pass the predicate will 690*8975f5c5SAndroid Build Coastguard Worker # be removed below. 691*8975f5c5SAndroid Build Coastguard Worker png_paths = [] 692*8975f5c5SAndroid Build Coastguard Worker for directory in dep_subdirs: 693*8975f5c5SAndroid Build Coastguard Worker for f in _IterFiles(directory): 694*8975f5c5SAndroid Build Coastguard Worker if not keep_predicate(f): 695*8975f5c5SAndroid Build Coastguard Worker os.remove(f) 696*8975f5c5SAndroid Build Coastguard Worker elif f.endswith('.png'): 697*8975f5c5SAndroid Build Coastguard Worker png_paths.append(f) 698*8975f5c5SAndroid Build Coastguard Worker 699*8975f5c5SAndroid Build Coastguard Worker return png_paths 700*8975f5c5SAndroid Build Coastguard Worker 701*8975f5c5SAndroid Build Coastguard Worker 702*8975f5c5SAndroid Build Coastguard Workerdef _PackageApk(options, build): 703*8975f5c5SAndroid Build Coastguard Worker """Compile and link resources with aapt2. 704*8975f5c5SAndroid Build Coastguard Worker 705*8975f5c5SAndroid Build Coastguard Worker Args: 706*8975f5c5SAndroid Build Coastguard Worker options: The command-line options. 707*8975f5c5SAndroid Build Coastguard Worker build: BuildContext object. 708*8975f5c5SAndroid Build Coastguard Worker Returns: 709*8975f5c5SAndroid Build Coastguard Worker The manifest package name for the APK. 710*8975f5c5SAndroid Build Coastguard Worker """ 711*8975f5c5SAndroid Build Coastguard Worker logging.debug('Extracting resource .zips') 712*8975f5c5SAndroid Build Coastguard Worker dep_subdirs = [] 713*8975f5c5SAndroid Build Coastguard Worker dep_subdir_overlay_set = set() 714*8975f5c5SAndroid Build Coastguard Worker for dependency_res_zip in options.dependencies_res_zips: 715*8975f5c5SAndroid Build Coastguard Worker extracted_dep_subdirs = resource_utils.ExtractDeps([dependency_res_zip], 716*8975f5c5SAndroid Build Coastguard Worker build.deps_dir) 717*8975f5c5SAndroid Build Coastguard Worker dep_subdirs += extracted_dep_subdirs 718*8975f5c5SAndroid Build Coastguard Worker if dependency_res_zip in options.dependencies_res_zip_overlays: 719*8975f5c5SAndroid Build Coastguard Worker dep_subdir_overlay_set.update(extracted_dep_subdirs) 720*8975f5c5SAndroid Build Coastguard Worker 721*8975f5c5SAndroid Build Coastguard Worker logging.debug('Applying locale transformations') 722*8975f5c5SAndroid Build Coastguard Worker path_info = resource_utils.ResourceInfoFile() 723*8975f5c5SAndroid Build Coastguard Worker _RenameLocaleResourceDirs(dep_subdirs, path_info) 724*8975f5c5SAndroid Build Coastguard Worker 725*8975f5c5SAndroid Build Coastguard Worker logging.debug('Applying file-based exclusions') 726*8975f5c5SAndroid Build Coastguard Worker keep_predicate = _CreateKeepPredicate(options.resource_exclusion_regex, 727*8975f5c5SAndroid Build Coastguard Worker options.resource_exclusion_exceptions) 728*8975f5c5SAndroid Build Coastguard Worker png_paths = _FilterResourceFiles(dep_subdirs, keep_predicate) 729*8975f5c5SAndroid Build Coastguard Worker 730*8975f5c5SAndroid Build Coastguard Worker if options.locale_allowlist or options.shared_resources_allowlist_locales: 731*8975f5c5SAndroid Build Coastguard Worker logging.debug('Applying locale-based string exclusions') 732*8975f5c5SAndroid Build Coastguard Worker _RemoveUnwantedLocalizedStrings(dep_subdirs, options) 733*8975f5c5SAndroid Build Coastguard Worker 734*8975f5c5SAndroid Build Coastguard Worker if png_paths and options.png_to_webp: 735*8975f5c5SAndroid Build Coastguard Worker logging.debug('Converting png->webp') 736*8975f5c5SAndroid Build Coastguard Worker _ConvertToWebP(options.webp_binary, png_paths, path_info, 737*8975f5c5SAndroid Build Coastguard Worker options.webp_cache_dir) 738*8975f5c5SAndroid Build Coastguard Worker logging.debug('Applying drawable transformations') 739*8975f5c5SAndroid Build Coastguard Worker for directory in dep_subdirs: 740*8975f5c5SAndroid Build Coastguard Worker _MoveImagesToNonMdpiFolders(directory, path_info) 741*8975f5c5SAndroid Build Coastguard Worker _RemoveImageExtensions(directory, path_info) 742*8975f5c5SAndroid Build Coastguard Worker 743*8975f5c5SAndroid Build Coastguard Worker logging.debug('Running aapt2 compile') 744*8975f5c5SAndroid Build Coastguard Worker exclusion_rules = [x.split(':', 1) for x in options.values_filter_rules] 745*8975f5c5SAndroid Build Coastguard Worker partials = _CompileDeps(options.aapt2_path, dep_subdirs, 746*8975f5c5SAndroid Build Coastguard Worker dep_subdir_overlay_set, build.temp_dir, 747*8975f5c5SAndroid Build Coastguard Worker exclusion_rules) 748*8975f5c5SAndroid Build Coastguard Worker 749*8975f5c5SAndroid Build Coastguard Worker link_command = [ 750*8975f5c5SAndroid Build Coastguard Worker options.aapt2_path, 751*8975f5c5SAndroid Build Coastguard Worker 'link', 752*8975f5c5SAndroid Build Coastguard Worker '--auto-add-overlay', 753*8975f5c5SAndroid Build Coastguard Worker '--no-version-vectors', 754*8975f5c5SAndroid Build Coastguard Worker '--output-text-symbols', 755*8975f5c5SAndroid Build Coastguard Worker build.r_txt_path, 756*8975f5c5SAndroid Build Coastguard Worker ] 757*8975f5c5SAndroid Build Coastguard Worker 758*8975f5c5SAndroid Build Coastguard Worker for j in options.include_resources: 759*8975f5c5SAndroid Build Coastguard Worker link_command += ['-I', j] 760*8975f5c5SAndroid Build Coastguard Worker if options.proguard_file: 761*8975f5c5SAndroid Build Coastguard Worker link_command += ['--proguard', build.proguard_path] 762*8975f5c5SAndroid Build Coastguard Worker link_command += ['--proguard-minimal-keep-rules'] 763*8975f5c5SAndroid Build Coastguard Worker if options.emit_ids_out: 764*8975f5c5SAndroid Build Coastguard Worker link_command += ['--emit-ids', build.emit_ids_path] 765*8975f5c5SAndroid Build Coastguard Worker 766*8975f5c5SAndroid Build Coastguard Worker # Note: only one of --proto-format, --shared-lib or --app-as-shared-lib 767*8975f5c5SAndroid Build Coastguard Worker # can be used with recent versions of aapt2. 768*8975f5c5SAndroid Build Coastguard Worker if options.shared_resources: 769*8975f5c5SAndroid Build Coastguard Worker link_command.append('--shared-lib') 770*8975f5c5SAndroid Build Coastguard Worker 771*8975f5c5SAndroid Build Coastguard Worker if int(options.min_sdk_version) > 21 and not options.xml_namespaces: 772*8975f5c5SAndroid Build Coastguard Worker link_command.append('--no-xml-namespaces') 773*8975f5c5SAndroid Build Coastguard Worker 774*8975f5c5SAndroid Build Coastguard Worker if options.package_id: 775*8975f5c5SAndroid Build Coastguard Worker link_command += [ 776*8975f5c5SAndroid Build Coastguard Worker '--package-id', 777*8975f5c5SAndroid Build Coastguard Worker '0x%02x' % options.package_id, 778*8975f5c5SAndroid Build Coastguard Worker '--allow-reserved-package-id', 779*8975f5c5SAndroid Build Coastguard Worker ] 780*8975f5c5SAndroid Build Coastguard Worker 781*8975f5c5SAndroid Build Coastguard Worker fixed_manifest, desired_manifest_package_name, fixed_manifest_package = ( 782*8975f5c5SAndroid Build Coastguard Worker _FixManifest(options, build.temp_dir)) 783*8975f5c5SAndroid Build Coastguard Worker if options.rename_manifest_package: 784*8975f5c5SAndroid Build Coastguard Worker desired_manifest_package_name = options.rename_manifest_package 785*8975f5c5SAndroid Build Coastguard Worker 786*8975f5c5SAndroid Build Coastguard Worker link_command += [ 787*8975f5c5SAndroid Build Coastguard Worker '--manifest', fixed_manifest, '--rename-manifest-package', 788*8975f5c5SAndroid Build Coastguard Worker desired_manifest_package_name 789*8975f5c5SAndroid Build Coastguard Worker ] 790*8975f5c5SAndroid Build Coastguard Worker 791*8975f5c5SAndroid Build Coastguard Worker if options.package_id is not None: 792*8975f5c5SAndroid Build Coastguard Worker package_id = options.package_id 793*8975f5c5SAndroid Build Coastguard Worker elif options.shared_resources: 794*8975f5c5SAndroid Build Coastguard Worker package_id = 0 795*8975f5c5SAndroid Build Coastguard Worker else: 796*8975f5c5SAndroid Build Coastguard Worker package_id = 0x7f 797*8975f5c5SAndroid Build Coastguard Worker _CreateStableIdsFile(options.use_resource_ids_path, build.stable_ids_path, 798*8975f5c5SAndroid Build Coastguard Worker fixed_manifest_package, package_id) 799*8975f5c5SAndroid Build Coastguard Worker link_command += ['--stable-ids', build.stable_ids_path] 800*8975f5c5SAndroid Build Coastguard Worker 801*8975f5c5SAndroid Build Coastguard Worker link_command += partials 802*8975f5c5SAndroid Build Coastguard Worker 803*8975f5c5SAndroid Build Coastguard Worker # We always create a binary arsc file first, then convert to proto, so flags 804*8975f5c5SAndroid Build Coastguard Worker # such as --shared-lib can be supported. 805*8975f5c5SAndroid Build Coastguard Worker link_command += ['-o', build.arsc_path] 806*8975f5c5SAndroid Build Coastguard Worker 807*8975f5c5SAndroid Build Coastguard Worker logging.debug('Starting: aapt2 link') 808*8975f5c5SAndroid Build Coastguard Worker link_proc = subprocess.Popen(link_command) 809*8975f5c5SAndroid Build Coastguard Worker 810*8975f5c5SAndroid Build Coastguard Worker # Create .res.info file in parallel. 811*8975f5c5SAndroid Build Coastguard Worker if options.info_path: 812*8975f5c5SAndroid Build Coastguard Worker logging.debug('Creating .res.info file') 813*8975f5c5SAndroid Build Coastguard Worker _CreateResourceInfoFile(path_info, build.info_path, 814*8975f5c5SAndroid Build Coastguard Worker options.dependencies_res_zips) 815*8975f5c5SAndroid Build Coastguard Worker 816*8975f5c5SAndroid Build Coastguard Worker exit_code = link_proc.wait() 817*8975f5c5SAndroid Build Coastguard Worker assert exit_code == 0, f'aapt2 link cmd failed with {exit_code=}' 818*8975f5c5SAndroid Build Coastguard Worker logging.debug('Finished: aapt2 link') 819*8975f5c5SAndroid Build Coastguard Worker 820*8975f5c5SAndroid Build Coastguard Worker if options.shared_resources: 821*8975f5c5SAndroid Build Coastguard Worker logging.debug('Resolving styleables in R.txt') 822*8975f5c5SAndroid Build Coastguard Worker # Need to resolve references because unused resource removal tool does not 823*8975f5c5SAndroid Build Coastguard Worker # support references in R.txt files. 824*8975f5c5SAndroid Build Coastguard Worker resource_utils.ResolveStyleableReferences(build.r_txt_path) 825*8975f5c5SAndroid Build Coastguard Worker 826*8975f5c5SAndroid Build Coastguard Worker if exit_code: 827*8975f5c5SAndroid Build Coastguard Worker raise subprocess.CalledProcessError(exit_code, link_command) 828*8975f5c5SAndroid Build Coastguard Worker 829*8975f5c5SAndroid Build Coastguard Worker if options.proguard_file and (options.shared_resources 830*8975f5c5SAndroid Build Coastguard Worker or options.app_as_shared_lib): 831*8975f5c5SAndroid Build Coastguard Worker # Make sure the R class associated with the manifest package does not have 832*8975f5c5SAndroid Build Coastguard Worker # its onResourcesLoaded method obfuscated or removed, so that the framework 833*8975f5c5SAndroid Build Coastguard Worker # can call it in the case where the APK is being loaded as a library. 834*8975f5c5SAndroid Build Coastguard Worker with open(build.proguard_path, 'a') as proguard_file: 835*8975f5c5SAndroid Build Coastguard Worker keep_rule = ''' 836*8975f5c5SAndroid Build Coastguard Worker -keep,allowoptimization class {package}.R {{ 837*8975f5c5SAndroid Build Coastguard Worker public static void onResourcesLoaded(int); 838*8975f5c5SAndroid Build Coastguard Worker }} 839*8975f5c5SAndroid Build Coastguard Worker '''.format(package=desired_manifest_package_name) 840*8975f5c5SAndroid Build Coastguard Worker proguard_file.write(textwrap.dedent(keep_rule)) 841*8975f5c5SAndroid Build Coastguard Worker 842*8975f5c5SAndroid Build Coastguard Worker logging.debug('Running aapt2 convert') 843*8975f5c5SAndroid Build Coastguard Worker build_utils.CheckOutput([ 844*8975f5c5SAndroid Build Coastguard Worker options.aapt2_path, 'convert', '--output-format', 'proto', '-o', 845*8975f5c5SAndroid Build Coastguard Worker build.proto_path, build.arsc_path 846*8975f5c5SAndroid Build Coastguard Worker ]) 847*8975f5c5SAndroid Build Coastguard Worker 848*8975f5c5SAndroid Build Coastguard Worker # Workaround for b/147674078. This is only needed for WebLayer and does not 849*8975f5c5SAndroid Build Coastguard Worker # affect WebView usage, since WebView does not used dynamic attributes. 850*8975f5c5SAndroid Build Coastguard Worker if options.shared_resources: 851*8975f5c5SAndroid Build Coastguard Worker logging.debug('Hardcoding dynamic attributes') 852*8975f5c5SAndroid Build Coastguard Worker protoresources.HardcodeSharedLibraryDynamicAttributes( 853*8975f5c5SAndroid Build Coastguard Worker build.proto_path, options.is_bundle_module, 854*8975f5c5SAndroid Build Coastguard Worker options.shared_resources_allowlist) 855*8975f5c5SAndroid Build Coastguard Worker 856*8975f5c5SAndroid Build Coastguard Worker build_utils.CheckOutput([ 857*8975f5c5SAndroid Build Coastguard Worker options.aapt2_path, 'convert', '--output-format', 'binary', '-o', 858*8975f5c5SAndroid Build Coastguard Worker build.arsc_path, build.proto_path 859*8975f5c5SAndroid Build Coastguard Worker ]) 860*8975f5c5SAndroid Build Coastguard Worker 861*8975f5c5SAndroid Build Coastguard Worker # Sanity check that the created resources have the expected package ID. 862*8975f5c5SAndroid Build Coastguard Worker logging.debug('Performing sanity check') 863*8975f5c5SAndroid Build Coastguard Worker _, actual_package_id = resource_utils.ExtractArscPackage( 864*8975f5c5SAndroid Build Coastguard Worker options.aapt2_path, 865*8975f5c5SAndroid Build Coastguard Worker build.arsc_path if options.arsc_path else build.proto_path) 866*8975f5c5SAndroid Build Coastguard Worker # When there are no resources, ExtractArscPackage returns (None, None), in 867*8975f5c5SAndroid Build Coastguard Worker # this case there is no need to check for matching package ID. 868*8975f5c5SAndroid Build Coastguard Worker if actual_package_id is not None and actual_package_id != package_id: 869*8975f5c5SAndroid Build Coastguard Worker raise Exception('Invalid package ID 0x%x (expected 0x%x)' % 870*8975f5c5SAndroid Build Coastguard Worker (actual_package_id, package_id)) 871*8975f5c5SAndroid Build Coastguard Worker 872*8975f5c5SAndroid Build Coastguard Worker return desired_manifest_package_name 873*8975f5c5SAndroid Build Coastguard Worker 874*8975f5c5SAndroid Build Coastguard Worker 875*8975f5c5SAndroid Build Coastguard Workerdef _CreateStableIdsFile(in_path, out_path, package_name, package_id): 876*8975f5c5SAndroid Build Coastguard Worker """Transforms a file generated by --emit-ids from another package. 877*8975f5c5SAndroid Build Coastguard Worker 878*8975f5c5SAndroid Build Coastguard Worker --stable-ids is generally meant to be used by different versions of the same 879*8975f5c5SAndroid Build Coastguard Worker package. To make it work for other packages, we need to transform the package 880*8975f5c5SAndroid Build Coastguard Worker name references to match the package that resources are being generated for. 881*8975f5c5SAndroid Build Coastguard Worker """ 882*8975f5c5SAndroid Build Coastguard Worker if in_path: 883*8975f5c5SAndroid Build Coastguard Worker data = pathlib.Path(in_path).read_text() 884*8975f5c5SAndroid Build Coastguard Worker else: 885*8975f5c5SAndroid Build Coastguard Worker # Force IDs to use 0x01 for the type byte in order to ensure they are 886*8975f5c5SAndroid Build Coastguard Worker # different from IDs generated by other apps. https://crbug.com/1293336 887*8975f5c5SAndroid Build Coastguard Worker data = 'pkg:id/fake_resource_id = 0x7f010000\n' 888*8975f5c5SAndroid Build Coastguard Worker # Replace "pkg:" with correct package name. 889*8975f5c5SAndroid Build Coastguard Worker data = re.sub(r'^.*?:', package_name + ':', data, flags=re.MULTILINE) 890*8975f5c5SAndroid Build Coastguard Worker # Replace "0x7f" with correct package id. 891*8975f5c5SAndroid Build Coastguard Worker data = re.sub(r'0x..', '0x%02x' % package_id, data) 892*8975f5c5SAndroid Build Coastguard Worker pathlib.Path(out_path).write_text(data) 893*8975f5c5SAndroid Build Coastguard Worker 894*8975f5c5SAndroid Build Coastguard Worker 895*8975f5c5SAndroid Build Coastguard Workerdef _WriteOutputs(options, build): 896*8975f5c5SAndroid Build Coastguard Worker possible_outputs = [ 897*8975f5c5SAndroid Build Coastguard Worker (options.srcjar_out, build.srcjar_path), 898*8975f5c5SAndroid Build Coastguard Worker (options.r_text_out, build.r_txt_path), 899*8975f5c5SAndroid Build Coastguard Worker (options.arsc_path, build.arsc_path), 900*8975f5c5SAndroid Build Coastguard Worker (options.proto_path, build.proto_path), 901*8975f5c5SAndroid Build Coastguard Worker (options.proguard_file, build.proguard_path), 902*8975f5c5SAndroid Build Coastguard Worker (options.emit_ids_out, build.emit_ids_path), 903*8975f5c5SAndroid Build Coastguard Worker (options.info_path, build.info_path), 904*8975f5c5SAndroid Build Coastguard Worker ] 905*8975f5c5SAndroid Build Coastguard Worker 906*8975f5c5SAndroid Build Coastguard Worker for final, temp in possible_outputs: 907*8975f5c5SAndroid Build Coastguard Worker # Write file only if it's changed. 908*8975f5c5SAndroid Build Coastguard Worker if final and not (os.path.exists(final) and filecmp.cmp(final, temp)): 909*8975f5c5SAndroid Build Coastguard Worker shutil.move(temp, final) 910*8975f5c5SAndroid Build Coastguard Worker 911*8975f5c5SAndroid Build Coastguard Worker 912*8975f5c5SAndroid Build Coastguard Workerdef _CreateNormalizedManifestForVerification(options): 913*8975f5c5SAndroid Build Coastguard Worker with build_utils.TempDir() as tempdir: 914*8975f5c5SAndroid Build Coastguard Worker fixed_manifest, _, _ = _FixManifest(options, tempdir) 915*8975f5c5SAndroid Build Coastguard Worker with open(fixed_manifest) as f: 916*8975f5c5SAndroid Build Coastguard Worker return manifest_utils.NormalizeManifest( 917*8975f5c5SAndroid Build Coastguard Worker f.read(), options.verification_version_code_offset, 918*8975f5c5SAndroid Build Coastguard Worker options.verification_library_version_offset) 919*8975f5c5SAndroid Build Coastguard Worker 920*8975f5c5SAndroid Build Coastguard Worker 921*8975f5c5SAndroid Build Coastguard Workerdef main(args): 922*8975f5c5SAndroid Build Coastguard Worker build_utils.InitLogging('RESOURCE_DEBUG') 923*8975f5c5SAndroid Build Coastguard Worker args = build_utils.ExpandFileArgs(args) 924*8975f5c5SAndroid Build Coastguard Worker options = _ParseArgs(args) 925*8975f5c5SAndroid Build Coastguard Worker 926*8975f5c5SAndroid Build Coastguard Worker if options.expected_file: 927*8975f5c5SAndroid Build Coastguard Worker actual_data = _CreateNormalizedManifestForVerification(options) 928*8975f5c5SAndroid Build Coastguard Worker diff_utils.CheckExpectations(actual_data, options) 929*8975f5c5SAndroid Build Coastguard Worker if options.only_verify_expectations: 930*8975f5c5SAndroid Build Coastguard Worker return 931*8975f5c5SAndroid Build Coastguard Worker 932*8975f5c5SAndroid Build Coastguard Worker path = options.arsc_path or options.proto_path 933*8975f5c5SAndroid Build Coastguard Worker debug_temp_resources_dir = os.environ.get('TEMP_RESOURCES_DIR') 934*8975f5c5SAndroid Build Coastguard Worker if debug_temp_resources_dir: 935*8975f5c5SAndroid Build Coastguard Worker path = os.path.join(debug_temp_resources_dir, os.path.basename(path)) 936*8975f5c5SAndroid Build Coastguard Worker else: 937*8975f5c5SAndroid Build Coastguard Worker # Use a deterministic temp directory since .pb files embed the absolute 938*8975f5c5SAndroid Build Coastguard Worker # path of resources: crbug.com/939984 939*8975f5c5SAndroid Build Coastguard Worker path = path + '.tmpdir' 940*8975f5c5SAndroid Build Coastguard Worker build_utils.DeleteDirectory(path) 941*8975f5c5SAndroid Build Coastguard Worker 942*8975f5c5SAndroid Build Coastguard Worker with resource_utils.BuildContext( 943*8975f5c5SAndroid Build Coastguard Worker temp_dir=path, keep_files=bool(debug_temp_resources_dir)) as build: 944*8975f5c5SAndroid Build Coastguard Worker 945*8975f5c5SAndroid Build Coastguard Worker manifest_package_name = _PackageApk(options, build) 946*8975f5c5SAndroid Build Coastguard Worker 947*8975f5c5SAndroid Build Coastguard Worker # If --shared-resources-allowlist is used, all the resources listed in the 948*8975f5c5SAndroid Build Coastguard Worker # corresponding R.txt file will be non-final, and an onResourcesLoaded() 949*8975f5c5SAndroid Build Coastguard Worker # will be generated to adjust them at runtime. 950*8975f5c5SAndroid Build Coastguard Worker # 951*8975f5c5SAndroid Build Coastguard Worker # Otherwise, if --shared-resources is used, the all resources will be 952*8975f5c5SAndroid Build Coastguard Worker # non-final, and an onResourcesLoaded() method will be generated too. 953*8975f5c5SAndroid Build Coastguard Worker # 954*8975f5c5SAndroid Build Coastguard Worker # Otherwise, all resources will be final, and no method will be generated. 955*8975f5c5SAndroid Build Coastguard Worker # 956*8975f5c5SAndroid Build Coastguard Worker rjava_build_options = resource_utils.RJavaBuildOptions() 957*8975f5c5SAndroid Build Coastguard Worker if options.shared_resources_allowlist: 958*8975f5c5SAndroid Build Coastguard Worker rjava_build_options.ExportSomeResources( 959*8975f5c5SAndroid Build Coastguard Worker options.shared_resources_allowlist) 960*8975f5c5SAndroid Build Coastguard Worker rjava_build_options.GenerateOnResourcesLoaded() 961*8975f5c5SAndroid Build Coastguard Worker if options.shared_resources: 962*8975f5c5SAndroid Build Coastguard Worker # The final resources will only be used in WebLayer, so hardcode the 963*8975f5c5SAndroid Build Coastguard Worker # package ID to be what WebLayer expects. 964*8975f5c5SAndroid Build Coastguard Worker rjava_build_options.SetFinalPackageId( 965*8975f5c5SAndroid Build Coastguard Worker protoresources.SHARED_LIBRARY_HARDCODED_ID) 966*8975f5c5SAndroid Build Coastguard Worker elif options.shared_resources or options.app_as_shared_lib: 967*8975f5c5SAndroid Build Coastguard Worker rjava_build_options.ExportAllResources() 968*8975f5c5SAndroid Build Coastguard Worker rjava_build_options.GenerateOnResourcesLoaded() 969*8975f5c5SAndroid Build Coastguard Worker 970*8975f5c5SAndroid Build Coastguard Worker custom_root_package_name = options.r_java_root_package_name 971*8975f5c5SAndroid Build Coastguard Worker grandparent_custom_package_name = None 972*8975f5c5SAndroid Build Coastguard Worker 973*8975f5c5SAndroid Build Coastguard Worker # Always generate an R.java file for the package listed in 974*8975f5c5SAndroid Build Coastguard Worker # AndroidManifest.xml because this is where Android framework looks to find 975*8975f5c5SAndroid Build Coastguard Worker # onResourcesLoaded() for shared library apks. While not actually necessary 976*8975f5c5SAndroid Build Coastguard Worker # for application apks, it also doesn't hurt. 977*8975f5c5SAndroid Build Coastguard Worker apk_package_name = manifest_package_name 978*8975f5c5SAndroid Build Coastguard Worker 979*8975f5c5SAndroid Build Coastguard Worker if options.package_name and not options.arsc_package_name: 980*8975f5c5SAndroid Build Coastguard Worker # Feature modules have their own custom root package name and should 981*8975f5c5SAndroid Build Coastguard Worker # inherit from the appropriate base module package. This behaviour should 982*8975f5c5SAndroid Build Coastguard Worker # not be present for test apks with an apk under test. Thus, 983*8975f5c5SAndroid Build Coastguard Worker # arsc_package_name is used as it is only defined for test apks with an 984*8975f5c5SAndroid Build Coastguard Worker # apk under test. 985*8975f5c5SAndroid Build Coastguard Worker custom_root_package_name = options.package_name 986*8975f5c5SAndroid Build Coastguard Worker grandparent_custom_package_name = options.r_java_root_package_name 987*8975f5c5SAndroid Build Coastguard Worker # Feature modules have the same manifest package as the base module but 988*8975f5c5SAndroid Build Coastguard Worker # they should not create an R.java for said manifest package because it 989*8975f5c5SAndroid Build Coastguard Worker # will be created in the base module. 990*8975f5c5SAndroid Build Coastguard Worker apk_package_name = None 991*8975f5c5SAndroid Build Coastguard Worker 992*8975f5c5SAndroid Build Coastguard Worker if options.srcjar_out: 993*8975f5c5SAndroid Build Coastguard Worker logging.debug('Creating R.srcjar') 994*8975f5c5SAndroid Build Coastguard Worker resource_utils.CreateRJavaFiles(build.srcjar_dir, apk_package_name, 995*8975f5c5SAndroid Build Coastguard Worker build.r_txt_path, 996*8975f5c5SAndroid Build Coastguard Worker options.extra_res_packages, 997*8975f5c5SAndroid Build Coastguard Worker rjava_build_options, options.srcjar_out, 998*8975f5c5SAndroid Build Coastguard Worker custom_root_package_name, 999*8975f5c5SAndroid Build Coastguard Worker grandparent_custom_package_name) 1000*8975f5c5SAndroid Build Coastguard Worker with action_helpers.atomic_output(build.srcjar_path) as f: 1001*8975f5c5SAndroid Build Coastguard Worker zip_helpers.zip_directory(f, build.srcjar_dir) 1002*8975f5c5SAndroid Build Coastguard Worker 1003*8975f5c5SAndroid Build Coastguard Worker logging.debug('Copying outputs') 1004*8975f5c5SAndroid Build Coastguard Worker _WriteOutputs(options, build) 1005*8975f5c5SAndroid Build Coastguard Worker 1006*8975f5c5SAndroid Build Coastguard Worker if options.depfile: 1007*8975f5c5SAndroid Build Coastguard Worker assert options.srcjar_out, 'Update first output below and remove assert.' 1008*8975f5c5SAndroid Build Coastguard Worker depfile_deps = (options.dependencies_res_zips + 1009*8975f5c5SAndroid Build Coastguard Worker options.dependencies_res_zip_overlays + 1010*8975f5c5SAndroid Build Coastguard Worker options.include_resources) 1011*8975f5c5SAndroid Build Coastguard Worker action_helpers.write_depfile(options.depfile, options.srcjar_out, 1012*8975f5c5SAndroid Build Coastguard Worker depfile_deps) 1013*8975f5c5SAndroid Build Coastguard Worker 1014*8975f5c5SAndroid Build Coastguard Worker 1015*8975f5c5SAndroid Build Coastguard Workerif __name__ == '__main__': 1016*8975f5c5SAndroid Build Coastguard Worker main(sys.argv[1:]) 1017