Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / build / android / gyp / javac.py
index c117004..6a3024b 100755 (executable)
@@ -7,32 +7,61 @@
 import fnmatch
 import optparse
 import os
+import shutil
+import re
 import sys
+import textwrap
 
 from util import build_utils
 from util import md5_check
 
+import jar
 
-def DoJavac(options):
-  output_dir = options.output_dir
+sys.path.append(build_utils.COLORAMA_ROOT)
+import colorama
 
-  src_dirs = build_utils.ParseGypList(options.src_dirs)
-  java_files = build_utils.FindInDirectories(src_dirs, '*.java')
-  if options.javac_includes:
-    javac_includes = build_utils.ParseGypList(options.javac_includes)
-    filtered_java_files = []
-    for f in java_files:
-      for include in javac_includes:
-        if fnmatch.fnmatch(f, include):
-          filtered_java_files.append(f)
-          break
-    java_files = filtered_java_files
 
-  # Compiling guava with certain orderings of input files causes a compiler
-  # crash... Sorted order works, so use that.
-  # See https://code.google.com/p/guava-libraries/issues/detail?id=950
-  java_files.sort()
-  classpath = build_utils.ParseGypList(options.classpath)
+def ColorJavacOutput(output):
+  fileline_prefix = r'(?P<fileline>(?P<file>[-.\w/\\]+.java):(?P<line>[0-9]+):)'
+  warning_re = re.compile(
+      fileline_prefix + r'(?P<full_message> warning: (?P<message>.*))$')
+  error_re = re.compile(
+      fileline_prefix + r'(?P<full_message> (?P<message>.*))$')
+  marker_re = re.compile(r'\s*(?P<marker>\^)\s*$')
+
+  warning_color = ['full_message', colorama.Fore.YELLOW + colorama.Style.DIM]
+  error_color = ['full_message', colorama.Fore.MAGENTA + colorama.Style.BRIGHT]
+  marker_color = ['marker',  colorama.Fore.BLUE + colorama.Style.BRIGHT]
+
+  def Colorize(line, regex, color):
+    match = regex.match(line)
+    start = match.start(color[0])
+    end = match.end(color[0])
+    return (line[:start]
+            + color[1] + line[start:end]
+            + colorama.Fore.RESET + colorama.Style.RESET_ALL
+            + line[end:])
+
+  def ApplyColor(line):
+    if warning_re.match(line):
+      line = Colorize(line, warning_re, warning_color)
+    elif error_re.match(line):
+      line = Colorize(line, error_re, error_color)
+    elif marker_re.match(line):
+      line = Colorize(line, marker_re, marker_color)
+    return line
+
+  return '\n'.join(map(ApplyColor, output.split('\n')))
+
+
+def DoJavac(
+    classpath, classes_dir, chromium_code, java_files):
+  """Runs javac.
+
+  Builds |java_files| with the provided |classpath| and puts the generated
+  .class files into |classes_dir|. If |chromium_code| is true, extra lint
+  checking will be enabled.
+  """
 
   jar_inputs = []
   for path in classpath:
@@ -41,28 +70,29 @@ def DoJavac(options):
     else:
       jar_inputs.append(path)
 
-  javac_cmd = [
-      'javac',
+  javac_args = [
       '-g',
-      '-source', '1.5',
-      '-target', '1.5',
+      '-source', '1.7',
+      '-target', '1.7',
       '-classpath', ':'.join(classpath),
-      '-d', output_dir,
-      '-Xlint:unchecked',
-      '-Xlint:deprecation',
-      ] + java_files
+      '-d', classes_dir]
+  if chromium_code:
+    javac_args.extend(['-Xlint:unchecked', '-Xlint:deprecation'])
+  else:
+    # XDignore.symbol.file makes javac compile against rt.jar instead of
+    # ct.sym. This means that using a java internal package/class will not
+    # trigger a compile warning or error.
+    javac_args.extend(['-XDignore.symbol.file'])
+
+  javac_cmd = ['javac'] + javac_args + java_files
 
   def Compile():
-    # Delete the classes directory. This ensures that all .class files in the
-    # output are actually from the input .java files. For example, if a .java
-    # file is deleted or an inner class is removed, the classes directory should
-    # not contain the corresponding old .class file after running this action.
-    build_utils.DeleteDirectory(output_dir)
-    build_utils.MakeDirectory(output_dir)
-    suppress_output = not options.chromium_code
-    build_utils.CheckCallDie(javac_cmd, suppress_output=suppress_output)
-
-  record_path = '%s/javac.md5.stamp' % options.output_dir
+    build_utils.CheckOutput(
+        javac_cmd,
+        print_stdout=chromium_code,
+        stderr_filter=ColorJavacOutput)
+
+  record_path = os.path.join(classes_dir, 'javac.md5.stamp')
   md5_check.CallAndRecordIfStale(
       Compile,
       record_path=record_path,
@@ -70,31 +100,165 @@ def DoJavac(options):
       input_strings=javac_cmd)
 
 
