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