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