blob: a122ef7e3eea050b5cd54f431143093fbbd7936f [file] [log] [blame]
#!/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.func_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
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
# 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, 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
for entry in entries:
if with_args:
print " X(%s, %s, (%s), (%s)) \\" % \
(entry.return_type, entry.func_name, entry.parameters,
entry.call)
else:
print " X(%s, %s, (%s)) \\" % \
(entry.return_type, entry.func_name, entry.parameters)
print ""
print ""
print "#endif // %s_FUNCTIONS_H" % prefix_name
# 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 gles30_custom.custom_share_processing.items():
translator_custom_share_processing[k] = v
for (k, v) in gles31_custom.custom_share_processing.items():
translator_custom_share_processing[k] = v
translator_custom_pre = { }
for (k, v) in gles30_custom.custom_preprocesses.items():
translator_custom_pre[k] = v
for (k, v) in gles31_custom.custom_preprocesses.items():
translator_custom_pre[k] = v
translator_custom_post = { }
for (k, v) in gles30_custom.custom_postprocesses.items():
translator_custom_post[k] = v
for (k, v) in gles31_custom.custom_postprocesses.items():
translator_custom_post[k] = v
translator_no_passthrough = {}
for (k, v) in gles30_custom.no_passthrough.items():
translator_no_passthrough[k] = v
for (k, v) in 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 translator_needexternc.has_key(entry.func_name):
return "extern \"C\" "
else:
return ""
def get_fail_code(entry):
if translator_nocontext_fail_codes.has_key(entry.func_name):
return translator_nocontext_fail_codes[entry.func_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.func_name.startswith("glGen")
isDelete = entry.func_name.startswith("glDelete")
isBufferOp = "Buffer" in entry.func_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 translator_custom_pre.has_key(entry.func_name):
print translator_custom_pre[entry.func_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 globalNameTypes.keys():
needsShareGroup = True
if needsShareGroup:
print " if (ctx->shareGroup().get()) {"
for key in zip(entry.vartypes, entry.varnames):
vartype, varname = key
if globalNames.has_key(key):
print " const GLuint %s = ctx->shareGroup()->getGlobalName(%s, %s);" % (globalNames[key], globalNameTypes[key], varname)
globalCall = ", ".join(map(lambda k: globalNames.get(k, k[1]), zip(entry.vartypes, entry.varnames)))
if needsShareGroup and translator_custom_share_processing.has_key(entry.func_name):
print translator_custom_share_processing[entry.func_name],
if (entry.return_type == "void"):
if (needsShareGroup):
print " ",
if not translator_no_passthrough.has_key(entry.func_name):
print " ctx->dispatcher().%s(%s);" % (entry.func_name, globalCall)
if needsShareGroup:
print " }"
if translator_custom_post.has_key(entry.func_name):
print translator_custom_post[entry.func_name];
else:
if (needsShareGroup):
print " ",
if not translator_no_passthrough.has_key(entry.func_name):
print " %s %s = ctx->dispatcher().%s(%s);" % (entry.return_type, entry.func_name + "RET", entry.func_name, globalCall)
else:
print " %s %s = %s" % (entry.return_type, entry_func_name + "RET", get_fail_code(entry))
if translator_custom_post.has_key(entry.func_name):
print translator_custom_post[entry.func_name];
print " return %s;" % (entry.func_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.func_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.func_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.func_name, entry.parameters)
ptr_name = ENTRY_PREFIX + entry.func_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.func_name
print " %s = (%s(*)(%s))dlsym(lib, \"%s\");" % \
(ptr_name,
entry.return_type,
entry.parameters,
entry.func_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.func_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.func_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.func_name)
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, errors = parse_entries_file(lines)
if errors:
for error in errors:
print >> sys.stderr, "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)
# List of valid --mode option values.
mode_list = [
'def', 'sym', 'translator_passthrough', 'wrapper', 'symbols', '_symbols', 'functions', 'funcargs'
]
# 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.
""")
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:
print >> sys.stderr, "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)