+_MAX_MANIFEST_LINE_LEN = 72
+
+
+def CreateManifest(manifest_path, classpath, main_class=None):
+  """Creates a manifest file with the given parameters.
+
+  This generates a manifest file that compiles with the spec found at
+  http://docs.oracle.com/javase/7/docs/technotes/guides/jar/jar.html#JAR_Manifest
+
+  Args:
+    manifest_path: The path to the manifest file that should be created.
+    classpath: The JAR files that should be listed on the manifest file's
+      classpath.
+    main_class: If present, the class containing the main() function.
+
+  """
+  output = ['Manifest-Version: 1.0']
+  if main_class:
+    output.append('Main-Class: %s' % main_class)
+  if classpath:
+    sanitized_paths = []
+    for path in classpath:
+      sanitized_paths.append(os.path.basename(path.strip('"')))
+    output.append('Class-Path: %s' % ' '.join(sanitized_paths))
+  output.append('Created-By: ')
+  output.append('')
+
+  wrapper = textwrap.TextWrapper(break_long_words=True,
+                                 drop_whitespace=False,
+                                 subsequent_indent=' ',
+                                 width=_MAX_MANIFEST_LINE_LEN - 2)
+  output = '\r\n'.join(w for l in output for w in wrapper.wrap(l))
+
+  with open(manifest_path, 'w') as f:
+    f.write(output)
+
+
 def main(argv):
+  colorama.init()
+
+  argv = build_utils.ExpandFileArgs(argv)
+
   parser = optparse.OptionParser()
-  parser.add_option('--src-dirs', help='Directories containing java files.')
-  parser.add_option('--javac-includes',
-      help='A list of file patterns. If provided, only java files that match' +
-        'one of the patterns will be compiled.')
-  parser.add_option('--classpath', help='Classpath for javac.')
-  parser.add_option('--output-dir', help='Directory for javac output.')
+  build_utils.AddDepfileOption(parser)
+
+  parser.add_option(
+      '--src-gendirs',
+      help='Directories containing generated java files.')
+  parser.add_option(
+      '--java-srcjars',
+      action='append',
+      default=[],
+      help='List of srcjars to include in compilation.')
+  parser.add_option(
+      '--classpath',
+      action='append',
+      help='Classpath for javac. If this is specified multiple times, they '
+      'will all be appended to construct the classpath.')
+  parser.add_option(
+      '--javac-includes',
+      help='A list of file patterns. If provided, only java files that match'
+      'one of the patterns will be compiled.')
+  parser.add_option(
+      '--jar-excluded-classes',
+      default='',
+      help='List of .class file patterns to exclude from the jar.')
+
+  parser.add_option(
+      '--chromium-code',
+      type='int',
+      help='Whether code being compiled should be built with stricter '
+      'warnings for chromium code.')
+
+  parser.add_option(
+      '--classes-dir',
+      help='Directory for compiled .class files.')
+  parser.add_option('--jar-path', help='Jar output path.')
+  parser.add_option(
+      '--main-class',
+      help='The class containing the main method.')
+
   parser.add_option('--stamp', help='Path to touch on success.')
-  parser.add_option('--chromium-code', type='int', help='Whether code being '
-                    'compiled should be built with stricter warnings for '
-                    'chromium code.')
 
-  # TODO(newt): remove this once http://crbug.com/177552 is fixed in ninja.
-  parser.add_option('--ignore', help='Ignored.')
+  options, args = parser.parse_args(argv)
+
+  if options.main_class and not options.jar_path:
+    parser.error('--main-class requires --jar-path')
+
+  classpath = []
+  for arg in options.classpath:
+    classpath += build_utils.ParseGypList(arg)
+
+  java_srcjars = []
+  for arg in options.java_srcjars:
+    java_srcjars += build_utils.ParseGypList(arg)
+
+  java_files = args
+  if options.src_gendirs:
+    src_gendirs = build_utils.ParseGypList(options.src_gendirs)
+    java_files += build_utils.FindInDirectories(src_gendirs, '*.java')
+
+  input_files = classpath + java_srcjars + java_files
+  with build_utils.TempDir() as temp_dir:
+    classes_dir = os.path.join(temp_dir, 'classes')
+    os.makedirs(classes_dir)
+    if java_srcjars:
+      java_dir = os.path.join(temp_dir, 'java')
+      os.makedirs(java_dir)
+      for srcjar in java_srcjars:
+        build_utils.ExtractAll(srcjar, path=java_dir, pattern='*.java')
+      java_files += build_utils.FindInDirectory(java_dir, '*.java')
+
+    if options.javac_includes:
+      javac_includes = build_utils.ParseGypList(options.javac_includes)
+      filtered_java_files = []
+      for f in java_files:
+        for include in javac_includes:
+          if fnmatch.fnmatch(f, include):
+            filtered_java_files.append(f)
+            break
+      java_files = filtered_java_files
+
+    DoJavac(
+        classpath,
+        classes_dir,
+        options.chromium_code,
+        java_files)
+
+    if options.jar_path:
+      if options.main_class:
+        manifest_file = os.path.join(temp_dir, 'manifest')
+        CreateManifest(manifest_file, classpath,
+                       options.main_class)
+      else:
+        manifest_file = None
+      jar.JarDirectory(classes_dir,
+                       build_utils.ParseGypList(options.jar_excluded_classes),
+                       options.jar_path,
+                       manifest_file=manifest_file)
 
-  options, _ = parser.parse_args()
+    if options.classes_dir:
+      # Delete the old classes directory. This ensures that all .class files in
+      # the output are actually from the input .java files. For example, if a
+      # .java file is deleted or an inner class is removed, the classes
+      # directory should not contain the corresponding old .class file after
+      # running this action.
+      build_utils.DeleteDirectory(options.classes_dir)
+      shutil.copytree(classes_dir, options.classes_dir)
 
-  DoJavac(options)
+  if options.depfile:
+    build_utils.WriteDepfile(
+        options.depfile,
+        input_files + build_utils.GetPythonDependencies())
 
   if options.stamp:
     build_utils.Touch(options.stamp)
 
 
 if __name__ == '__main__':
-  sys.exit(main(sys.argv))
+  sys.exit(main(sys.argv[1:]))