Index: build/android/gyp/javac.py |
diff --git a/build/android/gyp/javac.py b/build/android/gyp/javac.py |
index c95dc98eaa7c670703a79303ea22b11eaae8b524..728c49d5eef513191a56f85993dbf368898ffbfe 100755 |
--- a/build/android/gyp/javac.py |
+++ b/build/android/gyp/javac.py |
@@ -108,6 +108,102 @@ def _CreateManifest(manifest_path, classpath, main_class=None, |
f.write(output) |
+def _ExtractClassFiles(jar_path, dest_dir, java_files): |
+ """Extracts all .class files not corresponding to |java_files|.""" |
+ # Two challenges exist here: |
+ # 1. |java_files| have prefixes that are not represented in the the jar paths. |
+ # 2. A single .java file results in multiple .class files when it contains |
+ # nested classes. |
+ # Here's an example: |
+ # source path: ../../base/android/java/src/org/chromium/Foo.java |
+ # jar paths: org/chromium/Foo.class, org/chromium/Foo$Inner.class |
+ # To extract only .class files not related to the given .java files, we strip |
+ # off ".class" and "$*.class" and use a substring match against java_files. |
+ def extract_predicate(path): |
+ if not path.endswith('.class'): |
+ return False |
+ path_without_suffix = re.sub(r'(?:\$[^/]+)?\.class$', '', path) |
+ return not any(path_without_suffix in p for p in java_files) |
+ |
+ build_utils.ExtractAll(jar_path, path=dest_dir, predicate=extract_predicate) |
+ |
+ |
+def _OnStaleMd5(changes, options, javac_cmd, java_files, classpath_inputs, |
+ runtime_classpath): |
+ with build_utils.TempDir() as temp_dir: |
+ srcjars = options.java_srcjars |
+ # The .excluded.jar contains .class files excluded from the main jar. |
+ # It is used for incremental compiles. |
+ excluded_jar_path = options.jar_path.replace('.jar', '.excluded.jar') |
+ |
+ classes_dir = os.path.join(temp_dir, 'classes') |
+ os.makedirs(classes_dir) |
+ |
+ changed_paths = None |
+ if options.incremental and changes.AddedOrModifiedOnly(): |
+ changed_paths = set(changes.IterChangedPaths()) |
+ # Do a full compile if classpath has changed. |
+ if any(p in changed_paths for p in classpath_inputs): |
+ changed_paths = None |
+ else: |
+ java_files = [p for p in java_files if p in changed_paths] |
+ srcjars = [p for p in srcjars if p in changed_paths] |
+ |
+ if srcjars: |
+ java_dir = os.path.join(temp_dir, 'java') |
+ os.makedirs(java_dir) |
+ for srcjar in options.java_srcjars: |
+ extract_predicate = None |
+ if changed_paths: |
+ changed_subpaths = set(changes.IterChangedSubpaths(srcjar)) |
+ extract_predicate = lambda p: p in changed_subpaths |
+ build_utils.ExtractAll(srcjar, path=java_dir, pattern='*.java', |
+ predicate=extract_predicate) |
+ jar_srcs = build_utils.FindInDirectory(java_dir, '*.java') |
+ java_files.extend(_FilterJavaFiles(jar_srcs, options.javac_includes)) |
+ |
+ if java_files: |
+ if changed_paths: |
+ # When no files have been removed and the output jar already |
+ # exists, reuse .class files from the existing jar. |
+ _ExtractClassFiles(options.jar_path, classes_dir, java_files) |
+ _ExtractClassFiles(excluded_jar_path, classes_dir, java_files) |
+ # Add the extracted files to the classpath. |
+ classpath_idx = javac_cmd.index('-classpath') |
+ javac_cmd[classpath_idx + 1] += ':' + classes_dir |
+ |
+ # Don't include the output directory in the initial set of args since it |
+ # being in a temp dir makes it unstable (breaks md5 stamping). |
+ cmd = javac_cmd + ['-d', classes_dir] + java_files |
+ |
+ build_utils.CheckOutput( |
+ cmd, |
+ print_stdout=options.chromium_code, |
+ stderr_filter=ColorJavacOutput) |
+ |
+ if options.main_class or options.manifest_entry: |
+ entries = [] |
+ if options.manifest_entry: |
+ entries = [e.split(':') for e in options.manifest_entry] |
+ manifest_file = os.path.join(temp_dir, 'manifest') |
+ _CreateManifest(manifest_file, runtime_classpath, options.main_class, |
+ entries) |
+ else: |
+ manifest_file = None |
+ |
+ glob = options.jar_excluded_classes |
+ inclusion_predicate = lambda f: not build_utils.MatchesGlob(f, glob) |
+ exclusion_predicate = lambda f: not inclusion_predicate(f) |
+ |
+ jar.JarDirectory(classes_dir, |
+ options.jar_path, |
+ manifest_file=manifest_file, |
+ predicate=inclusion_predicate) |
+ jar.JarDirectory(classes_dir, |
+ excluded_jar_path, |
+ predicate=exclusion_predicate) |
+ |
+ |
def _ParseOptions(argv): |
parser = optparse.OptionParser() |
build_utils.AddDepfileOption(parser) |
@@ -136,6 +232,11 @@ def _ParseOptions(argv): |
action='store_true', |
help='Whether to use interface jars (.interface.jar) when compiling') |
parser.add_option( |
+ '--incremental', |
+ action='store_true', |
+ help='Whether to re-use .class files rather than recompiling them ' |
+ '(when possible).') |
+ parser.add_option( |
'--javac-includes', |
default='', |
help='A list of file patterns. If provided, only java files that match' |
@@ -239,62 +340,40 @@ def main(argv): |
# trigger a compile warning or error. |
javac_cmd.extend(['-XDignore.symbol.file']) |
- # Compute the list of paths that when changed, we need to rebuild. |
- input_paths = options.bootclasspath + options.java_srcjars + java_files |
+ classpath_inputs = options.bootclasspath |
# TODO(agrieve): Remove this .TOC heuristic once GYP is no more. |
- if not options.use_ijars: |
+ if options.use_ijars: |
+ classpath_inputs.extend(compile_classpath) |
+ else: |
for path in compile_classpath: |
if os.path.exists(path + '.TOC'): |
- input_paths.append(path + '.TOC') |
- else: |
- input_paths.append(path) |
- |
- def on_stale_md5(): |
- with build_utils.TempDir() as temp_dir: |
- if options.java_srcjars: |
- java_dir = os.path.join(temp_dir, 'java') |
- os.makedirs(java_dir) |
- for srcjar in options.java_srcjars: |
- build_utils.ExtractAll(srcjar, path=java_dir, pattern='*.java') |
- jar_srcs = build_utils.FindInDirectory(java_dir, '*.java') |
- java_files.extend(_FilterJavaFiles(jar_srcs, options.javac_includes)) |
- |
- classes_dir = os.path.join(temp_dir, 'classes') |
- os.makedirs(classes_dir) |
- |
- if java_files: |
- # Don't include the output directory in the initial set of args since it |
- # being in a temp dir makes it unstable (breaks md5 stamping). |
- cmd = javac_cmd + ['-d', classes_dir] + java_files |
- |
- build_utils.CheckOutput( |
- cmd, |
- print_stdout=options.chromium_code, |
- stderr_filter=ColorJavacOutput) |
- |
- if options.main_class or options.manifest_entry: |
- entries = [] |
- if options.manifest_entry: |
- entries = [e.split(':') for e in options.manifest_entry] |
- manifest_file = os.path.join(temp_dir, 'manifest') |
- _CreateManifest(manifest_file, runtime_classpath, options.main_class, |
- entries) |
+ classpath_inputs.append(path + '.TOC') |
else: |
- manifest_file = None |
- jar.JarDirectory(classes_dir, |
- options.jar_excluded_classes, |
- options.jar_path, |
- manifest_file=manifest_file) |
+ classpath_inputs.append(path) |
+ |
+ # Compute the list of paths that when changed, we need to rebuild. |
+ input_paths = classpath_inputs + options.java_srcjars + java_files |
+ |
+ output_paths = [ |
+ options.jar_path, |
+ options.jar_path.replace('.jar', '.excluded.jar'), |
+ ] |
+ # An escape hatch to be able to check if incremental compiles are causing |
+ # problems. |
+ force = int(os.environ.get('DISABLE_INCREMENTAL_JAVAC', 0)) |
# List python deps in input_strings rather than input_paths since the contents |
# of them does not change what gets written to the depsfile. |
build_utils.CallAndWriteDepfileIfStale( |
- on_stale_md5, |
+ lambda changes: _OnStaleMd5(changes, options, javac_cmd, java_files, |
+ classpath_inputs, runtime_classpath), |
options, |
input_paths=input_paths, |
input_strings=javac_cmd, |
- output_paths=[options.jar_path]) |
+ output_paths=output_paths, |
+ force=force, |
+ pass_changes=True) |
if __name__ == '__main__': |