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