 Chromium Code Reviews
 Chromium Code Reviews Issue 1373443004:
  Improve javac.py --incremental logic by having it recompile more files  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master
    
  
    Issue 1373443004:
  Improve javac.py --incremental logic by having it recompile more files  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master| OLD | NEW | 
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python | 
| 2 # | 2 # | 
| 3 # Copyright 2013 The Chromium Authors. All rights reserved. | 3 # Copyright 2013 The Chromium Authors. All rights reserved. | 
| 4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be | 
| 5 # found in the LICENSE file. | 5 # found in the LICENSE file. | 
| 6 | 6 | 
| 7 import optparse | 7 import optparse | 
| 8 import os | 8 import os | 
| 9 import shutil | 9 import shutil | 
| 10 import re | 10 import re | 
| 11 import sys | 11 import sys | 
| 12 import textwrap | 12 import textwrap | 
| 13 | 13 | 
| 14 from util import build_utils | 14 from util import build_utils | 
| 15 from util import md5_check | |
| 15 | 16 | 
| 16 import jar | 17 import jar | 
| 17 | 18 | 
| 18 sys.path.append(build_utils.COLORAMA_ROOT) | 19 sys.path.append(build_utils.COLORAMA_ROOT) | 
| 19 import colorama | 20 import colorama | 
| 20 | 21 | 
| 21 | 22 | 
| 22 def ColorJavacOutput(output): | 23 def ColorJavacOutput(output): | 
| 23 fileline_prefix = r'(?P<fileline>(?P<file>[-.\w/\\]+.java):(?P<line>[0-9]+):)' | 24 fileline_prefix = r'(?P<fileline>(?P<file>[-.\w/\\]+.java):(?P<line>[0-9]+):)' | 
| 24 warning_re = re.compile( | 25 warning_re = re.compile( | 
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 101 wrapper = textwrap.TextWrapper(break_long_words=True, | 102 wrapper = textwrap.TextWrapper(break_long_words=True, | 
| 102 drop_whitespace=False, | 103 drop_whitespace=False, | 
| 103 subsequent_indent=' ', | 104 subsequent_indent=' ', | 
| 104 width=_MAX_MANIFEST_LINE_LEN - 2) | 105 width=_MAX_MANIFEST_LINE_LEN - 2) | 
| 105 output = '\r\n'.join(w for l in output for w in wrapper.wrap(l)) | 106 output = '\r\n'.join(w for l in output for w in wrapper.wrap(l)) | 
| 106 | 107 | 
| 107 with open(manifest_path, 'w') as f: | 108 with open(manifest_path, 'w') as f: | 
| 108 f.write(output) | 109 f.write(output) | 
| 109 | 110 | 
| 110 | 111 | 
| 112 def _DetermineFilesToRecompile(java_files, changed_paths): | |
| 113 # Calculate the set of files that must be recompiled as those within | |
| 114 # |changed_paths| as well as those that contain a reference to a class within | |
| 115 # |changed_paths|. | |
| 116 # This approach only mostly works. It's possible for a constant to be inlined | |
| 117 # across multiple files like so: | |
| 118 # class A { static final int foo = 1; } | |
| 119 # class B { static final int bar = A.foo; } | |
| 120 # class C { static final int baz = B.bar; } | |
| 121 # In this case, when A changes, A and B will be recompiled, but C will not. | |
| 122 changed_java_files = [p for p in java_files if p in changed_paths] | |
| 123 java_files_to_recompile = list(changed_java_files) | |
| 124 | |
| 125 changed_class_names = [] | |
| 126 for path in java_files_to_recompile: | |
| 127 class_name, _ = os.path.splitext(os.path.basename(path)) | |
| 128 changed_class_names.append(class_name) | |
| 129 | |
| 130 class_name_pattern = re.compile(r'\b(?:%s)\b' % '|'.join(changed_class_names)) | |
| 
Yaron
2015/09/25 18:34:09
I'm having trouble thinking this through but make
 
agrieve
2015/09/25 18:49:44
You can't reference an inner class without typing
 | |
| 131 for path in java_files: | |
| 132 if path in changed_paths: | |
| 133 continue | |
| 134 with open(path, 'rb') as fileobj: | |
| 135 data = fileobj.read() | |
| 136 if class_name_pattern.search(data): | |
| 137 java_files_to_recompile.append(path) | |
| 138 return java_files_to_recompile | |
| 139 | |
| 140 | |
| 111 def _ExtractClassFiles(jar_path, dest_dir, java_files): | 141 def _ExtractClassFiles(jar_path, dest_dir, java_files): | 
| 112 """Extracts all .class files not corresponding to |java_files|.""" | 142 """Extracts all .class files not corresponding to |java_files|.""" | 
| 113 # Two challenges exist here: | 143 # Two challenges exist here: | 
| 114 # 1. |java_files| have prefixes that are not represented in the the jar paths. | 144 # 1. |java_files| have prefixes that are not represented in the the jar paths. | 
| 115 # 2. A single .java file results in multiple .class files when it contains | 145 # 2. A single .java file results in multiple .class files when it contains | 
| 116 # nested classes. | 146 # nested classes. | 
| 117 # Here's an example: | 147 # Here's an example: | 
| 118 # source path: ../../base/android/java/src/org/chromium/Foo.java | 148 # source path: ../../base/android/java/src/org/chromium/Foo.java | 
| 119 # jar paths: org/chromium/Foo.class, org/chromium/Foo$Inner.class | 149 # jar paths: org/chromium/Foo.class, org/chromium/Foo$Inner.class | 
| 120 # To extract only .class files not related to the given .java files, we strip | 150 # To extract only .class files not related to the given .java files, we strip | 
| 121 # off ".class" and "$*.class" and use a substring match against java_files. | 151 # off ".class" and "$*.class" and use a substring match against java_files. | 
| 122 def extract_predicate(path): | 152 def extract_predicate(path): | 
| 123 if not path.endswith('.class'): | 153 if not path.endswith('.class'): | 
| 124 return False | 154 return False | 
| 125 path_without_suffix = re.sub(r'(?:\$[^/]+)?\.class$', '', path) | 155 path_without_suffix = re.sub(r'(?:\$|\.)[^/]+class$', '', path) | 
| 126 return not any(path_without_suffix in p for p in java_files) | 156 partial_java_path = path_without_suffix + '.java' | 
| 157 ret = not any(p.endswith(partial_java_path) for p in java_files) | |
| 158 return ret | |
| 
Yaron
2015/09/25 18:34:09
merge with line above
 
agrieve
2015/09/25 18:49:44
Done.
 | |
| 127 | 159 | 
| 128 build_utils.ExtractAll(jar_path, path=dest_dir, predicate=extract_predicate) | 160 build_utils.ExtractAll(jar_path, path=dest_dir, predicate=extract_predicate) | 
| 129 | 161 | 
| 130 | 162 | 
| 131 def _OnStaleMd5(changes, options, javac_cmd, java_files, classpath_inputs, | 163 def _OnStaleMd5(changes, options, javac_cmd, java_files, classpath_inputs, | 
| 132 runtime_classpath): | 164 runtime_classpath): | 
| 133 with build_utils.TempDir() as temp_dir: | 165 with build_utils.TempDir() as temp_dir: | 
| 134 srcjars = options.java_srcjars | 166 srcjars = options.java_srcjars | 
| 135 # The .excluded.jar contains .class files excluded from the main jar. | 167 # The .excluded.jar contains .class files excluded from the main jar. | 
| 136 # It is used for incremental compiles. | 168 # It is used for incremental compiles. | 
| 137 excluded_jar_path = options.jar_path.replace('.jar', '.excluded.jar') | 169 excluded_jar_path = options.jar_path.replace('.jar', '.excluded.jar') | 
| 138 | 170 | 
| 139 classes_dir = os.path.join(temp_dir, 'classes') | 171 classes_dir = os.path.join(temp_dir, 'classes') | 
| 140 os.makedirs(classes_dir) | 172 os.makedirs(classes_dir) | 
| 141 | 173 | 
| 142 changed_paths = None | 174 changed_paths = None | 
| 143 if options.incremental and changes.AddedOrModifiedOnly(): | 175 if options.incremental and changes.AddedOrModifiedOnly(): | 
| 144 changed_paths = set(changes.IterChangedPaths()) | 176 changed_paths = set(changes.IterChangedPaths()) | 
| 145 # Do a full compile if classpath has changed. | 177 # Do a full compile if classpath has changed. | 
| 146 if any(p in changed_paths for p in classpath_inputs): | 178 if any(p in changed_paths for p in classpath_inputs): | 
| 147 changed_paths = None | 179 changed_paths = None | 
| 148 else: | 180 else: | 
| 149 java_files = [p for p in java_files if p in changed_paths] | |
| 150 srcjars = [p for p in srcjars if p in changed_paths] | 181 srcjars = [p for p in srcjars if p in changed_paths] | 
| 151 | 182 | 
| 152 if srcjars: | 183 if srcjars: | 
| 153 java_dir = os.path.join(temp_dir, 'java') | 184 java_dir = os.path.join(temp_dir, 'java') | 
| 154 os.makedirs(java_dir) | 185 os.makedirs(java_dir) | 
| 155 for srcjar in options.java_srcjars: | 186 for srcjar in options.java_srcjars: | 
| 156 extract_predicate = None | |
| 157 if changed_paths: | 187 if changed_paths: | 
| 158 changed_subpaths = set(changes.IterChangedSubpaths(srcjar)) | 188 changed_paths.update(changes.IterChangedSubpaths(srcjar)) | 
| 159 extract_predicate = lambda p: p in changed_subpaths | 189 build_utils.ExtractAll(srcjar, path=java_dir, pattern='*.java') | 
| 160 build_utils.ExtractAll(srcjar, path=java_dir, pattern='*.java', | |
| 161 predicate=extract_predicate) | |
| 162 jar_srcs = build_utils.FindInDirectory(java_dir, '*.java') | 190 jar_srcs = build_utils.FindInDirectory(java_dir, '*.java') | 
| 163 java_files.extend(_FilterJavaFiles(jar_srcs, options.javac_includes)) | 191 java_files.extend(_FilterJavaFiles(jar_srcs, options.javac_includes)) | 
| 164 | 192 | 
| 193 if changed_paths: | |
| 194 java_files = _DetermineFilesToRecompile(java_files, changed_paths) | |
| 195 if md5_check.PRINT_EXPLANATIONS: | |
| 196 print 'Files to recompile:' | |
| 197 print ' ' + '\n '.join(java_files) | |
| 198 | |
| 165 if java_files: | 199 if java_files: | 
| 166 if changed_paths: | 200 if changed_paths: | 
| 167 # When no files have been removed and the output jar already | 201 # When no files have been removed and the output jar already | 
| 168 # exists, reuse .class files from the existing jar. | 202 # exists, reuse .class files from the existing jar. | 
| 169 _ExtractClassFiles(options.jar_path, classes_dir, java_files) | 203 _ExtractClassFiles(options.jar_path, classes_dir, java_files) | 
| 170 _ExtractClassFiles(excluded_jar_path, classes_dir, java_files) | 204 _ExtractClassFiles(excluded_jar_path, classes_dir, java_files) | 
| 171 # Add the extracted files to the classpath. | 205 # Add the extracted files to the classpath. | 
| 172 classpath_idx = javac_cmd.index('-classpath') | 206 classpath_idx = javac_cmd.index('-classpath') | 
| 173 javac_cmd[classpath_idx + 1] += ':' + classes_dir | 207 javac_cmd[classpath_idx + 1] += ':' + classes_dir | 
| 174 | 208 | 
| (...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 371 options, | 405 options, | 
| 372 input_paths=input_paths, | 406 input_paths=input_paths, | 
| 373 input_strings=javac_cmd, | 407 input_strings=javac_cmd, | 
| 374 output_paths=output_paths, | 408 output_paths=output_paths, | 
| 375 force=force, | 409 force=force, | 
| 376 pass_changes=True) | 410 pass_changes=True) | 
| 377 | 411 | 
| 378 | 412 | 
| 379 if __name__ == '__main__': | 413 if __name__ == '__main__': | 
| 380 sys.exit(main(sys.argv[1:])) | 414 sys.exit(main(sys.argv[1:])) | 
| OLD | NEW |