Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / build / android / gyp / javac.py
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
7 import fnmatch
8 import optparse
9 import os
10 import shutil
11 import re
12 import sys
13 import textwrap
14
15 from util import build_utils
16 from util import md5_check
17
18 import jar
19
20 sys.path.append(build_utils.COLORAMA_ROOT)
21 import colorama
22
23
24 def ColorJavacOutput(output):
25   fileline_prefix = r'(?P<fileline>(?P<file>[-.\w/\\]+.java):(?P<line>[0-9]+):)'
26   warning_re = re.compile(
27       fileline_prefix + r'(?P<full_message> warning: (?P<message>.*))$')
28   error_re = re.compile(
29       fileline_prefix + r'(?P<full_message> (?P<message>.*))$')
30   marker_re = re.compile(r'\s*(?P<marker>\^)\s*$')
31
32   warning_color = ['full_message', colorama.Fore.YELLOW + colorama.Style.DIM]
33   error_color = ['full_message', colorama.Fore.MAGENTA + colorama.Style.BRIGHT]
34   marker_color = ['marker',  colorama.Fore.BLUE + colorama.Style.BRIGHT]
35
36   def Colorize(line, regex, color):
37     match = regex.match(line)
38     start = match.start(color[0])
39     end = match.end(color[0])
40     return (line[:start]
41             + color[1] + line[start:end]
42             + colorama.Fore.RESET + colorama.Style.RESET_ALL
43             + line[end:])
44
45   def ApplyColor(line):
46     if warning_re.match(line):
47       line = Colorize(line, warning_re, warning_color)
48     elif error_re.match(line):
49       line = Colorize(line, error_re, error_color)
50     elif marker_re.match(line):
51       line = Colorize(line, marker_re, marker_color)
52     return line
53
54   return '\n'.join(map(ApplyColor, output.split('\n')))
55
56
57 def DoJavac(
58     classpath, classes_dir, chromium_code, java_files):
59   """Runs javac.
60
61   Builds |java_files| with the provided |classpath| and puts the generated
62   .class files into |classes_dir|. If |chromium_code| is true, extra lint
63   checking will be enabled.
64   """
65
66   jar_inputs = []
67   for path in classpath:
68     if os.path.exists(path + '.TOC'):
69       jar_inputs.append(path + '.TOC')
70     else:
71       jar_inputs.append(path)
72
73   javac_args = [
74       '-g',
75       '-source', '1.7',
76       '-target', '1.7',
77       '-classpath', ':'.join(classpath),
78       '-d', classes_dir]
79   if chromium_code:
80     javac_args.extend(['-Xlint:unchecked', '-Xlint:deprecation'])
81   else:
82     # XDignore.symbol.file makes javac compile against rt.jar instead of
83     # ct.sym. This means that using a java internal package/class will not
84     # trigger a compile warning or error.
85     javac_args.extend(['-XDignore.symbol.file'])
86
87   javac_cmd = ['javac'] + javac_args + java_files
88
89   def Compile():
90     build_utils.CheckOutput(
91         javac_cmd,
92         print_stdout=chromium_code,
93         stderr_filter=ColorJavacOutput)
94
95   record_path = os.path.join(classes_dir, 'javac.md5.stamp')
96   md5_check.CallAndRecordIfStale(
97       Compile,
98       record_path=record_path,
99       input_paths=java_files + jar_inputs,
100       input_strings=javac_cmd)
101
102
103 _MAX_MANIFEST_LINE_LEN = 72
104
105
106 def CreateManifest(manifest_path, classpath, main_class=None):
107   """Creates a manifest file with the given parameters.
108
109   This generates a manifest file that compiles with the spec found at
110   http://docs.oracle.com/javase/7/docs/technotes/guides/jar/jar.html#JAR_Manifest
111
112   Args:
113     manifest_path: The path to the manifest file that should be created.
114     classpath: The JAR files that should be listed on the manifest file's
115       classpath.
116     main_class: If present, the class containing the main() function.
117
118   """
119   output = ['Manifest-Version: 1.0']
120   if main_class:
121     output.append('Main-Class: %s' % main_class)
122   if classpath:
123     sanitized_paths = []
124     for path in classpath:
125       sanitized_paths.append(os.path.basename(path.strip('"')))
126     output.append('Class-Path: %s' % ' '.join(sanitized_paths))
127   output.append('Created-By: ')
128   output.append('')
129
130   wrapper = textwrap.TextWrapper(break_long_words=True,
131                                  drop_whitespace=False,
132                                  subsequent_indent=' ',
133                                  width=_MAX_MANIFEST_LINE_LEN - 2)
134   output = '\r\n'.join(w for l in output for w in wrapper.wrap(l))
135
136   with open(manifest_path, 'w') as f:
137     f.write(output)
138
139
140 def main(argv):
141   colorama.init()
142
143   argv = build_utils.ExpandFileArgs(argv)
144
145   parser = optparse.OptionParser()
146   build_utils.AddDepfileOption(parser)
147
148   parser.add_option(
149       '--src-gendirs',
150       help='Directories containing generated java files.')
151   parser.add_option(
152       '--java-srcjars',
153       action='append',
154       default=[],
155       help='List of srcjars to include in compilation.')
156   parser.add_option(
157       '--classpath',
158       action='append',
159       help='Classpath for javac. If this is specified multiple times, they '
160       'will all be appended to construct the classpath.')
161   parser.add_option(
162       '--javac-includes',
163       help='A list of file patterns. If provided, only java files that match'
164       'one of the patterns will be compiled.')
165   parser.add_option(
166       '--jar-excluded-classes',
167       default='',
168       help='List of .class file patterns to exclude from the jar.')
169
170   parser.add_option(
171       '--chromium-code',
172       type='int',
173       help='Whether code being compiled should be built with stricter '
174       'warnings for chromium code.')
175
176   parser.add_option(
177       '--classes-dir',
178       help='Directory for compiled .class files.')
179   parser.add_option('--jar-path', help='Jar output path.')
180   parser.add_option(
181       '--main-class',
182       help='The class containing the main method.')
183
184   parser.add_option('--stamp', help='Path to touch on success.')
185
186   options, args = parser.parse_args(argv)
187
188   if options.main_class and not options.jar_path:
189     parser.error('--main-class requires --jar-path')
190
191   classpath = []
192   for arg in options.classpath:
193     classpath += build_utils.ParseGypList(arg)
194
195   java_srcjars = []
196   for arg in options.java_srcjars:
197     java_srcjars += build_utils.ParseGypList(arg)
198
199   java_files = args
200   if options.src_gendirs:
201     src_gendirs = build_utils.ParseGypList(options.src_gendirs)
202     java_files += build_utils.FindInDirectories(src_gendirs, '*.java')
203
204   input_files = classpath + java_srcjars + java_files
205   with build_utils.TempDir() as temp_dir:
206     classes_dir = os.path.join(temp_dir, 'classes')
207     os.makedirs(classes_dir)
208     if java_srcjars:
209       java_dir = os.path.join(temp_dir, 'java')
210       os.makedirs(java_dir)
211       for srcjar in java_srcjars:
212         build_utils.ExtractAll(srcjar, path=java_dir, pattern='*.java')
213       java_files += build_utils.FindInDirectory(java_dir, '*.java')
214
215     if options.javac_includes:
216       javac_includes = build_utils.ParseGypList(options.javac_includes)
217       filtered_java_files = []
218       for f in java_files:
219         for include in javac_includes:
220           if fnmatch.fnmatch(f, include):
221             filtered_java_files.append(f)
222             break
223       java_files = filtered_java_files
224
225     DoJavac(
226         classpath,
227         classes_dir,
228         options.chromium_code,
229         java_files)
230
231     if options.jar_path:
232       if options.main_class:
233         manifest_file = os.path.join(temp_dir, 'manifest')
234         CreateManifest(manifest_file, classpath,
235                        options.main_class)
236       else:
237         manifest_file = None
238       jar.JarDirectory(classes_dir,
239                        build_utils.ParseGypList(options.jar_excluded_classes),
240                        options.jar_path,
241                        manifest_file=manifest_file)
242
243     if options.classes_dir:
244       # Delete the old classes directory. This ensures that all .class files in
245       # the output are actually from the input .java files. For example, if a
246       # .java file is deleted or an inner class is removed, the classes
247       # directory should not contain the corresponding old .class file after
248       # running this action.
249       build_utils.DeleteDirectory(options.classes_dir)
250       shutil.copytree(classes_dir, options.classes_dir)
251
252   if options.depfile:
253     build_utils.WriteDepfile(
254         options.depfile,
255         input_files + build_utils.GetPythonDependencies())
256
257   if options.stamp:
258     build_utils.Touch(options.stamp)
259
260
261 if __name__ == '__main__':
262   sys.exit(main(sys.argv[1:]))
263
264