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 '-jar', 424 _JAVAC_EXTRACTOR, 425 ] 426 try: 427 # _RunCompiler()'s partial javac implementation does not support 428 # generating outputs in $KYTHE_OUTPUT_DIRECTORY. 429 _RunCompiler(changes, 430 options, 431 javac_extractor_cmd + javac_args, 432 java_files, 433 options.jar_path + '.javac_extractor', 434 enable_partial_javac=False) 435 except build_utils.CalledProcessError as e: 436 # Having no index for particular target is better than failing entire 437 # codesearch. Log and error and move on. 438 logging.error('Could not generate kzip: %s', e) 439 440 intermediates_out_dir = None 441 jar_info_path = None 442 if not options.enable_errorprone: 443 # Delete any stale files in the generated directory. The purpose of 444 # options.generated_dir is for codesearch. 445 shutil.rmtree(options.generated_dir, True) 446 intermediates_out_dir = options.generated_dir 447 448 jar_info_path = options.jar_path + '.info' 449 450 # Compiles with Error Prone take twice as long to run as pure javac. Thus GN 451 # rules run both in parallel, with Error Prone only used for checks. 452 try: 453 _RunCompiler(changes, 454 options, 455 javac_cmd + javac_args, 456 java_files, 457 options.jar_path, 458 kt_files=kt_files, 459 jar_info_path=jar_info_path, 460 intermediates_out_dir=intermediates_out_dir, 461 enable_partial_javac=True) 462 except build_utils.CalledProcessError as e: 463 # Do not output stacktrace as it takes up space on gerrit UI, forcing 464 # you to click though to find the actual compilation error. It's never 465 # interesting to see the Python stacktrace for a Java compilation error. 466 sys.stderr.write(e.output) 467 sys.exit(1) 468 469 logging.info('Completed all steps in _OnStaleMd5') 470 471 472def _RunCompiler(changes, 473 options, 474 javac_cmd, 475 java_files, 476 jar_path, 477 kt_files=None, 478 jar_info_path=None, 479 intermediates_out_dir=None, 480 enable_partial_javac=False): 481 """Runs java compiler. 482 483 Args: 484 changes: md5_check.Changes object. 485 options: Object with command line flags. 486 javac_cmd: Command to execute. 487 java_files: List of java files passed from command line. 488 jar_path: Path of output jar file. 489 kt_files: List of Kotlin files passed from command line if any. 490 jar_info_path: Path of the .info file to generate. 491 If None, .info file will not be generated. 492 intermediates_out_dir: Directory for saving intermediate outputs. 493 If None a temporary directory is used. 494 enable_partial_javac: Enables compiling only Java files which have changed 495 in the special case that no method signatures have changed. This is 496 useful for large GN targets. 497 Not supported if compiling generates outputs other than |jar_path| and 498 |jar_info_path|. 499 """ 500 logging.info('Starting _RunCompiler') 501 502 java_files = java_files.copy() 503 java_srcjars = options.java_srcjars 504 save_info_file = jar_info_path is not None 505 506 # Use jar_path's directory to ensure paths are relative (needed for goma). 507 temp_dir = jar_path + '.staging' 508 build_utils.DeleteDirectory(temp_dir) 509 os.makedirs(temp_dir) 510 info_file_context = None 511 try: 512 classes_dir = os.path.join(temp_dir, 'classes') 513 service_provider_configuration = os.path.join( 514 temp_dir, 'service_provider_configuration') 515 516 if java_files: 517 os.makedirs(classes_dir) 518 519 if enable_partial_javac: 520 all_changed_paths_are_java = all( 521 p.endswith(".java") for p in changes.IterChangedPaths()) 522 if (all_changed_paths_are_java and not changes.HasStringChanges() 523 and os.path.exists(jar_path) 524 and (jar_info_path is None or os.path.exists(jar_info_path))): 525 # Log message is used by tests to determine whether partial javac 526 # optimization was used. 527 logging.info('Using partial javac optimization for %s compile' % 528 (jar_path)) 529 530 # Header jar corresponding to |java_files| did not change. 531 # As a build speed optimization (crbug.com/1170778), re-compile only 532 # java files which have changed. Re-use old jar .info file. 533 java_files = list(changes.IterChangedPaths()) 534 java_srcjars = None 535 536 # Reuse old .info file. 537 save_info_file = False 538 539 build_utils.ExtractAll(jar_path, classes_dir, pattern='*.class') 540 541 if save_info_file: 542 info_file_context = _InfoFileContext(options.chromium_code, 543 options.jar_info_exclude_globs) 544 545 if intermediates_out_dir is None: 546 intermediates_out_dir = temp_dir 547 548 input_srcjars_dir = os.path.join(intermediates_out_dir, 'input_srcjars') 549 550 if java_srcjars: 551 logging.info('Extracting srcjars to %s', input_srcjars_dir) 552 build_utils.MakeDirectory(input_srcjars_dir) 553 for srcjar in options.java_srcjars: 554 extracted_files = build_utils.ExtractAll( 555 srcjar, no_clobber=True, path=input_srcjars_dir, pattern='*.java') 556 java_files.extend(extracted_files) 557 if save_info_file: 558 info_file_context.AddSrcJarSources(srcjar, extracted_files, 559 input_srcjars_dir) 560 logging.info('Done extracting srcjars') 561 562 if options.header_jar: 563 logging.info('Extracting service provider configs') 564 # Extract META-INF/services/* so that it can be copied into the output 565 # .jar 566 build_utils.ExtractAll(options.header_jar, 567 no_clobber=True, 568 path=service_provider_configuration, 569 pattern='META-INF/services/*') 570 logging.info('Done extracting service provider configs') 571 572 if save_info_file and java_files: 573 info_file_context.SubmitFiles(java_files) 574 info_file_context.SubmitFiles(kt_files) 575 576 if java_files: 577 # Don't include the output directory in the initial set of args since it 578 # being in a temp dir makes it unstable (breaks md5 stamping). 579 cmd = list(javac_cmd) 580 cmd += ['-d', classes_dir] 581 582 if options.classpath: 583 cmd += ['-classpath', ':'.join(options.classpath)] 584 585 # Pass source paths as response files to avoid extremely long command 586 # lines that are tedius to debug. 587 java_files_rsp_path = os.path.join(temp_dir, 'files_list.txt') 588 with open(java_files_rsp_path, 'w') as f: 589 f.write(' '.join(java_files)) 590 cmd += ['@' + java_files_rsp_path] 591 592 process_javac_output_partial = functools.partial( 593 ProcessJavacOutput, target_name=options.target_name) 594 595 logging.debug('Build command %s', cmd) 596 start = time.time() 597 build_utils.CheckOutput(cmd, 598 print_stdout=options.chromium_code, 599 stdout_filter=process_javac_output_partial, 600 stderr_filter=process_javac_output_partial, 601 fail_on_output=options.warnings_as_errors) 602 end = time.time() - start 603 logging.info('Java compilation took %ss', end) 604 605 CreateJarFile(jar_path, classes_dir, service_provider_configuration, 606 options.additional_jar_files, options.kotlin_jar_path) 607 608 if save_info_file: 609 info_file_context.Commit(jar_info_path) 610 611 logging.info('Completed all steps in _RunCompiler') 612 finally: 613 if info_file_context: 614 info_file_context.Close() 615 shutil.rmtree(temp_dir) 616 617 618def _ParseOptions(argv): 619 parser = optparse.OptionParser() 620 action_helpers.add_depfile_arg(parser) 621 622 parser.add_option('--target-name', help='Fully qualified GN target name.') 623 parser.add_option('--skip-build-server', 624 action='store_true', 625 help='Avoid using the build server.') 626 parser.add_option('--use-build-server', 627 action='store_true', 628 help='Always use the build server.') 629 parser.add_option( 630 '--java-srcjars', 631 action='append', 632 default=[], 633 help='List of srcjars to include in compilation.') 634 parser.add_option( 635 '--generated-dir', 636 help='Subdirectory within target_gen_dir to place extracted srcjars and ' 637 'annotation processor output for codesearch to find.') 638 parser.add_option('--classpath', action='append', help='Classpath to use.') 639 parser.add_option( 640 '--processorpath', 641 action='append', 642 help='GN list of jars that comprise the classpath used for Annotation ' 643 'Processors.') 644 parser.add_option( 645 '--processor-arg', 646 dest='processor_args', 647 action='append', 648 help='key=value arguments for the annotation processors.') 649 parser.add_option( 650 '--additional-jar-file', 651 dest='additional_jar_files', 652 action='append', 653 help='Additional files to package into jar. By default, only Java .class ' 654 'files are packaged into the jar. Files should be specified in ' 655 'format <filename>:<path to be placed in jar>.') 656 parser.add_option( 657 '--jar-info-exclude-globs', 658 help='GN list of exclude globs to filter from generated .info files.') 659 parser.add_option( 660 '--chromium-code', 661 type='int', 662 help='Whether code being compiled should be built with stricter ' 663 'warnings for chromium code.') 664 parser.add_option( 665 '--gomacc-path', help='When set, prefix javac command with gomacc') 666 parser.add_option( 667 '--errorprone-path', help='Use the Errorprone compiler at this path.') 668 parser.add_option( 669 '--enable-errorprone', 670 action='store_true', 671 help='Enable errorprone checks') 672 parser.add_option( 673 '--warnings-as-errors', 674 action='store_true', 675 help='Treat all warnings as errors.') 676 parser.add_option('--jar-path', help='Jar output path.') 677 parser.add_option( 678 '--javac-arg', 679 action='append', 680 default=[], 681 help='Additional arguments to pass to javac.') 682 parser.add_option( 683 '--enable-kythe-annotations', 684 action='store_true', 685 help='Enable generation of Kythe kzip, used for codesearch. Ensure ' 686 'proper environment variables are set before using this flag.') 687 parser.add_option( 688 '--header-jar', 689 help='This is the header jar for the current target that contains ' 690 'META-INF/services/* files to be included in the output jar.') 691 parser.add_option( 692 '--kotlin-jar-path', 693 help='Kotlin jar to be merged into the output jar. This contains the ' 694 ".class files from this target's .kt files.") 695 696 options, args = parser.parse_args(argv) 697 build_utils.CheckOptions(options, parser, required=('jar_path', )) 698 699 options.classpath = action_helpers.parse_gn_list(options.classpath) 700 options.processorpath = action_helpers.parse_gn_list(options.processorpath) 701 options.java_srcjars = action_helpers.parse_gn_list(options.java_srcjars) 702 options.jar_info_exclude_globs = action_helpers.parse_gn_list( 703 options.jar_info_exclude_globs) 704 705 additional_jar_files = [] 706 for arg in options.additional_jar_files or []: 707 filepath, jar_filepath = arg.split(':') 708 additional_jar_files.append((filepath, jar_filepath)) 709 options.additional_jar_files = additional_jar_files 710 711 files = [] 712 for arg in args: 713 # Interpret a path prefixed with @ as a file containing a list of sources. 714 if arg.startswith('@'): 715 files.extend(build_utils.ReadSourcesList(arg[1:])) 716 else: 717 files.append(arg) 718 719 # The target's .sources file contains both Java and Kotlin files. We use 720 # compile_kt.py to compile the Kotlin files to .class and header jars. Javac 721 # is run only on .java files. 722 java_files = [f for f in files if f.endswith('.java')] 723 # Kotlin files are needed to populate the info file and attribute size in 724 # supersize back to the appropriate Kotlin file. 725 kt_files = [f for f in files if f.endswith('.kt')] 726 727 return options, java_files, kt_files 728 729 730def main(argv): 731 build_utils.InitLogging('JAVAC_DEBUG') 732 argv = build_utils.ExpandFileArgs(argv) 733 options, java_files, kt_files = _ParseOptions(argv) 734 735 # Only use the build server for errorprone runs. 736 if (options.enable_errorprone and not options.skip_build_server 737 and server_utils.MaybeRunCommand(name=options.target_name, 738 argv=sys.argv, 739 stamp_file=options.jar_path, 740 force=options.use_build_server)): 741 return 742 743 javac_cmd = [] 744 if options.gomacc_path: 745 javac_cmd.append(options.gomacc_path) 746 javac_cmd.append(build_utils.JAVAC_PATH) 747 748 javac_args = [ 749 '-g', 750 # Jacoco does not currently support a higher value. 751 '--release', 752 '17', 753 # Chromium only allows UTF8 source files. Being explicit avoids 754 # javac pulling a default encoding from the user's environment. 755 '-encoding', 756 'UTF-8', 757 # Prevent compiler from compiling .java files not listed as inputs. 758 # See: http://blog.ltgt.net/most-build-tools-misuse-javac/ 759 '-sourcepath', 760 ':', 761 # protobuf-generated files fail this check (javadoc has @deprecated, 762 # but method missing @Deprecated annotation). 763 '-Xlint:-dep-ann', 764 # Do not warn about finalize() methods. Android still intends to support 765 # them. 766 '-Xlint:-removal', 767 ] 768 769 if options.enable_errorprone: 770 # All errorprone args are passed space-separated in a single arg. 771 errorprone_flags = ['-Xplugin:ErrorProne'] 772 # Make everything a warning so that when treat_warnings_as_errors is false, 773 # they do not fail the build. 774 errorprone_flags += ['-XepAllErrorsAsWarnings'] 775 # Don't check generated files. 776 errorprone_flags += ['-XepDisableWarningsInGeneratedCode'] 777 errorprone_flags.extend('-Xep:{}:OFF'.format(x) 778 for x in ERRORPRONE_WARNINGS_TO_DISABLE) 779 errorprone_flags.extend('-Xep:{}:WARN'.format(x) 780 for x in ERRORPRONE_WARNINGS_TO_ENABLE) 781 782 if ERRORPRONE_CHECKS_TO_APPLY: 783 errorprone_flags += [ 784 '-XepPatchLocation:IN_PLACE', 785 '-XepPatchChecks:,' + ','.join(ERRORPRONE_CHECKS_TO_APPLY) 786 ] 787 788 # These are required to use JDK 16, and are taken directly from 789 # https://errorprone.info/docs/installation 790 javac_args += [ 791 '-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED', 792 '-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED', 793 '-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED', 794 '-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED', 795 '-J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED', 796 '-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=' 797 'ALL-UNNAMED', 798 '-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED', 799 '-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED', 800 '-J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED', 801 '-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED', 802 ] 803 804 javac_args += ['-XDcompilePolicy=simple', ' '.join(errorprone_flags)] 805 806 # This flag quits errorprone after checks and before code generation, since 807 # we do not need errorprone outputs, this speeds up errorprone by 4 seconds 808 # for chrome_java. 809 if not ERRORPRONE_CHECKS_TO_APPLY: 810 javac_args += ['-XDshould-stop.ifNoError=FLOW'] 811 812 # This effectively disables all annotation processors, even including 813 # annotation processors in service provider configuration files named 814 # META-INF/. See the following link for reference: 815 # https://docs.oracle.com/en/java/javase/11/tools/javac.html 816 javac_args.extend(['-proc:none']) 817 818 if options.processorpath: 819 javac_args.extend(['-processorpath', ':'.join(options.processorpath)]) 820 if options.processor_args: 821 for arg in options.processor_args: 822 javac_args.extend(['-A%s' % arg]) 823 824 javac_args.extend(options.javac_arg) 825 826 classpath_inputs = options.classpath + options.processorpath 827 828 depfile_deps = classpath_inputs 829 # Files that are already inputs in GN should go in input_paths. 830 input_paths = ([build_utils.JAVAC_PATH] + depfile_deps + 831 options.java_srcjars + java_files + kt_files) 832 if options.header_jar: 833 input_paths.append(options.header_jar) 834 input_paths += [x[0] for x in options.additional_jar_files] 835 836 output_paths = [options.jar_path] 837 if not options.enable_errorprone: 838 output_paths += [options.jar_path + '.info'] 839 840 input_strings = (javac_cmd + javac_args + options.classpath + java_files + 841 kt_files + 842 [options.warnings_as_errors, options.jar_info_exclude_globs]) 843 844 # Use md5_check for |pass_changes| feature. 845 md5_check.CallAndWriteDepfileIfStale(lambda changes: _OnStaleMd5( 846 changes, options, javac_cmd, javac_args, java_files, kt_files), 847 options, 848 depfile_deps=depfile_deps, 849 input_paths=input_paths, 850 input_strings=input_strings, 851 output_paths=output_paths, 852 pass_changes=True) 853 854 855if __name__ == '__main__': 856 sys.exit(main(sys.argv[1:])) 857