1#!/usr/bin/env python 2 3# Copyright 2015 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17# Utility functions used to parse a list of DLL entry points. 18# Expected format: 19# 20# <empty-line> -> ignored 21# #<comment> -> ignored 22# %<verbatim> -> verbatim output for header files. 23# !<prefix> -> prefix name for header files. 24# <return-type> <function-name> <signature> ; -> entry point declaration. 25# 26# Anything else is an error. 27 28import re 29import sys 30import argparse 31 32re_func = re.compile(r"""^(.*[\* ])([A-Za-z_][A-Za-z0-9_]*)\((.*)\);$""") 33re_param = re.compile(r"""^(.*[\* ])([A-Za-z_][A-Za-z0-9_]*)$""") 34 35class Entry: 36 """Small class used to model a single DLL entry point.""" 37 def __init__(self, func_name, return_type, parameters): 38 """Initialize Entry instance. |func_name| is the function name, 39 |return_type| its return type, and |parameters| is a list of 40 (type,name) tuples from the entry's signature. 41 """ 42 self.func_name = func_name 43 self.return_type = return_type 44 self.parameters = "" 45 self.vartypes = [] 46 self.varnames = [] 47 self.call = "" 48 comma = "" 49 for param in parameters: 50 self.vartypes.append(param[0]) 51 self.varnames.append(param[1]) 52 self.parameters += "%s%s %s" % (comma, param[0], param[1]) 53 self.call += "%s%s" % (comma, param[1]) 54 comma = ", " 55 56def banner_command(argv): 57 """Return sanitized command-line description. 58 |argv| must be a list of command-line parameters, e.g. sys.argv. 59 Return a string corresponding to the command, with platform-specific 60 paths removed.""" 61 62 # Remove path from first parameter 63 argv = argv[:] 64 argv[0] = "host/commands/gen-entries.py" 65 return ' '.join(argv) 66 67def parse_entries_file(lines): 68 """Parse an .entries file and return a tuple of: 69 entries: list of Entry instances from the file. 70 prefix_name: prefix name from the file, or None. 71 verbatim: list of verbatim lines from the file. 72 errors: list of errors in the file, prefixed by line number. 73 """ 74 entries = [] 75 verbatim = [] 76 errors = [] 77 lineno = 0 78 prefix_name = None 79 for line in lines: 80 lineno += 1 81 line = line.strip() 82 if len(line) == 0: # Ignore empty lines 83 continue 84 if line[0] == '#': # Ignore comments 85 continue 86 if line[0] == '!': # Prefix name 87 prefix_name = line[1:] 88 continue 89 if line[0] == '%': # Verbatim line copy 90 verbatim.append(line[1:]) 91 continue 92 # Must be a function signature. 93 m = re_func.match(line) 94 if not m: 95 errors.append("%d: '%s'" % (lineno, line)) 96 continue 97 98 return_type, func_name, parameters = m.groups() 99 return_type = return_type.strip() 100 parameters = parameters.strip() 101 params = [] 102 failure = False 103 if parameters != "void": 104 for parameter in parameters.split(','): 105 parameter = parameter.strip() 106 m = re_param.match(parameter) 107 if not m: 108 errors.append("%d: parameter '%s'" % (lineno, parameter)) 109 failure = True 110 break 111 else: 112 param_type, param_name = m.groups() 113 params.append((param_type.strip(), param_name.strip())) 114 115 if not failure: 116 entries.append(Entry(func_name, return_type, params)) 117 118 return (entries, prefix_name, verbatim, errors) 119 120 121def gen_functions_header(entries, prefix_name, verbatim, filename, with_args): 122 """Generate a C header containing a macro listing all entry points. 123 |entries| is a list of Entry instances. 124 |prefix_name| is a prefix-name, it will be converted to upper-case. 125 |verbatim| is a list of verbatim lines that must appear before the 126 macro declaration. Useful to insert #include <> statements. 127 |filename| is the name of the original file. 128 """ 129 prefix_name = prefix_name.upper() 130 131 print "// Auto-generated with: %s" % banner_command(sys.argv) 132 print "// DO NOT EDIT THIS FILE" 133 print "" 134 print "#ifndef %s_FUNCTIONS_H" % prefix_name 135 print "#define %s_FUNCTIONS_H" % prefix_name 136 print "" 137 for line in verbatim: 138 print line 139 140 print "#define LIST_%s_FUNCTIONS(X) \\" % prefix_name 141 for entry in entries: 142 if with_args: 143 print " X(%s, %s, (%s), (%s)) \\" % \ 144 (entry.return_type, entry.func_name, entry.parameters, 145 entry.call) 146 else: 147 print " X(%s, %s, (%s)) \\" % \ 148 (entry.return_type, entry.func_name, entry.parameters) 149 150 print "" 151 print "" 152 print "#endif // %s_FUNCTIONS_H" % prefix_name 153 154def gen_dll_wrapper(entries, prefix_name, verbatim, filename): 155 """Generate a C source file that contains functions that act as wrappers 156 for entry points located in another shared library. This allows the 157 code that calls these functions to perform lazy-linking to system 158 libraries. 159 |entries|, |prefix_name|, |verbatim| and |filename| are the same as 160 for gen_functions_header() above. 161 """ 162 upper_name = prefix_name.upper() 163 164 ENTRY_PREFIX = "__dll_" 165 166 print "// Auto-generated with: %s" % banner_command(sys.argv) 167 print "// DO NOT EDIT THIS FILE" 168 print "" 169 print "#include <dlfcn.h>" 170 for line in verbatim: 171 print line 172 173 print "" 174 print "///" 175 print "/// W R A P P E R P O I N T E R S" 176 print "///" 177 print "" 178 for entry in entries: 179 ptr_name = ENTRY_PREFIX + entry.func_name 180 print "static %s (*%s)(%s) = 0;" % \ 181 (entry.return_type, ptr_name, entry.parameters) 182 183 print "" 184 print "///" 185 print "/// W R A P P E R F U N C T I O N S" 186 print "///" 187 print "" 188 189 for entry in entries: 190 print "%s %s(%s) {" % \ 191 (entry.return_type, entry.func_name, entry.parameters) 192 ptr_name = ENTRY_PREFIX + entry.func_name 193 if entry.return_type != "void": 194 print " return %s(%s);" % (ptr_name, entry.call) 195 else: 196 print " %s(%s);" % (ptr_name, entry.call) 197 print "}\n" 198 199 print "" 200 print "///" 201 print "/// I N I T I A L I Z A T I O N F U N C T I O N" 202 print "///" 203 print "" 204 205 print "int %s_dynlink_init(void* lib) {" % prefix_name 206 for entry in entries: 207 ptr_name = ENTRY_PREFIX + entry.func_name 208 print " %s = (%s(*)(%s))dlsym(lib, \"%s\");" % \ 209 (ptr_name, 210 entry.return_type, 211 entry.parameters, 212 entry.func_name) 213 print " if (!%s) return -1;" % ptr_name 214 print " return 0;" 215 print "}" 216 217 218def gen_windows_def_file(entries): 219 """Generate a windows DLL .def file. |entries| is a list of Entry instances. 220 """ 221 print "EXPORTS" 222 for entry in entries: 223 print " %s" % entry.func_name 224 225 226def gen_unix_sym_file(entries): 227 """Generate an ELF linker version file. |entries| is a list of Entry 228 instances. 229 """ 230 print "VERSION {" 231 print "\tglobal:" 232 for entry in entries: 233 print "\t\t%s;" % entry.func_name 234 print "\tlocal:" 235 print "\t\t*;" 236 print "};" 237 238def gen_symbols(entries, underscore): 239 """Generate a list of symbols from |entries|, a list of Entry instances. 240 |underscore| is a boolean. If True, then prepend an underscore to each 241 symbol name. 242 """ 243 prefix = "" 244 if underscore: 245 prefix = "_" 246 for entry in entries: 247 print "%s%s" % (prefix, entry.func_name) 248 249def parse_file(filename, lines, mode): 250 """Generate one of possible outputs from |filename|. |lines| must be a list 251 of text lines from the file, and |mode| is one of the --mode option 252 values. 253 """ 254 entries, prefix_name, verbatim, errors = parse_entries_file(lines) 255 if errors: 256 for error in errors: 257 print >> sys.stderr, "ERROR: %s:%s" % (filename, error) 258 sys.exit(1) 259 260 if not prefix_name: 261 prefix_name = "unknown" 262 263 if mode == 'def': 264 gen_windows_def_file(entries) 265 elif mode == 'sym': 266 gen_unix_sym_file(entries) 267 elif mode == 'wrapper': 268 gen_dll_wrapper(entries, prefix_name, verbatim, filename) 269 elif mode == 'symbols': 270 gen_symbols(entries, False) 271 elif mode == '_symbols': 272 gen_symbols(entries, True) 273 elif mode == 'functions': 274 gen_functions_header(entries, prefix_name, verbatim, filename, False) 275 elif mode == 'funcargs': 276 gen_functions_header(entries, prefix_name, verbatim, filename, True) 277 278 279# List of valid --mode option values. 280mode_list = [ 281 'def', 'sym', 'wrapper', 'symbols', '_symbols', 'functions', 'funcargs' 282] 283 284# Argument parsing. 285parser = argparse.ArgumentParser( 286 formatter_class=argparse.RawDescriptionHelpFormatter, 287 description="""\ 288A script used to parse an .entries input file containing a list of function 289declarations, and generate various output files depending on the value of 290the --mode option, which can be: 291 292 def Generate a windows DLL .def file. 293 sym Generate a Unix .so linker script. 294 wrapper Generate a C source file containing wrapper functions. 295 symbols Generate a simple list of symbols, one per line. 296 _symbols Generate a simple list of symbols, prefixed with _. 297 functions Generate a C header containing a macro listing all functions. 298 funcargs Like 'functions', but adds function call arguments to listing. 299 300""") 301parser.add_argument("--mode", help="Output mode", choices=mode_list) 302parser.add_argument("--output", help="output file") 303parser.add_argument("file", help=".entries file path") 304 305args = parser.parse_args() 306 307if not args.mode: 308 print >> sys.stderr, "ERROR: Please use --mode=<name>, see --help." 309 sys.exit(1) 310 311if args.output: 312 sys.stdout = open(args.output, "w+") 313 314if args.file == '--': 315 parse_file("<stdin>", sys.stdin, args.mode) 316else: 317 parse_file(args.file, open(args.file), args.mode) 318