diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-07-16 11:45:35 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-07-17 08:59:23 +0000 |
commit | 552906b0f222c5d5dd11b9fd73829d510980461a (patch) | |
tree | 3a11e6ed0538a81dd83b20cf3a4783e297f26d91 /chromium/build/android/gyp/util | |
parent | 1b05827804eaf047779b597718c03e7d38344261 (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.py | 123 | ||||
-rwxr-xr-x | chromium/build/android/gyp/util/diff_utils.py | 15 | ||||
-rw-r--r-- | chromium/build/android/gyp/util/jar_info_utils.py | 7 | ||||
-rw-r--r-- | chromium/build/android/gyp/util/md5_check.py | 56 | ||||
-rwxr-xr-x | chromium/build/android/gyp/util/md5_check_test.py | 2 | ||||
-rw-r--r-- | chromium/build/android/gyp/util/resource_utils.py | 109 | ||||
-rwxr-xr-x | chromium/build/android/gyp/util/resource_utils_test.py | 18 | ||||
-rw-r--r-- | chromium/build/android/gyp/util/resources_parser.py | 139 | ||||
-rw-r--r-- | chromium/build/android/gyp/util/zipalign.py | 4 |
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. |