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