summaryrefslogtreecommitdiffstats
path: root/chromium/build/android/gyp/util
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-07-16 11:45:35 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-07-17 08:59:23 +0000
commit552906b0f222c5d5dd11b9fd73829d510980461a (patch)
tree3a11e6ed0538a81dd83b20cf3a4783e297f26d91 /chromium/build/android/gyp/util
parent1b05827804eaf047779b597718c03e7d38344261 (diff)
BASELINE: Update Chromium to 83.0.4103.122
Change-Id: Ie3a82f5bb0076eec2a7c6a6162326b4301ee291e Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/build/android/gyp/util')
-rw-r--r--chromium/build/android/gyp/util/build_utils.py123
-rwxr-xr-xchromium/build/android/gyp/util/diff_utils.py15
-rw-r--r--chromium/build/android/gyp/util/jar_info_utils.py7
-rw-r--r--chromium/build/android/gyp/util/md5_check.py56
-rwxr-xr-xchromium/build/android/gyp/util/md5_check_test.py2
-rw-r--r--chromium/build/android/gyp/util/resource_utils.py109
-rwxr-xr-xchromium/build/android/gyp/util/resource_utils_test.py18
-rw-r--r--chromium/build/android/gyp/util/resources_parser.py139
-rw-r--r--chromium/build/android/gyp/util/zipalign.py4
9 files changed, 363 insertions, 110 deletions
diff --git a/chromium/build/android/gyp/util/build_utils.py b/chromium/build/android/gyp/util/build_utils.py
index 41b3a483849..bc15fbb61f2 100644
--- a/chromium/build/android/gyp/util/build_utils.py
+++ b/chromium/build/android/gyp/util/build_utils.py
@@ -4,11 +4,13 @@
"""Contains common helpers for GN action()s."""
+import atexit
import collections
import contextlib
import filecmp
import fnmatch
import json
+import logging
import os
import pipes
import re
@@ -19,22 +21,21 @@ import sys
import tempfile
import zipfile
-# Any new non-system import must be added to:
-# //build/config/android/internal_rules.gni
-
-from util import md5_check
-
sys.path.append(os.path.join(os.path.dirname(__file__),
os.pardir, os.pardir, os.pardir))
import gn_helpers
-# Definition copied from pylib/constants/__init__.py to avoid adding
-# a dependency on pylib.
-DIR_SOURCE_ROOT = os.environ.get('CHECKOUT_SOURCE_ROOT',
- os.path.abspath(os.path.join(os.path.dirname(__file__),
- os.pardir, os.pardir, os.pardir, os.pardir)))
-JAVA_PATH = os.path.join(DIR_SOURCE_ROOT, 'third_party', 'jdk', 'current',
- 'bin', 'java')
+# Use relative paths to improved hermetic property of build scripts.
+DIR_SOURCE_ROOT = os.path.relpath(
+ os.environ.get(
+ 'CHECKOUT_SOURCE_ROOT',
+ os.path.join(
+ os.path.dirname(__file__), os.pardir, os.pardir, os.pardir,
+ os.pardir)))
+JAVA_HOME = os.path.join(DIR_SOURCE_ROOT, 'third_party', 'jdk', 'current')
+JAVA_PATH = os.path.join(JAVA_HOME, 'bin', 'java')
+JAVAC_PATH = os.path.join(JAVA_HOME, 'bin', 'javac')
+JAVAP_PATH = os.path.join(JAVA_HOME, 'bin', 'javap')
RT_JAR_PATH = os.path.join(DIR_SOURCE_ROOT, 'third_party', 'jdk', 'extras',
'java_8', 'jre', 'lib', 'rt.jar')
@@ -45,8 +46,8 @@ except NameError:
@contextlib.contextmanager
-def TempDir():
- dirname = tempfile.mkdtemp()
+def TempDir(**kwargs):
+ dirname = tempfile.mkdtemp(**kwargs)
try:
yield dirname
finally:
@@ -207,7 +208,7 @@ def FilterLines(output, filter_string):
"""
re_filter = re.compile(filter_string)
return '\n'.join(
- line for line in output.splitlines() if not re_filter.search(line))
+ line for line in output.split('\n') if not re_filter.search(line))
def FilterReflectiveAccessJavaWarnings(output):
@@ -418,6 +419,8 @@ def DoZip(inputs, output, base_dir=None, compress_fn=None,
for tup in inputs:
if isinstance(tup, string_types):
tup = (os.path.relpath(tup, base_dir), tup)
+ if tup[0].startswith('..'):
+ raise Exception('Invalid zip_path: ' + tup[0])
input_tuples.append(tup)
# Sort by zip path to ensure stable zip ordering.
@@ -445,8 +448,20 @@ def ZipDir(output, base_dir, compress_fn=None, zip_prefix_path=None):
for f in files:
inputs.append(os.path.join(root, f))
- with AtomicOutput(output) as f:
- DoZip(inputs, f, base_dir, compress_fn=compress_fn,
+ if isinstance(output, zipfile.ZipFile):
+ DoZip(
+ inputs,
+ output,
+ base_dir,
+ compress_fn=compress_fn,
+ zip_prefix_path=zip_prefix_path)
+ else:
+ with AtomicOutput(output) as f:
+ DoZip(
+ inputs,
+ f,
+ base_dir,
+ compress_fn=compress_fn,
zip_prefix_path=zip_prefix_path)
@@ -526,7 +541,7 @@ def GetSortedTransitiveDependencies(top, deps_func):
return list(deps_map)
-def _ComputePythonDependencies():
+def ComputePythonDependencies():
"""Gets the paths of imported non-system python modules.
A path is assumed to be a "system" import if it is outside of chromium's
@@ -537,9 +552,11 @@ def _ComputePythonDependencies():
if m is not None and hasattr(m, '__file__'))
abs_module_paths = map(os.path.abspath, module_paths)
- assert os.path.isabs(DIR_SOURCE_ROOT)
+ abs_dir_source_root = os.path.abspath(DIR_SOURCE_ROOT)
non_system_module_paths = [
- p for p in abs_module_paths if p.startswith(DIR_SOURCE_ROOT)]
+ p for p in abs_module_paths if p.startswith(abs_dir_source_root)
+ ]
+
def ConvertPycToPy(s):
if s.endswith('.pyc'):
return s[:-1]
@@ -567,6 +584,23 @@ def _ForceLazyModulesToLoad():
break
+def InitLogging(enabling_env):
+ logging.basicConfig(
+ level=logging.DEBUG if os.environ.get(enabling_env) else logging.WARNING,
+ format='%(levelname).1s %(process)d %(relativeCreated)6d %(message)s')
+ script_name = os.path.basename(sys.argv[0])
+ logging.info('Started (%s)', script_name)
+
+ my_pid = os.getpid()
+
+ def log_exit():
+ # Do not log for fork'ed processes.
+ if os.getpid() == my_pid:
+ logging.info("Job's done (%s)", script_name)
+
+ atexit.register(log_exit)
+
+
def AddDepfileOption(parser):
# TODO(agrieve): Get rid of this once we've moved to argparse.
if hasattr(parser, 'add_option'):
@@ -582,7 +616,7 @@ def WriteDepfile(depfile_path, first_gn_output, inputs=None, add_pydeps=True):
assert not isinstance(inputs, string_types) # Easy mistake to make
inputs = inputs or []
if add_pydeps:
- inputs = _ComputePythonDependencies() + inputs
+ inputs = ComputePythonDependencies() + inputs
MakeDirectory(os.path.dirname(depfile_path))
# Ninja does not support multiple outputs in depfiles.
with open(depfile_path, 'w') as depfile:
@@ -654,50 +688,3 @@ def ReadSourcesList(sources_list_file_name):
"""
with open(sources_list_file_name) as f:
return [file_name.strip() for file_name in f]
-
-
-def CallAndWriteDepfileIfStale(on_stale_md5,
- options,
- record_path=None,
- input_paths=None,
- input_strings=None,
- output_paths=None,
- force=False,
- pass_changes=False,
- track_subpaths_whitelist=None,
- depfile_deps=None):
- """Wraps md5_check.CallAndRecordIfStale() and writes a depfile if applicable.
-
- Depfiles are automatically added to output_paths when present in the |options|
- argument. They are then created after |on_stale_md5| is called.
-
- By default, only python dependencies are added to the depfile. If there are
- other input paths that are not captured by GN deps, then they should be listed
- in depfile_deps. It's important to write paths to the depfile that are already
- captured by GN deps since GN args can cause GN deps to change, and such
- changes are not immediately reflected in depfiles (http://crbug.com/589311).
- """
- if not output_paths:
- raise Exception('At least one output_path must be specified.')
- input_paths = list(input_paths or [])
- input_strings = list(input_strings or [])
- output_paths = list(output_paths or [])
-
- input_paths += _ComputePythonDependencies()
-
- md5_check.CallAndRecordIfStale(
- on_stale_md5,
- record_path=record_path,
- input_paths=input_paths,
- input_strings=input_strings,
- output_paths=output_paths,
- force=force,
- pass_changes=pass_changes,
- track_subpaths_whitelist=track_subpaths_whitelist)
-
- # Write depfile even when inputs have not changed to ensure build correctness
- # on bots that build with & without patch, and the patch changes the depfile
- # location.
- if hasattr(options, 'depfile') and options.depfile:
- WriteDepfile(
- options.depfile, output_paths[0], depfile_deps, add_pydeps=False)
diff --git a/chromium/build/android/gyp/util/diff_utils.py b/chromium/build/android/gyp/util/diff_utils.py
index 4fc3a41634f..bac85280fa5 100755
--- a/chromium/build/android/gyp/util/diff_utils.py
+++ b/chromium/build/android/gyp/util/diff_utils.py
@@ -10,11 +10,24 @@ import difflib
from util import build_utils
+def _SkipOmitted(line):
+ """
+ Skip lines that are to be intentionally omitted from the expectations file.
+
+ This is required when the file to be compared against expectations contains
+ a line that changes from build to build because - for instance - it contains
+ version information.
+ """
+ if line.endswith('# OMIT FROM EXPECTATIONS\n'):
+ return '# THIS LINE WAS OMITTED\n'
+ return line
+
+
def DiffFileContents(expected_path, actual_path):
"""Check file contents for equality and return the diff or None."""
with open(expected_path) as f_expected, open(actual_path) as f_actual:
expected_lines = f_expected.readlines()
- actual_lines = f_actual.readlines()
+ actual_lines = [_SkipOmitted(line) for line in f_actual]
if expected_lines == actual_lines:
return None
diff --git a/chromium/build/android/gyp/util/jar_info_utils.py b/chromium/build/android/gyp/util/jar_info_utils.py
index 677e4e42616..355bcb09014 100644
--- a/chromium/build/android/gyp/util/jar_info_utils.py
+++ b/chromium/build/android/gyp/util/jar_info_utils.py
@@ -13,6 +13,13 @@ import os
# contains its .class definition instead.
+def ReadAarSourceInfo(info_path):
+ """Returns the source= path from an .aar's source.info file."""
+ # The .info looks like: "source=path/to/.aar\n".
+ with open(info_path) as f:
+ return f.read().rstrip().split('=', 1)[1]
+
+
def ParseJarInfoFile(info_path):
"""Parse a given .jar.info file as a dictionary.
diff --git a/chromium/build/android/gyp/util/md5_check.py b/chromium/build/android/gyp/util/md5_check.py
index 0ad6f1b4003..a8a815e7e4f 100644
--- a/chromium/build/android/gyp/util/md5_check.py
+++ b/chromium/build/android/gyp/util/md5_check.py
@@ -12,6 +12,7 @@ import os
import sys
import zipfile
+from util import build_utils
# When set and a difference is detected, a diff of what changed is printed.
PRINT_EXPLANATIONS = int(os.environ.get('PRINT_BUILD_EXPLANATIONS', 0))
@@ -20,6 +21,53 @@ PRINT_EXPLANATIONS = int(os.environ.get('PRINT_BUILD_EXPLANATIONS', 0))
_FORCE_REBUILD = int(os.environ.get('FORCE_REBUILD', 0))
+def CallAndWriteDepfileIfStale(on_stale_md5,
+ options,
+ record_path=None,
+ input_paths=None,
+ input_strings=None,
+ output_paths=None,
+ force=False,
+ pass_changes=False,
+ track_subpaths_allowlist=None,
+ depfile_deps=None):
+ """Wraps CallAndRecordIfStale() and writes a depfile if applicable.
+
+ Depfiles are automatically added to output_paths when present in the |options|
+ argument. They are then created after |on_stale_md5| is called.
+
+ By default, only python dependencies are added to the depfile. If there are
+ other input paths that are not captured by GN deps, then they should be listed
+ in depfile_deps. It's important to write paths to the depfile that are already
+ captured by GN deps since GN args can cause GN deps to change, and such
+ changes are not immediately reflected in depfiles (http://crbug.com/589311).
+ """
+ if not output_paths:
+ raise Exception('At least one output_path must be specified.')
+ input_paths = list(input_paths or [])
+ input_strings = list(input_strings or [])
+ output_paths = list(output_paths or [])
+
+ input_paths += build_utils.ComputePythonDependencies()
+
+ CallAndRecordIfStale(
+ on_stale_md5,
+ record_path=record_path,
+ input_paths=input_paths,
+ input_strings=input_strings,
+ output_paths=output_paths,
+ force=force,
+ pass_changes=pass_changes,
+ track_subpaths_allowlist=track_subpaths_allowlist)
+
+ # Write depfile even when inputs have not changed to ensure build correctness
+ # on bots that build with & without patch, and the patch changes the depfile
+ # location.
+ if hasattr(options, 'depfile') and options.depfile:
+ build_utils.WriteDepfile(
+ options.depfile, output_paths[0], depfile_deps, add_pydeps=False)
+
+
def CallAndRecordIfStale(function,
record_path=None,
input_paths=None,
@@ -27,7 +75,7 @@ def CallAndRecordIfStale(function,
output_paths=None,
force=False,
pass_changes=False,
- track_subpaths_whitelist=None):
+ track_subpaths_allowlist=None):
"""Calls function if outputs are stale.
Outputs are considered stale if:
@@ -48,7 +96,7 @@ def CallAndRecordIfStale(function,
force: Whether to treat outputs as missing regardless of whether they
actually are.
pass_changes: Whether to pass a Changes instance to |function|.
- track_subpaths_whitelist: Relevant only when pass_changes=True. List of .zip
+ track_subpaths_allowlist: Relevant only when pass_changes=True. List of .zip
files from |input_paths| to make subpath information available for.
"""
assert record_path or output_paths
@@ -64,11 +112,11 @@ def CallAndRecordIfStale(function,
new_metadata = _Metadata(track_entries=pass_changes or PRINT_EXPLANATIONS)
new_metadata.AddStrings(input_strings)
- zip_whitelist = set(track_subpaths_whitelist or [])
+ zip_allowlist = set(track_subpaths_allowlist or [])
for path in input_paths:
# It's faster to md5 an entire zip file than it is to just locate & hash
# its central directory (which is what this used to do).
- if path in zip_whitelist:
+ if path in zip_allowlist:
entries = _ExtractZipEntries(path)
new_metadata.AddZipFile(path, entries)
else:
diff --git a/chromium/build/android/gyp/util/md5_check_test.py b/chromium/build/android/gyp/util/md5_check_test.py
index cba7a6a354a..9b3b9039f39 100755
--- a/chromium/build/android/gyp/util/md5_check_test.py
+++ b/chromium/build/android/gyp/util/md5_check_test.py
@@ -73,7 +73,7 @@ class TestMd5Check(unittest.TestCase):
output_paths=output_paths,
force=force,
pass_changes=(expected_changes or added_or_modified_only) is not None,
- track_subpaths_whitelist=zip_paths if track_subentries else None)
+ track_subpaths_allowlist=zip_paths if track_subentries else None)
self.assertEqual(should_call, self.called, message)
if expected_changes:
description = self.changes.DescribeDifference()
diff --git a/chromium/build/android/gyp/util/resource_utils.py b/chromium/build/android/gyp/util/resource_utils.py
index f60788b9bd9..ea4696c41c4 100644
--- a/chromium/build/android/gyp/util/resource_utils.py
+++ b/chromium/build/android/gyp/util/resource_utils.py
@@ -216,20 +216,77 @@ def IterResourceFilesInDirectories(directories,
yield path, archive_path
-def CreateResourceInfoFile(files_to_zip, zip_path):
- """Given a mapping of archive paths to their source, write an info file.
+class ResourceInfoFile(object):
+ """Helper for building up .res.info files."""
- The info file contains lines of '{archive_path},{source_path}' for ease of
- parsing. Assumes that there is no comma in the file names.
+ def __init__(self):
+ # Dict of archive_path -> source_path for the current target.
+ self._entries = {}
+ # List of (old_archive_path, new_archive_path) tuples.
+ self._renames = []
+ # We don't currently support using both AddMapping and MergeInfoFile.
+ self._add_mapping_was_called = False
+
+ def AddMapping(self, archive_path, source_path):
+ """Adds a single |archive_path| -> |source_path| entry."""
+ self._add_mapping_was_called = True
+ # "values/" files do not end up in the apk except through resources.arsc.
+ if archive_path.startswith('values'):
+ return
+ source_path = os.path.normpath(source_path)
+ new_value = self._entries.setdefault(archive_path, source_path)
+ if new_value != source_path:
+ raise Exception('Duplicate AddMapping for "{}". old={} new={}'.format(
+ archive_path, new_value, source_path))
+
+ def RegisterRename(self, old_archive_path, new_archive_path):
+ """Records an archive_path rename.
+
+ |old_archive_path| does not need to currently exist in the mappings. Renames
+ are buffered and replayed only when Write() is called.
+ """
+ if not old_archive_path.startswith('values'):
+ self._renames.append((old_archive_path, new_archive_path))
- 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 MergeInfoFile(self, info_file_path):
+ """Merges the mappings from |info_file_path| into this object.
+
+ Any existing entries are overridden.
+ """
+ assert not self._add_mapping_was_called
+ # Allows clobbering, which is used when overriding resources.
+ with open(info_file_path) as f:
+ self._entries.update(l.rstrip().split('\t') for l in f)
+
+ def _ApplyRenames(self):
+ applied_renames = set()
+ ret = self._entries
+ for rename_tup in self._renames:
+ # Duplicate entries happen for resource overrides.
+ # Use a "seen" set to ensure we still error out if multiple renames
+ # happen for the same old_archive_path with different new_archive_paths.
+ if rename_tup in applied_renames:
+ continue
+ applied_renames.add(rename_tup)
+ old_archive_path, new_archive_path = rename_tup
+ ret[new_archive_path] = ret[old_archive_path]
+ del ret[old_archive_path]
+
+ self._entries = None
+ self._renames = None
+ return ret
+
+ def Write(self, info_file_path):
+ """Applies renames and writes out the file.
+
+ No other methods may be called after this.
+ """
+ entries = self._ApplyRenames()
+ lines = []
+ for archive_path, source_path in entries.iteritems():
+ lines.append('{}\t{}\n'.format(archive_path, source_path))
+ with open(info_file_path, 'w') as info_file:
+ info_file.writelines(sorted(lines))
def _ParseTextSymbolsFile(path, fix_package_ids=False):
@@ -283,26 +340,26 @@ def GetRTxtStringResourceNames(r_txt_path):
})
-def GenerateStringResourcesWhitelist(module_r_txt_path, whitelist_r_txt_path):
- """Generate a whitelist of string resource IDs.
+def GenerateStringResourcesAllowList(module_r_txt_path, allowlist_r_txt_path):
+ """Generate a allowlist of string resource IDs.
Args:
module_r_txt_path: Input base module R.txt path.
- whitelist_r_txt_path: Input whitelist R.txt path.
+ allowlist_r_txt_path: Input allowlist R.txt path.
Returns:
A dictionary mapping numerical resource IDs to the corresponding
string resource names. The ID values are taken from string resources in
- |module_r_txt_path| that are also listed by name in |whitelist_r_txt_path|.
+ |module_r_txt_path| that are also listed by name in |allowlist_r_txt_path|.
"""
- whitelisted_names = {
+ allowlisted_names = {
entry.name
- for entry in _ParseTextSymbolsFile(whitelist_r_txt_path)
+ for entry in _ParseTextSymbolsFile(allowlist_r_txt_path)
if entry.resource_type == 'string'
}
return {
int(entry.value, 0): entry.name
for entry in _ParseTextSymbolsFile(module_r_txt_path)
- if entry.resource_type == 'string' and entry.name in whitelisted_names
+ if entry.resource_type == 'string' and entry.name in allowlisted_names
}
@@ -319,21 +376,21 @@ class RJavaBuildOptions:
"""
def __init__(self):
self.has_constant_ids = True
- self.resources_whitelist = None
+ self.resources_allowlist = 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.resources_allowlist = 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
+ self.resources_allowlist = None
def ExportSomeResources(self, r_txt_file_path):
"""Only select specific resource IDs to be non-final.
@@ -344,7 +401,7 @@ class RJavaBuildOptions:
will be final.
"""
self.has_constant_ids = True
- self.resources_whitelist = _GetRTxtResourceNames(r_txt_file_path)
+ self.resources_allowlist = _GetRTxtResourceNames(r_txt_file_path)
def ExportAllStyleables(self):
"""Make all styleable constants non-final, even non-resources ones.
@@ -379,12 +436,12 @@ class RJavaBuildOptions:
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.
+ elif not self.resources_allowlist:
+ # No allowlist means all IDs are non-final.
return True
else:
# Otherwise, only those in the
- return entry.name not in self.resources_whitelist
+ return entry.name not in self.resources_allowlist
def CreateRJavaFiles(srcjar_dir,
diff --git a/chromium/build/android/gyp/util/resource_utils_test.py b/chromium/build/android/gyp/util/resource_utils_test.py
index dc1094aca08..60bfcf906c9 100755
--- a/chromium/build/android/gyp/util/resource_utils_test.py
+++ b/chromium/build/android/gyp/util/resource_utils_test.py
@@ -56,7 +56,7 @@ _TEST_RESOURCES_MAP_1 = {
_TEST_NAMESPACES_1 = {'android': 'http://schemas.android.com/apk/res/android'}
-_TEST_RESOURCES_WHITELIST_1 = ['low_memory_error', 'structured_text']
+_TEST_RESOURCES_ALLOWLIST_1 = ['low_memory_error', 'structured_text']
# Extracted from one generated Chromium R.txt file, with string resource
# names shuffled randomly.
@@ -78,10 +78,10 @@ int styleable SnackbarLayout_android_maxWidth 0
int styleable SnackbarLayout_elevation 2
'''
-# Test whitelist R.txt file. Note that AlternateErrorPagesEnabledTitle is
+# Test allowlist R.txt file. Note that AlternateErrorPagesEnabledTitle is
# listed as an 'anim' and should thus be skipped. Similarly the string
# 'ThisStringDoesNotAppear' should not be in the final result.
-_TEST_WHITELIST_R_TXT = r'''int anim AlternateErrorPagesEnabledTitle 0x7f0eeeee
+_TEST_ALLOWLIST_R_TXT = r'''int anim AlternateErrorPagesEnabledTitle 0x7f0eeeee
int string AllowedDomainsForAppsDesc 0x7f0c0105
int string AlternateErrorPagesEnabledDesc 0x7f0c0107
int string ThisStringDoesNotAppear 0x7f0fffff
@@ -119,14 +119,14 @@ class ResourceUtilsTest(unittest.TestCase):
resource_utils.GetRTxtStringResourceNames(tmp_file),
_TEST_R_TXT_STRING_RESOURCE_NAMES)
- def test_GenerateStringResourcesWhitelist(self):
+ def test_GenerateStringResourcesAllowList(self):
with build_utils.TempDir() as tmp_dir:
tmp_module_rtxt_file = _CreateTestFile(tmp_dir, "test_R.txt", _TEST_R_TXT)
- tmp_whitelist_rtxt_file = _CreateTestFile(tmp_dir, "test_whitelist_R.txt",
- _TEST_WHITELIST_R_TXT)
+ tmp_allowlist_rtxt_file = _CreateTestFile(tmp_dir, "test_allowlist_R.txt",
+ _TEST_ALLOWLIST_R_TXT)
self.assertDictEqual(
- resource_utils.GenerateStringResourcesWhitelist(
- tmp_module_rtxt_file, tmp_whitelist_rtxt_file),
+ resource_utils.GenerateStringResourcesAllowList(
+ tmp_module_rtxt_file, tmp_allowlist_rtxt_file),
_TEST_R_TEXT_RESOURCES_IDS)
def test_IsAndroidLocaleQualifier(self):
@@ -260,7 +260,7 @@ class ResourceUtilsTest(unittest.TestCase):
test_file = self._CreateTestResourceFile(
tmp_path, 'foo', _TEST_RESOURCES_MAP_1, _TEST_NAMESPACES_1)
resource_utils.FilterAndroidResourceStringsXml(
- test_file, lambda x: x in _TEST_RESOURCES_WHITELIST_1)
+ test_file, lambda x: x in _TEST_RESOURCES_ALLOWLIST_1)
self._CheckTestResourceFile(test_file, _TEST_XML_OUTPUT_2)
diff --git a/chromium/build/android/gyp/util/resources_parser.py b/chromium/build/android/gyp/util/resources_parser.py
new file mode 100644
index 00000000000..8abd0c5d1ee
--- /dev/null
+++ b/chromium/build/android/gyp/util/resources_parser.py
@@ -0,0 +1,139 @@
+# Copyright 2020 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 collections
+import os
+import re
+from xml.etree import ElementTree
+
+from util import build_utils
+from util import resource_utils
+
+_TextSymbolEntry = collections.namedtuple(
+ 'RTextEntry', ('java_type', 'resource_type', 'name', 'value'))
+
+_DUMMY_RTXT_ID = '0x7f010001'
+_DUMMY_RTXT_INDEX = '1'
+
+
+def _ResourceNameToJavaSymbol(resource_name):
+ return re.sub('[\.:]', '_', resource_name)
+
+
+class RTxtGenerator(object):
+ def __init__(self,
+ res_dirs,
+ ignore_pattern=resource_utils.AAPT_IGNORE_PATTERN):
+ self.res_dirs = res_dirs
+ self.ignore_pattern = ignore_pattern
+
+ def _ParseDeclareStyleable(self, node):
+ ret = set()
+ stylable_name = _ResourceNameToJavaSymbol(node.attrib['name'])
+ ret.add(
+ _TextSymbolEntry('int[]', 'styleable', stylable_name,
+ '{{{}}}'.format(_DUMMY_RTXT_ID)))
+ for child in node:
+ if child.tag == 'eat-comment':
+ continue
+ if child.tag != 'attr':
+ # This parser expects everything inside <declare-stylable/> to be either
+ # an attr or an eat-comment. If new resource xml files are added that do
+ # not conform to this, this parser needs updating.
+ raise Exception('Unexpected tag {} inside <delcare-stylable/>'.format(
+ child.tag))
+ entry_name = '{}_{}'.format(
+ stylable_name, _ResourceNameToJavaSymbol(child.attrib['name']))
+ ret.add(
+ _TextSymbolEntry('int', 'styleable', entry_name, _DUMMY_RTXT_INDEX))
+ if not child.attrib['name'].startswith('android:'):
+ resource_name = _ResourceNameToJavaSymbol(child.attrib['name'])
+ ret.add(_TextSymbolEntry('int', 'attr', resource_name, _DUMMY_RTXT_ID))
+ for entry in child:
+ if entry.tag not in ('enum', 'flag'):
+ # This parser expects everything inside <attr/> to be either an
+ # <enum/> or an <flag/>. If new resource xml files are added that do
+ # not conform to this, this parser needs updating.
+ raise Exception('Unexpected tag {} inside <attr/>'.format(entry.tag))
+ resource_name = _ResourceNameToJavaSymbol(entry.attrib['name'])
+ ret.add(_TextSymbolEntry('int', 'id', resource_name, _DUMMY_RTXT_ID))
+ return ret
+
+ def _ExtractNewIdsFromNode(self, node):
+ ret = set()
+ # Sometimes there are @+id/ in random attributes (not just in android:id)
+ # and apparently that is valid. See:
+ # https://developer.android.com/reference/android/widget/RelativeLayout.LayoutParams.html
+ for value in node.attrib.values():
+ if value.startswith('@+id/'):
+ resource_name = value[5:]
+ ret.add(_TextSymbolEntry('int', 'id', resource_name, _DUMMY_RTXT_ID))
+ for child in node:
+ ret.update(self._ExtractNewIdsFromNode(child))
+ return ret
+
+ def _ExtractNewIdsFromXml(self, xml_path):
+ root = ElementTree.parse(xml_path).getroot()
+ return self._ExtractNewIdsFromNode(root)
+
+ def _ParseValuesXml(self, xml_path):
+ ret = set()
+ root = ElementTree.parse(xml_path).getroot()
+ assert root.tag == 'resources'
+ for child in root:
+ if child.tag == 'eat-comment':
+ # eat-comment is just a dummy documentation element.
+ continue
+ if child.tag == 'declare-styleable':
+ ret.update(self._ParseDeclareStyleable(child))
+ else:
+ if child.tag == 'item':
+ resource_type = child.attrib['type']
+ elif child.tag in ('array', 'integer-array', 'string-array'):
+ resource_type = 'array'
+ else:
+ resource_type = child.tag
+ name = _ResourceNameToJavaSymbol(child.attrib['name'])
+ ret.add(_TextSymbolEntry('int', resource_type, name, _DUMMY_RTXT_ID))
+ return ret
+
+ def _CollectResourcesListFromDirectory(self, res_dir):
+ ret = set()
+ globs = resource_utils._GenerateGlobs(self.ignore_pattern)
+ for root, _, files in os.walk(res_dir):
+ resource_type = os.path.basename(root)
+ if '-' in resource_type:
+ resource_type = resource_type[:resource_type.index('-')]
+ for f in files:
+ if build_utils.MatchesGlob(f, globs):
+ continue
+ if resource_type == 'values':
+ ret.update(self._ParseValuesXml(os.path.join(root, f)))
+ else:
+ if '.' in f:
+ resource_name = f[:f.index('.')]
+ else:
+ resource_name = f
+ ret.add(
+ _TextSymbolEntry('int', resource_type, resource_name,
+ _DUMMY_RTXT_ID))
+ # Other types not just layouts can contain new ids (eg: Menus and
+ # Drawables). Just in case, look for new ids in all files.
+ if f.endswith('.xml'):
+ ret.update(self._ExtractNewIdsFromXml(os.path.join(root, f)))
+ return ret
+
+ def _CollectResourcesListFromDirectories(self):
+ ret = set()
+ for res_dir in self.res_dirs:
+ ret.update(self._CollectResourcesListFromDirectory(res_dir))
+ return ret
+
+ def WriteRTxtFile(self, rtxt_path):
+ resources = self._CollectResourcesListFromDirectories()
+ with open(rtxt_path, 'w') as f:
+ for resource in resources:
+ line = '{0.java_type} {0.resource_type} {0.name} {0.value}\n'.format(
+ resource)
+ f.write(line)
diff --git a/chromium/build/android/gyp/util/zipalign.py b/chromium/build/android/gyp/util/zipalign.py
index eb5aaed762a..8b8711851cc 100644
--- a/chromium/build/android/gyp/util/zipalign.py
+++ b/chromium/build/android/gyp/util/zipalign.py
@@ -69,7 +69,9 @@ def _SetAlignment(zip_obj, zip_info, alignment):
"""
cur_offset = zip_obj.fp.tell()
header_size = _FIXED_ZIP_HEADER_LEN + len(zip_info.filename)
- padding_needed = (cur_offset - header_size) % alignment
+ padding_needed = (alignment - (
+ (cur_offset + header_size) % alignment)) % alignment
+
# Extra field used to 4-byte align classes.dex. Alignment speeds up
# execution when dex files are used via incremental install.