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