Skip to content
Snippets Groups Projects
Select Git revision
  • c9ad0d1c820ce63e04afa472a59a7aa34a8a31c4
  • test default
2 results

gen_entries.py

Blame
  • user avatar
    Greg Hartman authored
    Change-Id: I2ddf71162da32b99e4c5f23bc0e505bcc43fb75e
    Test: Compiles
    b8b13e8c
    History
    gen_entries.py 10.49 KiB
    #!/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] = "host/commands/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
    
    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 == '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', '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)