| #!/usr/bin/env python |
| |
| # Copyright 2015 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| # Utility functions used to parse a list of DLL entry points. |
| # Expected format: |
| # |
| # <empty-line> -> ignored |
| # #<comment> -> ignored |
| # %<verbatim> -> verbatim output for header files. |
| # !<prefix> -> prefix name for header files. |
| # <return-type> <function-name> <signature> ; -> entry point declaration. |
| # |
| # Anything else is an error. |
| |
| import re |
| import sys |
| import argparse |
| |
| re_func = re.compile(r"""^(.*[\* ])([A-Za-z_][A-Za-z0-9_]*)\((.*)\);$""") |
| re_param = re.compile(r"""^(.*[\* ])([A-Za-z_][A-Za-z0-9_\[\]]*)$""") |
| |
| class Entry: |
| """Small class used to model a single DLL entry point.""" |
| def __init__(self, func_name, return_type, parameters): |
| """Initialize Entry instance. |func_name| is the function name, |
| |return_type| its return type, and |parameters| is a list of |
| (type,name) tuples from the entry's signature. |
| """ |
| self.__name__ = func_name |
| self.return_type = return_type |
| self.parameters = "" |
| self.vartypes = [] |
| self.varnames = [] |
| self.call = "" |
| comma = "" |
| for param in parameters: |
| self.vartypes.append(param[0]) |
| self.varnames.append(param[1]) |
| self.parameters += "%s%s %s" % (comma, param[0], param[1]) |
| self.call += "%s%s" % (comma, param[1]) |
| comma = ", " |
| |
| def banner_command(argv): |
| """Return sanitized command-line description. |
| |argv| must be a list of command-line parameters, e.g. sys.argv. |
| Return a string corresponding to the command, with platform-specific |
| paths removed.""" |
| |
| # Remove path from first parameter |
| argv = argv[:] |
| argv[0] = "android/scripts/gen-entries.py" |
| return ' '.join(argv) |
| |
| def parse_entries_file(lines): |
| """Parse an .entries file and return a tuple of: |
| entries: list of Entry instances from the file. |
| prefix_name: prefix name from the file, or None. |
| verbatim: list of verbatim lines from the file. |
| errors: list of errors in the file, prefixed by line number. |
| """ |
| entries = [] |
| verbatim = [] |
| errors = [] |
| lineno = 0 |
| prefix_name = None |
| namespaces = [] |
| for line in lines: |
| lineno += 1 |
| line = line.strip() |
| if len(line) == 0: # Ignore empty lines |
| continue |
| if line[0] == '#': # Ignore comments |
| continue |
| if line[0] == '!': # Prefix name |
| prefix_name = line[1:] |
| continue |
| if line[0] == '%': # Verbatim line copy |
| verbatim.append(line[1:]) |
| continue |
| if line.startswith("namespaces"): # Namespaces |
| namespaces = list(map(lambda t: t.strip(), line.split("namespaces")[1].strip().split(","))) |
| continue |
| # Must be a function signature. |
| m = re_func.match(line) |
| if not m: |
| errors.append("%d: '%s'" % (lineno, line)) |
| continue |
| |
| return_type, func_name, parameters = m.groups() |
| return_type = return_type.strip() |
| parameters = parameters.strip() |
| params = [] |
| failure = False |
| if parameters != "void": |
| for parameter in parameters.split(','): |
| parameter = parameter.strip() |
| m = re_param.match(parameter) |
| if not m: |
| errors.append("%d: parameter '%s'" % (lineno, parameter)) |
| failure = True |
| break |
| else: |
| param_type, param_name = m.groups() |
| params.append((param_type.strip(), param_name.strip())) |
| |
| if not failure: |
| entries.append(Entry(func_name, return_type, params)) |
| |
| return (entries, prefix_name, verbatim, namespaces, errors) |
| |
| |
| def gen_functions_header(entries, prefix_name, verbatim, filename, with_args): |
| """Generate a C header containing a macro listing all entry points. |
| |entries| is a list of Entry instances. |
| |prefix_name| is a prefix-name, it will be converted to upper-case. |
| |verbatim| is a list of verbatim lines that must appear before the |
| macro declaration. Useful to insert #include <> statements. |
| |filename| is the name of the original file. |
| """ |
| prefix_name = prefix_name.upper() |
| |
| print("// Auto-generated with: %s" % banner_command(sys.argv)) |
| print("// DO NOT EDIT THIS FILE") |
| print("") |
| print("#ifndef %s_FUNCTIONS_H" % prefix_name) |
| print("#define %s_FUNCTIONS_H" % prefix_name) |
| print("") |
| for line in verbatim: |
| print(line) |
| |
| print("#define LIST_%s_FUNCTIONS(X) \\" % prefix_name) |
| |
| new_header_apis = [ |
| # AEMU private exts |
| "eglGetMaxGLESVersion", |
| "eglBlitFromCurrentReadBufferANDROID", |
| "eglSetImageFenceANDROID", |
| "eglWaitImageFenceANDROID", |
| "eglAddLibrarySearchPathANDROID", |
| "eglQueryVulkanInteropSupportANDROID", |
| "eglQueryVulkanInteropSupportANDROID", |
| # For snapshotting |
| "eglLoadConfig", |
| "eglLoadContext", |
| "eglLoadAllImages", |
| "eglSaveConfig", |
| "eglSaveContext", |
| "eglSaveAllImages", |
| "eglPreSaveContext", |
| "eglPostLoadAllImages", |
| "eglPostSaveContext", |
| "eglUseOsEglApi", |
| "eglFillUsages", |
| "eglSetMaxGLESVersion", |
| ] |
| |
| need_decls = [] |
| |
| for entry in entries: |
| if entry.__name__ in new_header_apis: |
| need_decls.append(entry) |
| |
| if with_args: |
| print(" X(%s, %s, (%s), (%s)) \\" % \ |
| (entry.return_type, entry.__name__, entry.parameters, |
| entry.call)) |
| else: |
| print(" X(%s, %s, (%s)) \\" % \ |
| (entry.return_type, entry.__name__, entry.parameters)) |
| |
| |
| print("") |
| |
| for entry in need_decls: |
| print("EGLAPI %s EGLAPIENTRY %s(%s);" % (entry.return_type, entry.__name__, entry.parameters)) |
| |
| print("") |
| print("#endif // %s_FUNCTIONS_H" % prefix_name) |
| |
| def gen_static_translator_namespaced_header(entries, namespaces, prefix_name, verbatim, filename, with_args): |
| """Generate a C++ header containing a header for |entries| |
| with nesting inside |namespaces|. |
| |prefix_name| is a prefix-name, it will be converted to upper-case. |
| |verbatim| is a list of verbatim lines that must appear before the |
| macro declaration. Useful to insert #include <> statements. |
| |filename| is the name of the original file. |
| """ |
| prefix_name = prefix_name.upper() |
| |
| print("// Auto-generated with: %s" % banner_command(sys.argv)) |
| print("// DO NOT EDIT THIS FILE") |
| print("") |
| print("#pragma once") |
| print("") |
| for line in verbatim: |
| print(line) |
| |
| for ns in namespaces: |
| print("namespace %s {" % ns) |
| |
| for entry in entries: |
| if "gles" in filename: |
| print("GL_APICALL %s GL_APIENTRY %s(%s);" % (entry.return_type, entry.__name__, entry.parameters)) |
| else: |
| print("EGLAPI %s EGLAPIENTRY %s(%s);" % (entry.return_type, entry.__name__, entry.parameters)) |
| |
| for ns in namespaces: |
| print("} // namespace %s" % ns) |
| |
| def gen_static_translator_namespaced_stubs(entries, namespaces, prefix_name, verbatim, filename, with_args): |
| """Generate a C++ header containing a header for |entries| |
| with nesting inside |namespaces|. |
| |prefix_name| is a prefix-name, it will be converted to upper-case. |
| |verbatim| is a list of verbatim lines that must appear before the |
| macro declaration. Useful to insert #include <> statements. |
| |filename| is the name of the original file. |
| """ |
| prefix_name = prefix_name.upper() |
| |
| print("// Auto-generated with: %s" % banner_command(sys.argv)) |
| print("// DO NOT EDIT THIS FILE") |
| print("") |
| for line in verbatim: |
| print(line) |
| |
| for ns in namespaces: |
| print("namespace %s {" % ns) |
| |
| for entry in entries: |
| if "gles" in filename: |
| if "void" == entry.return_type: |
| return_part = "return" |
| else: |
| return_part = "return (%s)0" % entry.return_type |
| |
| print("GL_APICALL %s GL_APIENTRY %s(%s) { %s; }" % (entry.return_type, entry.__name__, ", ".join(entry.vartypes), return_part)) |
| else: |
| print("EGLAPI %s EGLAPIENTRY %s(%s);" % (entry.return_type, entry.__name__, entry.parameters)) |
| |
| namespaces.reverse() |
| |
| for ns in namespaces: |
| print("} // namespace %s" % ns) |
| |
| # The purpose of gen_translator() |
| # is to quickly generate implementations on the host Translator, |
| # which processes commands that just got onto the renderthread off goldfish pipe |
| # and are fed to system OpenGL. |
| |
| def gen_translator(entries): |
| # Definitions for custom implementation bodies go in |
| # android/scripts/gles3translatorgen/gles30_custom.py |
| # android/scripts/gles3translatorgen/gles31_custom.py |
| from gles3translatorgen import gles30_custom |
| from gles3translatorgen import gles31_custom |
| |
| translator_custom_share_processing = { } |
| for (k, v) in list(gles30_custom.custom_share_processing.items()): |
| translator_custom_share_processing[k] = v |
| for (k, v) in list(gles31_custom.custom_share_processing.items()): |
| translator_custom_share_processing[k] = v |
| |
| translator_custom_pre = { } |
| for (k, v) in list(gles30_custom.custom_preprocesses.items()): |
| translator_custom_pre[k] = v |
| for (k, v) in list(gles31_custom.custom_preprocesses.items()): |
| translator_custom_pre[k] = v |
| |
| translator_custom_post = { } |
| for (k, v) in list(gles30_custom.custom_postprocesses.items()): |
| translator_custom_post[k] = v |
| for (k, v) in list(gles31_custom.custom_postprocesses.items()): |
| translator_custom_post[k] = v |
| |
| translator_no_passthrough = {} |
| for (k, v) in list(gles30_custom.no_passthrough.items()): |
| translator_no_passthrough[k] = v |
| for (k, v) in list(gles31_custom.no_passthrough.items()): |
| translator_no_passthrough[k] = v |
| |
| translator_needexternc = { |
| "glGetStringi": 1, |
| "glUniform4ui": 1, |
| "glGetUniformIndices": 1, |
| "glTransformFeedbackVaryings": 1, |
| "glCreateShaderProgramv": 1, |
| "glProgramUniform2ui": 1, |
| "glProgramUniform3ui": 1, |
| "glProgramUniform4ui": 1, |
| "glBindVertexBuffer": 1, |
| }; |
| translator_nocontext_fail_codes = { |
| "glClientWaitSync" : "GL_WAIT_FAILED", |
| }; |
| def needExternC(entry): |
| if entry.__name__ in translator_needexternc: |
| return "extern \"C\" " |
| else: |
| return "" |
| def get_fail_code(entry): |
| if entry.__name__ in translator_nocontext_fail_codes: |
| return translator_nocontext_fail_codes[entry.__name__]; |
| else: |
| return "0" |
| def gen_cxt_getter(entry): |
| if (entry.return_type == "void"): |
| print(" GET_CTX_V2();") |
| else: |
| print(" GET_CTX_V2_RET(%s);" % get_fail_code(entry)) |
| |
| def gen_validations_custom_impl(entry): |
| isGen = entry.__name__.startswith("glGen") |
| isDelete = entry.__name__.startswith("glDelete") |
| isBufferOp = "Buffer" in entry.__name__ |
| |
| hasTargetArg = "target" in entry.varnames |
| hasProgramArg = "program" in entry.varnames |
| |
| def mySetError(condition, glerr): |
| if entry.return_type == "void": |
| return "SET_ERROR_IF(%s,%s)" % (condition, glerr); |
| else: |
| return "RET_AND_SET_ERROR_IF(%s,%s,%s)" % (condition, glerr, get_fail_code(entry)); |
| |
| if (isGen or isDelete) and ("n" in entry.varnames): |
| print(" %s;" % mySetError("n < 0", "GL_INVALID_VALUE")); |
| if (isBufferOp and hasTargetArg): |
| print(" %s;" % mySetError("!GLESv2Validate::bufferTarget(ctx, target)", "GL_INVALID_ENUM")); |
| if entry.__name__ in translator_custom_pre: |
| print(translator_custom_pre[entry.__name__]) |
| |
| def gen_call_ret(entry): |
| globalNameTypes = { |
| ("GLuint", "program") : "NamedObjectType::SHADER_OR_PROGRAM", |
| ("GLuint", "texture") : "NamedObjectType::TEXTURE", |
| ("GLuint", "buffer") : "NamedObjectType::VERTEXBUFFER", |
| ("GLuint", "sampler") : "NamedObjectType::SAMPLER", |
| ("GLuint", "query") : "NamedObjectType::QUERY", |
| } |
| globalNames = { |
| ("GLuint", "program") : "globalProgramName", |
| ("GLuint", "texture") : "globalTextureName", |
| ("GLuint", "buffer") : "globalBufferName", |
| ("GLuint", "sampler") : "globalSampler", |
| ("GLuint", "query") : "globalQuery", |
| } |
| |
| needsShareGroup = False |
| for v in zip(entry.vartypes, entry.varnames): |
| if v in list(globalNameTypes.keys()): |
| needsShareGroup = True |
| |
| if needsShareGroup: |
| print(" if (ctx->shareGroup().get()) {") |
| for key in zip(entry.vartypes, entry.varnames): |
| vartype, varname = key |
| if key in globalNames: |
| print(" const GLuint %s = ctx->shareGroup()->getGlobalName(%s, %s);" % (globalNames[key], globalNameTypes[key], varname)) |
| |
| globalCall = ", ".join([globalNames.get(k, k[1]) for k in zip(entry.vartypes, entry.varnames)]) |
| |
| if needsShareGroup and entry.__name__ in translator_custom_share_processing: |
| print(translator_custom_share_processing[entry.__name__]) |
| |
| if (entry.return_type == "void"): |
| if (needsShareGroup): |
| print(" ") |
| |
| if entry.__name__ not in translator_no_passthrough: |
| print(" ctx->dispatcher().%s(%s);" % (entry.__name__, globalCall)) |
| |
| if needsShareGroup: |
| print(" }") |
| if entry.__name__ in translator_custom_post: |
| print(translator_custom_post[entry.__name__]); |
| else: |
| if (needsShareGroup): |
| print(" ") |
| if entry.__name__ not in translator_no_passthrough: |
| print(" %s %s = ctx->dispatcher().%s(%s);" % (entry.return_type, entry.__name__ + "RET", entry.__name__, globalCall)) |
| else: |
| print(" %s %s = %s" % (entry.return_type, entry_func_name + "RET", get_fail_code(entry))) |
| |
| if entry.__name__ in translator_custom_post: |
| print(translator_custom_post[entry.__name__]); |
| |
| print(" return %s;" % (entry.__name__ + "RET")); |
| if needsShareGroup: |
| print(" } else return %s;" % (get_fail_code(entry))) |
| |
| print("// Auto-generated with: %s" % banner_command(sys.argv)) |
| print("// This file is best left unedited.") |
| print("// Try to make changes through gen_translator in gen-entries.py,") |
| print("// and/or parcel out custom functionality in separate code.") |
| for entry in entries: |
| print("%sGL_APICALL %s GL_APIENTRY %s(%s) {" % (needExternC(entry), entry.return_type, entry.__name__, entry.parameters)) |
| gen_cxt_getter(entry); |
| gen_validations_custom_impl(entry); |
| gen_call_ret(entry); |
| print("}\n") |
| |
| def gen_dll_wrapper(entries, prefix_name, verbatim, filename): |
| """Generate a C source file that contains functions that act as wrappers |
| for entry points located in another shared library. This allows the |
| code that calls these functions to perform lazy-linking to system |
| libraries. |
| |entries|, |prefix_name|, |verbatim| and |filename| are the same as |
| for gen_functions_header() above. |
| """ |
| upper_name = prefix_name.upper() |
| |
| ENTRY_PREFIX = "__dll_" |
| |
| print("// Auto-generated with: %s" % banner_command(sys.argv)) |
| print("// DO NOT EDIT THIS FILE") |
| print("") |
| print("#include <dlfcn.h>") |
| for line in verbatim: |
| print(line) |
| |
| print("") |
| print("///") |
| print("/// W R A P P E R P O I N T E R S") |
| print("///") |
| print("") |
| for entry in entries: |
| ptr_name = ENTRY_PREFIX + entry.__name__ |
| print("static %s (*%s)(%s) = 0;" % \ |
| (entry.return_type, ptr_name, entry.parameters)) |
| |
| print("") |
| print("///") |
| print("/// W R A P P E R F U N C T I O N S") |
| print("///") |
| print("") |
| |
| for entry in entries: |
| print("%s %s(%s) {" % \ |
| (entry.return_type, entry.__name__, entry.parameters)) |
| ptr_name = ENTRY_PREFIX + entry.__name__ |
| if entry.return_type != "void": |
| print(" return %s(%s);" % (ptr_name, entry.call)) |
| else: |
| print(" %s(%s);" % (ptr_name, entry.call)) |
| print("}\n") |
| |
| print("") |
| print("///") |
| print("/// I N I T I A L I Z A T I O N F U N C T I O N") |
| print("///") |
| print("") |
| |
| print("int %s_dynlink_init(void* lib) {" % prefix_name) |
| for entry in entries: |
| ptr_name = ENTRY_PREFIX + entry.__name__ |
| print(" %s = (%s(*)(%s))dlsym(lib, \"%s\");" % \ |
| (ptr_name, |
| entry.return_type, |
| entry.parameters, |
| entry.__name__)) |
| print(" if (!%s) return -1;" % ptr_name) |
| print(" return 0;") |
| print("}") |
| |
| |
| def gen_windows_def_file(entries): |
| """Generate a windows DLL .def file. |entries| is a list of Entry instances. |
| """ |
| print("EXPORTS") |
| for entry in entries: |
| print(" %s" % entry.__name__) |
| |
| |
| def gen_unix_sym_file(entries): |
| """Generate an ELF linker version file. |entries| is a list of Entry |
| instances. |
| """ |
| print("VERSION {") |
| print("\tglobal:") |
| for entry in entries: |
| print("\t\t%s;" % entry.__name__) |
| print("\tlocal:") |
| print("\t\t*;") |
| print("};") |
| |
| def gen_symbols(entries, underscore): |
| """Generate a list of symbols from |entries|, a list of Entry instances. |
| |underscore| is a boolean. If True, then prepend an underscore to each |
| symbol name. |
| """ |
| prefix = "" |
| if underscore: |
| prefix = "_" |
| for entry in entries: |
| print("%s%s" % (prefix, entry.__name__)) |
| |
| |
| VARTYPE_TO_PRINT_FORMAT = { |
| 'GLenum': '0x%X', |
| 'GLboolean': '%d', |
| 'GLbitfield': '%d', |
| 'GLvoid': '%d', |
| 'GLubyte': '%u', |
| 'GLbyte': '%c', |
| 'GLshort': '%d', |
| 'GLushort': '%d', |
| 'GLint': '%d', |
| 'GLuint': '%d', |
| 'GLclampx': '%d', |
| 'GLsizei': '%d', |
| 'GLfloat': '%f', |
| 'GLclampf': '%f', |
| 'GLdouble': '%f', |
| 'GLclampd': '%f', |
| 'GLchar': '%c', |
| 'GLcharARB': '%c', |
| 'GLfixed': '%d', |
| 'GLintptr': '%ld', |
| 'GLsizeiptr': '%ld', |
| 'GLsync': '%p', |
| 'GLuint64': "%lu", |
| 'GLeglImageOES': '%p', |
| } |
| |
| def get_printf_format(var_type, var_name): |
| if '*' in var_type: |
| return "%p" |
| |
| if '[' in var_name: |
| return "%p" |
| |
| # Function pointer |
| if 'PROC' in var_type: |
| return "%p" |
| |
| return VARTYPE_TO_PRINT_FORMAT[var_type] |
| |
| def get_printf_name(var_name): |
| if '[' in var_name: |
| return var_name[:var_name.index('[')] |
| |
| return var_name |
| |
| def gen_dispatch_logging_wrappers(entries): |
| print("// Auto-generated with: %s" % banner_command(sys.argv)) |
| print("// DO NOT EDIT THIS FILE") |
| print("") |
| |
| for entry in entries: |
| print("%s %s_dispatchLoggingWrapper(%s) {" % \ |
| (entry.return_type, entry.__name__, entry.parameters)) |
| |
| print_var_formats = [] |
| print_var_names = [] |
| for (param_type, param_name) in zip(entry.vartypes, entry.varnames): |
| print_var_formats.append("%s:%s" % (param_name, get_printf_format(param_type, param_name))) |
| print_var_names.append(get_printf_name(param_name)) |
| |
| optional_comma = ", " if print_var_formats else "" |
| print("\tDISPATCH_DEBUG_LOG(\"%s(%s)\"%s%s);" |
| % (entry.__name__, ", ".join(print_var_formats), optional_comma, ", ".join(print_var_names))) |
| |
| if entry.return_type == "void": |
| optional_return = "" |
| else: |
| optional_return = "return " |
| |
| print("\t%sGLDispatch::%s_underlying(%s);" % (optional_return, entry.__name__, ", ".join(print_var_names))) |
| print("}") |
| print("") |
| |
| |
| def parse_file(filename, lines, mode): |
| """Generate one of possible outputs from |filename|. |lines| must be a list |
| of text lines from the file, and |mode| is one of the --mode option |
| values. |
| """ |
| entries, prefix_name, verbatim, namespaces, errors = parse_entries_file(lines) |
| if errors: |
| for error in errors: |
| sys.stderr.write("ERROR: %s:%s" % (filename, error)) |
| sys.exit(1) |
| |
| if not prefix_name: |
| prefix_name = "unknown" |
| |
| if mode == 'def': |
| gen_windows_def_file(entries) |
| elif mode == 'sym': |
| gen_unix_sym_file(entries) |
| elif mode == 'translator_passthrough': |
| gen_translator(entries) |
| elif mode == 'wrapper': |
| gen_dll_wrapper(entries, prefix_name, verbatim, filename) |
| elif mode == 'symbols': |
| gen_symbols(entries, False) |
| elif mode == '_symbols': |
| gen_symbols(entries, True) |
| elif mode == 'functions': |
| gen_functions_header(entries, prefix_name, verbatim, filename, False) |
| elif mode == 'funcargs': |
| gen_functions_header(entries, prefix_name, verbatim, filename, True) |
| elif mode == 'static_translator_namespaced_header': |
| gen_static_translator_namespaced_header(entries, namespaces, prefix_name, verbatim, filename, True) |
| elif mode == 'static_translator_namespaced_stubs': |
| gen_static_translator_namespaced_stubs(entries, namespaces, prefix_name, verbatim, filename, True) |
| elif mode == 'dispatch_logging_wrappers': |
| gen_dispatch_logging_wrappers(entries) |
| |
| # List of valid --mode option values. |
| mode_list = [ |
| 'def', 'sym', 'translator_passthrough', 'wrapper', 'symbols', '_symbols', 'functions', 'funcargs', 'static_translator_namespaced_header', 'static_translator_namespaced_stubs', 'dispatch_logging_wrappers', |
| ] |
| |
| # Argument parsing. |
| parser = argparse.ArgumentParser( |
| formatter_class=argparse.RawDescriptionHelpFormatter, |
| description="""\ |
| A script used to parse an .entries input file containing a list of function |
| declarations, and generate various output files depending on the value of |
| the --mode option, which can be: |
| |
| def Generate a windows DLL .def file. |
| sym Generate a Unix .so linker script. |
| wrapper Generate a C source file containing wrapper functions. |
| symbols Generate a simple list of symbols, one per line. |
| _symbols Generate a simple list of symbols, prefixed with _. |
| functions Generate a C header containing a macro listing all functions. |
| funcargs Like 'functions', but adds function call arguments to listing. |
| static_translator_namespaced_header Generate C++ header with namespaced versions of the api declarations. |
| dispatch_logging_wrappers Generate C++ functions which debug log the function with arguments and then call the underlying function. |
| |
| """) |
| parser.add_argument("--mode", help="Output mode", choices=mode_list) |
| parser.add_argument("--output", help="output file") |
| parser.add_argument("file", help=".entries file path") |
| |
| args = parser.parse_args() |
| |
| if not args.mode: |
| sys,stderr.write("ERROR: Please use --mode=<name>, see --help.") |
| sys.exit(1) |
| |
| if args.output: |
| sys.stdout = open(args.output, "w+") |
| |
| if args.file == '--': |
| parse_file("<stdin>", sys.stdin, args.mode) |
| else: |
| parse_file(args.file, open(args.file), args.mode) |