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