extract_symbol_info.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. # Copyright 2024 Google LLC
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import functools
  15. import os
  16. import sys
  17. SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
  18. sys.path.append(os.path.dirname(SCRIPT_DIR))
  19. from generate_native_sdk import parse_c_decl
  20. import clang
  21. def extract_exported_functions(node, functions=[], types=[], defines=[]):
  22. def update_matching_export(exports, node):
  23. spelling = parse_c_decl.get_node_spelling(node)
  24. for e in exports:
  25. impl_name = e.impl_name if hasattr(e, 'impl_name') else ""
  26. definition_name = impl_name if impl_name else e.name
  27. if spelling == definition_name:
  28. # Found a matching node! Before we update our export make sure this attribute is larger
  29. # than the one we may already have. This is to handle the case where we have typedef and
  30. # a struct as part of the same definition, we want to make sure we get the outer typedef.
  31. definition = parse_c_decl.get_string_from_file(node.extent)
  32. if e.full_definition is None or len(definition) > len(e.full_definition):
  33. if node.kind == clang.cindex.CursorKind.MACRO_DEFINITION:
  34. e.full_definition = "#define " + definition
  35. else:
  36. e.full_definition = definition
  37. # Update the exports with comments / definition info from both the
  38. # 'implName' and 'name'. Keep whatever is longer and does not start
  39. # with @internal (meaning the whole docstring is internal).
  40. if spelling == e.name or (impl_name and spelling == impl_name):
  41. comment = parse_c_decl.get_comment_string_for_decl(node)
  42. if comment is not None and not comment.startswith("//! @internal"):
  43. if e.comment is None or len(comment) > len(e.comment):
  44. e.comment = comment
  45. return None
  46. if node.kind == clang.cindex.CursorKind.FUNCTION_DECL:
  47. update_matching_export(functions, node)
  48. elif node.kind == clang.cindex.CursorKind.STRUCT_DECL or \
  49. node.kind == clang.cindex.CursorKind.ENUM_DECL or \
  50. node.kind == clang.cindex.CursorKind.TYPEDEF_DECL:
  51. update_matching_export(types, node)
  52. elif node.kind == clang.cindex.CursorKind.MACRO_DEFINITION:
  53. update_matching_export(defines, node)
  54. def extract_symbol_info(filenames, functions, types, defines, output_dir, internal_sdk_build=False,
  55. compiler_flags=None):
  56. # Parse all the headers at the same time since that is much faster than
  57. # parsing each one individually
  58. all_headers_file = os.path.join(output_dir, "all_sdk_headers.h")
  59. with open(all_headers_file, 'w') as outfile:
  60. for f in filenames:
  61. outfile.write('#include "%s"\n' % f)
  62. parse_c_decl.parse_file(all_headers_file, filenames,
  63. functools.partial(extract_exported_functions,
  64. functions=functions,
  65. types=types,
  66. defines=defines),
  67. internal_sdk_build=internal_sdk_build,
  68. compiler_flags=compiler_flags)
  69. if __name__ == '__main__':
  70. parse_c_decl.dump_tree = True
  71. class Export(object):
  72. def __init__(self, name):
  73. self.name = name
  74. self.full_definition = None
  75. self.comment = None
  76. #clang.cindex.Config.library_file = "/home/brad/src/llvmbuild/Debug+Asserts/lib/libclang.so"
  77. extract_symbol_info((sys.argv[1],), [], [], [])