tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

prepare_resources.py (7565B)


      1 #!/usr/bin/env python3
      2 #
      3 # Copyright 2012 The Chromium Authors
      4 # Use of this source code is governed by a BSD-style license that can be
      5 # found in the LICENSE file.
      6 
      7 """Process Android resource directories to generate .resources.zip and R.txt
      8 files."""
      9 
     10 import argparse
     11 import os
     12 import shutil
     13 import sys
     14 import zipfile
     15 
     16 from util import build_utils
     17 from util import jar_info_utils
     18 from util import md5_check
     19 from util import resources_parser
     20 from util import resource_utils
     21 import action_helpers  # build_utils adds //build to sys.path.
     22 import zip_helpers
     23 
     24 
     25 def _ParseArgs(args):
     26  """Parses command line options.
     27 
     28  Returns:
     29    An options object as from argparse.ArgumentParser.parse_args()
     30  """
     31  parser = argparse.ArgumentParser(description=__doc__)
     32  action_helpers.add_depfile_arg(parser)
     33 
     34  parser.add_argument('--res-sources-path',
     35                      required=True,
     36                      help='Path to a list of input resources for this target.')
     37 
     38  parser.add_argument(
     39      '--r-text-in',
     40      help='Path to pre-existing R.txt. Its resource IDs override those found '
     41      'in the generated R.txt when generating R.java.')
     42 
     43  parser.add_argument(
     44      '--allow-missing-resources',
     45      action='store_true',
     46      help='Do not fail if some resources exist in the res/ dir but are not '
     47      'listed in the sources.')
     48 
     49  parser.add_argument(
     50      '--resource-zip-out',
     51      help='Path to a zip archive containing all resources from '
     52      '--resource-dirs, merged into a single directory tree.')
     53 
     54  parser.add_argument('--r-text-out',
     55                      help='Path to store the generated R.txt file.')
     56 
     57  parser.add_argument('--strip-drawables',
     58                      action="store_true",
     59                      help='Remove drawables from the resources.')
     60 
     61  options = parser.parse_args(args)
     62 
     63  with open(options.res_sources_path) as f:
     64    options.sources = f.read().splitlines()
     65  options.resource_dirs = resource_utils.DeduceResourceDirsFromFileList(
     66      options.sources)
     67 
     68  return options
     69 
     70 
     71 def _CheckAllFilesListed(resource_files, resource_dirs):
     72  resource_files = set(resource_files)
     73  missing_files = []
     74  for path, _ in resource_utils.IterResourceFilesInDirectories(resource_dirs):
     75    if path not in resource_files:
     76      missing_files.append(path)
     77 
     78  if missing_files:
     79    sys.stderr.write('Error: Found files not listed in the sources list of '
     80                     'the BUILD.gn target:\n')
     81    for path in missing_files:
     82      sys.stderr.write('{}\n'.format(path))
     83    sys.exit(1)
     84 
     85 
     86 def _ZipResources(resource_dirs, zip_path, ignore_pattern):
     87  # ignore_pattern is a string of ':' delimited list of globs used to ignore
     88  # files that should not be part of the final resource zip.
     89  files_to_zip = []
     90  path_info = resource_utils.ResourceInfoFile()
     91  for index, resource_dir in enumerate(resource_dirs):
     92    attributed_aar = None
     93    if not resource_dir.startswith('..'):
     94      aar_source_info_path = os.path.join(
     95          os.path.dirname(resource_dir), 'source.info')
     96      if os.path.exists(aar_source_info_path):
     97        attributed_aar = jar_info_utils.ReadAarSourceInfo(aar_source_info_path)
     98 
     99    for path, archive_path in resource_utils.IterResourceFilesInDirectories(
    100        [resource_dir], ignore_pattern):
    101      attributed_path = path
    102      if attributed_aar:
    103        attributed_path = os.path.join(attributed_aar, 'res',
    104                                       path[len(resource_dir) + 1:])
    105      # Use the non-prefixed archive_path in the .info file.
    106      path_info.AddMapping(archive_path, attributed_path)
    107 
    108      resource_dir_name = os.path.basename(resource_dir)
    109      archive_path = '{}_{}/{}'.format(index, resource_dir_name, archive_path)
    110      files_to_zip.append((archive_path, path))
    111 
    112  path_info.Write(zip_path + '.info')
    113 
    114  with zipfile.ZipFile(zip_path, 'w') as z:
    115    # This magic comment signals to resource_utils.ExtractDeps that this zip is
    116    # not just the contents of a single res dir, without the encapsulating res/
    117    # (like the outputs of android_generated_resources targets), but instead has
    118    # the contents of possibly multiple res/ dirs each within an encapsulating
    119    # directory within the zip.
    120    z.comment = resource_utils.MULTIPLE_RES_MAGIC_STRING
    121    zip_helpers.add_files_to_zip(files_to_zip, z)
    122 
    123 
    124 def _GenerateRTxt(options, r_txt_path):
    125  """Generate R.txt file.
    126 
    127  Args:
    128    options: The command-line options tuple.
    129    r_txt_path: Locates where the R.txt file goes.
    130  """
    131  ignore_pattern = resource_utils.AAPT_IGNORE_PATTERN
    132  if options.strip_drawables:
    133    ignore_pattern += ':*drawable*'
    134 
    135  resources_parser.RTxtGenerator(options.resource_dirs,
    136                                 ignore_pattern).WriteRTxtFile(r_txt_path)
    137 
    138 
    139 def _OnStaleMd5(options):
    140  with resource_utils.BuildContext() as build:
    141    if options.sources and not options.allow_missing_resources:
    142      _CheckAllFilesListed(options.sources, options.resource_dirs)
    143    if options.r_text_in:
    144      r_txt_path = options.r_text_in
    145    else:
    146      _GenerateRTxt(options, build.r_txt_path)
    147      r_txt_path = build.r_txt_path
    148 
    149    if options.r_text_out:
    150      shutil.copyfile(r_txt_path, options.r_text_out)
    151 
    152    if options.resource_zip_out:
    153      ignore_pattern = resource_utils.AAPT_IGNORE_PATTERN
    154      if options.strip_drawables:
    155        ignore_pattern += ':*drawable*'
    156      _ZipResources(options.resource_dirs, options.resource_zip_out,
    157                    ignore_pattern)
    158 
    159 
    160 def main(args):
    161  args = build_utils.ExpandFileArgs(args)
    162  options = _ParseArgs(args)
    163 
    164  # Order of these must match order specified in GN so that the correct one
    165  # appears first in the depfile.
    166  output_paths = [
    167      options.resource_zip_out,
    168      options.resource_zip_out + '.info',
    169      options.r_text_out,
    170  ]
    171 
    172  input_paths = [options.res_sources_path]
    173  if options.r_text_in:
    174    input_paths += [options.r_text_in]
    175 
    176  # Resource files aren't explicitly listed in GN. Listing them in the depfile
    177  # ensures the target will be marked stale when resource files are removed.
    178  depfile_deps = []
    179  resource_names = []
    180  for resource_dir in options.resource_dirs:
    181    for resource_file in build_utils.FindInDirectory(resource_dir, '*'):
    182      # Don't list the empty .keep file in depfile. Since it doesn't end up
    183      # included in the .zip, it can lead to -w 'dupbuild=err' ninja errors
    184      # if ever moved.
    185      if not resource_file.endswith(os.path.join('empty', '.keep')):
    186        input_paths.append(resource_file)
    187        depfile_deps.append(resource_file)
    188      resource_names.append(os.path.relpath(resource_file, resource_dir))
    189 
    190  # Resource filenames matter to the output, so add them to strings as well.
    191  # This matters if a file is renamed but not changed (http://crbug.com/597126).
    192  input_strings = sorted(resource_names) + [
    193      options.strip_drawables,
    194  ]
    195 
    196  # Since android_resources targets like *__all_dfm_resources depend on java
    197  # targets that they do not need (in reality it only needs the transitive
    198  # resource targets that those java targets depend on), md5_check is used to
    199  # prevent outputs from being re-written when real inputs have not changed.
    200  md5_check.CallAndWriteDepfileIfStale(lambda: _OnStaleMd5(options),
    201                                       options,
    202                                       input_paths=input_paths,
    203                                       input_strings=input_strings,
    204                                       output_paths=output_paths,
    205                                       depfile_deps=depfile_deps)
    206 
    207 
    208 if __name__ == '__main__':
    209  main(sys.argv[1:])