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 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
115 # 2. A single .java file results in multiple .class files when it contains | 116 # 2. A single .java file results in multiple .class files when it contains |
116 # nested classes. | 117 # nested classes. |
117 # Here's an example: | 118 # Here's an example: |
118 # source path: ../../base/android/java/src/org/chromium/Foo.java | 119 # source path: ../../base/android/java/src/org/chromium/Foo.java |
119 # jar paths: org/chromium/Foo.class, org/chromium/Foo$Inner.class | 120 # 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 | 121 # 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. | 122 # off ".class" and "$*.class" and use a substring match against java_files. |
122 def extract_predicate(path): | 123 def extract_predicate(path): |
123 if not path.endswith('.class'): | 124 if not path.endswith('.class'): |
124 return False | 125 return False |
125 path_without_suffix = re.sub(r'(?:\$[^/]+)?\.class$', '', path) | 126 path_without_suffix = re.sub(r'(?:\$|\.)[^/]+class$', '', path) |
126 return not any(path_without_suffix in p for p in java_files) | 127 partial_java_path = path_without_suffix + '.java' |
128 return not any(p.endswith(partial_java_path) for p in java_files) | |
127 | 129 |
128 build_utils.ExtractAll(jar_path, path=dest_dir, predicate=extract_predicate) | 130 build_utils.ExtractAll(jar_path, path=dest_dir, predicate=extract_predicate) |
131 for path in build_utils.FindInDirectory(dest_dir, '*.class'): | |
132 shutil.copystat(jar_path, path) | |
133 | |
134 | |
135 def _ConvertToJMakeArgs(javac_cmd, pdb_path): | |
136 compiler = javac_cmd[0] | |
137 new_args = ['-C' + arg for arg in javac_cmd] | |
Yaron
2015/09/30 02:19:25
I feel like these lines would be a little clearer
agrieve
2015/10/01 16:26:11
Done.
| |
138 if compiler != 'javac': | |
139 new_args.extend(('-jcexec', new_args[0])) | |
140 new_args[0] = 'bin/jmake' | |
141 new_args.extend(('-pdb', pdb_path)) | |
142 if md5_check.PRINT_EXPLANATIONS: | |
143 new_args.append('-Xtiming') | |
144 | |
145 def revert_arg(index): | |
146 new_args[index] = javac_cmd[index] | |
147 new_args[index + 1] = javac_cmd[index + 1] | |
148 | |
149 # Unprefix classpath args. | |
Yaron
2015/09/30 02:19:25
Yuck :(
agrieve
2015/10/01 16:26:11
Done.
| |
150 revert_arg(javac_cmd.index('-classpath')) | |
151 if '-bootclasspath' in javac_cmd: | |
152 revert_arg(javac_cmd.index('-bootclasspath')) | |
153 return new_args | |
154 | |
155 | |
156 def _FilterJMakeOutput(stdout): | |
157 if md5_check.PRINT_EXPLANATIONS: | |
158 return stdout | |
159 return re.sub(r'\b(Jmake version|Writing project database).*?\n', '', stdout) | |
160 | |
161 | |
162 def _FixTempPathsInIncrementalMetadata(pdb_path, temp_dir): | |
163 # The .pdb records absolute paths. Fix up paths witin /tmp (srcjars). | |
Yaron
2015/09/30 02:19:25
within
agrieve
2015/10/01 16:26:11
Done.
agrieve
2015/10/01 16:26:11
Done.
| |
164 if os.path.exists(pdb_path): | |
165 with open(pdb_path) as fileobj: | |
Yaron
2015/09/30 02:19:25
use fileinput.input: http://stackoverflow.com/ques
agrieve
2015/10/01 16:26:11
It's a binary file. Added a comment.
| |
166 pdb_data = fileobj.read() | |
167 with open(pdb_path, 'w') as fileobj: | |
168 fileobj.write(re.sub(r'/tmp/[^/]*', temp_dir, pdb_data)) | |
129 | 169 |
130 | 170 |
131 def _OnStaleMd5(changes, options, javac_cmd, java_files, classpath_inputs, | 171 def _OnStaleMd5(changes, options, javac_cmd, java_files, classpath_inputs, |
132 runtime_classpath): | 172 runtime_classpath): |
133 with build_utils.TempDir() as temp_dir: | 173 with build_utils.TempDir() as temp_dir: |
134 srcjars = options.java_srcjars | 174 srcjars = options.java_srcjars |
135 # The .excluded.jar contains .class files excluded from the main jar. | 175 # The .excluded.jar contains .class files excluded from the main jar. |
136 # It is used for incremental compiles. | 176 # It is used for incremental compiles. |
137 excluded_jar_path = options.jar_path.replace('.jar', '.excluded.jar') | 177 excluded_jar_path = options.jar_path.replace('.jar', '.excluded.jar') |
138 | 178 |
139 classes_dir = os.path.join(temp_dir, 'classes') | 179 classes_dir = os.path.join(temp_dir, 'classes') |
140 os.makedirs(classes_dir) | 180 os.makedirs(classes_dir) |
141 | 181 |
142 changed_paths = None | 182 changed_paths = None |
183 # jmake can handle deleted files, but it's a rare case and it would | |
184 # complicate this script's logic. | |
143 if options.incremental and changes.AddedOrModifiedOnly(): | 185 if options.incremental and changes.AddedOrModifiedOnly(): |
144 changed_paths = set(changes.IterChangedPaths()) | 186 changed_paths = set(changes.IterChangedPaths()) |
145 # Do a full compile if classpath has changed. | 187 # Do a full compile if classpath has changed. |
188 # jmake doesn't seem to do this on its own... Might be that ijars mess up | |
189 # its change-detection logic. | |
146 if any(p in changed_paths for p in classpath_inputs): | 190 if any(p in changed_paths for p in classpath_inputs): |
147 changed_paths = None | 191 changed_paths = None |
148 else: | 192 |
149 java_files = [p for p in java_files if p in changed_paths] | 193 if options.incremental: |
150 srcjars = [p for p in srcjars if p in changed_paths] | 194 # jmake is a compiler wrapper that figures out the minimal set of .java |
195 # files that need to be rebuilt given a set of .java files that have | |
196 # changed. | |
197 # jmake determines what files are stale based on timestamps between .java | |
198 # and .class files. Since we use .jars, .srcjars, and md5 checks, | |
199 # timestamp info isn't accurate for this purpose. Rather than use jmake's | |
200 # programatic interface (like we eventually should), we ensure that all | |
201 # .class files are newer than their .java files, and convey to jmake which | |
202 # sources are stale by having their .class files be missing entirely | |
203 # (by not extracting them). | |
204 pdb_path = options.jar_path + '.pdb' | |
205 javac_cmd = _ConvertToJMakeArgs(javac_cmd, pdb_path) | |
206 if srcjars: | |
207 _FixTempPathsInIncrementalMetadata(pdb_path, temp_dir) | |
151 | 208 |
152 if srcjars: | 209 if srcjars: |
153 java_dir = os.path.join(temp_dir, 'java') | 210 java_dir = os.path.join(temp_dir, 'java') |
154 os.makedirs(java_dir) | 211 os.makedirs(java_dir) |
155 for srcjar in options.java_srcjars: | 212 for srcjar in options.java_srcjars: |
156 extract_predicate = None | |
157 if changed_paths: | 213 if changed_paths: |
158 changed_subpaths = set(changes.IterChangedSubpaths(srcjar)) | 214 changed_paths.update(changes.IterChangedSubpaths(srcjar)) |
159 extract_predicate = lambda p: p in changed_subpaths | 215 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') | 216 jar_srcs = build_utils.FindInDirectory(java_dir, '*.java') |
163 java_files.extend(_FilterJavaFiles(jar_srcs, options.javac_includes)) | 217 jar_srcs = _FilterJavaFiles(jar_srcs, options.javac_includes) |
218 java_files.extend(jar_srcs) | |
219 if changed_paths: | |
220 # Set the mtime of all sources to 0 since we use the absense of .class | |
221 # files to tell jmake which files are stale. | |
Yaron
2015/09/30 02:19:25
how does this impact non-incremental builds which
agrieve
2015/10/01 16:26:11
It won't be hit since changed_paths == None for no
| |
222 for path in jar_srcs: | |
223 os.utime(path, (0, 0)) | |
164 | 224 |
165 if java_files: | 225 if java_files: |
166 if changed_paths: | 226 if changed_paths: |
167 # When no files have been removed and the output jar already | 227 changed_java_files = [p for p in java_files if p in changed_paths] |
168 # exists, reuse .class files from the existing jar. | 228 if os.path.exists(options.jar_path): |
169 _ExtractClassFiles(options.jar_path, classes_dir, java_files) | 229 _ExtractClassFiles(options.jar_path, classes_dir, changed_java_files) |
170 _ExtractClassFiles(excluded_jar_path, classes_dir, java_files) | 230 if os.path.exists(excluded_jar_path): |
171 # Add the extracted files to the classpath. | 231 _ExtractClassFiles(excluded_jar_path, classes_dir, changed_java_files) |
232 # Add the extracted files to the classpath. This is required because | |
233 # when compiling only a subset of files, classes that haven't changed | |
234 # need to be findable. | |
172 classpath_idx = javac_cmd.index('-classpath') | 235 classpath_idx = javac_cmd.index('-classpath') |
173 javac_cmd[classpath_idx + 1] += ':' + classes_dir | 236 javac_cmd[classpath_idx + 1] += ':' + classes_dir |
174 | 237 |
175 # Don't include the output directory in the initial set of args since it | 238 # Don't include the output directory in the initial set of args since it |
176 # being in a temp dir makes it unstable (breaks md5 stamping). | 239 # being in a temp dir makes it unstable (breaks md5 stamping). |
177 cmd = javac_cmd + ['-d', classes_dir] + java_files | 240 cmd = javac_cmd + ['-d', classes_dir] + java_files |
178 | 241 |
179 build_utils.CheckOutput( | 242 build_utils.CheckOutput( |
180 cmd, | 243 cmd, |
181 print_stdout=options.chromium_code, | 244 print_stdout=options.chromium_code, |
245 stdout_filter=_FilterJMakeOutput, | |
182 stderr_filter=ColorJavacOutput) | 246 stderr_filter=ColorJavacOutput) |
183 | 247 |
184 if options.main_class or options.manifest_entry: | 248 if options.main_class or options.manifest_entry: |
185 entries = [] | 249 entries = [] |
186 if options.manifest_entry: | 250 if options.manifest_entry: |
187 entries = [e.split(':') for e in options.manifest_entry] | 251 entries = [e.split(':') for e in options.manifest_entry] |
188 manifest_file = os.path.join(temp_dir, 'manifest') | 252 manifest_file = os.path.join(temp_dir, 'manifest') |
189 _CreateManifest(manifest_file, runtime_classpath, options.main_class, | 253 _CreateManifest(manifest_file, runtime_classpath, options.main_class, |
190 entries) | 254 entries) |
191 else: | 255 else: |
(...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
354 else: | 418 else: |
355 classpath_inputs.append(path) | 419 classpath_inputs.append(path) |
356 | 420 |
357 # Compute the list of paths that when changed, we need to rebuild. | 421 # Compute the list of paths that when changed, we need to rebuild. |
358 input_paths = classpath_inputs + options.java_srcjars + java_files | 422 input_paths = classpath_inputs + options.java_srcjars + java_files |
359 | 423 |
360 output_paths = [ | 424 output_paths = [ |
361 options.jar_path, | 425 options.jar_path, |
362 options.jar_path.replace('.jar', '.excluded.jar'), | 426 options.jar_path.replace('.jar', '.excluded.jar'), |
363 ] | 427 ] |
428 if options.incremental: | |
429 output_paths.append(options.jar_path + '.pdb') | |
364 | 430 |
365 # An escape hatch to be able to check if incremental compiles are causing | 431 # An escape hatch to be able to check if incremental compiles are causing |
366 # problems. | 432 # problems. |
367 force = int(os.environ.get('DISABLE_INCREMENTAL_JAVAC', 0)) | 433 force = int(os.environ.get('DISABLE_INCREMENTAL_JAVAC', 0)) |
368 | 434 |
369 # List python deps in input_strings rather than input_paths since the contents | 435 # List python deps in input_strings rather than input_paths since the contents |
370 # of them does not change what gets written to the depsfile. | 436 # of them does not change what gets written to the depsfile. |
371 build_utils.CallAndWriteDepfileIfStale( | 437 build_utils.CallAndWriteDepfileIfStale( |
372 lambda changes: _OnStaleMd5(changes, options, javac_cmd, java_files, | 438 lambda changes: _OnStaleMd5(changes, options, javac_cmd, java_files, |
373 classpath_inputs, runtime_classpath), | 439 classpath_inputs, runtime_classpath), |
374 options, | 440 options, |
375 input_paths=input_paths, | 441 input_paths=input_paths, |
376 input_strings=javac_cmd, | 442 input_strings=javac_cmd, |
377 output_paths=output_paths, | 443 output_paths=output_paths, |
378 force=force, | 444 force=force, |
379 pass_changes=True) | 445 pass_changes=True) |
380 | 446 |
381 | 447 |
382 if __name__ == '__main__': | 448 if __name__ == '__main__': |
383 sys.exit(main(sys.argv[1:])) | 449 sys.exit(main(sys.argv[1:])) |
OLD | NEW |