9 # Pattern that filters file names that are headers.
10 headerFileNamePattern = re.compile('^[a-zA-Z0-9_]+\\.[hH]$')
11 # Marker that marks API function/data member (at its beginning).
12 apiMemberMarker = 'CLDNN_API'
13 # Macros that should be expanded to API functions.
14 apiMacroMemberMatchers = [
15 (re.compile('^\\s*CLDNN_DECLARE_PRIMITIVE_TYPE_ID\\s*\\(\\s*([a-zA-Z0-9_]+)\\s*\\)\\s*;', re.MULTILINE),
16 'cldnn_primitive_type_id cldnn_\\1_type_id(cldnn_status* status)')
18 # C language and project reserved keywords (that cannot be used as function/parameter name).
20 'auto', 'else', 'long', 'switch', 'break', 'enum', 'register', 'typedef', 'case', 'extern', 'return', 'union',
21 'char', 'float', 'short', 'unsigned', 'const', 'for', 'signed', 'void', 'continue', 'goto', 'sizeof', 'volatile',
22 'default', 'if', 'static', 'while', 'do', 'int', 'struct', '_Packed', 'double'
24 # C language and project reserved keyword patterns (that cannot be used as function/parameter name).
25 reservedKeywordPatterns = [
26 re.compile('^__[a-z0-9_]+__$', re.IGNORECASE)
30 apiMemberMatcher = re.compile('^\\s*' + re.escape(apiMemberMarker) + '\\s+([^;]+);', re.MULTILINE)
31 typeIdentifierSplitter = re.compile('^(.*?)([a-zA-Z_][a-zA-Z0-9_]*)$')
34 def stripCommentsAndPreprocessor(content):
35 """ Strips out comments and preprocessor constructs from text written in C language (or compatible).
37 :param content: Text with code written in C language (or compatible).
39 :return: Content of C language code with comments and preprocessor constructs stripped out.
43 # 0 - normal context, start state
45 # 2 - string context, after \ character (character escape)
46 # 3 - normal context, after / character (possible comment)
47 # 4 - multi-line comment context
48 # 5 - multi-line comment context, after * character (possible end of comment)
49 # 6 - single-line comment context
50 # 7 - single-line comment context, after \ character (escape)
51 # 8 - preprocessor definition/instruction context
52 # 9 - preprocessor definition/instruction context, after \ character (escape)
55 strippedOutputArray = []
57 # normal context, start state
61 strippedOutputArray.append(c)
63 state = 3 # possible comment (no out)
65 state = 8 # preprocessor (no out)
67 strippedOutputArray.append(c)
71 state = 2 # escape sequence
72 strippedOutputArray.append(c)
75 strippedOutputArray.append(c)
77 strippedOutputArray.append(c)
78 # string context, after \ character (character escape)
80 state = 1 # do not leave string context on any character
81 strippedOutputArray.append(c)
82 # normal context, after / character (possible comment)
85 state = 4 # multi-line comment (no out)
87 state = 6 # single-line comment (no out)
89 state = 0 # not comment (flush previous token)
90 strippedOutputArray.append('/')
91 strippedOutputArray.append(c)
92 # multi-line comment context
95 state = 5 # possible end of comment (no out)
96 # multi-line comment context, after * character (possible end of comment)
99 state = 0 # end of comment (no out)
101 pass # not end of comment, but check next token for possible end of comment (no out)
103 state = 4 # not end of comment (no out)
104 # single-line comment context
107 state = 0 # end of comment (append new line)
108 strippedOutputArray.append('\n')
110 state = 7 # escape in comment (can escape new line character) (no out)
111 # single-line comment context, after \ character (escape)
113 state = 6 # do not leave comment on any character (no out)
114 # preprocessor definition/instruction context
117 state = 0 # end of preprocessor construct (no out)
119 state = 9 # escape in preprocessor construct (no out)
120 # preprocessor definition/instruction context, after \ character (escape)
122 state = 8 # do not leave preprocessor construct on any character (no out)
124 return ''.join(strippedOutputArray)
127 def isReservedName(name):
128 """ Determines whether specified name is reserved in C language or project.
130 :param name: Name to check.
132 :return: True, if name is reserved; otherwise, False.
135 if name.strip() in reservedKeywords:
137 for keywordPattern in reservedKeywordPatterns:
138 if keywordPattern.match(name.strip()):
143 automaticSplitVarIndex = 0
146 def splitTypeAndIdentifier(decl):
147 match = typeIdentifierSplitter.match(decl.strip())
148 if match and not isReservedName(match.group(2)):
149 return match.group(1).strip(), match.group(2).strip()
151 global automaticSplitVarIndex
152 automaticSplitVarIndex += 1
153 return decl.strip(), 'arg{0:05d}'.format(automaticSplitVarIndex)
156 def parseApiMemberDeclarator(apiDecl):
163 paramDecls = [] # Collection of extracted parameter declarations
166 # Reversed array where tokens are collected:
167 nameRArray = [] # API member name
168 returnTypeRArray = [] # Return type declaration
169 paramRArray = [] # Parameter declarator
172 cAttributeSplitLoc = cLoc
178 # API member declarator context, start state
181 state = 1 # possible function declarator
183 attrs = apiDecl[cLoc + 1:]
184 # function parameter declaration
186 if c == ')': # nesting of parentheses (stop normal parsing, only collect tokens)
188 paramRArray.append(c)
190 state = 2 # end of parameters declaration (move to function name, store parameter if needed)
191 if len(paramRArray) > 0:
192 paramDecls.append(''.join(paramRArray[::-1]).strip())
194 elif c == ',': # start of next parameter declaration
195 paramDecls.append(''.join(paramRArray[::-1]).strip())
198 paramRArray.append(c)
199 # function name (optional whitespace)
203 state = 3 # ignore whitespace until non-whitespace character is encountered (re-parse token)
206 if c.isalnum() or c == '_':
209 name = ''.join(nameRArray[::-1]).strip()
212 cLoc += 1 # re-parse unmatched token
213 if isReservedName(name):
214 cAttributeSplitLoc = cLoc
220 attrs = apiDecl[cLoc:]
222 state = 0 # if parsed function declaration has reserved name, it need to be treated as attribute
224 state = 4 # name is not reserved - treat next tokens as return type
225 # return type declarator
227 returnTypeRArray.append(c)
229 # Nesting of parentheses - collect tokens only.
234 paramRArray.append(c)
237 if len(nameRArray) > 0:
238 name = ''.join(nameRArray[::-1]).strip()
239 if len(returnTypeRArray) > 0:
240 returnType = ''.join(returnTypeRArray[::-1]).strip()
241 if len(paramRArray) > 0:
242 paramDecls.append(''.join(paramRArray[::-1]).strip())
244 returnType, name = splitTypeAndIdentifier(apiDecl[:cAttributeSplitLoc])
247 for decl in reversed(paramDecls):
248 paramType, paramName = splitTypeAndIdentifier(decl)
249 paramDeclInfos.append({'name': paramName, 'type': paramType})
253 'isFunction': isFunction,
254 'returnType': returnType,
255 'params': paramDeclInfos,
261 # print parseApiMemberDeclarator('int const __attribute__((pure)) ')
262 # print parseApiMemberDeclarator('int foo1 __attribute__((pure)) ')
263 # print parseApiMemberDeclarator('int foo1')
264 # print parseApiMemberDeclarator('void a(int, const a*bb)')
265 # print parseApiMemberDeclarator('int foo __attribute__((static))')
266 # print parseApiMemberDeclarator('int foo()__attribute__((static))')
267 # print parseApiMemberDeclarator('int foo()__attribute__((static)) __attribute__((data(1,2,3))) do() NN')
268 # print parseApiMemberDeclarator('int foo (int a, int b)__attribute__((static)) __attribute__((data(1,2,3))) do() NN')
269 # print parseApiMemberDeclarator('DD(int,a)* foo(int a, const D(1,I())* b)__attribute__((static)) __attribute__((data(1,2,3))) do() NN')
272 def parseHeaderFile(headerFilePath):
273 """ Opens, reads and parses header file and extracts information about API functions inside.
275 :param headerFilePath: Path to header file that will be parsed.
276 :return: List of API function declarations. Each declaration contains dictionary describing function name,
277 parameter types and return type.
282 headerFile = file(headerFilePath)
283 headerContent = headerFile.read()
284 strippedContent = stripCommentsAndPreprocessor(headerContent)
285 matchedFunctionDecls = apiMemberMatcher.findall(strippedContent)
286 for decl in matchedFunctionDecls:
287 apiMembersInfo.append(parseApiMemberDeclarator(decl))
289 for matcher, replace in apiMacroMemberMatchers:
290 matchedMacroDecls = matcher.finditer(strippedContent)
291 for decl in matchedMacroDecls:
292 apiMembersInfo.append(parseApiMemberDeclarator(decl.expand(replace)))
294 return apiMembersInfo
297 def main(parsedOptions):
298 """ Main script function.
300 The script generates header file with wrappers for all API functions from headers contained in specific directory.
302 :param parsedOptions: Arguments parsed by argparse.ArgumentParser class.
303 :return: Exit code for script.
306 scanDirectory = parsedOptions.dir if parsedOptions.dir is not None and parsedOptions.dir != '' else os.curdir
310 for scanDir, scanSubdirectories, scanFileNames in os.walk(scanDirectory):
311 for scanFileName in scanFileNames:
312 if headerFileNamePattern.match(scanFileName):
313 apiMembersInfo.extend(parseHeaderFile(os.path.join(scanDir, scanFileName)))
315 print r'''/*******************************************************************************
316 * Copyright 2016 Intel Corporation
318 * Licensed under the Apache License, Version 2.0 (the "License");
319 * you may not use this file except in compliance with the License.
320 * You may obtain a copy of the License at
322 * http://www.apache.org/licenses/LICENSE-2.0
324 * Unless required by applicable law or agreed to in writing, software
325 * distributed under the License is distributed on an "AS IS" BASIS,
326 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
327 * See the License for the specific language governing permissions and
328 * limitations under the License.
329 *******************************************************************************/
331 /*********************************************************
332 * AUTOGENERATED FILE; DO NOT EDIT
333 ********************************************************/
337 typedef HINSTANCE lib_handle_t;
340 #define _GNU_SOURCE /* for dlvsym() */
342 typedef void * lib_handle_t;
343 #define NULL_LIB NULL
349 #include "cldnn_prv.h"
350 #include "cldnn/cldnn.h"
352 static inline lib_handle_t load_library(const char *lib_name)
355 return LoadLibraryEx(lib_name, NULL, 0);
357 return dlopen(lib_name, RTLD_LAZY | RTLD_GLOBAL);
361 static inline void *load_symbol(const lib_handle_t lib,
365 return GetProcAddress(lib, name);
367 return dlsym(lib, name);
373 for apiMemberInfo in apiMembersInfo:
374 if apiMemberInfo['isFunction']:
375 print '{0} (*{1}_fptr)({2}){5} = NULL;\n{0} {1}({4}){5} {{\n assert({1}_fptr != NULL);\n return {1}_fptr({3});\n}}\n'.format(
376 apiMemberInfo['returnType'],
377 apiMemberInfo['name'],
378 ', '.join([x['type'] for x in apiMemberInfo['params']]),
379 ', '.join([x['name'] for x in apiMemberInfo['params']]),
380 ', '.join([x['type'] + ' ' + x['name'] for x in apiMemberInfo['params']]),
381 (' ' + apiMemberInfo['attrs']) if len(apiMemberInfo['attrs']) > 0 else '')
383 print 'int cldnn_load_symbols(lib_handle_t handle) {'
384 for apiMemberInfo in apiMembersInfo:
385 if apiMemberInfo['isFunction']:
386 print ' {1}_fptr = ({0} (*)({2}){5}) load_symbol(handle, "{1}");\n if ({1}_fptr == NULL) {{\n return -1;\n }}\n'.format(
387 apiMemberInfo['returnType'],
388 apiMemberInfo['name'],
389 ', '.join([x['type'] for x in apiMemberInfo['params']]),
390 ', '.join([x['name'] for x in apiMemberInfo['params']]),
391 ', '.join([x['type'] + ' ' + x['name'] for x in apiMemberInfo['params']]),
392 (' ' + apiMemberInfo['attrs']) if len(apiMemberInfo['attrs']) > 0 else '')
393 print ' return 0;\n}'
403 int cldnn_load_lib(const char *lib_name)
405 printf("begin cldnn_load_lib: %s\n", lib_name);
406 static int lib_status = lib_unloaded;
407 lib_handle_t lib_handle = NULL;
409 if (lib_status != lib_unloaded)
412 lib_handle = load_library(lib_name);
413 if (lib_handle == NULL)
415 lib_status = lib_failed;
416 printf("Could not load library '%s'\n", lib_name);
420 lib_status = cldnn_load_symbols(lib_handle) == 0 ? lib_loaded : lib_failed;
426 if __name__ == "__main__":
427 optParser = argparse.ArgumentParser(description = 'Generates wrappers for all API functions contained in headers' +
428 'of specific directory.')
430 optParser.add_argument('dir', metavar = '<dir>', type = str, default = None,
431 help = 'Directory to scan for header files. Default/None specified:' +
432 ' current working directory.')
433 optParser.add_argument('--version', action = 'version', version = '%(prog)s 1.0')
435 options = optParser.parse_args()
437 exitCode = main(options)
438 optParser.exit(exitCode)