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 |