• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2"""apis_generator.py - Generate a C++ interface that automates loading OpenCL.
3
4Usage: apis_generator.py <headerPaths...>
5
6The generated code looks roughly like this:
7------------------------------------------------------------------------
8
9// apis.h
10
11CL_MACRO ( returnType, funcname, (fargs...), (callArgs...) )
12
13"""
14
15import os.path
16import re
17import sys
18
19GENERATED_FILE_WARNING = """
20/*
21 * This file is generated by {}
22 * Do not edit this file directly.
23 */""".format(os.path.basename(__file__))
24
25MACRO_GUARD = """
26#ifndef CL_MACRO
27#error You need to define CL_MACRO before including apis
28#endif"""
29
30
31def include_for_header(header):
32  return '#include <CL/{}>'.format(header)
33
34
35def extract_license_lines(lines):
36  license_lines = []
37  for line in lines:
38    license_lines.append(line)
39    if line.find('*/') != -1:
40      return license_lines
41  sys.exit("License text didn't terminate")
42
43
44assert (extract_license_lines(['/* LICENSE */',
45                               'something']) == ['/* LICENSE */'])
46assert (extract_license_lines(['/* LICENSE', ' * TEXT */',
47                               'something']) == ['/* LICENSE', ' * TEXT */'])
48assert (extract_license_lines(['/* LICENSE', ' * TEXT', ' */', 'something'
49                              ]) == ['/* LICENSE', ' * TEXT', ' */'])
50
51
52def parse_arg_strs(str):
53  paren_depth = 0
54  current_arg = ''
55  ret = []
56  for c in str:
57    if c == '(':
58      paren_depth += 1
59    elif c == ')':
60      paren_depth -= 1
61    if c == ',' and paren_depth == 0:
62      ret.append(current_arg)
63      current_arg = ''
64    else:
65      current_arg += c
66  if current_arg != '':
67    ret.append(current_arg)
68  return ret
69
70
71def process_type(raw):
72  # strip redundant [] (where one is before the name)
73  raw = re.sub(r'(\[[0-9]*\])\s*(\w+)\s*\[[0-9]*\]', r'\2\1', raw)
74  # strip cases where the name comment hinted at the number of elements in an array
75  raw = re.sub(r'\*\s*(\w+)\s*\[[0-9]+\]', r'*\1', raw)
76  raw = ' '.join(raw.split())
77  return raw
78
79
80def parse_api(api_signature):
81  m = None
82
83  api_signature = re.sub(r'\bextern\b', '', api_signature)
84  api_signature = re.sub('CL_\w+', '', api_signature)
85
86  m = re.match(r'\s*(.*)\s+(\w+)\((.*)\)\s*;', api_signature)
87  if m == None:
88    print(api_signature)
89
90  assert (m is not None)
91  assert (len(m.groups()) == 3)
92  arg_strs = None
93  if re.match('\s*void\s*', m.group(3)):
94    arg_strs = []
95  else:
96    arg_strs = parse_arg_strs(m.group(3))
97  args = []
98  for arg_str in arg_strs:
99    nm = re.search(r'(\w+)\s*(\)|\[|$)', arg_str)
100    assert (nm is not None)
101    args.append({'type': process_type(arg_str), 'name': nm.group(1)})
102  return {'return': m.group(1).strip(), 'name': m.group(2), 'args': args}
103
104
105def extract_apis(lines):
106  state = 'scanning'
107  apis = []
108  api_signature = ''
109  for line in lines:
110    if state == 'scanning':
111      if line.find('CL_API_ENTRY') != -1 and line.find('typedef') == -1:
112        api_signature = line
113        if line.find(';') != -1:
114          apis.append(
115              parse_api(
116                  api_signature.replace('/*', '').replace('*/', '').replace(
117                      'CL_CALLBACK ', '')))
118          api_signature = ''
119        else:
120          state = 'expectAPILine'
121    elif state == 'expectAPILine':
122      api_signature += ' ' + line
123      if line.find(';') != -1:
124        apis.append(
125            parse_api(
126                api_signature.replace('/*', '').replace('*/', '').replace(
127                    'CL_CALLBACK ', '')))
128        api_signature = ''
129        state = 'scanning'
130  return apis
131
132
133def generate_apis(apis):
134  print(GENERATED_FILE_WARNING)
135  print()
136  print(MACRO_GUARD)
137  print()
138
139  for api in apis:
140    fargs = (arg['type'] for arg in api['args'])
141    cargs = (arg['name'] for arg in api['args'])
142    print('CL_MACRO( {}, {}, ({}), ({}) )\n'.format(api['return'], api['name'],
143                                                    ', '.join(fargs),
144                                                    ', '.join(cargs)))
145
146
147def main():
148  headers = sys.argv[1:]
149  apis = []
150
151  with open(headers[0]) as header:
152    lines = [line.strip() for line in header.readlines()]
153    license_lines = extract_license_lines(lines)
154
155  for header_name in headers:
156    with open(header_name) as header:
157      lines = [line.strip() for line in header.readlines()]
158      apis = apis + extract_apis(lines)
159
160  generate_apis(apis)
161
162
163if __name__ == '__main__':
164  main()
165