diff options
Diffstat (limited to 'chromium/build/android/gyp/dex.py')
-rwxr-xr-x | chromium/build/android/gyp/dex.py | 187 |
1 files changed, 159 insertions, 28 deletions
diff --git a/chromium/build/android/gyp/dex.py b/chromium/build/android/gyp/dex.py index a2e17b4e282..043a08ab272 100755 --- a/chromium/build/android/gyp/dex.py +++ b/chromium/build/android/gyp/dex.py @@ -27,7 +27,27 @@ def _ParseArgs(args): build_utils.AddDepfileOption(parser) parser.add_argument('--output', required=True, help='Dex output path.') - parser.add_argument('--input-list', help='GN-list of additional input paths.') + parser.add_argument( + '--class-inputs', + action='append', + help='GN-list of .jars with .class files.') + parser.add_argument( + '--class-inputs-filearg', + action='append', + help='GN-list of .jars with .class files (added to depfile).') + parser.add_argument( + '--dex-inputs', action='append', help='GN-list of .jars with .dex files.') + parser.add_argument( + '--dex-inputs-filearg', + action='append', + help='GN-list of .jars with .dex files (added to depfile).') + parser.add_argument( + '--incremental-dir', + help='Path of directory to put intermediate dex files.') + parser.add_argument( + '--merge-incrementals', + action='store_true', + help='Combine all per-class .dex files into a single classes.dex') parser.add_argument( '--main-dex-list-path', help='File containing a list of the classes to include in the main dex.') @@ -35,7 +55,7 @@ def _ParseArgs(args): '--multi-dex', action='store_true', help='Allow multiple dex files within output.') - parser.add_argument('--d8-jar-path', required=True, help='Path to D8 jar.') + parser.add_argument('--r8-jar-path', required=True, help='Path to R8 jar.') parser.add_argument( '--release', action='store_true', @@ -44,7 +64,6 @@ def _ParseArgs(args): 'main dex and keeps all line number information, and then some.') parser.add_argument( '--min-api', help='Minimum Android API level compatibility.') - parser.add_argument('inputs', nargs='*', help='Input .jar files.') group = parser.add_argument_group('Dexlayout') group.add_argument( @@ -79,8 +98,12 @@ def _ParseArgs(args): if options.main_dex_list_path and not options.multi_dex: parser.error('--main-dex-list-path is unused if multidex is not enabled') - if options.input_list: - options.inputs += build_utils.ParseGnList(options.input_list) + options.class_inputs = build_utils.ParseGnList(options.class_inputs) + options.class_inputs_filearg = build_utils.ParseGnList( + options.class_inputs_filearg) + options.dex_inputs = build_utils.ParseGnList(options.dex_inputs) + options.dex_inputs_filearg = build_utils.ParseGnList( + options.dex_inputs_filearg) return options @@ -249,48 +272,156 @@ def _PerformDexlayout(tmp_dir, tmp_dex_output, options): return final_output -def _PerformDexing(options): - dex_cmd = ['java', '-jar', options.d8_jar_path, '--no-desugaring'] - if options.multi_dex and options.main_dex_list_path: - dex_cmd += ['--main-dex-list', options.main_dex_list_path] - if options.release: - dex_cmd += ['--release'] - if options.min_api: - dex_cmd += ['--min-api', options.min_api] +def _CreateFinalDex(options, d8_inputs, tmp_dir, dex_cmd): + tmp_dex_output = os.path.join(tmp_dir, 'tmp_dex_output.zip') + if (options.merge_incrementals or options.output.endswith('.dex') + or not all(f.endswith('.dex') for f in d8_inputs)): + if options.multi_dex and options.main_dex_list_path: + # Provides a list of classes that should be included in the main dex file. + dex_cmd = dex_cmd + ['--main-dex-list', options.main_dex_list_path] - with build_utils.TempDir() as tmp_dir: tmp_dex_dir = os.path.join(tmp_dir, 'tmp_dex_dir') os.mkdir(tmp_dex_dir) - _RunD8(dex_cmd, options.inputs, tmp_dex_dir) + _RunD8(dex_cmd, d8_inputs, tmp_dex_dir) + logging.info('Performed dex merging') + dex_files = [os.path.join(tmp_dex_dir, f) for f in os.listdir(tmp_dex_dir)] - if not options.output.endswith('.dex'): - tmp_dex_output = os.path.join(tmp_dir, 'tmp_dex_output.zip') - _ZipAligned(sorted(dex_files), tmp_dex_output) - else: - # Output to a .dex file. + if options.output.endswith('.dex'): if len(dex_files) > 1: raise Exception('%d files created, expected 1' % len(dex_files)) tmp_dex_output = dex_files[0] + else: + _ZipAligned(sorted(dex_files), tmp_dex_output) + else: + # Skip dexmerger. Just put all incrementals into the .jar individually. + _ZipAligned(sorted(d8_inputs), tmp_dex_output) + logging.info('Quick-zipped %d files', len(d8_inputs)) - if options.dexlayout_profile: - tmp_dex_output = _PerformDexlayout(tmp_dir, tmp_dex_output, options) + if options.dexlayout_profile: + tmp_dex_output = _PerformDexlayout(tmp_dir, tmp_dex_output, options) + + # The dex file is complete and can be moved out of tmp_dir. + shutil.move(tmp_dex_output, options.output) + + +def _IntermediateDexFilePathsFromInputJars(class_inputs, incremental_dir): + """Returns a list of all intermediate dex file paths.""" + dex_files = [] + for jar in class_inputs: + with zipfile.ZipFile(jar, 'r') as z: + for subpath in z.namelist(): + if subpath.endswith('.class'): + subpath = subpath[:-5] + 'dex' + dex_files.append(os.path.join(incremental_dir, subpath)) + return dex_files + + +def _DeleteStaleIncrementalDexFiles(dex_dir, dex_files): + """Deletes intermediate .dex files that are no longer needed.""" + all_files = build_utils.FindInDirectory(dex_dir) + desired_files = set(dex_files) + for path in all_files: + if path not in desired_files: + os.unlink(path) + + +def _ExtractClassFiles(changes, tmp_dir, class_inputs): + classes_list = [] + for jar in class_inputs: + if changes: + changed_class_list = set(changes.IterChangedSubpaths(jar)) + predicate = lambda x: x in changed_class_list and x.endswith('.class') + else: + predicate = lambda x: x.endswith('.class') + + classes_list.extend( + build_utils.ExtractAll(jar, path=tmp_dir, predicate=predicate)) + return classes_list + + +def _CreateIntermediateDexFiles(changes, options, tmp_dir, dex_cmd): + # Create temporary directory for classes to be extracted to. + tmp_extract_dir = os.path.join(tmp_dir, 'tmp_extract_dir') + os.mkdir(tmp_extract_dir) + + # Check whether changes were to a non-jar file, requiring full re-dex. + # E.g. r8.jar updated. + rebuild_all = changes.HasStringChanges() or not all( + p.endswith('.jar') for p in changes.IterChangedPaths()) + + if rebuild_all: + changes = None + class_files = _ExtractClassFiles(changes, tmp_extract_dir, + options.class_inputs) + logging.info('Extracted class files: %d', len(class_files)) + + # If the only change is deleting a file, class_files will be empty. + if class_files: + # Dex necessary classes into intermediate dex files. + dex_cmd = dex_cmd + ['--intermediate', '--file-per-class'] + _RunD8(dex_cmd, class_files, options.incremental_dir) + logging.info('Dexed class files.') + + +def _OnStaleMd5(changes, options, final_dex_inputs, dex_cmd): + logging.info('_OnStaleMd5') + with build_utils.TempDir() as tmp_dir: + if options.incremental_dir: + # Create directory for all intermediate dex files. + if not os.path.exists(options.incremental_dir): + os.makedirs(options.incremental_dir) - # The dex file is complete and can be moved out of tmp_dir. - shutil.move(tmp_dex_output, options.output) + _DeleteStaleIncrementalDexFiles(options.incremental_dir, final_dex_inputs) + logging.info('Stale files deleted') + _CreateIntermediateDexFiles(changes, options, tmp_dir, dex_cmd) + + _CreateFinalDex(options, final_dex_inputs, tmp_dir, dex_cmd) + logging.info('Dex finished for: %s', options.output) def main(args): + logging.basicConfig( + level=logging.INFO if os.environ.get('DEX_DEBUG') else logging.WARNING, + format='%(levelname).1s %(relativeCreated)6d %(message)s') options = _ParseArgs(args) - input_paths = list(options.inputs) + options.class_inputs += options.class_inputs_filearg + options.dex_inputs += options.dex_inputs_filearg + + input_paths = options.class_inputs + options.dex_inputs if options.multi_dex and options.main_dex_list_path: input_paths.append(options.main_dex_list_path) + input_paths.append(options.r8_jar_path) + + output_paths = [options.output] + + if options.incremental_dir: + final_dex_inputs = _IntermediateDexFilePathsFromInputJars( + options.class_inputs, options.incremental_dir) + output_paths += final_dex_inputs + else: + final_dex_inputs = list(options.class_inputs) + final_dex_inputs += options.dex_inputs - _PerformDexing(options) + dex_cmd = [ + build_utils.JAVA_PATH, '-jar', options.r8_jar_path, 'd8', + '--no-desugaring' + ] + if options.release: + dex_cmd += ['--release'] + if options.min_api: + dex_cmd += ['--min-api', options.min_api] - build_utils.WriteDepfile( - options.depfile, options.output, input_paths, add_pydeps=False) + build_utils.CallAndWriteDepfileIfStale( + lambda changes: _OnStaleMd5(changes, options, final_dex_inputs, dex_cmd), + options, + depfile_deps=options.class_inputs_filearg + options.dex_inputs_filearg, + output_paths=output_paths, + input_paths=input_paths, + input_strings=dex_cmd + [bool(options.incremental_dir)], + pass_changes=True, + track_subpaths_whitelist=options.class_inputs) if __name__ == '__main__': |