-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpeel-gen.py
More file actions
executable file
·229 lines (198 loc) · 8.77 KB
/
peel-gen.py
File metadata and controls
executable file
·229 lines (198 loc) · 8.77 KB
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
#! /usr/bin/env python3
import sys
from argparse import ArgumentParser
from pathlib import Path
# These will be set by Meson in the installed file.
peel_gen_module_path = r'@module_path@'
builtin_api_tweaks_path = r'@api_tweaks_path@'
peel_version = r'@peel_version@'
if peel_gen_module_path == '@' + 'module_path' + '@':
# Running uninstalled from the source tree.
peel_gen_module_path = str(Path(__file__).parent)
builtin_api_tweaks_path = Path(peel_gen_module_path) / 'api-tweaks.txt'
peel_version = '(uninstalled)'
sys.path.insert(0, peel_gen_module_path)
if __name__ == '__main__' and __package__ is None:
__package__ = 'peel_gen'
from peel_gen import api_tweaks
from peel_gen.type import AnyType
from peel_gen.function import Function
from peel_gen.exceptions import UnsupportedForNowException
from peel_gen.repository import find_and_parse_gir_repo, repository_map
import peel_gen.namespace
def write_required_forward_decls(target_file, forward_members):
per_ns = dict()
for member in forward_members:
if member.ns not in per_ns:
per_ns[member.ns] = [member]
else:
per_ns[member.ns].append(member)
first = True
for ns in per_ns:
if not first:
target_file.write('\n')
first = False
target_file.write('namespace {}\n{{\n'.format(ns.name))
for member in per_ns[ns]:
try:
s = member.generate_forward_decl()
except UnsupportedForNowException:
continue
if s:
target_file.write(s + '\n')
target_file.write('}} /* namespace {} */\n'.format(ns.name))
def emit_umbrella_header(repo, file_path, includes):
with file_path.open(mode='w') as target_file:
header = repo.generate_basic_header()
target_file.write(header + '\n')
for include in includes:
target_file.write('#include <{}>\n'.format(include))
def emit_file(repo, file_path, members):
extra_include_members = set()
extra_forward_members = set()
extra_include_at_end_members = set()
for member in members:
try:
extra_include_members.update(member.generate_extra_include_members())
extra_forward_members.update(member.generate_extra_forward_members())
extra_include_at_end_members.update(member.generate_extra_include_at_end_members())
except UnsupportedForNowException:
continue
extra_includes = []
for extra_include_member in extra_include_members:
if isinstance(extra_include_member, str):
extra_includes.append(extra_include_member)
continue
if not extra_include_member.ns.should_emit_file(extra_include_member) and not extra_include_member.ns.is_manual_member(extra_include_member):
continue
extra_include_at_end_members.discard(extra_include_member)
# extra_forward_members.discard(extra_include_member)
if not extra_include_member.ns.emit_raw:
extra_includes.append(extra_include_member.make_file_path())
else:
extra_includes.extend(extra_include_member.ns.c_includes)
extra_forward_members.update([member for member in members if isinstance(member, AnyType)])
for member in members:
extra_include_at_end_members.discard(member)
extra_includes_at_end = []
for extra_include_member in extra_include_at_end_members:
if not extra_include_member.ns.should_emit_file(extra_include_member):
continue
if not extra_include_member.ns.emit_raw:
extra_includes_at_end.append(extra_include_member.make_file_path())
else:
# FIXME: does this make sense?
extra_includes_at_end.extend(extra_include_member.ns.c_includes)
extra_includes = sorted(set(map(str, extra_includes)))
extra_forward_members = sorted(extra_forward_members, key=lambda m: m.emit_name_for_context(None))
extra_includes_at_end = sorted(set(map(str, extra_includes_at_end)))
per_ns = dict()
for member in members:
if member.ns not in per_ns:
per_ns[member.ns] = [member]
else:
per_ns[member.ns].append(member)
# Avoid creating the file if we're binding a single member and we're skipping it.
if len(members) == 1:
try:
member_generated = member.generate()
except UnsupportedForNowException:
return False
else:
member_generated = None
with file_path.open(mode='w') as target_file:
header = repo.generate_header(extra_includes=extra_includes)
target_file.write(header)
write_required_forward_decls(target_file, extra_forward_members)
for member in members:
if isinstance(member, AnyType):
try:
s = member.generate_specializations()
except UnsupportedForNowException:
continue
else:
if s:
target_file.write('\n' + s + '\n')
for ns in per_ns:
target_file.write('\n\nnamespace {}\n{{\n'.format(member.ns.name))
for member in per_ns[ns]:
if member_generated is not None:
target_file.write(member_generated)
break
else:
try:
s = member.generate()
except UnsupportedForNowException as e:
s = '/* Unsupported for now: {}: {} */'.format(member.name, e.reason)
target_file.write(s + '\n\n')
target_file.write('\n\n}} /* namespace {} */\n'.format(member.ns.name))
target_file.write(repo.generate_footer())
if extra_includes_at_end:
target_file.write('\n')
for include in extra_includes_at_end:
target_file.write('#include <{}>\n'.format(include))
return True
def emit_repo(repo, out_dir):
out_dir = Path(out_dir)
for ns in repo.namespaces:
if ns.name in peel_gen.namespace.raw_namespace_names:
continue
base_path = Path('peel') / ns.name
(out_dir / base_path).mkdir(parents=True, exist_ok=True)
functions = []
emitted_files = []
for member in ns.members:
if isinstance(member, Function):
functions.append(member)
continue
# Include manually implemented members in the umbrella header
# but don't emit files for them
if ns.is_manual_member(member):
file_path = member.make_file_path()
emitted_files.append(file_path)
continue
if not ns.should_emit_file(member):
continue
file_path = member.make_file_path()
if emit_file(repo, out_dir / file_path, [member]):
emitted_files.append(file_path)
if functions:
file_path = base_path / 'functions.h'
if emit_file(repo, out_dir / file_path, functions):
emitted_files.append(file_path)
file_path = base_path / '{}.h'.format(ns.name)
emit_umbrella_header(repo, out_dir / file_path, emitted_files)
def main():
arg_parser = ArgumentParser(prog='peel-gen')
arg_parser.add_argument('repos', nargs='+', metavar='repo-name repo-version')
arg_parser.add_argument('--version', action='version', version='peel ' + peel_version)
arg_parser.add_argument('--raw', action='append', default=[], metavar='namespace')
arg_parser.add_argument('--api-tweaks', action='append', default=[], metavar='path')
arg_parser.add_argument('-r', '--recursive', action='store_true')
arg_parser.add_argument('--out-dir', default='.', metavar='path')
args = arg_parser.parse_args()
peel_gen.namespace.raw_namespace_names = args.raw
api_tweaks.load_from_file(builtin_api_tweaks_path)
for path in args.api_tweaks:
api_tweaks.load_from_file(path)
if len(args.repos) % 2 != 0:
arg_parser.error("Each repository needs both a name and a version")
requested_repos = []
for i in range(0, len(args.repos), 2):
name = args.repos[i]
version = args.repos[i+1]
# The repo could have already found as a dependency of another repository,
# and added to the repository map. Let's add it to our requested list from
# the map in this case.
if (name, version) not in repository_map:
requested_repos.append(find_and_parse_gir_repo(name, version))
else:
requested_repos.append(repository_map[(name, version)])
if args.recursive:
for repo in repository_map.values():
emit_repo(repo, out_dir=args.out_dir)
else:
for repo in requested_repos:
emit_repo(repo, out_dir=args.out_dir)
if __name__ == '__main__':
main()