Commit 3ca66047 authored by bulach@chromium.org's avatar bulach@chromium.org

A few improvements to make jni_generator work outside of chromium.

* Print a usage message if called with no (valid) arguments.
* Determine the script name by stripping before either 'base' or
    'third_party', or strip nothing if neither name can be found in
    the path.
* Allow overriding the script name, and use this in the tests to be
  path-agnostic.

TBR=rmcilroy@chromium.org

Review URL: https://codereview.chromium.org/23702057

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@226503 0039d316-1c4b-4281-b951-d872f2087c98
parent 785b0af2
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
// This file is autogenerated by // This file is autogenerated by
// base/android/jni_generator/jni_generator_tests.py // base/android/jni_generator/jni_generator.py
// For // For
// org/chromium/example/jni_generator/SampleForTests // org/chromium/example/jni_generator/SampleForTests
......
...@@ -449,9 +449,9 @@ def ExtractCalledByNatives(contents): ...@@ -449,9 +449,9 @@ def ExtractCalledByNatives(contents):
class JNIFromJavaP(object): class JNIFromJavaP(object):
"""Uses 'javap' to parse a .class file and generate the JNI header file.""" """Uses 'javap' to parse a .class file and generate the JNI header file."""
def __init__(self, contents, namespace): def __init__(self, contents, options):
self.contents = contents self.contents = contents
self.namespace = namespace self.namespace = options.namespace
self.fully_qualified_class = re.match( self.fully_qualified_class = re.match(
'.*?(class|interface) (?P<class_name>.*?)( |{)', '.*?(class|interface) (?P<class_name>.*?)( |{)',
contents[1]).group('class_name') contents[1]).group('class_name')
...@@ -496,27 +496,28 @@ class JNIFromJavaP(object): ...@@ -496,27 +496,28 @@ class JNIFromJavaP(object):
is_constructor=True)] is_constructor=True)]
self.called_by_natives = MangleCalledByNatives(self.called_by_natives) self.called_by_natives = MangleCalledByNatives(self.called_by_natives)
self.inl_header_file_generator = InlHeaderFileGenerator( self.inl_header_file_generator = InlHeaderFileGenerator(
self.namespace, self.fully_qualified_class, [], self.called_by_natives) self.namespace, self.fully_qualified_class, [],
self.called_by_natives, options)
def GetContent(self): def GetContent(self):
return self.inl_header_file_generator.GetContent() return self.inl_header_file_generator.GetContent()
@staticmethod @staticmethod
def CreateFromClass(class_file, namespace): def CreateFromClass(class_file, options):
class_name = os.path.splitext(os.path.basename(class_file))[0] class_name = os.path.splitext(os.path.basename(class_file))[0]
p = subprocess.Popen(args=['javap', class_name], p = subprocess.Popen(args=['javap', class_name],
cwd=os.path.dirname(class_file), cwd=os.path.dirname(class_file),
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE) stderr=subprocess.PIPE)
stdout, _ = p.communicate() stdout, _ = p.communicate()
jni_from_javap = JNIFromJavaP(stdout.split('\n'), namespace) jni_from_javap = JNIFromJavaP(stdout.split('\n'), options)
return jni_from_javap return jni_from_javap
class JNIFromJavaSource(object): class JNIFromJavaSource(object):
"""Uses the given java source file to generate the JNI header file.""" """Uses the given java source file to generate the JNI header file."""
def __init__(self, contents, fully_qualified_class): def __init__(self, contents, fully_qualified_class, options):
contents = self._RemoveComments(contents) contents = self._RemoveComments(contents)
JniParams.SetFullyQualifiedClass(fully_qualified_class) JniParams.SetFullyQualifiedClass(fully_qualified_class)
JniParams.ExtractImportsAndInnerClasses(contents) JniParams.ExtractImportsAndInnerClasses(contents)
...@@ -527,7 +528,8 @@ class JNIFromJavaSource(object): ...@@ -527,7 +528,8 @@ class JNIFromJavaSource(object):
raise SyntaxError('Unable to find any JNI methods for %s.' % raise SyntaxError('Unable to find any JNI methods for %s.' %
fully_qualified_class) fully_qualified_class)
inl_header_file_generator = InlHeaderFileGenerator( inl_header_file_generator = InlHeaderFileGenerator(
jni_namespace, fully_qualified_class, natives, called_by_natives) jni_namespace, fully_qualified_class, natives, called_by_natives,
options)
self.content = inl_header_file_generator.GetContent() self.content = inl_header_file_generator.GetContent()
def _RemoveComments(self, contents): def _RemoveComments(self, contents):
...@@ -552,24 +554,25 @@ class JNIFromJavaSource(object): ...@@ -552,24 +554,25 @@ class JNIFromJavaSource(object):
return self.content return self.content
@staticmethod @staticmethod
def CreateFromFile(java_file_name): def CreateFromFile(java_file_name, options):
contents = file(java_file_name).read() contents = file(java_file_name).read()
fully_qualified_class = ExtractFullyQualifiedJavaClassName(java_file_name, fully_qualified_class = ExtractFullyQualifiedJavaClassName(java_file_name,
contents) contents)
return JNIFromJavaSource(contents, fully_qualified_class) return JNIFromJavaSource(contents, fully_qualified_class, options)
class InlHeaderFileGenerator(object): class InlHeaderFileGenerator(object):
"""Generates an inline header file for JNI integration.""" """Generates an inline header file for JNI integration."""
def __init__(self, namespace, fully_qualified_class, natives, def __init__(self, namespace, fully_qualified_class, natives,
called_by_natives): called_by_natives, options):
self.namespace = namespace self.namespace = namespace
self.fully_qualified_class = fully_qualified_class self.fully_qualified_class = fully_qualified_class
self.class_name = self.fully_qualified_class.split('/')[-1] self.class_name = self.fully_qualified_class.split('/')[-1]
self.natives = natives self.natives = natives
self.called_by_natives = called_by_natives self.called_by_natives = called_by_natives
self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI' self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI'
self.script_name = options.script_name
def GetContent(self): def GetContent(self):
"""Returns the content of the JNI binding file.""" """Returns the content of the JNI binding file."""
...@@ -616,11 +619,8 @@ $REGISTER_NATIVES_IMPL ...@@ -616,11 +619,8 @@ $REGISTER_NATIVES_IMPL
$CLOSE_NAMESPACE $CLOSE_NAMESPACE
#endif // ${HEADER_GUARD} #endif // ${HEADER_GUARD}
""") """)
script_components = os.path.abspath(sys.argv[0]).split(os.path.sep)
base_index = script_components.index('base')
script_name = os.sep.join(script_components[base_index:])
values = { values = {
'SCRIPT_NAME': script_name, 'SCRIPT_NAME': self.script_name,
'FULLY_QUALIFIED_CLASS': self.fully_qualified_class, 'FULLY_QUALIFIED_CLASS': self.fully_qualified_class,
'CLASS_PATH_DEFINITIONS': self.GetClassPathDefinitionsString(), 'CLASS_PATH_DEFINITIONS': self.GetClassPathDefinitionsString(),
'FORWARD_DECLARATIONS': self.GetForwardDeclarationsString(), 'FORWARD_DECLARATIONS': self.GetForwardDeclarationsString(),
...@@ -993,13 +993,14 @@ def ExtractJarInputFile(jar_file, input_file, out_dir): ...@@ -993,13 +993,14 @@ def ExtractJarInputFile(jar_file, input_file, out_dir):
return extracted_file_name return extracted_file_name
def GenerateJNIHeader(input_file, output_file, namespace, skip_if_same): def GenerateJNIHeader(input_file, output_file, options):
try: try:
if os.path.splitext(input_file)[1] == '.class': if os.path.splitext(input_file)[1] == '.class':
jni_from_javap = JNIFromJavaP.CreateFromClass(input_file, namespace) jni_from_javap = JNIFromJavaP.CreateFromClass(input_file, options)
content = jni_from_javap.GetContent() content = jni_from_javap.GetContent()
else: else:
jni_from_java_source = JNIFromJavaSource.CreateFromFile(input_file) jni_from_java_source = JNIFromJavaSource.CreateFromFile(
input_file, options)
content = jni_from_java_source.GetContent() content = jni_from_java_source.GetContent()
except ParseError, e: except ParseError, e:
print e print e
...@@ -1007,7 +1008,7 @@ def GenerateJNIHeader(input_file, output_file, namespace, skip_if_same): ...@@ -1007,7 +1008,7 @@ def GenerateJNIHeader(input_file, output_file, namespace, skip_if_same):
if output_file: if output_file:
if not os.path.exists(os.path.dirname(os.path.abspath(output_file))): if not os.path.exists(os.path.dirname(os.path.abspath(output_file))):
os.makedirs(os.path.dirname(os.path.abspath(output_file))) os.makedirs(os.path.dirname(os.path.abspath(output_file)))
if skip_if_same and os.path.exists(output_file): if options.optimize_generation and os.path.exists(output_file):
with file(output_file, 'r') as f: with file(output_file, 'r') as f:
existing_content = f.read() existing_content = f.read()
if existing_content == content: if existing_content == content:
...@@ -1018,6 +1019,16 @@ def GenerateJNIHeader(input_file, output_file, namespace, skip_if_same): ...@@ -1018,6 +1019,16 @@ def GenerateJNIHeader(input_file, output_file, namespace, skip_if_same):
print output print output
def GetScriptName():
script_components = os.path.abspath(sys.argv[0]).split(os.path.sep)
base_index = 0
for idx, value in enumerate(script_components):
if value == 'base' or value == 'third_party':
base_index = idx
break
return os.sep.join(script_components[base_index:])
def main(argv): def main(argv):
usage = """usage: %prog [OPTIONS] usage = """usage: %prog [OPTIONS]
This script will parse the given java source code extracting the native This script will parse the given java source code extracting the native
...@@ -1047,12 +1058,19 @@ See SampleForTests.java for more details. ...@@ -1047,12 +1058,19 @@ See SampleForTests.java for more details.
'not changed.') 'not changed.')
option_parser.add_option('--jarjar', option_parser.add_option('--jarjar',
help='Path to optional jarjar rules file.') help='Path to optional jarjar rules file.')
option_parser.add_option('--script_name', default=GetScriptName(),
help='The name of this script in the generated '
'header.')
options, args = option_parser.parse_args(argv) options, args = option_parser.parse_args(argv)
if options.jar_file: if options.jar_file:
input_file = ExtractJarInputFile(options.jar_file, options.input_file, input_file = ExtractJarInputFile(options.jar_file, options.input_file,
options.output_dir) options.output_dir)
else: elif options.input_file:
input_file = options.input_file input_file = options.input_file
else:
option_parser.print_help()
print '\nError: Must specify --jar_file or --input_file.'
return 1
output_file = None output_file = None
if options.output_dir: if options.output_dir:
root_name = os.path.splitext(os.path.basename(input_file))[0] root_name = os.path.splitext(os.path.basename(input_file))[0]
...@@ -1060,8 +1078,7 @@ See SampleForTests.java for more details. ...@@ -1060,8 +1078,7 @@ See SampleForTests.java for more details.
if options.jarjar: if options.jarjar:
with open(options.jarjar) as f: with open(options.jarjar) as f:
JniParams.SetJarJarMappings(f.read()) JniParams.SetJarJarMappings(f.read())
GenerateJNIHeader(input_file, output_file, options.namespace, GenerateJNIHeader(input_file, output_file, options)
options.optimize_generation)
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -19,6 +19,17 @@ import jni_generator ...@@ -19,6 +19,17 @@ import jni_generator
from jni_generator import CalledByNative, JniParams, NativeMethod, Param from jni_generator import CalledByNative, JniParams, NativeMethod, Param
SCRIPT_NAME = 'base/android/jni_generator/jni_generator.py'
class TestOptions(object):
"""The mock options object which is passed to the jni_generator.py script."""
def __init__(self):
self.namespace = None
self.script_name = SCRIPT_NAME
class TestGenerator(unittest.TestCase): class TestGenerator(unittest.TestCase):
def assertObjEquals(self, first, second): def assertObjEquals(self, first, second):
dict_first = first.__dict__ dict_first = first.__dict__
...@@ -206,14 +217,14 @@ class TestGenerator(unittest.TestCase): ...@@ -206,14 +217,14 @@ class TestGenerator(unittest.TestCase):
] ]
self.assertListEquals(golden_natives, natives) self.assertListEquals(golden_natives, natives)
h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni', h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni',
natives, []) natives, [], TestOptions())
golden_content = """\ golden_content = """\
// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// This file is autogenerated by // This file is autogenerated by
// base/android/jni_generator/jni_generator_tests.py // base/android/jni_generator/jni_generator.py
// For // For
// org/chromium/TestJni // org/chromium/TestJni
...@@ -443,14 +454,14 @@ static bool RegisterNativesImpl(JNIEnv* env) { ...@@ -443,14 +454,14 @@ static bool RegisterNativesImpl(JNIEnv* env) {
] ]
self.assertListEquals(golden_natives, natives) self.assertListEquals(golden_natives, natives)
h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni', h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni',
natives, []) natives, [], TestOptions())
golden_content = """\ golden_content = """\
// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// This file is autogenerated by // This file is autogenerated by
// base/android/jni_generator/jni_generator_tests.py // base/android/jni_generator/jni_generator.py
// For // For
// org/chromium/TestJni // org/chromium/TestJni
...@@ -530,14 +541,14 @@ static bool RegisterNativesImpl(JNIEnv* env) { ...@@ -530,14 +541,14 @@ static bool RegisterNativesImpl(JNIEnv* env) {
] ]
self.assertListEquals(golden_natives, natives) self.assertListEquals(golden_natives, natives)
h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni', h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni',
natives, []) natives, [], TestOptions())
golden_content = """\ golden_content = """\
// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// This file is autogenerated by // This file is autogenerated by
// base/android/jni_generator/jni_generator_tests.py // base/android/jni_generator/jni_generator.py
// For // For
// org/chromium/TestJni // org/chromium/TestJni
...@@ -636,14 +647,14 @@ static bool RegisterNativesImpl(JNIEnv* env) { ...@@ -636,14 +647,14 @@ static bool RegisterNativesImpl(JNIEnv* env) {
] ]
self.assertListEquals(golden_natives, natives) self.assertListEquals(golden_natives, natives)
h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni', h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni',
natives, []) natives, [], TestOptions())
golden_content = """\ golden_content = """\
// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// This file is autogenerated by // This file is autogenerated by
// base/android/jni_generator/jni_generator_tests.py // base/android/jni_generator/jni_generator.py
// For // For
// org/chromium/TestJni // org/chromium/TestJni
...@@ -1024,14 +1035,15 @@ static bool RegisterNativesImpl(JNIEnv* env) { ...@@ -1024,14 +1035,15 @@ static bool RegisterNativesImpl(JNIEnv* env) {
] ]
self.assertListEquals(golden_called_by_natives, called_by_natives) self.assertListEquals(golden_called_by_natives, called_by_natives)
h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni', h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni',
[], called_by_natives) [], called_by_natives,
TestOptions())
golden_content = """\ golden_content = """\
// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// This file is autogenerated by // This file is autogenerated by
// base/android/jni_generator/jni_generator_tests.py // base/android/jni_generator/jni_generator.py
// For // For
// org/chromium/TestJni // org/chromium/TestJni
...@@ -1573,7 +1585,8 @@ public abstract class java.util.HashSet<T> extends java.util.AbstractSet<E> ...@@ -1573,7 +1585,8 @@ public abstract class java.util.HashSet<T> extends java.util.AbstractSet<E>
public void dummy(); public void dummy();
} }
""" """
jni_from_javap = jni_generator.JNIFromJavaP(contents.split('\n'), None) jni_from_javap = jni_generator.JNIFromJavaP(contents.split('\n'),
TestOptions())
self.assertEquals(1, len(jni_from_javap.called_by_natives)) self.assertEquals(1, len(jni_from_javap.called_by_natives))
golden_content = """\ golden_content = """\
// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
...@@ -1581,7 +1594,7 @@ public abstract class java.util.HashSet<T> extends java.util.AbstractSet<E> ...@@ -1581,7 +1594,7 @@ public abstract class java.util.HashSet<T> extends java.util.AbstractSet<E>
// found in the LICENSE file. // found in the LICENSE file.
// This file is autogenerated by // This file is autogenerated by
// base/android/jni_generator/jni_generator_tests.py // base/android/jni_generator/jni_generator.py
// For // For
// java/util/HashSet // java/util/HashSet
...@@ -1661,7 +1674,8 @@ public abstract class java.io.InputStream extends java.lang.Object ...@@ -1661,7 +1674,8 @@ public abstract class java.io.InputStream extends java.lang.Object
public long skip(long) throws java.io.IOException; public long skip(long) throws java.io.IOException;
} }
""" """
jni_from_javap = jni_generator.JNIFromJavaP(contents.split('\n'), None) jni_from_javap = jni_generator.JNIFromJavaP(contents.split('\n'),
TestOptions())
self.assertEquals(10, len(jni_from_javap.called_by_natives)) self.assertEquals(10, len(jni_from_javap.called_by_natives))
golden_content = """\ golden_content = """\
// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
...@@ -1669,7 +1683,7 @@ public abstract class java.io.InputStream extends java.lang.Object ...@@ -1669,7 +1683,7 @@ public abstract class java.io.InputStream extends java.lang.Object
// found in the LICENSE file. // found in the LICENSE file.
// This file is autogenerated by // This file is autogenerated by
// base/android/jni_generator/jni_generator_tests.py // base/android/jni_generator/jni_generator.py
// For // For
// java/io/InputStream // java/io/InputStream
...@@ -1975,7 +1989,8 @@ static bool RegisterNativesImpl(JNIEnv* env) { ...@@ -1975,7 +1989,8 @@ static bool RegisterNativesImpl(JNIEnv* env) {
private native void nativeSyncSetupEnded( private native void nativeSyncSetupEnded(
int nativeAndroidSyncSetupFlowHandler); int nativeAndroidSyncSetupFlowHandler);
""" """
jni_from_java = jni_generator.JNIFromJavaSource(test_data, 'foo/bar') jni_from_java = jni_generator.JNIFromJavaSource(
test_data, 'foo/bar', TestOptions())
def testRaisesOnNonJNIMethod(self): def testRaisesOnNonJNIMethod(self):
test_data = """ test_data = """
...@@ -1986,7 +2001,7 @@ static bool RegisterNativesImpl(JNIEnv* env) { ...@@ -1986,7 +2001,7 @@ static bool RegisterNativesImpl(JNIEnv* env) {
""" """
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
jni_generator.JNIFromJavaSource, jni_generator.JNIFromJavaSource,
test_data, 'foo/bar') test_data, 'foo/bar', TestOptions())
def testJniSelfDocumentingExample(self): def testJniSelfDocumentingExample(self):
script_dir = os.path.dirname(sys.argv[0]) script_dir = os.path.dirname(sys.argv[0])
...@@ -1996,7 +2011,8 @@ static bool RegisterNativesImpl(JNIEnv* env) { ...@@ -1996,7 +2011,8 @@ static bool RegisterNativesImpl(JNIEnv* env) {
golden_content = file(os.path.join(script_dir, golden_content = file(os.path.join(script_dir,
'golden_sample_for_tests_jni.h')).read() 'golden_sample_for_tests_jni.h')).read()
jni_from_java = jni_generator.JNIFromJavaSource( jni_from_java = jni_generator.JNIFromJavaSource(
content, 'org/chromium/example/jni_generator/SampleForTests') content, 'org/chromium/example/jni_generator/SampleForTests',
TestOptions())
self.assertTextEquals(golden_content, jni_from_java.GetContent()) self.assertTextEquals(golden_content, jni_from_java.GetContent())
def testNoWrappingPreprocessorLines(self): def testNoWrappingPreprocessorLines(self):
...@@ -2009,7 +2025,8 @@ static bool RegisterNativesImpl(JNIEnv* env) { ...@@ -2009,7 +2025,8 @@ static bool RegisterNativesImpl(JNIEnv* env) {
""" """
jni_from_java = jni_generator.JNIFromJavaSource( jni_from_java = jni_generator.JNIFromJavaSource(
test_data, ('com/google/lookhowextremelylongiam/snarf/' test_data, ('com/google/lookhowextremelylongiam/snarf/'
'icankeepthisupallday/ReallyLongClassNamesAreAllTheRage')) 'icankeepthisupallday/ReallyLongClassNamesAreAllTheRage'),
TestOptions())
jni_lines = jni_from_java.GetContent().split('\n') jni_lines = jni_from_java.GetContent().split('\n')
line = filter(lambda line: line.lstrip().startswith('#ifndef'), line = filter(lambda line: line.lstrip().startswith('#ifndef'),
jni_lines)[0] jni_lines)[0]
...@@ -2030,7 +2047,7 @@ static bool RegisterNativesImpl(JNIEnv* env) { ...@@ -2030,7 +2047,7 @@ static bool RegisterNativesImpl(JNIEnv* env) {
"""rule org.chromium.example.** com.test.@1 """rule org.chromium.example.** com.test.@1
rule org.chromium.example2.** org.test2.@0""") rule org.chromium.example2.** org.test2.@0""")
jni_from_java = jni_generator.JNIFromJavaSource( jni_from_java = jni_generator.JNIFromJavaSource(
test_data, 'org/chromium/example/jni_generator/Example') test_data, 'org/chromium/example/jni_generator/Example', TestOptions())
jni_generator.JniParams.SetJarJarMappings('') jni_generator.JniParams.SetJarJarMappings('')
golden_content = """\ golden_content = """\
// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
...@@ -2038,7 +2055,7 @@ static bool RegisterNativesImpl(JNIEnv* env) { ...@@ -2038,7 +2055,7 @@ static bool RegisterNativesImpl(JNIEnv* env) {
// found in the LICENSE file. // found in the LICENSE file.
// This file is autogenerated by // This file is autogenerated by
// base/android/jni_generator/jni_generator_tests.py // base/android/jni_generator/jni_generator.py
// For // For
// org/chromium/example/jni_generator/Example // org/chromium/example/jni_generator/Example
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment