export_script_gen.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import argparse
  2. import collections
  3. import sys
  4. def parse_export_file(src):
  5. for line in src:
  6. line = line.strip()
  7. if line and '#' not in line:
  8. words = line.split()
  9. if len(words) == 2 and words[0] == 'linux_version':
  10. yield {'linux_version': words[1]}
  11. elif len(words) == 2:
  12. yield {'lang': words[0], 'sym': words[1]}
  13. elif len(words) == 1:
  14. yield {'lang': 'C', 'sym': words[0]}
  15. else:
  16. raise Exception('unsupported exports line: "{}"'.format(line))
  17. def to_c(sym):
  18. symbols = collections.deque(sym.split('::'))
  19. c_prefixes = [ # demangle prefixes for c++ symbols
  20. '_ZN', # namespace
  21. '_ZTIN', # typeinfo for
  22. '_ZTSN', # typeinfo name for
  23. '_ZTTN', # VTT for
  24. '_ZTVN', # vtable for
  25. '_ZNK', # const methods
  26. ]
  27. c_sym = ''
  28. while symbols:
  29. s = symbols.popleft()
  30. if s == '*':
  31. c_sym += '*'
  32. break
  33. if '*' in s and len(s) > 1:
  34. raise Exception('Unsupported format, cannot guess length of symbol: ' + s)
  35. c_sym += str(len(s)) + s
  36. if symbols:
  37. raise Exception('Unsupported format: ' + sym)
  38. if c_sym[-1] != '*':
  39. c_sym += 'E*'
  40. return ['{prefix}{sym}'.format(prefix=prefix, sym=c_sym) for prefix in c_prefixes]
  41. def to_gnu(src, dest):
  42. d = collections.defaultdict(list)
  43. version = None
  44. for item in parse_export_file(src):
  45. if item.get('linux_version'):
  46. if not version:
  47. version = item.get('linux_version')
  48. else:
  49. raise Exception('More than one linux_version defined')
  50. elif item['lang'] == 'C++':
  51. d['C'].extend(to_c(item['sym']))
  52. else:
  53. d[item['lang']].append(item['sym'])
  54. if version:
  55. dest.write('{} {{\nglobal:\n'.format(version))
  56. else:
  57. dest.write('{\nglobal:\n')
  58. for k, v in d.items():
  59. dest.write(' extern "' + k + '" {\n')
  60. for x in v:
  61. dest.write(' ' + x + ';\n')
  62. dest.write(' };\n')
  63. dest.write('local: *;\n};\n')
  64. def to_msvc(src, dest):
  65. dest.write('EXPORTS\n')
  66. for item in parse_export_file(src):
  67. if item.get('linux_version'):
  68. continue
  69. if item.get('lang') == 'C':
  70. dest.write(' {}\n'.format(item.get('sym')))
  71. def to_darwin(src, dest):
  72. pre = ''
  73. for item in parse_export_file(src):
  74. if item.get('linux_version'):
  75. continue
  76. if item['lang'] == 'C':
  77. dest.write(pre + '-Wl,-exported_symbol,_' + item['sym'])
  78. elif item['lang'] == 'C++':
  79. for sym in to_c(item['sym']):
  80. dest.write(pre + '-Wl,-exported_symbol,_' + sym)
  81. else:
  82. raise Exception('unsupported lang: ' + item['lang'])
  83. if pre == '':
  84. pre = ' '
  85. def main():
  86. parser = argparse.ArgumentParser(
  87. description='Convert self-invented platform independent export file format to the format required by specific linker'
  88. )
  89. parser.add_argument(
  90. 'src', type=argparse.FileType('r', encoding='UTF-8'), help='platform independent export file path'
  91. )
  92. parser.add_argument(
  93. 'dest', type=argparse.FileType('w', encoding='UTF-8'), help='destination export file for required linker'
  94. )
  95. parser.add_argument('--format', help='destination file type format: gnu, msvc or darwin')
  96. args = parser.parse_args()
  97. if args.format == 'gnu':
  98. to_gnu(args.src, args.dest)
  99. elif args.format == 'msvc':
  100. to_msvc(args.src, args.dest)
  101. elif args.format == 'darwin':
  102. to_darwin(args.src, args.dest)
  103. else:
  104. print('Unknown destination file format: {}'.format(args.format), file=sys.stderr)
  105. sys.exit(1)
  106. args.src.close()
  107. args.dest.close()
  108. if __name__ == '__main__':
  109. main()