Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(243)

Side by Side Diff: build/android/gyp/javac.py

Issue 1373723003: Fix javac --incremental by using jmake for dependency analysis (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@aidl
Patch Set: Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | build/android/gyp/util/md5_check.py » ('j') | build/android/gyp/util/md5_check.py » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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:]))
OLDNEW
« no previous file with comments | « no previous file | build/android/gyp/util/md5_check.py » ('j') | build/android/gyp/util/md5_check.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698