Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(394)

Side by Side Diff: base/android/jni_generator/jni_generator.py

Issue 23702057: Android: improvements to make jni_generator work outside of chromium (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Comment Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # 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 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Extracts native methods from a Java file and generates the JNI bindings. 6 """Extracts native methods from a Java file and generates the JNI bindings.
7 If you change this, please run and update the tests.""" 7 If you change this, please run and update the tests."""
8 8
9 import collections 9 import collections
10 import errno 10 import errno
(...skipping 431 matching lines...) Expand 10 before | Expand all | Expand 10 after
442 for line1, line2 in zip(unmatched_lines, unmatched_lines[1:]): 442 for line1, line2 in zip(unmatched_lines, unmatched_lines[1:]):
443 if '@CalledByNative' in line1: 443 if '@CalledByNative' in line1:
444 raise ParseError('could not parse @CalledByNative method signature', 444 raise ParseError('could not parse @CalledByNative method signature',
445 line1, line2) 445 line1, line2)
446 return MangleCalledByNatives(called_by_natives) 446 return MangleCalledByNatives(called_by_natives)
447 447
448 448
449 class JNIFromJavaP(object): 449 class JNIFromJavaP(object):
450 """Uses 'javap' to parse a .class file and generate the JNI header file.""" 450 """Uses 'javap' to parse a .class file and generate the JNI header file."""
451 451
452 def __init__(self, contents, namespace): 452 def __init__(self, contents, options):
453 self.contents = contents 453 self.contents = contents
454 self.namespace = namespace 454 self.namespace = options.namespace
455 self.fully_qualified_class = re.match( 455 self.fully_qualified_class = re.match(
456 '.*?(class|interface) (?P<class_name>.*?)( |{)', 456 '.*?(class|interface) (?P<class_name>.*?)( |{)',
457 contents[1]).group('class_name') 457 contents[1]).group('class_name')
458 self.fully_qualified_class = self.fully_qualified_class.replace('.', '/') 458 self.fully_qualified_class = self.fully_qualified_class.replace('.', '/')
459 # Java 7's javap includes type parameters in output, like HashSet<T>. Strip 459 # Java 7's javap includes type parameters in output, like HashSet<T>. Strip
460 # away the <...> and use the raw class name that Java 6 would've given us. 460 # away the <...> and use the raw class name that Java 6 would've given us.
461 self.fully_qualified_class = self.fully_qualified_class.split('<', 1)[0] 461 self.fully_qualified_class = self.fully_qualified_class.split('<', 1)[0]
462 JniParams.SetFullyQualifiedClass(self.fully_qualified_class) 462 JniParams.SetFullyQualifiedClass(self.fully_qualified_class)
463 self.java_class_name = self.fully_qualified_class.split('/')[-1] 463 self.java_class_name = self.fully_qualified_class.split('/')[-1]
464 if not self.namespace: 464 if not self.namespace:
(...skipping 24 matching lines...) Expand all
489 system_class=True, 489 system_class=True,
490 unchecked=False, 490 unchecked=False,
491 static=False, 491 static=False,
492 java_class_name='', 492 java_class_name='',
493 return_type=self.fully_qualified_class, 493 return_type=self.fully_qualified_class,
494 name='Constructor', 494 name='Constructor',
495 params=JniParams.Parse(match.group('params').replace('.', '/')), 495 params=JniParams.Parse(match.group('params').replace('.', '/')),
496 is_constructor=True)] 496 is_constructor=True)]
497 self.called_by_natives = MangleCalledByNatives(self.called_by_natives) 497 self.called_by_natives = MangleCalledByNatives(self.called_by_natives)
498 self.inl_header_file_generator = InlHeaderFileGenerator( 498 self.inl_header_file_generator = InlHeaderFileGenerator(
499 self.namespace, self.fully_qualified_class, [], self.called_by_natives) 499 self.namespace, self.fully_qualified_class, [],
500 self.called_by_natives, options)
500 501
501 def GetContent(self): 502 def GetContent(self):
502 return self.inl_header_file_generator.GetContent() 503 return self.inl_header_file_generator.GetContent()
503 504
504 @staticmethod 505 @staticmethod
505 def CreateFromClass(class_file, namespace): 506 def CreateFromClass(class_file, options):
506 class_name = os.path.splitext(os.path.basename(class_file))[0] 507 class_name = os.path.splitext(os.path.basename(class_file))[0]
507 p = subprocess.Popen(args=['javap', class_name], 508 p = subprocess.Popen(args=['javap', class_name],
508 cwd=os.path.dirname(class_file), 509 cwd=os.path.dirname(class_file),
509 stdout=subprocess.PIPE, 510 stdout=subprocess.PIPE,
510 stderr=subprocess.PIPE) 511 stderr=subprocess.PIPE)
511 stdout, _ = p.communicate() 512 stdout, _ = p.communicate()
512 jni_from_javap = JNIFromJavaP(stdout.split('\n'), namespace) 513 jni_from_javap = JNIFromJavaP(stdout.split('\n'), options)
513 return jni_from_javap 514 return jni_from_javap
514 515
515 516
516 class JNIFromJavaSource(object): 517 class JNIFromJavaSource(object):
517 """Uses the given java source file to generate the JNI header file.""" 518 """Uses the given java source file to generate the JNI header file."""
518 519
519 def __init__(self, contents, fully_qualified_class): 520 def __init__(self, contents, fully_qualified_class, options):
520 contents = self._RemoveComments(contents) 521 contents = self._RemoveComments(contents)
521 JniParams.SetFullyQualifiedClass(fully_qualified_class) 522 JniParams.SetFullyQualifiedClass(fully_qualified_class)
522 JniParams.ExtractImportsAndInnerClasses(contents) 523 JniParams.ExtractImportsAndInnerClasses(contents)
523 jni_namespace = ExtractJNINamespace(contents) 524 jni_namespace = ExtractJNINamespace(contents)
524 natives = ExtractNatives(contents) 525 natives = ExtractNatives(contents)
525 called_by_natives = ExtractCalledByNatives(contents) 526 called_by_natives = ExtractCalledByNatives(contents)
526 if len(natives) == 0 and len(called_by_natives) == 0: 527 if len(natives) == 0 and len(called_by_natives) == 0:
527 raise SyntaxError('Unable to find any JNI methods for %s.' % 528 raise SyntaxError('Unable to find any JNI methods for %s.' %
528 fully_qualified_class) 529 fully_qualified_class)
529 inl_header_file_generator = InlHeaderFileGenerator( 530 inl_header_file_generator = InlHeaderFileGenerator(
530 jni_namespace, fully_qualified_class, natives, called_by_natives) 531 jni_namespace, fully_qualified_class, natives, called_by_natives,
532 options)
531 self.content = inl_header_file_generator.GetContent() 533 self.content = inl_header_file_generator.GetContent()
532 534
533 def _RemoveComments(self, contents): 535 def _RemoveComments(self, contents):
534 # We need to support both inline and block comments, and we need to handle 536 # We need to support both inline and block comments, and we need to handle
535 # strings that contain '//' or '/*'. Rather than trying to do all that with 537 # strings that contain '//' or '/*'. Rather than trying to do all that with
536 # regexps, we just pipe the contents through the C preprocessor. We tell cpp 538 # regexps, we just pipe the contents through the C preprocessor. We tell cpp
537 # the file has already been preprocessed, so it just removes comments and 539 # the file has already been preprocessed, so it just removes comments and
538 # doesn't try to parse #include, #pragma etc. 540 # doesn't try to parse #include, #pragma etc.
539 # 541 #
540 # TODO(husky): This is a bit hacky. It would be cleaner to use a real Java 542 # TODO(husky): This is a bit hacky. It would be cleaner to use a real Java
541 # parser. Maybe we could ditch JNIFromJavaSource and just always use 543 # parser. Maybe we could ditch JNIFromJavaSource and just always use
542 # JNIFromJavaP; or maybe we could rewrite this script in Java and use APT. 544 # JNIFromJavaP; or maybe we could rewrite this script in Java and use APT.
543 # http://code.google.com/p/chromium/issues/detail?id=138941 545 # http://code.google.com/p/chromium/issues/detail?id=138941
544 p = subprocess.Popen(args=['cpp', '-fpreprocessed'], 546 p = subprocess.Popen(args=['cpp', '-fpreprocessed'],
545 stdin=subprocess.PIPE, 547 stdin=subprocess.PIPE,
546 stdout=subprocess.PIPE, 548 stdout=subprocess.PIPE,
547 stderr=subprocess.PIPE) 549 stderr=subprocess.PIPE)
548 stdout, _ = p.communicate(contents) 550 stdout, _ = p.communicate(contents)
549 return stdout 551 return stdout
550 552
551 def GetContent(self): 553 def GetContent(self):
552 return self.content 554 return self.content
553 555
554 @staticmethod 556 @staticmethod
555 def CreateFromFile(java_file_name): 557 def CreateFromFile(java_file_name, options):
556 contents = file(java_file_name).read() 558 contents = file(java_file_name).read()
557 fully_qualified_class = ExtractFullyQualifiedJavaClassName(java_file_name, 559 fully_qualified_class = ExtractFullyQualifiedJavaClassName(java_file_name,
558 contents) 560 contents)
559 return JNIFromJavaSource(contents, fully_qualified_class) 561 return JNIFromJavaSource(contents, fully_qualified_class, options)
560 562
561 563
562 class InlHeaderFileGenerator(object): 564 class InlHeaderFileGenerator(object):
563 """Generates an inline header file for JNI integration.""" 565 """Generates an inline header file for JNI integration."""
564 566
565 def __init__(self, namespace, fully_qualified_class, natives, 567 def __init__(self, namespace, fully_qualified_class, natives,
566 called_by_natives): 568 called_by_natives, options):
567 self.namespace = namespace 569 self.namespace = namespace
568 self.fully_qualified_class = fully_qualified_class 570 self.fully_qualified_class = fully_qualified_class
569 self.class_name = self.fully_qualified_class.split('/')[-1] 571 self.class_name = self.fully_qualified_class.split('/')[-1]
570 self.natives = natives 572 self.natives = natives
571 self.called_by_natives = called_by_natives 573 self.called_by_natives = called_by_natives
572 self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI' 574 self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI'
575 self.script_name = options.script_name
573 576
574 def GetContent(self): 577 def GetContent(self):
575 """Returns the content of the JNI binding file.""" 578 """Returns the content of the JNI binding file."""
576 template = Template("""\ 579 template = Template("""\
577 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 580 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
578 // Use of this source code is governed by a BSD-style license that can be 581 // Use of this source code is governed by a BSD-style license that can be
579 // found in the LICENSE file. 582 // found in the LICENSE file.
580 583
581 584
582 // This file is autogenerated by 585 // This file is autogenerated by
(...skipping 26 matching lines...) Expand all
609 612
610 // Step 3: RegisterNatives. 613 // Step 3: RegisterNatives.
611 614
612 static bool RegisterNativesImpl(JNIEnv* env) { 615 static bool RegisterNativesImpl(JNIEnv* env) {
613 $REGISTER_NATIVES_IMPL 616 $REGISTER_NATIVES_IMPL
614 return true; 617 return true;
615 } 618 }
616 $CLOSE_NAMESPACE 619 $CLOSE_NAMESPACE
617 #endif // ${HEADER_GUARD} 620 #endif // ${HEADER_GUARD}
618 """) 621 """)
619 script_components = os.path.abspath(sys.argv[0]).split(os.path.sep)
620 base_index = script_components.index('base')
621 script_name = os.sep.join(script_components[base_index:])
622 values = { 622 values = {
623 'SCRIPT_NAME': script_name, 623 'SCRIPT_NAME': self.script_name,
624 'FULLY_QUALIFIED_CLASS': self.fully_qualified_class, 624 'FULLY_QUALIFIED_CLASS': self.fully_qualified_class,
625 'CLASS_PATH_DEFINITIONS': self.GetClassPathDefinitionsString(), 625 'CLASS_PATH_DEFINITIONS': self.GetClassPathDefinitionsString(),
626 'FORWARD_DECLARATIONS': self.GetForwardDeclarationsString(), 626 'FORWARD_DECLARATIONS': self.GetForwardDeclarationsString(),
627 'METHOD_STUBS': self.GetMethodStubsString(), 627 'METHOD_STUBS': self.GetMethodStubsString(),
628 'OPEN_NAMESPACE': self.GetOpenNamespaceString(), 628 'OPEN_NAMESPACE': self.GetOpenNamespaceString(),
629 'REGISTER_NATIVES_IMPL': self.GetRegisterNativesImplString(), 629 'REGISTER_NATIVES_IMPL': self.GetRegisterNativesImplString(),
630 'CLOSE_NAMESPACE': self.GetCloseNamespaceString(), 630 'CLOSE_NAMESPACE': self.GetCloseNamespaceString(),
631 'HEADER_GUARD': self.header_guard, 631 'HEADER_GUARD': self.header_guard,
632 } 632 }
633 return WrapOutput(template.substitute(values)) 633 return WrapOutput(template.substitute(values))
(...skipping 352 matching lines...) Expand 10 before | Expand all | Expand 10 after
986 except OSError as e: 986 except OSError as e:
987 if e.errno != errno.EEXIST: 987 if e.errno != errno.EEXIST:
988 raise 988 raise
989 extracted_file_name = os.path.join(out_dir, os.path.basename(input_file)) 989 extracted_file_name = os.path.join(out_dir, os.path.basename(input_file))
990 with open(extracted_file_name, 'w') as outfile: 990 with open(extracted_file_name, 'w') as outfile:
991 outfile.write(jar_file.read(input_file)) 991 outfile.write(jar_file.read(input_file))
992 992
993 return extracted_file_name 993 return extracted_file_name
994 994
995 995
996 def GenerateJNIHeader(input_file, output_file, namespace, skip_if_same): 996 def GenerateJNIHeader(input_file, output_file, options):
997 try: 997 try:
998 if os.path.splitext(input_file)[1] == '.class': 998 if os.path.splitext(input_file)[1] == '.class':
999 jni_from_javap = JNIFromJavaP.CreateFromClass(input_file, namespace) 999 jni_from_javap = JNIFromJavaP.CreateFromClass(input_file, options)
1000 content = jni_from_javap.GetContent() 1000 content = jni_from_javap.GetContent()
1001 else: 1001 else:
1002 jni_from_java_source = JNIFromJavaSource.CreateFromFile(input_file) 1002 jni_from_java_source = JNIFromJavaSource.CreateFromFile(
1003 input_file, options)
1003 content = jni_from_java_source.GetContent() 1004 content = jni_from_java_source.GetContent()
1004 except ParseError, e: 1005 except ParseError, e:
1005 print e 1006 print e
1006 sys.exit(1) 1007 sys.exit(1)
1007 if output_file: 1008 if output_file:
1008 if not os.path.exists(os.path.dirname(os.path.abspath(output_file))): 1009 if not os.path.exists(os.path.dirname(os.path.abspath(output_file))):
1009 os.makedirs(os.path.dirname(os.path.abspath(output_file))) 1010 os.makedirs(os.path.dirname(os.path.abspath(output_file)))
1010 if skip_if_same and os.path.exists(output_file): 1011 if options.optimize_generation and os.path.exists(output_file):
1011 with file(output_file, 'r') as f: 1012 with file(output_file, 'r') as f:
1012 existing_content = f.read() 1013 existing_content = f.read()
1013 if existing_content == content: 1014 if existing_content == content:
1014 return 1015 return
1015 with file(output_file, 'w') as f: 1016 with file(output_file, 'w') as f:
1016 f.write(content) 1017 f.write(content)
1017 else: 1018 else:
1018 print output 1019 print output
1019 1020
1020 1021
1022 def GetScriptName():
1023 script_components = os.path.abspath(sys.argv[0]).split(os.path.sep)
1024 base_index = 0
1025 for idx, value in enumerate(script_components):
1026 if value == 'base' or value == 'third_party':
1027 base_index = idx
1028 break
1029 return os.sep.join(script_components[base_index:])
1030
1031
1021 def main(argv): 1032 def main(argv):
1022 usage = """usage: %prog [OPTIONS] 1033 usage = """usage: %prog [OPTIONS]
1023 This script will parse the given java source code extracting the native 1034 This script will parse the given java source code extracting the native
1024 declarations and print the header file to stdout (or a file). 1035 declarations and print the header file to stdout (or a file).
1025 See SampleForTests.java for more details. 1036 See SampleForTests.java for more details.
1026 """ 1037 """
1027 option_parser = optparse.OptionParser(usage=usage) 1038 option_parser = optparse.OptionParser(usage=usage)
1028 option_parser.add_option('-j', dest='jar_file', 1039 option_parser.add_option('-j', dest='jar_file',
1029 help='Extract the list of input files from' 1040 help='Extract the list of input files from'
1030 ' a specified jar file.' 1041 ' a specified jar file.'
1031 ' Uses javap to extract the methods from a' 1042 ' Uses javap to extract the methods from a'
1032 ' pre-compiled class. --input should point' 1043 ' pre-compiled class. --input should point'
1033 ' to pre-compiled Java .class files.') 1044 ' to pre-compiled Java .class files.')
1034 option_parser.add_option('-n', dest='namespace', 1045 option_parser.add_option('-n', dest='namespace',
1035 help='Uses as a namespace in the generated header,' 1046 help='Uses as a namespace in the generated header,'
1036 ' instead of the javap class name.') 1047 ' instead of the javap class name.')
1037 option_parser.add_option('--input_file', 1048 option_parser.add_option('--input_file',
1038 help='Single input file name. The output file name ' 1049 help='Single input file name. The output file name '
1039 'will be derived from it. Must be used with ' 1050 'will be derived from it. Must be used with '
1040 '--output_dir.') 1051 '--output_dir.')
1041 option_parser.add_option('--output_dir', 1052 option_parser.add_option('--output_dir',
1042 help='The output directory. Must be used with ' 1053 help='The output directory. Must be used with '
1043 '--input') 1054 '--input')
1044 option_parser.add_option('--optimize_generation', type="int", 1055 option_parser.add_option('--optimize_generation', type="int",
1045 default=0, help='Whether we should optimize JNI ' 1056 default=0, help='Whether we should optimize JNI '
1046 'generation by not regenerating files if they have ' 1057 'generation by not regenerating files if they have '
1047 'not changed.') 1058 'not changed.')
1048 option_parser.add_option('--jarjar', 1059 option_parser.add_option('--jarjar',
1049 help='Path to optional jarjar rules file.') 1060 help='Path to optional jarjar rules file.')
1061 option_parser.add_option('--script_name', default=GetScriptName(),
1062 help='The name of this script in the generated '
1063 'header.')
1050 options, args = option_parser.parse_args(argv) 1064 options, args = option_parser.parse_args(argv)
1051 if options.jar_file: 1065 if options.jar_file:
1052 input_file = ExtractJarInputFile(options.jar_file, options.input_file, 1066 input_file = ExtractJarInputFile(options.jar_file, options.input_file,
1053 options.output_dir) 1067 options.output_dir)
1068 elif options.input_file:
1069 input_file = options.input_file
1054 else: 1070 else:
1055 input_file = options.input_file 1071 option_parser.print_help()
1072 print '\nError: Must specify --jar_file or --input_file.'
1073 return 1
1056 output_file = None 1074 output_file = None
1057 if options.output_dir: 1075 if options.output_dir:
1058 root_name = os.path.splitext(os.path.basename(input_file))[0] 1076 root_name = os.path.splitext(os.path.basename(input_file))[0]
1059 output_file = os.path.join(options.output_dir, root_name) + '_jni.h' 1077 output_file = os.path.join(options.output_dir, root_name) + '_jni.h'
1060 if options.jarjar: 1078 if options.jarjar:
1061 with open(options.jarjar) as f: 1079 with open(options.jarjar) as f:
1062 JniParams.SetJarJarMappings(f.read()) 1080 JniParams.SetJarJarMappings(f.read())
1063 GenerateJNIHeader(input_file, output_file, options.namespace, 1081 GenerateJNIHeader(input_file, output_file, options)
1064 options.optimize_generation)
1065 1082
1066 1083
1067 if __name__ == '__main__': 1084 if __name__ == '__main__':
1068 sys.exit(main(sys.argv)) 1085 sys.exit(main(sys.argv))
OLDNEW
« no previous file with comments | « base/android/jni_generator/golden_sample_for_tests_jni.h ('k') | base/android/jni_generator/jni_generator_tests.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698