summaryrefslogtreecommitdiff
path: root/build/android/gyp/util/resource_utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'build/android/gyp/util/resource_utils.py')
-rw-r--r--build/android/gyp/util/resource_utils.py511
1 files changed, 0 insertions, 511 deletions
diff --git a/build/android/gyp/util/resource_utils.py b/build/android/gyp/util/resource_utils.py
deleted file mode 100644
index 875fd12631..0000000000
--- a/build/android/gyp/util/resource_utils.py
+++ /dev/null
@@ -1,511 +0,0 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import argparse
-import collections
-import contextlib
-import os
-import re
-import shutil
-import sys
-import tempfile
-from xml.etree import ElementTree
-
-import util.build_utils as build_utils
-
-_SOURCE_ROOT = os.path.abspath(
- os.path.join(os.path.dirname(__file__), '..', '..', '..', '..'))
-# Import jinja2 from third_party/jinja2
-sys.path.insert(1, os.path.join(_SOURCE_ROOT, 'third_party'))
-from jinja2 import Template # pylint: disable=F0401
-
-
-EMPTY_ANDROID_MANIFEST_PATH = os.path.join(
- _SOURCE_ROOT, 'build', 'android', 'AndroidManifest.xml')
-
-
-# A variation of this lists also exists in:
-# //base/android/java/src/org/chromium/base/LocaleUtils.java
-# //ui/android/java/src/org/chromium/base/LocalizationUtils.java
-CHROME_TO_ANDROID_LOCALE_MAP = {
- 'en-GB': 'en-rGB',
- 'en-US': 'en-rUS',
- 'es-419': 'es-rUS',
- 'fil': 'tl',
- 'he': 'iw',
- 'id': 'in',
- 'pt-PT': 'pt-rPT',
- 'pt-BR': 'pt-rBR',
- 'yi': 'ji',
- 'zh-CN': 'zh-rCN',
- 'zh-TW': 'zh-rTW',
-}
-
-# Represents a line from a R.txt file.
-_TextSymbolEntry = collections.namedtuple('RTextEntry',
- ('java_type', 'resource_type', 'name', 'value'))
-
-
-def CreateResourceInfoFile(files_to_zip, zip_path):
- """Given a mapping of archive paths to their source, write an info file.
-
- The info file contains lines of '{archive_path},{source_path}' for ease of
- parsing. Assumes that there is no comma in the file names.
-
- Args:
- files_to_zip: Dict mapping path in the zip archive to original source.
- zip_path: Path where the zip file ends up, this is where the info file goes.
- """
- info_file_path = zip_path + '.info'
- with open(info_file_path, 'w') as info_file:
- for archive_path, source_path in files_to_zip.iteritems():
- info_file.write('{},{}\n'.format(archive_path, source_path))
-
-
-def _ParseTextSymbolsFile(path, fix_package_ids=False):
- """Given an R.txt file, returns a list of _TextSymbolEntry.
-
- Args:
- path: Input file path.
- fix_package_ids: if True, all packaged IDs read from the file
- will be fixed to 0x7f.
- Returns:
- A list of _TextSymbolEntry instances.
- Raises:
- Exception: An unexpected line was detected in the input.
- """
- ret = []
- with open(path) as f:
- for line in f:
- m = re.match(r'(int(?:\[\])?) (\w+) (\w+) (.+)$', line)
- if not m:
- raise Exception('Unexpected line in R.txt: %s' % line)
- java_type, resource_type, name, value = m.groups()
- if fix_package_ids:
- value = _FixPackageIds(value)
- ret.append(_TextSymbolEntry(java_type, resource_type, name, value))
- return ret
-
-
-def _FixPackageIds(resource_value):
- # Resource IDs for resources belonging to regular APKs have their first byte
- # as 0x7f (package id). However with webview, since it is not a regular apk
- # but used as a shared library, aapt is passed the --shared-resources flag
- # which changes some of the package ids to 0x02 and 0x00. This function just
- # normalises all package ids to 0x7f, which the generated code in R.java
- # changes to the correct package id at runtime.
- # resource_value is a string with either, a single value '0x12345678', or an
- # array of values like '{ 0xfedcba98, 0x01234567, 0x56789abc }'
- return re.sub(r'0x(?!01)\d\d', r'0x7f', resource_value)
-
-
-def _GetRTxtResourceNames(r_txt_path):
- """Parse an R.txt file and extract the set of resource names from it."""
- result = set()
- for entry in _ParseTextSymbolsFile(r_txt_path):
- result.add(entry.name)
- return result
-
-
-class RJavaBuildOptions:
- """A class used to model the various ways to build an R.java file.
-
- This is used to control which resource ID variables will be final or
- non-final, and whether an onResourcesLoaded() method will be generated
- to adjust the non-final ones, when the corresponding library is loaded
- at runtime.
-
- Note that by default, all resources are final, and there is no
- method generated, which corresponds to calling ExportNoResources().
- """
- def __init__(self):
- self.has_constant_ids = True
- self.resources_whitelist = None
- self.has_on_resources_loaded = False
- self.export_const_styleable = False
-
- def ExportNoResources(self):
- """Make all resource IDs final, and don't generate a method."""
- self.has_constant_ids = True
- self.resources_whitelist = None
- self.has_on_resources_loaded = False
- self.export_const_styleable = False
-
- def ExportAllResources(self):
- """Make all resource IDs non-final in the R.java file."""
- self.has_constant_ids = False
- self.resources_whitelist = None
-
- def ExportSomeResources(self, r_txt_file_path):
- """Only select specific resource IDs to be non-final.
-
- Args:
- r_txt_file_path: The path to an R.txt file. All resources named
- int it will be non-final in the generated R.java file, all others
- will be final.
- """
- self.has_constant_ids = True
- self.resources_whitelist = _GetRTxtResourceNames(r_txt_file_path)
-
- def ExportAllStyleables(self):
- """Make all styleable constants non-final, even non-resources ones.
-
- Resources that are styleable but not of int[] type are not actually
- resource IDs but constants. By default they are always final. Call this
- method to make them non-final anyway in the final R.java file.
- """
- self.export_const_styleable = True
-
- def GenerateOnResourcesLoaded(self):
- """Generate an onResourcesLoaded() method.
-
- This Java method will be called at runtime by the framework when
- the corresponding library (which includes the R.java source file)
- will be loaded at runtime. This corresponds to the --shared-resources
- or --app-as-shared-lib flags of 'aapt package'.
- """
- self.has_on_resources_loaded = True
-
- def _IsResourceFinal(self, entry):
- """Determines whether a resource should be final or not.
-
- Args:
- entry: A _TextSymbolEntry instance.
- Returns:
- True iff the corresponding entry should be final.
- """
- if entry.resource_type == 'styleable' and entry.java_type != 'int[]':
- # A styleable constant may be exported as non-final after all.
- return not self.export_const_styleable
- elif not self.has_constant_ids:
- # Every resource is non-final
- return False
- elif not self.resources_whitelist:
- # No whitelist means all IDs are non-final.
- return True
- else:
- # Otherwise, only those in the
- return entry.name not in self.resources_whitelist
-
-
-def CreateRJavaFiles(srcjar_dir, package, main_r_txt_file,
- extra_res_packages, extra_r_txt_files,
- rjava_build_options):
- """Create all R.java files for a set of packages and R.txt files.
-
- Args:
- srcjar_dir: The top-level output directory for the generated files.
- package: Top-level package name.
- main_r_txt_file: The main R.txt file containing the valid values
- of _all_ resource IDs.
- extra_res_packages: A list of extra package names.
- extra_r_txt_files: A list of extra R.txt files. One per item in
- |extra_res_packages|. Note that all resource IDs in them will be ignored,
- |and replaced by the values extracted from |main_r_txt_file|.
- rjava_build_options: An RJavaBuildOptions instance that controls how
- exactly the R.java file is generated.
- Raises:
- Exception if a package name appears several times in |extra_res_packages|
- """
- assert len(extra_res_packages) == len(extra_r_txt_files), \
- 'Need one R.txt file per package'
-
- packages = list(extra_res_packages)
- r_txt_files = list(extra_r_txt_files)
-
- if package and package not in packages:
- # Sometimes, an apk target and a resources target share the same
- # AndroidManifest.xml and thus |package| will already be in |packages|.
- packages.append(package)
- r_txt_files.append(main_r_txt_file)
-
- # Map of (resource_type, name) -> Entry.
- # Contains the correct values for resources.
- all_resources = {}
- for entry in _ParseTextSymbolsFile(main_r_txt_file, fix_package_ids=True):
- all_resources[(entry.resource_type, entry.name)] = entry
-
- # Map of package_name->resource_type->entry
- resources_by_package = (
- collections.defaultdict(lambda: collections.defaultdict(list)))
- # Build the R.java files using each package's R.txt file, but replacing
- # each entry's placeholder value with correct values from all_resources.
- for package, r_txt_file in zip(packages, r_txt_files):
- if package in resources_by_package:
- raise Exception(('Package name "%s" appeared twice. All '
- 'android_resources() targets must use unique package '
- 'names, or no package name at all.') % package)
- resources_by_type = resources_by_package[package]
- # The sub-R.txt files have the wrong values at this point. Read them to
- # figure out which entries belong to them, but use the values from the
- # main R.txt file.
- for entry in _ParseTextSymbolsFile(r_txt_file):
- entry = all_resources.get((entry.resource_type, entry.name))
- # For most cases missing entry here is an error. It means that some
- # library claims to have or depend on a resource that isn't included into
- # the APK. There is one notable exception: Google Play Services (GMS).
- # GMS is shipped as a bunch of AARs. One of them - basement - contains
- # R.txt with ids of all resources, but most of the resources are in the
- # other AARs. However, all other AARs reference their resources via
- # basement's R.java so the latter must contain all ids that are in its
- # R.txt. Most targets depend on only a subset of GMS AARs so some
- # resources are missing, which is okay because the code that references
- # them is missing too. We can't get an id for a resource that isn't here
- # so the only solution is to skip the resource entry entirely.
- #
- # We can verify that all entries referenced in the code were generated
- # correctly by running Proguard on the APK: it will report missing
- # fields.
- if entry:
- resources_by_type[entry.resource_type].append(entry)
-
- for package, resources_by_type in resources_by_package.iteritems():
- _CreateRJavaSourceFile(srcjar_dir, package, resources_by_type,
- rjava_build_options)
-
-
-def _CreateRJavaSourceFile(srcjar_dir, package, resources_by_type,
- rjava_build_options):
- """Generates an R.java source file."""
- package_r_java_dir = os.path.join(srcjar_dir, *package.split('.'))
- build_utils.MakeDirectory(package_r_java_dir)
- package_r_java_path = os.path.join(package_r_java_dir, 'R.java')
- java_file_contents = _RenderRJavaSource(package, resources_by_type,
- rjava_build_options)
- with open(package_r_java_path, 'w') as f:
- f.write(java_file_contents)
-
-
-# Resource IDs inside resource arrays are sorted. Application resource IDs start
-# with 0x7f but system resource IDs start with 0x01 thus system resource ids are
-# always at the start of the array. This function finds the index of the first
-# non system resource id to be used for package ID rewriting (we should not
-# rewrite system resource ids).
-def _GetNonSystemIndex(entry):
- """Get the index of the first application resource ID within a resource
- array."""
- res_ids = re.findall(r'0x[0-9a-f]{8}', entry.value)
- for i, res_id in enumerate(res_ids):
- if res_id.startswith('0x7f'):
- return i
- return len(res_ids)
-
-
-def _RenderRJavaSource(package, resources_by_type, rjava_build_options):
- """Render an R.java source file. See _CreateRJaveSourceFile for args info."""
- final_resources_by_type = collections.defaultdict(list)
- non_final_resources_by_type = collections.defaultdict(list)
- for res_type, resources in resources_by_type.iteritems():
- for entry in resources:
- # Entries in stylable that are not int[] are not actually resource ids
- # but constants.
- if rjava_build_options._IsResourceFinal(entry):
- final_resources_by_type[res_type].append(entry)
- else:
- non_final_resources_by_type[res_type].append(entry)
-
- # Keep these assignments all on one line to make diffing against regular
- # aapt-generated files easier.
- create_id = ('{{ e.resource_type }}.{{ e.name }} ^= packageIdTransform;')
- create_id_arr = ('{{ e.resource_type }}.{{ e.name }}[i] ^='
- ' packageIdTransform;')
- for_loop_condition = ('int i = {{ startIndex(e) }}; i < '
- '{{ e.resource_type }}.{{ e.name }}.length; ++i')
-
- # Here we diverge from what aapt does. Because we have so many
- # resources, the onResourcesLoaded method was exceeding the 64KB limit that
- # Java imposes. For this reason we split onResourcesLoaded into different
- # methods for each resource type.
- template = Template("""/* AUTO-GENERATED FILE. DO NOT MODIFY. */
-
-package {{ package }};
-
-public final class R {
- private static boolean sResourcesDidLoad;
- {% for resource_type in resource_types %}
- public static final class {{ resource_type }} {
- {% for e in final_resources[resource_type] %}
- public static final {{ e.java_type }} {{ e.name }} = {{ e.value }};
- {% endfor %}
- {% for e in non_final_resources[resource_type] %}
- public static {{ e.java_type }} {{ e.name }} = {{ e.value }};
- {% endfor %}
- }
- {% endfor %}
- {% if has_on_resources_loaded %}
- public static void onResourcesLoaded(int packageId) {
- assert !sResourcesDidLoad;
- sResourcesDidLoad = true;
- int packageIdTransform = (packageId ^ 0x7f) << 24;
- {% for resource_type in resource_types %}
- onResourcesLoaded{{ resource_type|title }}(packageIdTransform);
- {% for e in non_final_resources[resource_type] %}
- {% if e.java_type == 'int[]' %}
- for(""" + for_loop_condition + """) {
- """ + create_id_arr + """
- }
- {% endif %}
- {% endfor %}
- {% endfor %}
- }
- {% for res_type in resource_types %}
- private static void onResourcesLoaded{{ res_type|title }} (
- int packageIdTransform) {
- {% for e in non_final_resources[res_type] %}
- {% if res_type != 'styleable' and e.java_type != 'int[]' %}
- """ + create_id + """
- {% endif %}
- {% endfor %}
- }
- {% endfor %}
- {% endif %}
-}
-""", trim_blocks=True, lstrip_blocks=True)
-
- return template.render(
- package=package,
- resource_types=sorted(resources_by_type),
- has_on_resources_loaded=rjava_build_options.has_on_resources_loaded,
- final_resources=final_resources_by_type,
- non_final_resources=non_final_resources_by_type,
- startIndex=_GetNonSystemIndex)
-
-
-def ExtractPackageFromManifest(manifest_path):
- """Extract package name from Android manifest file."""
- doc = ElementTree.parse(manifest_path)
- return doc.getroot().get('package')
-
-
-def ExtractDeps(dep_zips, deps_dir):
- """Extract a list of resource dependency zip files.
-
- Args:
- dep_zips: A list of zip file paths, each one will be extracted to
- a subdirectory of |deps_dir|, named after the zip file (e.g.
- '/some/path/foo.zip' -> '{deps_dir}/foo/').
- deps_dir: Top-level extraction directory.
- Returns:
- The list of all sub-directory paths, relative to |deps_dir|.
- Raises:
- Exception: If a sub-directory already exists with the same name before
- extraction.
- """
- dep_subdirs = []
- for z in dep_zips:
- subdir = os.path.join(deps_dir, os.path.basename(z))
- if os.path.exists(subdir):
- raise Exception('Resource zip name conflict: ' + os.path.basename(z))
- build_utils.ExtractAll(z, path=subdir)
- dep_subdirs.append(subdir)
- return dep_subdirs
-
-
-class _ResourceBuildContext(object):
- """A temporary directory for packaging and compiling Android resources."""
- def __init__(self):
- """Initialized the context."""
- # The top-level temporary directory.
- self.temp_dir = tempfile.mkdtemp()
- # A location to store resources extracted form dependency zip files.
- self.deps_dir = os.path.join(self.temp_dir, 'deps')
- os.mkdir(self.deps_dir)
- # A location to place aapt-generated files.
- self.gen_dir = os.path.join(self.temp_dir, 'gen')
- os.mkdir(self.gen_dir)
- # Location of the generated R.txt file.
- self.r_txt_path = os.path.join(self.gen_dir, 'R.txt')
- # A location to place generated R.java files.
- self.srcjar_dir = os.path.join(self.temp_dir, 'java')
- os.mkdir(self.srcjar_dir)
-
- def Close(self):
- """Close the context and destroy all temporary files."""
- shutil.rmtree(self.temp_dir)
-
-
-@contextlib.contextmanager
-def BuildContext():
- """Generator for a _ResourceBuildContext instance."""
- try:
- context = _ResourceBuildContext()
- yield context
- finally:
- context.Close()
-
-
-def ResourceArgsParser():
- """Create an argparse.ArgumentParser instance with common argument groups.
-
- Returns:
- A tuple of (parser, in_group, out_group) corresponding to the parser
- instance, and the input and output argument groups for it, respectively.
- """
- parser = argparse.ArgumentParser(description=__doc__)
-
- input_opts = parser.add_argument_group('Input options')
- output_opts = parser.add_argument_group('Output options')
-
- build_utils.AddDepfileOption(output_opts)
-
- input_opts.add_argument('--android-sdk-jars', required=True,
- help='Path to the android.jar file.')
-
- input_opts.add_argument('--aapt-path', required=True,
- help='Path to the Android aapt tool')
-
- input_opts.add_argument('--aapt2-path',
- help='Path to the Android aapt2 tool. If in different'
- ' directory from --aapt-path.')
-
- input_opts.add_argument('--dependencies-res-zips', required=True,
- help='Resources zip archives from dependents. Required to '
- 'resolve @type/foo references into dependent '
- 'libraries.')
-
- input_opts.add_argument(
- '--r-text-in',
- help='Path to pre-existing R.txt. Its resource IDs override those found '
- 'in the aapt-generated R.txt when generating R.java.')
-
- input_opts.add_argument(
- '--extra-res-packages',
- help='Additional package names to generate R.java files for.')
-
- input_opts.add_argument(
- '--extra-r-text-files',
- help='For each additional package, the R.txt file should contain a '
- 'list of resources to be included in the R.java file in the format '
- 'generated by aapt.')
-
- return (parser, input_opts, output_opts)
-
-
-def HandleCommonOptions(options):
- """Handle common command-line options after parsing.
-
- Args:
- options: the result of parse_args() on the parser returned by
- ResourceArgsParser(). This function updates a few common fields.
- """
- options.android_sdk_jars = build_utils.ParseGnList(options.android_sdk_jars)
-
- options.dependencies_res_zips = (
- build_utils.ParseGnList(options.dependencies_res_zips))
-
- # Don't use [] as default value since some script explicitly pass "".
- if options.extra_res_packages:
- options.extra_res_packages = (
- build_utils.ParseGnList(options.extra_res_packages))
- else:
- options.extra_res_packages = []
-
- if options.extra_r_text_files:
- options.extra_r_text_files = (
- build_utils.ParseGnList(options.extra_r_text_files))
- else:
- options.extra_r_text_files = []
-
- if not options.aapt2_path:
- options.aapt2_path = options.aapt_path + '2'