blob: 18312c66583a814f8e07c02263ea414a656d71e0 [file] [log] [blame]
maruel@chromium.orgbe8446a2012-04-21 23:30:11 +09001#!/usr/bin/env python
bulach@chromium.org6079a072012-02-24 09:09:38 +09002# Copyright (c) 2012 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Extracts native methods from a Java file and generates the JNI bindings.
7If you change this, please run and update the tests."""
8
9import collections
10import optparse
11import os
12import re
13import string
14from string import Template
15import subprocess
16import sys
17import textwrap
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +090018import zipfile
bulach@chromium.org6079a072012-02-24 09:09:38 +090019
bulach@chromium.org6079a072012-02-24 09:09:38 +090020
21class ParseError(Exception):
22 """Exception thrown when we can't parse the input file."""
23
24 def __init__(self, description, *context_lines):
25 Exception.__init__(self)
26 self.description = description
27 self.context_lines = context_lines
28
29 def __str__(self):
30 context = '\n'.join(self.context_lines)
31 return '***\nERROR: %s\n\n%s\n***' % (self.description, context)
32
33
34class Param(object):
35 """Describes a param for a method, either java or native."""
36
37 def __init__(self, **kwargs):
38 self.datatype = kwargs['datatype']
39 self.name = kwargs['name']
bulach@chromium.org6079a072012-02-24 09:09:38 +090040
41
42class NativeMethod(object):
43 """Describes a C/C++ method that is called by Java code"""
44
45 def __init__(self, **kwargs):
46 self.static = kwargs['static']
47 self.java_class_name = kwargs['java_class_name']
48 self.return_type = kwargs['return_type']
49 self.name = kwargs['name']
50 self.params = kwargs['params']
51 if self.params:
52 assert type(self.params) is list
53 assert type(self.params[0]) is Param
54 if (self.params and
55 self.params[0].datatype == 'int' and
56 self.params[0].name.startswith('native')):
57 self.type = 'method'
bulach@chromium.org1c775752012-06-22 19:03:16 +090058 self.p0_type = self.params[0].name[len('native'):]
59 if kwargs.get('native_class_name'):
60 self.p0_type = kwargs['native_class_name']
bulach@chromium.org6079a072012-02-24 09:09:38 +090061 else:
62 self.type = 'function'
63 self.method_id_var_name = kwargs.get('method_id_var_name', None)
64
65
66class CalledByNative(object):
67 """Describes a java method exported to c/c++"""
68
69 def __init__(self, **kwargs):
70 self.system_class = kwargs['system_class']
71 self.unchecked = kwargs['unchecked']
72 self.static = kwargs['static']
73 self.java_class_name = kwargs['java_class_name']
74 self.return_type = kwargs['return_type']
bulach@chromium.org6079a072012-02-24 09:09:38 +090075 self.name = kwargs['name']
76 self.params = kwargs['params']
77 self.method_id_var_name = kwargs.get('method_id_var_name', None)
bulach@chromium.org31af7532012-09-24 20:01:41 +090078 self.is_constructor = kwargs.get('is_constructor', False)
79 self.env_call = GetEnvCall(self.is_constructor, self.static,
80 self.return_type)
81 self.static_cast = GetStaticCastForReturnType(self.return_type)
bulach@chromium.org6079a072012-02-24 09:09:38 +090082
83
84def JavaDataTypeToC(java_type):
85 """Returns a C datatype for the given java type."""
86 java_pod_type_map = {
87 'int': 'jint',
88 'byte': 'jbyte',
89 'boolean': 'jboolean',
90 'long': 'jlong',
91 'double': 'jdouble',
92 'float': 'jfloat',
93 }
94 java_type_map = {
95 'void': 'void',
96 'String': 'jstring',
bulach@chromium.org0bafda72012-11-08 04:06:51 +090097 'java/lang/String': 'jstring',
dtrainor@chromium.orgf7e99772012-12-06 08:27:41 +090098 'Class': 'jclass',
99 'java/lang/Class': 'jclass',
bulach@chromium.org6079a072012-02-24 09:09:38 +0900100 }
dtrainor@chromium.orgf7e99772012-12-06 08:27:41 +0900101
bulach@chromium.org6079a072012-02-24 09:09:38 +0900102 if java_type in java_pod_type_map:
103 return java_pod_type_map[java_type]
104 elif java_type in java_type_map:
105 return java_type_map[java_type]
106 elif java_type.endswith('[]'):
107 if java_type[:-2] in java_pod_type_map:
108 return java_pod_type_map[java_type[:-2]] + 'Array'
109 return 'jobjectArray'
110 else:
111 return 'jobject'
112
113
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900114class JniParams(object):
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900115 _imports = []
116 _fully_qualified_class = ''
117 _package = ''
118 _inner_classes = []
bulach@chromium.org6079a072012-02-24 09:09:38 +0900119
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900120 @staticmethod
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900121 def SetFullyQualifiedClass(fully_qualified_class):
122 JniParams._fully_qualified_class = 'L' + fully_qualified_class
123 JniParams._package = '/'.join(fully_qualified_class.split('/')[:-1])
124
125 @staticmethod
126 def ExtractImportsAndInnerClasses(contents):
127 contents = contents.replace('\n', '')
128 re_import = re.compile(r'import.*?(?P<class>\S*?);')
129 for match in re.finditer(re_import, contents):
130 JniParams._imports += ['L' + match.group('class').replace('.', '/')]
131
132 re_inner = re.compile(r'(class|interface)\s+?(?P<name>\w+?)\W')
133 for match in re.finditer(re_inner, contents):
134 inner = match.group('name')
135 if not JniParams._fully_qualified_class.endswith(inner):
136 JniParams._inner_classes += [JniParams._fully_qualified_class + '$' +
137 inner]
bulach@chromium.org6079a072012-02-24 09:09:38 +0900138
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900139 @staticmethod
140 def JavaToJni(param):
141 """Converts a java param into a JNI signature type."""
142 pod_param_map = {
143 'int': 'I',
144 'boolean': 'Z',
145 'long': 'J',
146 'double': 'D',
147 'float': 'F',
148 'byte': 'B',
149 'void': 'V',
150 }
151 object_param_list = [
152 'Ljava/lang/Boolean',
153 'Ljava/lang/Integer',
154 'Ljava/lang/Long',
155 'Ljava/lang/Object',
156 'Ljava/lang/String',
dtrainor@chromium.orgf7e99772012-12-06 08:27:41 +0900157 'Ljava/lang/Class',
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900158 ]
159 if param == 'byte[][]':
160 return '[[B'
161 prefix = ''
162 # Array?
163 if param[-2:] == '[]':
164 prefix = '['
165 param = param[:-2]
166 # Generic?
167 if '<' in param:
168 param = param[:param.index('<')]
169 if param in pod_param_map:
170 return prefix + pod_param_map[param]
171 if '/' in param:
172 # Coming from javap, use the fully qualified param directly.
173 return 'L' + param + ';'
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900174 for qualified_name in (object_param_list +
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900175 [JniParams._fully_qualified_class] +
176 JniParams._inner_classes):
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900177 if (qualified_name.endswith('/' + param) or
178 qualified_name.endswith('$' + param.replace('.', '$')) or
179 qualified_name == 'L' + param):
180 return prefix + qualified_name + ';'
bulach@chromium.org7156c762012-11-13 23:35:00 +0900181
182 # Is it from an import? (e.g. referecing Class from import pkg.Class;
183 # note that referencing an inner class Inner from import pkg.Class.Inner
184 # is not supported).
185 for qualified_name in JniParams._imports:
186 if qualified_name.endswith('/' + param):
187 # Ensure it's not an inner class.
188 components = qualified_name.split('/')
189 if len(components) > 2 and components[-2][0].isupper():
190 raise SyntaxError('Inner class (%s) can not be imported '
191 'and used by JNI (%s). Please import the outer '
192 'class and use Outer.Inner instead.' %
193 (qualified_name, param))
194 return prefix + qualified_name + ';'
195
196 # Is it an inner class from an outer class import? (e.g. referencing
197 # Class.Inner from import pkg.Class).
198 if '.' in param:
199 components = param.split('.')
200 outer = '/'.join(components[:-1])
201 inner = components[-1]
202 for qualified_name in JniParams._imports:
203 if qualified_name.endswith('/' + outer):
204 return prefix + qualified_name + '$' + inner
205
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900206 # Type not found, falling back to same package as this class.
207 return prefix + 'L' + JniParams._package + '/' + param + ';'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900208
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900209 @staticmethod
210 def Signature(params, returns, wrap):
211 """Returns the JNI signature for the given datatypes."""
212 items = ['(']
213 items += [JniParams.JavaToJni(param.datatype) for param in params]
214 items += [')']
215 items += [JniParams.JavaToJni(returns)]
216 if wrap:
217 return '\n' + '\n'.join(['"' + item + '"' for item in items])
218 else:
219 return '"' + ''.join(items) + '"'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900220
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900221 @staticmethod
222 def Parse(params):
223 """Parses the params into a list of Param objects."""
224 if not params:
225 return []
226 ret = []
227 for p in [p.strip() for p in params.split(',')]:
228 items = p.split(' ')
229 if 'final' in items:
230 items.remove('final')
231 param = Param(
232 datatype=items[0],
233 name=(items[1] if len(items) > 1 else 'p%s' % len(ret)),
234 )
235 ret += [param]
236 return ret
bulach@chromium.org6079a072012-02-24 09:09:38 +0900237
bulach@chromium.org6079a072012-02-24 09:09:38 +0900238
bulach@chromium.org1c775752012-06-22 19:03:16 +0900239def ExtractJNINamespace(contents):
240 re_jni_namespace = re.compile('.*?@JNINamespace\("(.*?)"\)')
241 m = re.findall(re_jni_namespace, contents)
242 if not m:
243 return ''
244 return m[0]
245
246
bulach@chromium.org6079a072012-02-24 09:09:38 +0900247def ExtractFullyQualifiedJavaClassName(java_file_name, contents):
248 re_package = re.compile('.*?package (.*?);')
249 matches = re.findall(re_package, contents)
250 if not matches:
251 raise SyntaxError('Unable to find "package" line in %s' % java_file_name)
252 return (matches[0].replace('.', '/') + '/' +
253 os.path.splitext(os.path.basename(java_file_name))[0])
254
255
256def ExtractNatives(contents):
257 """Returns a list of dict containing information about a native method."""
258 contents = contents.replace('\n', '')
259 natives = []
bulach@chromium.org1c775752012-06-22 19:03:16 +0900260 re_native = re.compile(r'(@NativeClassQualifiedName'
261 '\(\"(?P<native_class_name>.*?)\"\))?\s*'
262 '(@NativeCall(\(\"(?P<java_class_name>.*?)\"\)))?\s*'
263 '(?P<qualifiers>\w+\s\w+|\w+|\s+)\s*?native '
264 '(?P<return>\S*?) '
265 '(?P<name>\w+?)\((?P<params>.*?)\);')
266 for match in re.finditer(re_native, contents):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900267 native = NativeMethod(
bulach@chromium.org1c775752012-06-22 19:03:16 +0900268 static='static' in match.group('qualifiers'),
269 java_class_name=match.group('java_class_name'),
270 native_class_name=match.group('native_class_name'),
271 return_type=match.group('return'),
272 name=match.group('name').replace('native', ''),
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900273 params=JniParams.Parse(match.group('params')))
bulach@chromium.org6079a072012-02-24 09:09:38 +0900274 natives += [native]
275 return natives
276
277
bulach@chromium.org31af7532012-09-24 20:01:41 +0900278def GetStaticCastForReturnType(return_type):
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900279 if return_type in ['String', 'java/lang/String']:
bulach@chromium.org31af7532012-09-24 20:01:41 +0900280 return 'jstring'
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900281 elif return_type.endswith('[]'):
282 return 'jobjectArray'
bulach@chromium.org31af7532012-09-24 20:01:41 +0900283 return None
284
285
286def GetEnvCall(is_constructor, is_static, return_type):
bulach@chromium.org6079a072012-02-24 09:09:38 +0900287 """Maps the types availabe via env->Call__Method."""
bulach@chromium.org31af7532012-09-24 20:01:41 +0900288 if is_constructor:
289 return 'NewObject'
290 env_call_map = {'boolean': 'Boolean',
291 'byte': 'Byte',
292 'char': 'Char',
293 'short': 'Short',
294 'int': 'Int',
295 'long': 'Long',
296 'float': 'Float',
297 'void': 'Void',
298 'double': 'Double',
299 'Object': 'Object',
bulach@chromium.org6079a072012-02-24 09:09:38 +0900300 }
bulach@chromium.org31af7532012-09-24 20:01:41 +0900301 call = env_call_map.get(return_type, 'Object')
302 if is_static:
303 call = 'Static' + call
304 return 'Call' + call + 'Method'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900305
306
bulach@chromium.org0c6805b2012-09-28 21:34:33 +0900307def GetMangledParam(datatype):
308 """Returns a mangled identifier for the datatype."""
309 if len(datatype) <= 2:
310 return datatype.replace('[', 'A')
311 ret = ''
312 for i in range(1, len(datatype)):
313 c = datatype[i]
314 if c == '[':
315 ret += 'A'
316 elif c.isupper() or datatype[i - 1] in ['/', 'L']:
317 ret += c.upper()
318 return ret
319
320
321def GetMangledMethodName(name, params, return_type):
322 """Returns a mangled method name for the given signature.
bulach@chromium.org6079a072012-02-24 09:09:38 +0900323
324 The returned name can be used as a C identifier and will be unique for all
325 valid overloads of the same method.
326
327 Args:
328 name: string.
bulach@chromium.org0c6805b2012-09-28 21:34:33 +0900329 params: list of Param.
330 return_type: string.
bulach@chromium.org6079a072012-02-24 09:09:38 +0900331
332 Returns:
333 A mangled name.
334 """
bulach@chromium.org0c6805b2012-09-28 21:34:33 +0900335 mangled_items = []
336 for datatype in [return_type] + [x.datatype for x in params]:
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900337 mangled_items += [GetMangledParam(JniParams.JavaToJni(datatype))]
bulach@chromium.org0c6805b2012-09-28 21:34:33 +0900338 mangled_name = name + '_'.join(mangled_items)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900339 assert re.match(r'[0-9a-zA-Z_]+', mangled_name)
340 return mangled_name
341
342
343def MangleCalledByNatives(called_by_natives):
344 """Mangles all the overloads from the call_by_natives list."""
345 method_counts = collections.defaultdict(
346 lambda: collections.defaultdict(lambda: 0))
347 for called_by_native in called_by_natives:
348 java_class_name = called_by_native.java_class_name
349 name = called_by_native.name
350 method_counts[java_class_name][name] += 1
351 for called_by_native in called_by_natives:
352 java_class_name = called_by_native.java_class_name
353 method_name = called_by_native.name
354 method_id_var_name = method_name
355 if method_counts[java_class_name][method_name] > 1:
bulach@chromium.org0c6805b2012-09-28 21:34:33 +0900356 method_id_var_name = GetMangledMethodName(method_name,
357 called_by_native.params,
358 called_by_native.return_type)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900359 called_by_native.method_id_var_name = method_id_var_name
360 return called_by_natives
361
362
363# Regex to match the JNI return types that should be included in a
364# ScopedJavaLocalRef.
365RE_SCOPED_JNI_RETURN_TYPES = re.compile('jobject|jclass|jstring|.*Array')
366
367# Regex to match a string like "@CalledByNative public void foo(int bar)".
368RE_CALLED_BY_NATIVE = re.compile(
369 '@CalledByNative(?P<Unchecked>(Unchecked)*?)(?:\("(?P<annotation>.*)"\))?'
370 '\s+(?P<prefix>[\w ]*?)'
371 '\s*(?P<return_type>\w+)'
372 '\s+(?P<name>\w+)'
373 '\s*\((?P<params>[^\)]*)\)')
374
375
376def ExtractCalledByNatives(contents):
377 """Parses all methods annotated with @CalledByNative.
378
379 Args:
380 contents: the contents of the java file.
381
382 Returns:
383 A list of dict with information about the annotated methods.
384 TODO(bulach): return a CalledByNative object.
385
386 Raises:
387 ParseError: if unable to parse.
388 """
389 called_by_natives = []
390 for match in re.finditer(RE_CALLED_BY_NATIVE, contents):
391 called_by_natives += [CalledByNative(
392 system_class=False,
393 unchecked='Unchecked' in match.group('Unchecked'),
394 static='static' in match.group('prefix'),
395 java_class_name=match.group('annotation') or '',
396 return_type=match.group('return_type'),
bulach@chromium.org6079a072012-02-24 09:09:38 +0900397 name=match.group('name'),
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900398 params=JniParams.Parse(match.group('params')))]
bulach@chromium.org6079a072012-02-24 09:09:38 +0900399 # Check for any @CalledByNative occurrences that weren't matched.
400 unmatched_lines = re.sub(RE_CALLED_BY_NATIVE, '', contents).split('\n')
401 for line1, line2 in zip(unmatched_lines, unmatched_lines[1:]):
402 if '@CalledByNative' in line1:
403 raise ParseError('could not parse @CalledByNative method signature',
404 line1, line2)
405 return MangleCalledByNatives(called_by_natives)
406
407
408class JNIFromJavaP(object):
409 """Uses 'javap' to parse a .class file and generate the JNI header file."""
410
411 def __init__(self, contents, namespace):
412 self.contents = contents
413 self.namespace = namespace
bulach@chromium.org31af7532012-09-24 20:01:41 +0900414 self.fully_qualified_class = re.match('.*?class (?P<class_name>.*?) ',
415 contents[1]).group('class_name')
bulach@chromium.org6079a072012-02-24 09:09:38 +0900416 self.fully_qualified_class = self.fully_qualified_class.replace('.', '/')
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900417 JniParams.SetFullyQualifiedClass(self.fully_qualified_class)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900418 self.java_class_name = self.fully_qualified_class.split('/')[-1]
419 if not self.namespace:
420 self.namespace = 'JNI_' + self.java_class_name
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900421 re_method = re.compile('(?P<prefix>.*?)(?P<return_type>\S+?) (?P<name>\w+?)'
bulach@chromium.org31af7532012-09-24 20:01:41 +0900422 '\((?P<params>.*?)\)')
bulach@chromium.org6079a072012-02-24 09:09:38 +0900423 self.called_by_natives = []
bulach@chromium.org31af7532012-09-24 20:01:41 +0900424 for content in contents[2:]:
425 match = re.match(re_method, content)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900426 if not match:
427 continue
428 self.called_by_natives += [CalledByNative(
429 system_class=True,
430 unchecked=False,
bulach@chromium.org31af7532012-09-24 20:01:41 +0900431 static='static' in match.group('prefix'),
bulach@chromium.org6079a072012-02-24 09:09:38 +0900432 java_class_name='',
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900433 return_type=match.group('return_type').replace('.', '/'),
bulach@chromium.org31af7532012-09-24 20:01:41 +0900434 name=match.group('name'),
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900435 params=JniParams.Parse(match.group('params').replace('.', '/')))]
bulach@chromium.org31af7532012-09-24 20:01:41 +0900436 re_constructor = re.compile('.*? public ' +
437 self.fully_qualified_class.replace('/', '.') +
438 '\((?P<params>.*?)\)')
439 for content in contents[2:]:
440 match = re.match(re_constructor, content)
441 if not match:
442 continue
443 self.called_by_natives += [CalledByNative(
bulach@chromium.org208f05b2012-10-03 06:13:55 +0900444 system_class=True,
bulach@chromium.org31af7532012-09-24 20:01:41 +0900445 unchecked=False,
446 static=False,
447 java_class_name='',
448 return_type=self.fully_qualified_class,
449 name='Constructor',
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900450 params=JniParams.Parse(match.group('params').replace('.', '/')),
bulach@chromium.org31af7532012-09-24 20:01:41 +0900451 is_constructor=True)]
bulach@chromium.org6079a072012-02-24 09:09:38 +0900452 self.called_by_natives = MangleCalledByNatives(self.called_by_natives)
453 self.inl_header_file_generator = InlHeaderFileGenerator(
454 self.namespace, self.fully_qualified_class, [], self.called_by_natives)
455
456 def GetContent(self):
457 return self.inl_header_file_generator.GetContent()
458
459 @staticmethod
460 def CreateFromClass(class_file, namespace):
461 class_name = os.path.splitext(os.path.basename(class_file))[0]
462 p = subprocess.Popen(args=['javap', class_name],
463 cwd=os.path.dirname(class_file),
464 stdout=subprocess.PIPE,
465 stderr=subprocess.PIPE)
466 stdout, _ = p.communicate()
467 jni_from_javap = JNIFromJavaP(stdout.split('\n'), namespace)
468 return jni_from_javap
469
470
471class JNIFromJavaSource(object):
472 """Uses the given java source file to generate the JNI header file."""
473
474 def __init__(self, contents, fully_qualified_class):
475 contents = self._RemoveComments(contents)
bulach@chromium.org0bafda72012-11-08 04:06:51 +0900476 JniParams.SetFullyQualifiedClass(fully_qualified_class)
477 JniParams.ExtractImportsAndInnerClasses(contents)
bulach@chromium.org1c775752012-06-22 19:03:16 +0900478 jni_namespace = ExtractJNINamespace(contents)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900479 natives = ExtractNatives(contents)
480 called_by_natives = ExtractCalledByNatives(contents)
bulach@chromium.orge2530d02012-07-03 01:23:35 +0900481 if len(natives) == 0 and len(called_by_natives) == 0:
482 raise SyntaxError('Unable to find any JNI methods for %s.' %
483 fully_qualified_class)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900484 inl_header_file_generator = InlHeaderFileGenerator(
bulach@chromium.org1c775752012-06-22 19:03:16 +0900485 jni_namespace, fully_qualified_class, natives, called_by_natives)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900486 self.content = inl_header_file_generator.GetContent()
487
488 def _RemoveComments(self, contents):
husky@chromium.org8c438d72012-07-31 02:27:48 +0900489 # We need to support both inline and block comments, and we need to handle
490 # strings that contain '//' or '/*'. Rather than trying to do all that with
491 # regexps, we just pipe the contents through the C preprocessor. We tell cpp
492 # the file has already been preprocessed, so it just removes comments and
493 # doesn't try to parse #include, #pragma etc.
494 #
495 # TODO(husky): This is a bit hacky. It would be cleaner to use a real Java
496 # parser. Maybe we could ditch JNIFromJavaSource and just always use
497 # JNIFromJavaP; or maybe we could rewrite this script in Java and use APT.
498 # http://code.google.com/p/chromium/issues/detail?id=138941
499 p = subprocess.Popen(args=['cpp', '-fpreprocessed'],
500 stdin=subprocess.PIPE,
501 stdout=subprocess.PIPE,
502 stderr=subprocess.PIPE)
503 stdout, _ = p.communicate(contents)
504 return stdout
bulach@chromium.org6079a072012-02-24 09:09:38 +0900505
506 def GetContent(self):
507 return self.content
508
509 @staticmethod
510 def CreateFromFile(java_file_name):
511 contents = file(java_file_name).read()
512 fully_qualified_class = ExtractFullyQualifiedJavaClassName(java_file_name,
513 contents)
514 return JNIFromJavaSource(contents, fully_qualified_class)
515
516
517class InlHeaderFileGenerator(object):
518 """Generates an inline header file for JNI integration."""
519
520 def __init__(self, namespace, fully_qualified_class, natives,
521 called_by_natives):
522 self.namespace = namespace
523 self.fully_qualified_class = fully_qualified_class
524 self.class_name = self.fully_qualified_class.split('/')[-1]
525 self.natives = natives
526 self.called_by_natives = called_by_natives
527 self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900528
529 def GetContent(self):
530 """Returns the content of the JNI binding file."""
531 template = Template("""\
532// Copyright (c) 2012 The Chromium Authors. All rights reserved.
533// Use of this source code is governed by a BSD-style license that can be
534// found in the LICENSE file.
535
536
537// This file is autogenerated by
538// ${SCRIPT_NAME}
539// For
540// ${FULLY_QUALIFIED_CLASS}
541
542#ifndef ${HEADER_GUARD}
543#define ${HEADER_GUARD}
544
545#include <jni.h>
546
547#include "base/android/jni_android.h"
548#include "base/android/scoped_java_ref.h"
549#include "base/basictypes.h"
550#include "base/logging.h"
551
552using base::android::ScopedJavaLocalRef;
553
554// Step 1: forward declarations.
555namespace {
556$CLASS_PATH_DEFINITIONS
557} // namespace
bulach@chromium.org1c775752012-06-22 19:03:16 +0900558
559$OPEN_NAMESPACE
bulach@chromium.org6079a072012-02-24 09:09:38 +0900560$FORWARD_DECLARATIONS
561
562// Step 2: method stubs.
563$METHOD_STUBS
564
bulach@chromium.org231cff62012-10-17 03:35:10 +0900565// Step 3: RegisterNatives.
bulach@chromium.org6079a072012-02-24 09:09:38 +0900566
567static bool RegisterNativesImpl(JNIEnv* env) {
bulach@chromium.org6079a072012-02-24 09:09:38 +0900568$REGISTER_NATIVES_IMPL
569 return true;
570}
571$CLOSE_NAMESPACE
572#endif // ${HEADER_GUARD}
573""")
574 script_components = os.path.abspath(sys.argv[0]).split(os.path.sep)
575 base_index = script_components.index('base')
576 script_name = os.sep.join(script_components[base_index:])
577 values = {
578 'SCRIPT_NAME': script_name,
579 'FULLY_QUALIFIED_CLASS': self.fully_qualified_class,
580 'CLASS_PATH_DEFINITIONS': self.GetClassPathDefinitionsString(),
581 'FORWARD_DECLARATIONS': self.GetForwardDeclarationsString(),
582 'METHOD_STUBS': self.GetMethodStubsString(),
583 'OPEN_NAMESPACE': self.GetOpenNamespaceString(),
bulach@chromium.org6079a072012-02-24 09:09:38 +0900584 'REGISTER_NATIVES_IMPL': self.GetRegisterNativesImplString(),
585 'CLOSE_NAMESPACE': self.GetCloseNamespaceString(),
586 'HEADER_GUARD': self.header_guard,
587 }
588 return WrapOutput(template.substitute(values))
589
590 def GetClassPathDefinitionsString(self):
591 ret = []
592 ret += [self.GetClassPathDefinitions()]
593 return '\n'.join(ret)
594
595 def GetForwardDeclarationsString(self):
596 ret = []
597 for native in self.natives:
598 if native.type != 'method':
599 ret += [self.GetForwardDeclaration(native)]
600 return '\n'.join(ret)
601
602 def GetMethodStubsString(self):
603 ret = []
604 for native in self.natives:
605 if native.type == 'method':
606 ret += [self.GetNativeMethodStub(native)]
607 for called_by_native in self.called_by_natives:
608 ret += [self.GetCalledByNativeMethodStub(called_by_native)]
609 return '\n'.join(ret)
610
611 def GetKMethodsString(self, clazz):
612 ret = []
613 for native in self.natives:
614 if (native.java_class_name == clazz or
615 (not native.java_class_name and clazz == self.class_name)):
616 ret += [self.GetKMethodArrayEntry(native)]
617 return '\n'.join(ret)
618
bulach@chromium.org6079a072012-02-24 09:09:38 +0900619 def GetRegisterNativesImplString(self):
620 """Returns the implementation for RegisterNatives."""
621 template = Template("""\
622 static const JNINativeMethod kMethods${JAVA_CLASS}[] = {
623${KMETHODS}
624 };
625 const int kMethods${JAVA_CLASS}Size = arraysize(kMethods${JAVA_CLASS});
626
bulach@chromium.org1a8e1552012-03-02 00:51:51 +0900627 if (env->RegisterNatives(g_${JAVA_CLASS}_clazz,
bulach@chromium.org6079a072012-02-24 09:09:38 +0900628 kMethods${JAVA_CLASS},
629 kMethods${JAVA_CLASS}Size) < 0) {
630 LOG(ERROR) << "RegisterNatives failed in " << __FILE__;
631 return false;
632 }
633""")
bulach@chromium.org231cff62012-10-17 03:35:10 +0900634 ret = [self.GetFindClasses()]
bulach@chromium.org6079a072012-02-24 09:09:38 +0900635 all_classes = self.GetUniqueClasses(self.natives)
636 all_classes[self.class_name] = self.fully_qualified_class
637 for clazz in all_classes:
638 kmethods = self.GetKMethodsString(clazz)
639 if kmethods:
640 values = {'JAVA_CLASS': clazz,
641 'KMETHODS': kmethods}
642 ret += [template.substitute(values)]
643 if not ret: return ''
644 return '\n' + '\n'.join(ret)
645
646 def GetOpenNamespaceString(self):
647 if self.namespace:
bulach@chromium.org16dde202012-07-24 00:31:35 +0900648 all_namespaces = ['namespace %s {' % ns
649 for ns in self.namespace.split('::')]
650 return '\n'.join(all_namespaces)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900651 return ''
652
653 def GetCloseNamespaceString(self):
654 if self.namespace:
bulach@chromium.org16dde202012-07-24 00:31:35 +0900655 all_namespaces = ['} // namespace %s' % ns
656 for ns in self.namespace.split('::')]
657 all_namespaces.reverse()
658 return '\n'.join(all_namespaces) + '\n'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900659 return ''
660
661 def GetJNIFirstParam(self, native):
662 ret = []
663 if native.type == 'method':
664 ret = ['jobject obj']
665 elif native.type == 'function':
666 if native.static:
667 ret = ['jclass clazz']
668 else:
669 ret = ['jobject obj']
670 return ret
671
672 def GetParamsInDeclaration(self, native):
673 """Returns the params for the stub declaration.
674
675 Args:
676 native: the native dictionary describing the method.
677
678 Returns:
679 A string containing the params.
680 """
681 return ',\n '.join(self.GetJNIFirstParam(native) +
682 [JavaDataTypeToC(param.datatype) + ' ' +
683 param.name
684 for param in native.params])
685
686 def GetCalledByNativeParamsInDeclaration(self, called_by_native):
687 return ',\n '.join([JavaDataTypeToC(param.datatype) + ' ' +
688 param.name
689 for param in called_by_native.params])
690
691 def GetForwardDeclaration(self, native):
692 template = Template("""
pliard@chromium.org5fa58ac2012-07-02 19:31:31 +0900693static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS});
bulach@chromium.org6079a072012-02-24 09:09:38 +0900694""")
bulach@chromium.org16dde202012-07-24 00:31:35 +0900695 values = {'RETURN': JavaDataTypeToC(native.return_type),
bulach@chromium.org6079a072012-02-24 09:09:38 +0900696 'NAME': native.name,
697 'PARAMS': self.GetParamsInDeclaration(native)}
698 return template.substitute(values)
699
700 def GetNativeMethodStub(self, native):
701 """Returns stubs for native methods."""
702 template = Template("""\
703static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS_IN_DECLARATION}) {
704 DCHECK(${PARAM0_NAME}) << "${NAME}";
705 ${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME});
706 return native->${NAME}(env, obj${PARAMS_IN_CALL})${POST_CALL};
707}
708""")
709 params_for_call = ', '.join(p.name for p in native.params[1:])
710 if params_for_call:
711 params_for_call = ', ' + params_for_call
712
713 return_type = JavaDataTypeToC(native.return_type)
714 if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type):
715 scoped_return_type = 'ScopedJavaLocalRef<' + return_type + '>'
716 post_call = '.Release()'
717 else:
718 scoped_return_type = return_type
719 post_call = ''
720 values = {
721 'RETURN': return_type,
722 'SCOPED_RETURN': scoped_return_type,
723 'NAME': native.name,
724 'PARAMS_IN_DECLARATION': self.GetParamsInDeclaration(native),
725 'PARAM0_NAME': native.params[0].name,
726 'P0_TYPE': native.p0_type,
727 'PARAMS_IN_CALL': params_for_call,
728 'POST_CALL': post_call
729 }
730 return template.substitute(values)
731
732 def GetCalledByNativeMethodStub(self, called_by_native):
733 """Returns a string."""
734 function_signature_template = Template("""\
bulach@chromium.org0c6805b2012-09-28 21:34:33 +0900735static ${RETURN_TYPE} Java_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}(\
bulach@chromium.org6079a072012-02-24 09:09:38 +0900736JNIEnv* env${FIRST_PARAM_IN_DECLARATION}${PARAMS_IN_DECLARATION})""")
737 function_header_template = Template("""\
738${FUNCTION_SIGNATURE} {""")
739 function_header_with_unused_template = Template("""\
740${FUNCTION_SIGNATURE} __attribute__ ((unused));
741${FUNCTION_SIGNATURE} {""")
742 template = Template("""
bulach@chromium.org231cff62012-10-17 03:35:10 +0900743static base::subtle::AtomicWord g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = 0;
bulach@chromium.org6079a072012-02-24 09:09:38 +0900744${FUNCTION_HEADER}
745 /* Must call RegisterNativesImpl() */
bulach@chromium.org1a8e1552012-03-02 00:51:51 +0900746 DCHECK(g_${JAVA_CLASS}_clazz);
bulach@chromium.org231cff62012-10-17 03:35:10 +0900747 jmethodID method_id =
748 ${GET_METHOD_ID_IMPL}
bulach@chromium.org6079a072012-02-24 09:09:38 +0900749 ${RETURN_DECLARATION}
bulach@chromium.org31af7532012-09-24 20:01:41 +0900750 ${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL},
bulach@chromium.org231cff62012-10-17 03:35:10 +0900751 method_id${PARAMS_IN_CALL})${POST_CALL};
bulach@chromium.org6079a072012-02-24 09:09:38 +0900752 ${CHECK_EXCEPTION}
753 ${RETURN_CLAUSE}
754}""")
bulach@chromium.org31af7532012-09-24 20:01:41 +0900755 if called_by_native.static or called_by_native.is_constructor:
bulach@chromium.org6079a072012-02-24 09:09:38 +0900756 first_param_in_declaration = ''
bulach@chromium.org1a8e1552012-03-02 00:51:51 +0900757 first_param_in_call = ('g_%s_clazz' %
bulach@chromium.org6079a072012-02-24 09:09:38 +0900758 (called_by_native.java_class_name or
759 self.class_name))
760 else:
761 first_param_in_declaration = ', jobject obj'
762 first_param_in_call = 'obj'
763 params_in_declaration = self.GetCalledByNativeParamsInDeclaration(
764 called_by_native)
765 if params_in_declaration:
766 params_in_declaration = ', ' + params_in_declaration
767 params_for_call = ', '.join(param.name
768 for param in called_by_native.params)
769 if params_for_call:
770 params_for_call = ', ' + params_for_call
771 pre_call = ''
772 post_call = ''
bulach@chromium.org31af7532012-09-24 20:01:41 +0900773 if called_by_native.static_cast:
774 pre_call = 'static_cast<%s>(' % called_by_native.static_cast
bulach@chromium.org6079a072012-02-24 09:09:38 +0900775 post_call = ')'
776 check_exception = ''
777 if not called_by_native.unchecked:
778 check_exception = 'base::android::CheckException(env);'
779 return_type = JavaDataTypeToC(called_by_native.return_type)
780 return_declaration = ''
781 return_clause = ''
782 if return_type != 'void':
783 pre_call = ' ' + pre_call
784 return_declaration = return_type + ' ret ='
785 if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type):
786 return_type = 'ScopedJavaLocalRef<' + return_type + '>'
787 return_clause = 'return ' + return_type + '(env, ret);'
788 else:
789 return_clause = 'return ret;'
790 values = {
791 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
792 'METHOD': called_by_native.name,
793 'RETURN_TYPE': return_type,
794 'RETURN_DECLARATION': return_declaration,
795 'RETURN_CLAUSE': return_clause,
796 'FIRST_PARAM_IN_DECLARATION': first_param_in_declaration,
797 'PARAMS_IN_DECLARATION': params_in_declaration,
798 'STATIC': 'Static' if called_by_native.static else '',
799 'PRE_CALL': pre_call,
800 'POST_CALL': post_call,
bulach@chromium.org31af7532012-09-24 20:01:41 +0900801 'ENV_CALL': called_by_native.env_call,
bulach@chromium.org6079a072012-02-24 09:09:38 +0900802 'FIRST_PARAM_IN_CALL': first_param_in_call,
803 'PARAMS_IN_CALL': params_for_call,
804 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
805 'CHECK_EXCEPTION': check_exception,
bulach@chromium.org231cff62012-10-17 03:35:10 +0900806 'GET_METHOD_ID_IMPL': self.GetMethodIDImpl(called_by_native)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900807 }
808 values['FUNCTION_SIGNATURE'] = (
809 function_signature_template.substitute(values))
810 if called_by_native.system_class:
811 values['FUNCTION_HEADER'] = (
812 function_header_with_unused_template.substitute(values))
813 else:
814 values['FUNCTION_HEADER'] = function_header_template.substitute(values)
815 return template.substitute(values)
816
817 def GetKMethodArrayEntry(self, native):
818 template = Template("""\
819 { "native${NAME}", ${JNI_SIGNATURE}, reinterpret_cast<void*>(${NAME}) },""")
820 values = {'NAME': native.name,
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900821 'JNI_SIGNATURE': JniParams.Signature(native.params,
822 native.return_type,
823 True)}
bulach@chromium.org6079a072012-02-24 09:09:38 +0900824 return template.substitute(values)
825
826 def GetUniqueClasses(self, origin):
827 ret = {self.class_name: self.fully_qualified_class}
828 for entry in origin:
829 class_name = self.class_name
830 jni_class_path = self.fully_qualified_class
831 if entry.java_class_name:
832 class_name = entry.java_class_name
833 jni_class_path = self.fully_qualified_class + '$' + class_name
834 ret[class_name] = jni_class_path
835 return ret
836
837 def GetClassPathDefinitions(self):
838 """Returns the ClassPath constants."""
839 ret = []
840 template = Template("""\
bulach@chromium.org1a8e1552012-03-02 00:51:51 +0900841const char k${JAVA_CLASS}ClassPath[] = "${JNI_CLASS_PATH}";""")
bulach@chromium.org6079a072012-02-24 09:09:38 +0900842 native_classes = self.GetUniqueClasses(self.natives)
843 called_by_native_classes = self.GetUniqueClasses(self.called_by_natives)
844 all_classes = native_classes
845 all_classes.update(called_by_native_classes)
846 for clazz in all_classes:
847 values = {
848 'JAVA_CLASS': clazz,
849 'JNI_CLASS_PATH': all_classes[clazz],
850 }
851 ret += [template.substitute(values)]
852 ret += ''
853 for clazz in called_by_native_classes:
854 template = Template("""\
bulach@chromium.org1a8e1552012-03-02 00:51:51 +0900855// Leaking this jclass as we cannot use LazyInstance from some threads.
856jclass g_${JAVA_CLASS}_clazz = NULL;""")
bulach@chromium.org6079a072012-02-24 09:09:38 +0900857 values = {
858 'JAVA_CLASS': clazz,
859 }
860 ret += [template.substitute(values)]
861 return '\n'.join(ret)
862
863 def GetFindClasses(self):
864 """Returns the imlementation of FindClass for all known classes."""
865 template = Template("""\
bulach@chromium.org1a8e1552012-03-02 00:51:51 +0900866 g_${JAVA_CLASS}_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
867 base::android::GetUnscopedClass(env, k${JAVA_CLASS}ClassPath)));""")
bulach@chromium.org6079a072012-02-24 09:09:38 +0900868 ret = []
869 for clazz in self.GetUniqueClasses(self.called_by_natives):
870 values = {'JAVA_CLASS': clazz}
871 ret += [template.substitute(values)]
872 return '\n'.join(ret)
873
874 def GetMethodIDImpl(self, called_by_native):
875 """Returns the implementation of GetMethodID."""
876 template = Template("""\
bulach@chromium.org231cff62012-10-17 03:35:10 +0900877 base::android::MethodID::LazyGet<
878 base::android::MethodID::TYPE_${STATIC}>(
879 env, g_${JAVA_CLASS}_clazz,
880 "${JNI_NAME}",
881 ${JNI_SIGNATURE},
882 &g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME});
bulach@chromium.org6079a072012-02-24 09:09:38 +0900883""")
bulach@chromium.org31af7532012-09-24 20:01:41 +0900884 jni_name = called_by_native.name
885 jni_return_type = called_by_native.return_type
886 if called_by_native.is_constructor:
887 jni_name = '<init>'
888 jni_return_type = 'void'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900889 values = {
890 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
bulach@chromium.org31af7532012-09-24 20:01:41 +0900891 'JNI_NAME': jni_name,
bulach@chromium.org6079a072012-02-24 09:09:38 +0900892 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
bulach@chromium.org231cff62012-10-17 03:35:10 +0900893 'STATIC': 'STATIC' if called_by_native.static else 'INSTANCE',
bulach@chromium.orgc6b75842012-11-01 07:58:15 +0900894 'JNI_SIGNATURE': JniParams.Signature(called_by_native.params,
895 jni_return_type,
896 True)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900897 }
898 return template.substitute(values)
899
900
901def WrapOutput(output):
902 ret = []
903 for line in output.splitlines():
tedchoc@chromium.org72992182012-08-03 11:00:17 +0900904 # Do not wrap lines under 80 characters or preprocessor directives.
905 if len(line) < 80 or line.lstrip()[:1] == '#':
bulach@chromium.org1c775752012-06-22 19:03:16 +0900906 stripped = line.rstrip()
907 if len(ret) == 0 or len(ret[-1]) or len(stripped):
908 ret.append(stripped)
bulach@chromium.org6079a072012-02-24 09:09:38 +0900909 else:
910 first_line_indent = ' ' * (len(line) - len(line.lstrip()))
911 subsequent_indent = first_line_indent + ' ' * 4
912 if line.startswith('//'):
913 subsequent_indent = '//' + subsequent_indent
914 wrapper = textwrap.TextWrapper(width=80,
915 subsequent_indent=subsequent_indent,
916 break_long_words=False)
917 ret += [wrapped.rstrip() for wrapped in wrapper.wrap(line)]
918 ret += ['']
919 return '\n'.join(ret)
920
921
bulach@chromium.org4c248ed2012-07-20 05:02:55 +0900922def ExtractJarInputFile(jar_file, input_file, out_dir):
923 """Extracts input file from jar and returns the filename.
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +0900924
bulach@chromium.org4c248ed2012-07-20 05:02:55 +0900925 The input file is extracted to the same directory that the generated jni
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +0900926 headers will be placed in. This is passed as an argument to script.
927
928 Args:
bulach@chromium.org4c248ed2012-07-20 05:02:55 +0900929 jar_file: the jar file containing the input files to extract.
930 input_files: the list of files to extract from the jar file.
931 out_dir: the name of the directories to extract to.
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +0900932
933 Returns:
bulach@chromium.org4c248ed2012-07-20 05:02:55 +0900934 the name of extracted input file.
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +0900935 """
936 jar_file = zipfile.ZipFile(jar_file)
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +0900937
bulach@chromium.org4c248ed2012-07-20 05:02:55 +0900938 out_dir = os.path.join(out_dir, os.path.dirname(input_file))
939 if not os.path.exists(out_dir):
940 os.makedirs(out_dir)
941 extracted_file_name = os.path.join(out_dir, os.path.basename(input_file))
942 with open(extracted_file_name, 'w') as outfile:
943 outfile.write(jar_file.read(input_file))
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +0900944
bulach@chromium.org4c248ed2012-07-20 05:02:55 +0900945 return extracted_file_name
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +0900946
947
bulach@chromium.org4c248ed2012-07-20 05:02:55 +0900948def GenerateJNIHeader(input_file, output_file, namespace):
949 try:
950 if os.path.splitext(input_file)[1] == '.class':
951 jni_from_javap = JNIFromJavaP.CreateFromClass(input_file, namespace)
952 content = jni_from_javap.GetContent()
bulach@chromium.org6079a072012-02-24 09:09:38 +0900953 else:
bulach@chromium.org4c248ed2012-07-20 05:02:55 +0900954 jni_from_java_source = JNIFromJavaSource.CreateFromFile(input_file)
955 content = jni_from_java_source.GetContent()
956 except ParseError, e:
957 print e
958 sys.exit(1)
959 if output_file:
960 if not os.path.exists(os.path.dirname(os.path.abspath(output_file))):
961 os.makedirs(os.path.dirname(os.path.abspath(output_file)))
962 with file(output_file, 'w') as f:
963 f.write(content)
964 else:
965 print output
bulach@chromium.org6079a072012-02-24 09:09:38 +0900966
967
968def main(argv):
bulach@chromium.org4c248ed2012-07-20 05:02:55 +0900969 usage = """usage: %prog [OPTIONS]
bulach@chromium.org6079a072012-02-24 09:09:38 +0900970This script will parse the given java source code extracting the native
971declarations and print the header file to stdout (or a file).
972See SampleForTests.java for more details.
973 """
974 option_parser = optparse.OptionParser(usage=usage)
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +0900975 option_parser.add_option('-j', dest='jar_file',
976 help='Extract the list of input files from'
bulach@chromium.org7f85d462012-05-30 18:56:02 +0900977 ' a specified jar file.'
978 ' Uses javap to extract the methods from a'
bulach@chromium.org4c248ed2012-07-20 05:02:55 +0900979 ' pre-compiled class. --input should point'
bulach@chromium.org6079a072012-02-24 09:09:38 +0900980 ' to pre-compiled Java .class files.')
981 option_parser.add_option('-n', dest='namespace',
982 help='Uses as a namespace in the generated header,'
983 ' instead of the javap class name.')
bulach@chromium.org4c248ed2012-07-20 05:02:55 +0900984 option_parser.add_option('--input_file',
985 help='Single input file name. The output file name '
986 'will be derived from it. Must be used with '
987 '--output_dir.')
988 option_parser.add_option('--output_dir',
989 help='The output directory. Must be used with '
990 '--input')
bulach@chromium.org6079a072012-02-24 09:09:38 +0900991 options, args = option_parser.parse_args(argv)
yfriedman@chromium.org28e6a042012-05-24 02:53:53 +0900992 if options.jar_file:
bulach@chromium.org4c248ed2012-07-20 05:02:55 +0900993 input_file = ExtractJarInputFile(options.jar_file, options.input_file,
994 options.output_dir)
995 else:
996 input_file = options.input_file
997 output_file = None
998 if options.output_dir:
999 root_name = os.path.splitext(os.path.basename(input_file))[0]
1000 output_file = os.path.join(options.output_dir, root_name) + '_jni.h'
1001 GenerateJNIHeader(input_file, output_file, options.namespace)
bulach@chromium.org6079a072012-02-24 09:09:38 +09001002
1003
1004if __name__ == '__main__':
1005 sys.exit(main(sys.argv))