1#!/usr/bin/env python
2#
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
5# found in the LICENSE file.
6
7import distutils.spawn
8import logging
9import multiprocessing
10import optparse
11import os
12import re
13import shutil
14import sys
15import time
16import zipfile
17
18from util import build_utils
19from util import md5_check
20from util import jar_info_utils
21
22sys.path.insert(
23    0,
24    os.path.join(build_utils.DIR_SOURCE_ROOT, 'third_party', 'colorama', 'src'))
25import colorama
26
27_JAVAC_EXTRACTOR = os.path.join(build_utils.DIR_SOURCE_ROOT, 'third_party',
28                                'android_prebuilts', 'build_tools', 'common',
29                                'framework', 'javac_extractor.jar')
30
31# Full list of checks: https://errorprone.info/bugpatterns
32ERRORPRONE_WARNINGS_TO_TURN_OFF = [
33    # This one should really be turned on.
34    'ParameterNotNullable',
35    # TODO(crbug.com/834807): Follow steps in bug
36    'DoubleBraceInitialization',
37    # TODO(crbug.com/834790): Follow steps in bug.
38    'CatchAndPrintStackTrace',
39    # TODO(crbug.com/801210): Follow steps in bug.
40    'SynchronizeOnNonFinalField',
41    # TODO(crbug.com/802073): Follow steps in bug.
42    'TypeParameterUnusedInFormals',
43    # TODO(crbug.com/803484): Follow steps in bug.
44    'CatchFail',
45    # TODO(crbug.com/803485): Follow steps in bug.
46    'JUnitAmbiguousTestClass',
47    # TODO(crbug.com/1027683): Follow steps in bug.
48    'UnnecessaryParentheses',
49    # TODO(wnwen): Fix issue in JavaUploadDataSinkBase.java
50    'PrimitiveAtomicReference',
51    # Android platform default is always UTF-8.
52    # https://developer.android.com/reference/java/nio/charset/Charset.html#defaultCharset()
53    'DefaultCharset',
54    # Low priority since the alternatives still work.
55    'JdkObsolete',
56    # We don't use that many lambdas.
57    'FunctionalInterfaceClash',
58    # There are lots of times when we just want to post a task.
59    'FutureReturnValueIgnored',
60    # Nice to be explicit about operators, but not necessary.
61    'OperatorPrecedence',
62    # Just false positives in our code.
63    'ThreadJoinLoop',
64    # Low priority corner cases with String.split.
65    # Linking Guava and using Splitter was rejected
66    # in the https://chromium-review.googlesource.com/c/chromium/src/+/871630.
67    'StringSplitter',
68    # Preferred to use another method since it propagates exceptions better.
69    'ClassNewInstance',
70    # Nice to have static inner classes but not necessary.
71    'ClassCanBeStatic',
72    # Explicit is better than implicit.
73    'FloatCast',
74    # Results in false positives.
75    'ThreadLocalUsage',
76    # Also just false positives.
77    'Finally',
78    # False positives for Chromium.
79    'FragmentNotInstantiable',
80    # Low priority to fix.
81    'HidingField',
82    # Low priority.
83    'IntLongMath',
84    # Low priority.
85    'BadComparable',
86    # Low priority.
87    'EqualsHashCode',
88    # Nice to fix but low priority.
89    'TypeParameterShadowing',
90    # Good to have immutable enums, also low priority.
91    'ImmutableEnumChecker',
92    # False positives for testing.
93    'InputStreamSlowMultibyteRead',
94    # Nice to have better primitives.
95    'BoxedPrimitiveConstructor',
96    # Not necessary for tests.
97    'OverrideThrowableToString',
98    # Nice to have better type safety.
99    'CollectionToArraySafeParameter',
100    # Makes logcat debugging more difficult, and does not provide obvious
101    # benefits in the Chromium codebase.
102    'ObjectToString',
103    # Triggers on private methods that are @CalledByNative.
104    'UnusedMethod',
105    # Triggers on generated R.java files.
106    'UnusedVariable',
107    # Not that useful.
108    'UnsafeReflectiveConstructionCast',
109    # Not that useful.
110    'MixedMutabilityReturnType',
111    # Nice to have.
112    'EqualsGetClass',
113    # A lot of false-positives from CharSequence.equals().
114    'UndefinedEquals',
115    # Nice to have.
116    'ExtendingJUnitAssert',
117    # Nice to have.
118    'SystemExitOutsideMain',
119    # Nice to have.
120    'TypeParameterNaming',
121    # Nice to have.
122    'UnusedException',
123    # Nice to have.
124    'UngroupedOverloads',
125    # Nice to have.
126    'FunctionalInterfaceClash',
127    # Nice to have.
128    'InconsistentOverloads',
129    # Dagger generated code triggers this.
130    'SameNameButDifferent',
131    # Nice to have.
132    'UnnecessaryLambda',
133    # Nice to have.
134    'UnnecessaryAnonymousClass',
135    # Nice to have.
136    'LiteProtoToString',
137    # Must be off since we are now passing in annotation processor generated
138    # code as a source jar (deduplicating work with turbine).
139    'RefersToDaggerCodegen',
140]
141
142# Full list of checks: https://errorprone.info/bugpatterns
143# Only those marked as "experimental" need to be listed here in order to be
144# enabled. We build with -Werror, so all default checks cause builds to fail.
145ERRORPRONE_WARNINGS_TO_ERROR = [
146    'BinderIdentityRestoredDangerously',
147    'EmptyIf',
148    'EqualsBrokenForNull',
149    'InvalidThrows',
150    'LongLiteralLowerCaseSuffix',
151    'MultiVariableDeclaration',
152    'RedundantOverride',
153    'RemoveUnusedImports',
154    'StaticQualifiedUsingExpression',
155    'StringEquality',
156    'TimeUnitMismatch',
157    'UnnecessaryStaticImport',
158    'UseBinds',
159    'WildcardImport',
160]
161
162
163def ProcessJavacOutput(output):
164  fileline_prefix = r'(?P<fileline>(?P<file>[-.\w/\\]+.java):(?P<line>[0-9]+):)'
165  warning_re = re.compile(fileline_prefix +
166                          r'(?P<full_message> warning: (?P<message>.*))$')
167  error_re = re.compile(fileline_prefix +
168                        r'(?P<full_message> (?P<message>.*))$')
169  marker_re = re.compile(r'\s*(?P<marker>\^)\s*$')
170
171  # These warnings cannot be suppressed even for third party code. Deprecation
172  # warnings especially do not help since we must support older android version.
173  deprecated_re = re.compile(
174      r'(Note: .* uses? or overrides? a deprecated API.)$')
175  unchecked_re = re.compile(
176      r'(Note: .* uses? unchecked or unsafe operations.)$')
177  recompile_re = re.compile(r'(Note: Recompile with -Xlint:.* for details.)$')
178
179  warning_color = ['full_message', colorama.Fore.YELLOW + colorama.Style.DIM]
180  error_color = ['full_message', colorama.Fore.MAGENTA + colorama.Style.BRIGHT]
181  marker_color = ['marker', colorama.Fore.BLUE + colorama.Style.BRIGHT]
182
183  def Colorize(line, regex, color):
184    match = regex.match(line)
185    start = match.start(color[0])
186    end = match.end(color[0])
187    return (line[:start] + color[1] + line[start:end] + colorama.Fore.RESET +
188            colorama.Style.RESET_ALL + line[end:])
189
190  def ApplyFilters(line):
191    return not (deprecated_re.match(line) or unchecked_re.match(line)
192                or recompile_re.match(line))
193
194  def ApplyColors(line):
195    if warning_re.match(line):
196      line = Colorize(line, warning_re, warning_color)
197    elif error_re.match(line):
198      line = Colorize(line, error_re, error_color)
199    elif marker_re.match(line):
200      line = Colorize(line, marker_re, marker_color)
201    return line
202
203  return '\n'.join(map(ApplyColors, filter(ApplyFilters, output.split('\n'))))
204
205
206def _ExtractClassFiles(jar_path, dest_dir, java_files):
207  """Extracts all .class files not corresponding to |java_files|."""
208  # Two challenges exist here:
209  # 1. |java_files| have prefixes that are not represented in the the jar paths.
210  # 2. A single .java file results in multiple .class files when it contains
211  #    nested classes.
212  # Here's an example:
213  #   source path: ../../base/android/java/src/org/chromium/Foo.java
214  #   jar paths: org/chromium/Foo.class, org/chromium/Foo$Inner.class
215  # To extract only .class files not related to the given .java files, we strip
216  # off ".class" and "$*.class" and use a substring match against java_files.
217  def extract_predicate(path):
218    if not path.endswith('.class'):
219      return False
220    path_without_suffix = re.sub(r'(?:\$|\.)[^/]*class$', '', path)
221    partial_java_path = path_without_suffix + '.java'
222    return not any(p.endswith(partial_java_path) for p in java_files)
223
224  logging.info('Extracting class files from %s', jar_path)
225  build_utils.ExtractAll(jar_path, path=dest_dir, predicate=extract_predicate)
226  for path in build_utils.FindInDirectory(dest_dir, '*.class'):
227    shutil.copystat(jar_path, path)
228
229
230def _ParsePackageAndClassNames(java_file):
231  package_name = ''
232  class_names = []
233  with open(java_file) as f:
234    for l in f:
235      # Strip unindented comments.
236      # Considers a leading * as a continuation of a multi-line comment (our
237      # linter doesn't enforce a space before it like there should be).
238      l = re.sub(r'^(?://.*|/?\*.*?(?:\*/\s*|$))', '', l)
239
240      m = re.match(r'package\s+(.*?);', l)
241      if m and not package_name:
242        package_name = m.group(1)
243
244      # Not exactly a proper parser, but works for sources that Chrome uses.
245      # In order to not match nested classes, it just checks for lack of indent.
246      m = re.match(r'(?:\S.*?)?(?:class|@?interface|enum)\s+(.+?)\b', l)
247      if m:
248        class_names.append(m.group(1))
249  return package_name, class_names
250
251
252def _ProcessJavaFileForInfo(java_file):
253  package_name, class_names = _ParsePackageAndClassNames(java_file)
254  return java_file, package_name, class_names
255
256
257class _InfoFileContext(object):
258  """Manages the creation of the class->source file .info file."""
259
260  def __init__(self, chromium_code, excluded_globs):
261    self._chromium_code = chromium_code
262    self._excluded_globs = excluded_globs
263    # Map of .java path -> .srcjar/nested/path.java.
264    self._srcjar_files = {}
265    # List of generators from pool.imap_unordered().
266    self._results = []
267    # Lazily created multiprocessing.Pool.
268    self._pool = None
269
270  def AddSrcJarSources(self, srcjar_path, extracted_paths, parent_dir):
271    for path in extracted_paths:
272      # We want the path inside the srcjar so the viewer can have a tree
273      # structure.
274      self._srcjar_files[path] = '{}/{}'.format(
275          srcjar_path, os.path.relpath(path, parent_dir))
276
277  def SubmitFiles(self, java_files):
278    if self._pool is None:
279      # Restrict to just one process to not slow down compiling. Compiling
280      # is always slower.
281      self._pool = multiprocessing.Pool(1)
282    logging.info('Submitting %d files for info', len(java_files))
283    self._results.append(
284        self._pool.imap_unordered(
285            _ProcessJavaFileForInfo, java_files, chunksize=1000))
286
287  def _CheckPathMatchesClassName(self, java_file, package_name, class_name):
288    parts = package_name.split('.') + [class_name + '.java']
289    expected_path_suffix = os.path.sep.join(parts)
290    if not java_file.endswith(expected_path_suffix):
291      raise Exception(('Java package+class name do not match its path.\n'
292                       'Actual path: %s\nExpected path: %s') %
293                      (java_file, expected_path_suffix))
294
295  def _ProcessInfo(self, java_file, package_name, class_names, source):
296    for class_name in class_names:
297      yield '{}.{}'.format(package_name, class_name)
298      # Skip aidl srcjars since they don't indent code correctly.
299      if '_aidl.srcjar' in source:
300        continue
301      assert not self._chromium_code or len(class_names) == 1, (
302          'Chromium java files must only have one class: {}'.format(source))
303      if self._chromium_code:
304        # This check is not necessary but nice to check this somewhere.
305        self._CheckPathMatchesClassName(java_file, package_name, class_names[0])
306
307  def _ShouldIncludeInJarInfo(self, fully_qualified_name):
308    name_as_class_glob = fully_qualified_name.replace('.', '/') + '.class'
309    return not build_utils.MatchesGlob(name_as_class_glob, self._excluded_globs)
310
311  def _Collect(self):
312    if self._pool is None:
313      return {}
314    ret = {}
315    for result in self._results:
316      for java_file, package_name, class_names in result:
317        source = self._srcjar_files.get(java_file, java_file)
318        for fully_qualified_name in self._ProcessInfo(java_file, package_name,
319                                                      class_names, source):
320          if self._ShouldIncludeInJarInfo(fully_qualified_name):
321            ret[fully_qualified_name] = java_file
322    self._pool.terminate()
323    return ret
324
325  def __del__(self):
326    # Work around for Python 2.x bug with multiprocessing and daemon threads:
327    # https://bugs.python.org/issue4106
328    if self._pool is not None:
329      logging.info('Joining multiprocessing.Pool')
330      self._pool.terminate()
331      self._pool.join()
332      logging.info('Done.')
333
334  def Commit(self, output_path):
335    """Writes a .jar.info file.
336
337    Maps fully qualified names for classes to either the java file that they
338    are defined in or the path of the srcjar that they came from.
339    """
340    logging.info('Collecting info file entries')
341    entries = self._Collect()
342
343    logging.info('Writing info file: %s', output_path)
344    with build_utils.AtomicOutput(output_path) as f:
345      jar_info_utils.WriteJarInfoFile(f, entries, self._srcjar_files)
346    logging.info('Completed info file: %s', output_path)
347
348
349def _CreateJarFile(jar_path, provider_configurations, additional_jar_files,
350                   classes_dir):
351  logging.info('Start creating jar file: %s', jar_path)
352  with build_utils.AtomicOutput(jar_path) as f:
353    with zipfile.ZipFile(f.name, 'w') as z:
354      build_utils.ZipDir(z, classes_dir)
355      if provider_configurations:
356        for config in provider_configurations:
357          zip_path = 'META-INF/services/' + os.path.basename(config)
358          build_utils.AddToZipHermetic(z, zip_path, src_path=config)
359
360      if additional_jar_files:
361        for src_path, zip_path in additional_jar_files:
362          build_utils.AddToZipHermetic(z, zip_path, src_path=src_path)
363  logging.info('Completed jar file: %s', jar_path)
364
365
366def _OnStaleMd5(options, javac_cmd, javac_args, java_files):
367  logging.info('Starting _OnStaleMd5')
368  if options.enable_kythe_annotations:
369    # Kythe requires those env variables to be set and compile_java.py does the
370    # same
371    if not os.environ.get('KYTHE_ROOT_DIRECTORY') or \
372        not os.environ.get('KYTHE_OUTPUT_DIRECTORY'):
373      raise Exception('--enable-kythe-annotations requires '
374                      'KYTHE_ROOT_DIRECTORY and KYTHE_OUTPUT_DIRECTORY '
375                      'environment variables to be set.')
376    javac_extractor_cmd = [
377        build_utils.JAVA_PATH,
378        '-jar',
379        _JAVAC_EXTRACTOR,
380    ]
381    try:
382      _RunCompiler(options, javac_extractor_cmd + javac_args, java_files,
383                   options.classpath, options.jar_path + '.javac_extractor',
384                   save_outputs=False),
385    except build_utils.CalledProcessError as e:
386      # Having no index for particular target is better than failing entire
387      # codesearch. Log and error and move on.
388      logging.error('Could not generate kzip: %s', e)
389
390  # Compiles with Error Prone take twice as long to run as pure javac. Thus GN
391  # rules run both in parallel, with Error Prone only used for checks.
392  _RunCompiler(options, javac_cmd + javac_args, java_files,
393               options.classpath, options.jar_path,
394               save_outputs=not options.enable_errorprone)
395  logging.info('Completed all steps in _OnStaleMd5')
396
397
398def _RunCompiler(options, javac_cmd, java_files, classpath, jar_path,
399                 save_outputs=True):
400  logging.info('Starting _RunCompiler')
401
402  # Compiles with Error Prone take twice as long to run as pure javac. Thus GN
403  # rules run both in parallel, with Error Prone only used for checks.
404  save_outputs = not options.enable_errorprone
405
406  # Use jar_path's directory to ensure paths are relative (needed for goma).
407  temp_dir = jar_path + '.staging'
408  shutil.rmtree(temp_dir, True)
409  os.makedirs(temp_dir)
410  try:
411    classes_dir = os.path.join(temp_dir, 'classes')
412
413    if save_outputs:
414      input_srcjars_dir = os.path.join(options.generated_dir, 'input_srcjars')
415      annotation_processor_outputs_dir = os.path.join(
416          options.generated_dir, 'annotation_processor_outputs')
417      # Delete any stale files in the generated directory. The purpose of
418      # options.generated_dir is for codesearch.
419      shutil.rmtree(options.generated_dir, True)
420      info_file_context = _InfoFileContext(options.chromium_code,
421                                           options.jar_info_exclude_globs)
422    else:
423      input_srcjars_dir = os.path.join(temp_dir, 'input_srcjars')
424      annotation_processor_outputs_dir = os.path.join(
425          temp_dir, 'annotation_processor_outputs')
426
427    if options.java_srcjars:
428      logging.info('Extracting srcjars to %s', input_srcjars_dir)
429      build_utils.MakeDirectory(input_srcjars_dir)
430      for srcjar in options.java_srcjars:
431        extracted_files = build_utils.ExtractAll(
432            srcjar, no_clobber=True, path=input_srcjars_dir, pattern='*.java')
433        java_files.extend(extracted_files)
434        if save_outputs:
435          info_file_context.AddSrcJarSources(srcjar, extracted_files,
436                                             input_srcjars_dir)
437      logging.info('Done extracting srcjars')
438
439    if save_outputs and java_files:
440      info_file_context.SubmitFiles(java_files)
441
442    if java_files:
443      # Don't include the output directory in the initial set of args since it
444      # being in a temp dir makes it unstable (breaks md5 stamping).
445      cmd = list(javac_cmd)
446      cmd += ['-d', classes_dir]
447      cmd += ['-s', annotation_processor_outputs_dir]
448
449      # Pass classpath and source paths as response files to avoid extremely
450      # long command lines that are tedius to debug.
451      if classpath:
452        cmd += ['-classpath', ':'.join(classpath)]
453
454      java_files_rsp_path = os.path.join(temp_dir, 'files_list.txt')
455      with open(java_files_rsp_path, 'w') as f:
456        f.write(' '.join(java_files))
457      cmd += ['@' + java_files_rsp_path]
458
459      logging.debug('Build command %s', cmd)
460      os.makedirs(classes_dir)
461      os.makedirs(annotation_processor_outputs_dir)
462      start = time.time()
463      build_utils.CheckOutput(
464          cmd,
465          print_stdout=options.chromium_code,
466          stdout_filter=ProcessJavacOutput,
467          stderr_filter=ProcessJavacOutput)
468      end = time.time() - start
469      logging.info('Java compilation took %ss', end)
470
471    if save_outputs:
472      annotation_processor_java_files = build_utils.FindInDirectory(
473          annotation_processor_outputs_dir)
474      if annotation_processor_java_files:
475        info_file_context.SubmitFiles(annotation_processor_java_files)
476
477      _CreateJarFile(jar_path, options.provider_configurations,
478                     options.additional_jar_files, classes_dir)
479
480      info_file_context.Commit(jar_path + '.info')
481    else:
482      build_utils.Touch(jar_path)
483
484    logging.info('Completed all steps in _RunCompiler')
485  finally:
486    shutil.rmtree(temp_dir)
487
488
489def _ParseOptions(argv):
490  parser = optparse.OptionParser()
491  build_utils.AddDepfileOption(parser)
492
493  parser.add_option(
494      '--java-srcjars',
495      action='append',
496      default=[],
497      help='List of srcjars to include in compilation.')
498  parser.add_option(
499      '--generated-dir',
500      help='Subdirectory within target_gen_dir to place extracted srcjars and '
501      'annotation processor output for codesearch to find.')
502  parser.add_option(
503      '--bootclasspath',
504      action='append',
505      default=[],
506      help='Boot classpath for javac. If this is specified multiple times, '
507      'they will all be appended to construct the classpath.')
508  parser.add_option(
509      '--java-version',
510      help='Java language version to use in -source and -target args to javac.')
511  parser.add_option('--classpath', action='append', help='Classpath to use.')
512  parser.add_option(
513      '--processors',
514      action='append',
515      help='GN list of annotation processor main classes.')
516  parser.add_option(
517      '--processorpath',
518      action='append',
519      help='GN list of jars that comprise the classpath used for Annotation '
520      'Processors.')
521  parser.add_option(
522      '--processor-arg',
523      dest='processor_args',
524      action='append',
525      help='key=value arguments for the annotation processors.')
526  parser.add_option(
527      '--provider-configuration',
528      dest='provider_configurations',
529      action='append',
530      help='File to specify a service provider. Will be included '
531      'in the jar under META-INF/services.')
532  parser.add_option(
533      '--additional-jar-file',
534      dest='additional_jar_files',
535      action='append',
536      help='Additional files to package into jar. By default, only Java .class '
537      'files are packaged into the jar. Files should be specified in '
538      'format <filename>:<path to be placed in jar>.')
539  parser.add_option(
540      '--jar-info-exclude-globs',
541      help='GN list of exclude globs to filter from generated .info files.')
542  parser.add_option(
543      '--chromium-code',
544      type='int',
545      help='Whether code being compiled should be built with stricter '
546      'warnings for chromium code.')
547  parser.add_option(
548      '--gomacc-path', help='When set, prefix javac command with gomacc')
549  parser.add_option(
550      '--errorprone-path', help='Use the Errorprone compiler at this path.')
551  parser.add_option(
552      '--enable-errorprone',
553      action='store_true',
554      help='Enable errorprone checks')
555  parser.add_option(
556      '--warnings-as-errors',
557      action='store_true',
558      help='Treat all warnings as errors.')
559  parser.add_option('--jar-path', help='Jar output path.')
560  parser.add_option(
561      '--javac-arg',
562      action='append',
563      default=[],
564      help='Additional arguments to pass to javac.')
565  parser.add_option(
566      '--enable-kythe-annotations',
567      action='store_true',
568      help='Enable generation of Kythe kzip, used for codesearch. Ensure '
569      'proper environment variables are set before using this flag.')
570
571  options, args = parser.parse_args(argv)
572  build_utils.CheckOptions(options, parser, required=('jar_path', ))
573
574  options.bootclasspath = build_utils.ParseGnList(options.bootclasspath)
575  options.classpath = build_utils.ParseGnList(options.classpath)
576  options.processorpath = build_utils.ParseGnList(options.processorpath)
577  options.processors = build_utils.ParseGnList(options.processors)
578  options.java_srcjars = build_utils.ParseGnList(options.java_srcjars)
579  options.jar_info_exclude_globs = build_utils.ParseGnList(
580      options.jar_info_exclude_globs)
581
582  additional_jar_files = []
583  for arg in options.additional_jar_files or []:
584    filepath, jar_filepath = arg.split(':')
585    additional_jar_files.append((filepath, jar_filepath))
586  options.additional_jar_files = additional_jar_files
587
588  java_files = []
589  for arg in args:
590    # Interpret a path prefixed with @ as a file containing a list of sources.
591    if arg.startswith('@'):
592      java_files.extend(build_utils.ReadSourcesList(arg[1:]))
593    else:
594      java_files.append(arg)
595
596  return options, java_files
597
598
599def main(argv):
600  build_utils.InitLogging('JAVAC_DEBUG')
601  colorama.init()
602
603  argv = build_utils.ExpandFileArgs(argv)
604  options, java_files = _ParseOptions(argv)
605
606  javac_cmd = []
607  if options.gomacc_path:
608    javac_cmd.append(options.gomacc_path)
609  javac_cmd.append(build_utils.JAVAC_PATH)
610
611  javac_args = [
612      '-g',
613      # Chromium only allows UTF8 source files.  Being explicit avoids
614      # javac pulling a default encoding from the user's environment.
615      '-encoding',
616      'UTF-8',
617      # Prevent compiler from compiling .java files not listed as inputs.
618      # See: http://blog.ltgt.net/most-build-tools-misuse-javac/
619      '-sourcepath',
620      ':',
621  ]
622
623  if options.enable_errorprone:
624    # All errorprone args are passed space-separated in a single arg.
625    errorprone_flags = ['-Xplugin:ErrorProne']
626    for warning in ERRORPRONE_WARNINGS_TO_TURN_OFF:
627      errorprone_flags.append('-Xep:{}:OFF'.format(warning))
628    for warning in ERRORPRONE_WARNINGS_TO_ERROR:
629      errorprone_flags.append('-Xep:{}:ERROR'.format(warning))
630    if not options.warnings_as_errors:
631      errorprone_flags.append('-XepAllErrorsAsWarnings')
632    javac_args += ['-XDcompilePolicy=simple', ' '.join(errorprone_flags)]
633    # This flag quits errorprone after checks and before code generation, since
634    # we do not need errorprone outputs, this speeds up errorprone by 4 seconds
635    # for chrome_java.
636    javac_args += ['-XDshould-stop.ifNoError=FLOW']
637
638  if options.java_version:
639    javac_args.extend([
640        '-source',
641        options.java_version,
642        '-target',
643        options.java_version,
644    ])
645  if options.java_version == '1.8':
646    # Android's boot jar doesn't contain all java 8 classes.
647    options.bootclasspath.append(build_utils.RT_JAR_PATH)
648
649  if options.warnings_as_errors:
650    javac_args.extend(['-Werror'])
651  else:
652    # XDignore.symbol.file makes javac compile against rt.jar instead of
653    # ct.sym. This means that using a java internal package/class will not
654    # trigger a compile warning or error.
655    javac_args.extend(['-XDignore.symbol.file'])
656
657  if options.processors:
658    javac_args.extend(['-processor', ','.join(options.processors)])
659  else:
660    # This effectively disables all annotation processors, even including
661    # annotation processors in service provider configuration files named
662    # META-INF/. See the following link for reference:
663    #     https://docs.oracle.com/en/java/javase/11/tools/javac.html
664    javac_args.extend(['-proc:none'])
665
666  if options.bootclasspath:
667    javac_args.extend(['-bootclasspath', ':'.join(options.bootclasspath)])
668
669  if options.processorpath:
670    javac_args.extend(['-processorpath', ':'.join(options.processorpath)])
671  if options.processor_args:
672    for arg in options.processor_args:
673      javac_args.extend(['-A%s' % arg])
674
675  javac_args.extend(options.javac_arg)
676
677  classpath_inputs = (
678      options.bootclasspath + options.classpath + options.processorpath)
679
680  # GN already knows of java_files, so listing them just make things worse when
681  # they change.
682  depfile_deps = classpath_inputs + options.java_srcjars
683  input_paths = depfile_deps + java_files
684  input_paths += [x[0] for x in options.additional_jar_files]
685
686  output_paths = [
687      options.jar_path,
688      options.jar_path + '.info',
689  ]
690
691  input_strings = javac_cmd + javac_args + options.classpath + java_files
692  if options.jar_info_exclude_globs:
693    input_strings.append(options.jar_info_exclude_globs)
694
695  md5_check.CallAndWriteDepfileIfStale(
696      lambda: _OnStaleMd5(options, javac_cmd, javac_args, java_files),
697      options,
698      depfile_deps=depfile_deps,
699      input_paths=input_paths,
700      input_strings=input_strings,
701      output_paths=output_paths)
702
703
704if __name__ == '__main__':
705  sys.exit(main(sys.argv[1:]))
706