1#!/usr/bin/env python3 2# 3# Copyright 2013 The Chromium Authors 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7import functools 8import logging 9import multiprocessing 10import optparse 11import os 12import re 13import shutil 14import sys 15import time 16import zipfile 17 18import javac_output_processor 19from util import build_utils 20from util import md5_check 21from util import jar_info_utils 22from util import server_utils 23import action_helpers # build_utils adds //build to sys.path. 24import zip_helpers 25 26_JAVAC_EXTRACTOR = os.path.join(build_utils.DIR_SOURCE_ROOT, 'third_party', 27 'android_prebuilts', 'build_tools', 'common', 28 'framework', 'javac_extractor.jar') 29 30# Add a check here to cause the suggested fix to be applied while compiling. 31# Use this when trying to enable more checks. 32ERRORPRONE_CHECKS_TO_APPLY = [] 33 34# Full list of checks: https://errorprone.info/bugpatterns 35ERRORPRONE_WARNINGS_TO_DISABLE = [ 36 # Temporarily disabling to roll doubledown. 37 # TODO(wnwen): Re-enable this upstream. 38 'InlineMeInliner', 39 # The following are super useful, but existing issues need to be fixed first 40 # before they can start failing the build on new errors. 41 'InvalidParam', 42 'InvalidLink', 43 'InvalidInlineTag', 44 'EmptyBlockTag', 45 'PublicConstructorForAbstractClass', 46 'InvalidBlockTag', 47 'StaticAssignmentInConstructor', 48 'MutablePublicArray', 49 'UnescapedEntity', 50 'NonCanonicalType', 51 'AlmostJavadoc', 52 'ReturnValueIgnored', 53 # The following are added for errorprone update: https://crbug.com/1216032 54 'InlineMeSuggester', 55 'DoNotClaimAnnotations', 56 'JavaUtilDate', 57 'IdentityHashMapUsage', 58 'UnnecessaryMethodReference', 59 'LongFloatConversion', 60 'CharacterGetNumericValue', 61 'ErroneousThreadPoolConstructorChecker', 62 'StaticMockMember', 63 'MissingSuperCall', 64 'ToStringReturnsNull', 65 # If possible, this should be automatically fixed if turned on: 66 'MalformedInlineTag', 67 # TODO(crbug.com/834807): Follow steps in bug 68 'DoubleBraceInitialization', 69 # TODO(crbug.com/834790): Follow steps in bug. 70 'CatchAndPrintStackTrace', 71 # TODO(crbug.com/801210): Follow steps in bug. 72 'SynchronizeOnNonFinalField', 73 # TODO(crbug.com/802073): Follow steps in bug. 74 'TypeParameterUnusedInFormals', 75 # TODO(crbug.com/803484): Follow steps in bug. 76 'CatchFail', 77 # TODO(crbug.com/803485): Follow steps in bug. 78 'JUnitAmbiguousTestClass', 79 # Android platform default is always UTF-8. 80 # https://developer.android.com/reference/java/nio/charset/Charset.html#defaultCharset() 81 'DefaultCharset', 82 # Low priority since there are lots of tags that don't fit this check. 83 'UnrecognisedJavadocTag', 84 # Low priority since the alternatives still work. 85 'JdkObsolete', 86 # We don't use that many lambdas. 87 'FunctionalInterfaceClash', 88 # There are lots of times when we just want to post a task. 89 'FutureReturnValueIgnored', 90 # Nice to be explicit about operators, but not necessary. 91 'OperatorPrecedence', 92 # Just false positives in our code. 93 'ThreadJoinLoop', 94 # Low priority corner cases with String.split. 95 # Linking Guava and using Splitter was rejected 96 # in the https://chromium-review.googlesource.com/c/chromium/src/+/871630. 97 'StringSplitter', 98 # Preferred to use another method since it propagates exceptions better. 99 'ClassNewInstance', 100 # Nice to have static inner classes but not necessary. 101 'ClassCanBeStatic', 102 # Explicit is better than implicit. 103 'FloatCast', 104 # Results in false positives. 105 'ThreadLocalUsage', 106 # Also just false positives. 107 'Finally', 108 # False positives for Chromium. 109 'FragmentNotInstantiable', 110 # Low priority to fix. 111 'HidingField', 112 # Low priority. 113 'IntLongMath', 114 # Low priority. 115 'BadComparable', 116 # Low priority. 117 'EqualsHashCode', 118 # Nice to fix but low priority. 119 'TypeParameterShadowing', 120 # Good to have immutable enums, also low priority. 121 'ImmutableEnumChecker', 122 # False positives for testing. 123 'InputStreamSlowMultibyteRead', 124 # Nice to have better primitives. 125 'BoxedPrimitiveConstructor', 126 # Not necessary for tests. 127 'OverrideThrowableToString', 128 # Nice to have better type safety. 129 'CollectionToArraySafeParameter', 130 # Makes logcat debugging more difficult, and does not provide obvious 131 # benefits in the Chromium codebase. 132 'ObjectToString', 133 # Triggers on private methods that are @CalledByNative. 134 'UnusedMethod', 135 # Triggers on generated R.java files. 136 'UnusedVariable', 137 # Not that useful. 138 'UnsafeReflectiveConstructionCast', 139 # Not that useful. 140 'MixedMutabilityReturnType', 141 # Nice to have. 142 'EqualsGetClass', 143 # A lot of false-positives from CharSequence.equals(). 144 'UndefinedEquals', 145 # Nice to have. 146 'ExtendingJUnitAssert', 147 # Nice to have. 148 'SystemExitOutsideMain', 149 # Nice to have. 150 'TypeParameterNaming', 151 # Nice to have. 152 'UnusedException', 153 # Nice to have. 154 'UngroupedOverloads', 155 # Nice to have. 156 'FunctionalInterfaceClash', 157 # Nice to have. 158 'InconsistentOverloads', 159 # Dagger generated code triggers this. 160 'SameNameButDifferent', 161 # Nice to have. 162 'UnnecessaryLambda', 163 # Nice to have. 164 'UnnecessaryAnonymousClass', 165 # Nice to have. 166 'LiteProtoToString', 167 # Nice to have. 168 'MissingSummary', 169 # Nice to have. 170 'ReturnFromVoid', 171 # Nice to have. 172 'EmptyCatch', 173 # Nice to have. 174 'BadImport', 175 # Nice to have. 176 'UseCorrectAssertInTests', 177 # Nice to have. 178 'InlineFormatString', 179 # Nice to have. 180 'DefaultPackage', 181 # Must be off since we are now passing in annotation processor generated 182 # code as a source jar (deduplicating work with turbine). 183 'RefersToDaggerCodegen', 184 # We already have presubmit checks for this. Not necessary to warn on 185 # every build. 186 'RemoveUnusedImports', 187 # We do not care about unnecessary parenthesis enough to check for them. 188 'UnnecessaryParentheses', 189 # The only time we trigger this is when it is better to be explicit in a 190 # list of unicode characters, e.g. FindAddress.java 191 'UnicodeEscape', 192 # Nice to have. 193 'AlreadyChecked', 194] 195 196# Full list of checks: https://errorprone.info/bugpatterns 197# Only those marked as "experimental" need to be listed here in order to be 198# enabled. 199ERRORPRONE_WARNINGS_TO_ENABLE = [ 200 'BinderIdentityRestoredDangerously', 201 'EmptyIf', 202 'EqualsBrokenForNull', 203 'InvalidThrows', 204 'LongLiteralLowerCaseSuffix', 205 'MultiVariableDeclaration', 206 'RedundantOverride', 207 'StaticQualifiedUsingExpression', 208 'StringEquality', 209 'TimeUnitMismatch', 210 'UnnecessaryStaticImport', 211 'UseBinds', 212 'WildcardImport', 213] 214 215 216def ProcessJavacOutput(output, target_name): 217 # These warnings cannot be suppressed even for third party code. Deprecation 218 # warnings especially do not help since we must support older android version. 219 deprecated_re = re.compile(r'Note: .* uses? or overrides? a deprecated API') 220 unchecked_re = re.compile( 221 r'(Note: .* uses? unchecked or unsafe operations.)$') 222 recompile_re = re.compile(r'(Note: Recompile with -Xlint:.* for details.)$') 223 224 def ApplyFilters(line): 225 return not (deprecated_re.match(line) or unchecked_re.match(line) 226 or recompile_re.match(line)) 227 228 output = build_utils.FilterReflectiveAccessJavaWarnings(output) 229 230 # Warning currently cannot be silenced via javac flag. 231 if 'Unsafe is internal proprietary API' in output: 232 # Example: 233 # HiddenApiBypass.java:69: warning: Unsafe is internal proprietary API and 234 # may be removed in a future release 235 # import sun.misc.Unsafe; 236 # ^ 237 output = re.sub(r'.*?Unsafe is internal proprietary API[\s\S]*?\^\n', '', 238 output) 239 output = re.sub(r'\d+ warnings\n', '', output) 240 241 lines = (l for l in output.split('\n') if ApplyFilters(l)) 242 243 output_processor = javac_output_processor.JavacOutputProcessor(target_name) 244 lines = output_processor.Process(lines) 245 246 return '\n'.join(lines) 247 248 249def CreateJarFile(jar_path, 250 classes_dir, 251 service_provider_configuration_dir=None, 252 additional_jar_files=None, 253 extra_classes_jar=None): 254 """Zips files from compilation into a single jar.""" 255 logging.info('Start creating jar file: %s', jar_path) 256 with action_helpers.atomic_output(jar_path) as f: 257 with zipfile.ZipFile(f.name, 'w') as z: 258 zip_helpers.zip_directory(z, classes_dir) 259 if service_provider_configuration_dir: 260 config_files = build_utils.FindInDirectory( 261 service_provider_configuration_dir) 262 for config_file in config_files: 263 zip_path = os.path.relpath(config_file, 264 service_provider_configuration_dir) 265 zip_helpers.add_to_zip_hermetic(z, zip_path, src_path=config_file) 266 267 if additional_jar_files: 268 for src_path, zip_path in additional_jar_files: 269 zip_helpers.add_to_zip_hermetic(z, zip_path, src_path=src_path) 270 if extra_classes_jar: 271 path_transform = lambda p: p if p.endswith('.class') else None 272 zip_helpers.merge_zips(z, [extra_classes_jar], 273 path_transform=path_transform) 274 logging.info('Completed jar file: %s', jar_path) 275 276 277def _ParsePackageAndClassNames(source_file): 278 """This should support both Java and Kotlin files.""" 279 package_name = '' 280 class_names = [] 281 with open(source_file) as f: 282 for l in f: 283 # Strip unindented comments. 284 # Considers a leading * as a continuation of a multi-line comment (our 285 # linter doesn't enforce a space before it like there should be). 286 l = re.sub(r'^(?://.*|/?\*.*?(?:\*/\s*|$))', '', l) 287 # Stripping things between double quotes (strings), so if the word "class" 288 # shows up in a string this doesn't trigger. This isn't strictly correct 289 # (with escaped quotes) but covers a very large percentage of cases. 290 l = re.sub('(?:".*?")', '', l) 291 292 # Java lines end in semicolon, whereas Kotlin lines do not. 293 m = re.match(r'package\s+(.*?)(;|\s*$)', l) 294 if m and not package_name: 295 package_name = m.group(1) 296 297 # Not exactly a proper parser, but works for sources that Chrome uses. 298 # In order to not match nested classes, it just checks for lack of indent. 299 m = re.match(r'(?:\S.*?)?(?:class|@?interface|enum)\s+(.+?)\b', l) 300 if m: 301 class_names.append(m.group(1)) 302 return package_name, class_names 303 304 305def _ProcessSourceFileForInfo(source_file): 306 package_name, class_names = _ParsePackageAndClassNames(source_file) 307 return source_file, package_name, class_names 308 309 310class _InfoFileContext: 311 """Manages the creation of the class->source file .info file.""" 312 313 def __init__(self, chromium_code, excluded_globs): 314 self._chromium_code = chromium_code 315 self._excluded_globs = excluded_globs 316 # Map of .java path -> .srcjar/nested/path.java. 317 self._srcjar_files = {} 318 # List of generators from pool.imap_unordered(). 319 self._results = [] 320 # Lazily created multiprocessing.Pool. 321 self._pool = None 322 323 def AddSrcJarSources(self, srcjar_path, extracted_paths, parent_dir): 324 for path in extracted_paths: 325 # We want the path inside the srcjar so the viewer can have a tree 326 # structure. 327 self._srcjar_files[path] = '{}/{}'.format( 328 srcjar_path, os.path.relpath(path, parent_dir)) 329 330 def SubmitFiles(self, source_files): 331 if not source_files: 332 return 333 if self._pool is None: 334 # Restrict to just one process to not slow down compiling. Compiling 335 # is always slower. 336 self._pool = multiprocessing.Pool(1) 337 logging.info('Submitting %d files for info', len(source_files)) 338 self._results.append( 339 self._pool.imap_unordered(_ProcessSourceFileForInfo, 340 source_files, 341 chunksize=1000)) 342 343 def _CheckPathMatchesClassName(self, source_file, package_name, class_name): 344 if source_file.endswith('.java'): 345 parts = package_name.split('.') + [class_name + '.java'] 346 else: 347 parts = package_name.split('.') + [class_name + '.kt'] 348 expected_suffix = os.path.sep.join(parts) 349 if not source_file.endswith(expected_suffix): 350 raise Exception(('Source package+class name do not match its path.\n' 351 'Actual path: %s\nExpected path: %s') % 352 (source_file, expected_suffix)) 353 354 def _ProcessInfo(self, java_file, package_name, class_names, source): 355 for class_name in class_names: 356 yield '{}.{}'.format(package_name, class_name) 357 # Skip aidl srcjars since they don't indent code correctly. 358 if '_aidl.srcjar' in source: 359 continue 360 assert not self._chromium_code or len(class_names) == 1, ( 361 'Chromium java files must only have one class: {}'.format(source)) 362 if self._chromium_code: 363 # This check is not necessary but nice to check this somewhere. 364 self._CheckPathMatchesClassName(java_file, package_name, class_names[0]) 365 366 def _ShouldIncludeInJarInfo(self, fully_qualified_name): 367 name_as_class_glob = fully_qualified_name.replace('.', '/') + '.class' 368 return not build_utils.MatchesGlob(name_as_class_glob, self._excluded_globs) 369 370 def _Collect(self): 371 if self._pool is None: 372 return {} 373 ret = {} 374 for result in self._results: 375 for java_file, package_name, class_names in result: 376 source = self._srcjar_files.get(java_file, java_file) 377 for fully_qualified_name in self._ProcessInfo(java_file, package_name, 378 class_names, source): 379 if self._ShouldIncludeInJarInfo(fully_qualified_name): 380 ret[fully_qualified_name] = java_file 381 return ret 382 383 def Close(self): 384 # Work around for Python 2.x bug with multiprocessing and daemon threads: 385 # https://bugs.python.org/issue4106 386 if self._pool is not None: 387 logging.info('Joining multiprocessing.Pool') 388 self._pool.terminate() 389 self._pool.join() 390 logging.info('Done.') 391 392 def Commit(self, output_path): 393 """Writes a .jar.info file. 394 395 Maps fully qualified names for classes to either the java file that they 396 are defined in or the path of the srcjar that they came from. 397 """ 398 logging.info('Collecting info file entries') 399 entries = self._Collect() 400 401 logging.info('Writing info file: %s', output_path) 402 with action_helpers.atomic_output(output_path, mode='wb') as f: 403 jar_info_utils.WriteJarInfoFile(f, entries, self._srcjar_files) 404 logging.info('Completed info file: %s', output_path) 405 406 407def _OnStaleMd5(changes, options, javac_cmd, javac_args, java_files, kt_files): 408 logging.info('Starting _OnStaleMd5') 409 if options.enable_kythe_annotations: 410 # Kythe requires those env variables to be set and compile_java.py does the 411 # same 412 if not os.environ.get('KYTHE_ROOT_DIRECTORY') or \ 413 not os.environ.get('KYTHE_OUTPUT_DIRECTORY'): 414 raise Exception('--enable-kythe-annotations requires ' 415 'KYTHE_ROOT_DIRECTORY and KYTHE_OUTPUT_DIRECTORY ' 416 'environment variables to be set.') 417 javac_extractor_cmd = build_utils.JavaCmd() + [ 418 '--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED', 419 '--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED', 420 '--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED', 421 '--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED', 422 '--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED', 423 '--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED', 424 '--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED', 425 '-jar', 426 _JAVAC_EXTRACTOR, 427 ] 428 try: 429 # _RunCompiler()'s partial javac implementation does not support 430 # generating outputs in $KYTHE_OUTPUT_DIRECTORY. 431 _RunCompiler(changes, 432 options, 433 javac_extractor_cmd + javac_args, 434 java_files, 435 options.jar_path + '.javac_extractor', 436 enable_partial_javac=False) 437 except build_utils.CalledProcessError as e: 438 # Having no index for particular target is better than failing entire 439 # codesearch. Log and error and move on. 440 logging.error('Could not generate kzip: %s', e) 441 442 intermediates_out_dir = None 443 jar_info_path = None 444 if not options.enable_errorprone: 445 # Delete any stale files in the generated directory. The purpose of 446 # options.generated_dir is for codesearch. 447 shutil.rmtree(options.generated_dir, True) 448 intermediates_out_dir = options.generated_dir 449 450 jar_info_path = options.jar_path + '.info' 451 452 # Compiles with Error Prone take twice as long to run as pure javac. Thus GN 453 # rules run both in parallel, with Error Prone only used for checks. 454 try: 455 _RunCompiler(changes, 456 options, 457 javac_cmd + javac_args, 458 java_files, 459 options.jar_path, 460 kt_files=kt_files, 461 jar_info_path=jar_info_path, 462 intermediates_out_dir=intermediates_out_dir, 463 enable_partial_javac=True) 464 except build_utils.CalledProcessError as e: 465 # Do not output stacktrace as it takes up space on gerrit UI, forcing 466 # you to click though to find the actual compilation error. It's never 467 # interesting to see the Python stacktrace for a Java compilation error. 468 sys.stderr.write(e.output) 469 sys.exit(1) 470 471 logging.info('Completed all steps in _OnStaleMd5') 472 473 474def _RunCompiler(changes, 475 options, 476 javac_cmd, 477 java_files, 478 jar_path, 479 kt_files=None, 480 jar_info_path=None, 481 intermediates_out_dir=None, 482 enable_partial_javac=False): 483 """Runs java compiler. 484 485 Args: 486 changes: md5_check.Changes object. 487 options: Object with command line flags. 488 javac_cmd: Command to execute. 489 java_files: List of java files passed from command line. 490 jar_path: Path of output jar file. 491 kt_files: List of Kotlin files passed from command line if any. 492 jar_info_path: Path of the .info file to generate. 493 If None, .info file will not be generated. 494 intermediates_out_dir: Directory for saving intermediate outputs. 495 If None a temporary directory is used. 496 enable_partial_javac: Enables compiling only Java files which have changed 497 in the special case that no method signatures have changed. This is 498 useful for large GN targets. 499 Not supported if compiling generates outputs other than |jar_path| and 500 |jar_info_path|. 501 """ 502 logging.info('Starting _RunCompiler') 503 504 java_files = java_files.copy() 505 java_srcjars = options.java_srcjars 506 save_info_file = jar_info_path is not None 507 508 # Use jar_path's directory to ensure paths are relative (needed for goma). 509 temp_dir = jar_path + '.staging' 510 build_utils.DeleteDirectory(temp_dir) 511 os.makedirs(temp_dir) 512 info_file_context = None 513 try: 514 classes_dir = os.path.join(temp_dir, 'classes') 515 service_provider_configuration = os.path.join( 516 temp_dir, 'service_provider_configuration') 517 518 if java_files: 519 os.makedirs(classes_dir) 520 521 if enable_partial_javac: 522 all_changed_paths_are_java = all( 523 p.endswith(".java") for p in changes.IterChangedPaths()) 524 if (all_changed_paths_are_java and not changes.HasStringChanges() 525 and os.path.exists(jar_path) 526 and (jar_info_path is None or os.path.exists(jar_info_path))): 527 # Log message is used by tests to determine whether partial javac 528 # optimization was used. 529 logging.info('Using partial javac optimization for %s compile' % 530 (jar_path)) 531 532 # Header jar corresponding to |java_files| did not change. 533 # As a build speed optimization (crbug.com/1170778), re-compile only 534 # java files which have changed. Re-use old jar .info file. 535 java_files = list(changes.IterChangedPaths()) 536 java_srcjars = None 537 538 # Reuse old .info file. 539 save_info_file = False 540 541 build_utils.ExtractAll(jar_path, classes_dir, pattern='*.class') 542 543 if save_info_file: 544 info_file_context = _InfoFileContext(options.chromium_code, 545 options.jar_info_exclude_globs) 546 547 if intermediates_out_dir is None: 548 intermediates_out_dir = temp_dir 549 550 input_srcjars_dir = os.path.join(intermediates_out_dir, 'input_srcjars') 551 552 if java_srcjars: 553 logging.info('Extracting srcjars to %s', input_srcjars_dir) 554 build_utils.MakeDirectory(input_srcjars_dir) 555 for srcjar in options.java_srcjars: 556 extracted_files = build_utils.ExtractAll( 557 srcjar, no_clobber=True, path=input_srcjars_dir, pattern='*.java') 558 java_files.extend(extracted_files) 559 if save_info_file: 560 info_file_context.AddSrcJarSources(srcjar, extracted_files, 561 input_srcjars_dir) 562 logging.info('Done extracting srcjars') 563 564 if options.header_jar: 565 logging.info('Extracting service provider configs') 566 # Extract META-INF/services/* so that it can be copied into the output 567 # .jar 568 build_utils.ExtractAll(options.header_jar, 569 no_clobber=True, 570 path=service_provider_configuration, 571 pattern='META-INF/services/*') 572 logging.info('Done extracting service provider configs') 573 574 if save_info_file and java_files: 575 info_file_context.SubmitFiles(java_files) 576 info_file_context.SubmitFiles(kt_files) 577 578 if java_files: 579 # Don't include the output directory in the initial set of args since it 580 # being in a temp dir makes it unstable (breaks md5 stamping). 581 cmd = list(javac_cmd) 582 cmd += ['-d', classes_dir] 583 584 if options.classpath: 585 cmd += ['-classpath', ':'.join(options.classpath)] 586 587 # Pass source paths as response files to avoid extremely long command 588 # lines that are tedius to debug. 589 java_files_rsp_path = os.path.join(temp_dir, 'files_list.txt') 590 with open(java_files_rsp_path, 'w') as f: 591 f.write(' '.join(java_files)) 592 cmd += ['@' + java_files_rsp_path] 593 594 process_javac_output_partial = functools.partial( 595 ProcessJavacOutput, target_name=options.target_name) 596 597 logging.debug('Build command %s', cmd) 598 start = time.time() 599 build_utils.CheckOutput(cmd, 600 print_stdout=options.chromium_code, 601 stdout_filter=process_javac_output_partial, 602 stderr_filter=process_javac_output_partial, 603 fail_on_output=options.warnings_as_errors) 604 end = time.time() - start 605 logging.info('Java compilation took %ss', end) 606 607 CreateJarFile(jar_path, classes_dir, service_provider_configuration, 608 options.additional_jar_files, options.kotlin_jar_path) 609 610 if save_info_file: 611 info_file_context.Commit(jar_info_path) 612 613 logging.info('Completed all steps in _RunCompiler') 614 finally: 615 if info_file_context: 616 info_file_context.Close() 617 shutil.rmtree(temp_dir) 618 619 620def _ParseOptions(argv): 621 parser = optparse.OptionParser() 622 action_helpers.add_depfile_arg(parser) 623 624 parser.add_option('--target-name', help='Fully qualified GN target name.') 625 parser.add_option('--skip-build-server', 626 action='store_true', 627 help='Avoid using the build server.') 628 parser.add_option('--use-build-server', 629 action='store_true', 630 help='Always use the build server.') 631 parser.add_option( 632 '--java-srcjars', 633 action='append', 634 default=[], 635 help='List of srcjars to include in compilation.') 636 parser.add_option( 637 '--generated-dir', 638 help='Subdirectory within target_gen_dir to place extracted srcjars and ' 639 'annotation processor output for codesearch to find.') 640 parser.add_option('--classpath', action='append', help='Classpath to use.') 641 parser.add_option( 642 '--processorpath', 643 action='append', 644 help='GN list of jars that comprise the classpath used for Annotation ' 645 'Processors.') 646 parser.add_option( 647 '--processor-arg', 648 dest='processor_args', 649 action='append', 650 help='key=value arguments for the annotation processors.') 651 parser.add_option( 652 '--additional-jar-file', 653 dest='additional_jar_files', 654 action='append', 655 help='Additional files to package into jar. By default, only Java .class ' 656 'files are packaged into the jar. Files should be specified in ' 657 'format <filename>:<path to be placed in jar>.') 658 parser.add_option( 659 '--jar-info-exclude-globs', 660 help='GN list of exclude globs to filter from generated .info files.') 661 parser.add_option( 662 '--chromium-code', 663 type='int', 664 help='Whether code being compiled should be built with stricter ' 665 'warnings for chromium code.') 666 parser.add_option( 667 '--gomacc-path', help='When set, prefix javac command with gomacc') 668 parser.add_option( 669 '--errorprone-path', help='Use the Errorprone compiler at this path.') 670 parser.add_option( 671 '--enable-errorprone', 672 action='store_true', 673 help='Enable errorprone checks') 674 parser.add_option( 675 '--warnings-as-errors', 676 action='store_true', 677 help='Treat all warnings as errors.') 678 parser.add_option('--jar-path', help='Jar output path.') 679 parser.add_option( 680 '--javac-arg', 681 action='append', 682 default=[], 683 help='Additional arguments to pass to javac.') 684 parser.add_option( 685 '--enable-kythe-annotations', 686 action='store_true', 687 help='Enable generation of Kythe kzip, used for codesearch. Ensure ' 688 'proper environment variables are set before using this flag.') 689 parser.add_option( 690 '--header-jar', 691 help='This is the header jar for the current target that contains ' 692 'META-INF/services/* files to be included in the output jar.') 693 parser.add_option( 694 '--kotlin-jar-path', 695 help='Kotlin jar to be merged into the output jar. This contains the ' 696 ".class files from this target's .kt files.") 697 698 options, args = parser.parse_args(argv) 699 build_utils.CheckOptions(options, parser, required=('jar_path', )) 700 701 options.classpath = action_helpers.parse_gn_list(options.classpath) 702 options.processorpath = action_helpers.parse_gn_list(options.processorpath) 703 options.java_srcjars = action_helpers.parse_gn_list(options.java_srcjars) 704 options.jar_info_exclude_globs = action_helpers.parse_gn_list( 705 options.jar_info_exclude_globs) 706 707 additional_jar_files = [] 708 for arg in options.additional_jar_files or []: 709 filepath, jar_filepath = arg.split(':') 710 additional_jar_files.append((filepath, jar_filepath)) 711 options.additional_jar_files = additional_jar_files 712 713 files = [] 714 for arg in args: 715 # Interpret a path prefixed with @ as a file containing a list of sources. 716 if arg.startswith('@'): 717 files.extend(build_utils.ReadSourcesList(arg[1:])) 718 else: 719 files.append(arg) 720 721 # The target's .sources file contains both Java and Kotlin files. We use 722 # compile_kt.py to compile the Kotlin files to .class and header jars. Javac 723 # is run only on .java files. 724 java_files = [f for f in files if f.endswith('.java')] 725 # Kotlin files are needed to populate the info file and attribute size in 726 # supersize back to the appropriate Kotlin file. 727 kt_files = [f for f in files if f.endswith('.kt')] 728 729 return options, java_files, kt_files 730 731 732def main(argv): 733 build_utils.InitLogging('JAVAC_DEBUG') 734 argv = build_utils.ExpandFileArgs(argv) 735 options, java_files, kt_files = _ParseOptions(argv) 736 737 # Only use the build server for errorprone runs. 738 if (options.enable_errorprone and not options.skip_build_server 739 and server_utils.MaybeRunCommand(name=options.target_name, 740 argv=sys.argv, 741 stamp_file=options.jar_path, 742 force=options.use_build_server)): 743 return 744 745 javac_cmd = [] 746 if options.gomacc_path: 747 javac_cmd.append(options.gomacc_path) 748 javac_cmd.append(build_utils.JAVAC_PATH) 749 750 javac_args = [ 751 '-g', 752 # Jacoco does not currently support a higher value. 753 '--release', 754 '17', 755 # Chromium only allows UTF8 source files. Being explicit avoids 756 # javac pulling a default encoding from the user's environment. 757 '-encoding', 758 'UTF-8', 759 # Prevent compiler from compiling .java files not listed as inputs. 760 # See: http://blog.ltgt.net/most-build-tools-misuse-javac/ 761 '-sourcepath', 762 ':', 763 # protobuf-generated files fail this check (javadoc has @deprecated, 764 # but method missing @Deprecated annotation). 765 '-Xlint:-dep-ann', 766 # Do not warn about finalize() methods. Android still intends to support 767 # them. 768 '-Xlint:-removal', 769 # https://crbug.com/1441023 770 '-J-XX:+PerfDisableSharedMem', 771 ] 772 773 if options.enable_errorprone: 774 # All errorprone args are passed space-separated in a single arg. 775 errorprone_flags = ['-Xplugin:ErrorProne'] 776 # Make everything a warning so that when treat_warnings_as_errors is false, 777 # they do not fail the build. 778 errorprone_flags += ['-XepAllErrorsAsWarnings'] 779 # Don't check generated files. 780 errorprone_flags += ['-XepDisableWarningsInGeneratedCode'] 781 errorprone_flags.extend('-Xep:{}:OFF'.format(x) 782 for x in ERRORPRONE_WARNINGS_TO_DISABLE) 783 errorprone_flags.extend('-Xep:{}:WARN'.format(x) 784 for x in ERRORPRONE_WARNINGS_TO_ENABLE) 785 786 if ERRORPRONE_CHECKS_TO_APPLY: 787 errorprone_flags += [ 788 '-XepPatchLocation:IN_PLACE', 789 '-XepPatchChecks:,' + ','.join(ERRORPRONE_CHECKS_TO_APPLY) 790 ] 791 792 # These are required to use JDK 16, and are taken directly from 793 # https://errorprone.info/docs/installation 794 javac_args += [ 795 '-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED', 796 '-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED', 797 '-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED', 798 '-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED', 799 '-J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED', 800 '-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=' 801 'ALL-UNNAMED', 802 '-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED', 803 '-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED', 804 '-J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED', 805 '-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED', 806 ] 807 808 javac_args += ['-XDcompilePolicy=simple', ' '.join(errorprone_flags)] 809 810 # This flag quits errorprone after checks and before code generation, since 811 # we do not need errorprone outputs, this speeds up errorprone by 4 seconds 812 # for chrome_java. 813 if not ERRORPRONE_CHECKS_TO_APPLY: 814 javac_args += ['-XDshould-stop.ifNoError=FLOW'] 815 816 # This effectively disables all annotation processors, even including 817 # annotation processors in service provider configuration files named 818 # META-INF/. See the following link for reference: 819 # https://docs.oracle.com/en/java/javase/11/tools/javac.html 820 javac_args.extend(['-proc:none']) 821 822 if options.processorpath: 823 javac_args.extend(['-processorpath', ':'.join(options.processorpath)]) 824 if options.processor_args: 825 for arg in options.processor_args: 826 javac_args.extend(['-A%s' % arg]) 827 828 javac_args.extend(options.javac_arg) 829 830 classpath_inputs = options.classpath + options.processorpath 831 832 depfile_deps = classpath_inputs 833 # Files that are already inputs in GN should go in input_paths. 834 input_paths = ([build_utils.JAVAC_PATH] + depfile_deps + 835 options.java_srcjars + java_files + kt_files) 836 if options.header_jar: 837 input_paths.append(options.header_jar) 838 input_paths += [x[0] for x in options.additional_jar_files] 839 840 output_paths = [options.jar_path] 841 if not options.enable_errorprone: 842 output_paths += [options.jar_path + '.info'] 843 844 input_strings = (javac_cmd + javac_args + options.classpath + java_files + 845 kt_files + 846 [options.warnings_as_errors, options.jar_info_exclude_globs]) 847 848 # Use md5_check for |pass_changes| feature. 849 md5_check.CallAndWriteDepfileIfStale(lambda changes: _OnStaleMd5( 850 changes, options, javac_cmd, javac_args, java_files, kt_files), 851 options, 852 depfile_deps=depfile_deps, 853 input_paths=input_paths, 854 input_strings=input_strings, 855 output_paths=output_paths, 856 pass_changes=True) 857 858 859if __name__ == '__main__': 860 sys.exit(main(sys.argv[1:])) 861