1#!/usr/bin/env python3 2 3# 4# //===----------------------------------------------------------------------===// 5# // 6# // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 7# // See https://llvm.org/LICENSE.txt for license information. 8# // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 9# // 10# //===----------------------------------------------------------------------===// 11# 12 13import argparse 14import os 15import re 16import sys 17from libomputils import error, ScriptError, print_error_line 18 19 20class DllExports(object): 21 def __init__(self): 22 self.filename = None 23 self.exports = {} 24 self.ordinals = set([]) 25 26 def add_uppercase_entries(self): 27 # Ignored entries are C/C++ only functions 28 ignores = [ 29 "omp_alloc", 30 "omp_free", 31 "omp_calloc", 32 "omp_realloc", 33 "omp_aligned_alloc", 34 "omp_aligned_calloc", 35 ] 36 keys = list(self.exports.keys()) 37 for entry in keys: 38 info = self.exports[entry] 39 if info["obsolete"] or info["is_data"] or entry in ignores: 40 continue 41 if entry.startswith("omp_") or entry.startswith("kmp_"): 42 newentry = entry.upper() 43 if info["ordinal"]: 44 newordinal = info["ordinal"] + 1000 45 else: 46 newordinal = None 47 self.exports[newentry] = { 48 "obsolete": False, 49 "is_data": False, 50 "ordinal": newordinal, 51 } 52 53 @staticmethod 54 def create(inputFile, defs=None): 55 """Creates DllExports object from inputFile""" 56 dllexports = DllExports() 57 dllexports.filename = inputFile 58 # Create a (possibly empty) list of definitions 59 if defs: 60 definitions = set(list(defs)) 61 else: 62 definitions = set([]) 63 # Different kinds of lines to parse 64 kw = r"[a-zA-Z_][a-zA-Z0-9_]*" 65 ifndef = re.compile(r"%ifndef\s+({})".format(kw)) 66 ifdef = re.compile(r"%ifdef\s+({})".format(kw)) 67 endif = re.compile(r"%endif") 68 export = re.compile(r"(-)?\s*({0})(=({0}))?(\s+([0-9]+|DATA))?".format(kw)) 69 70 def err(fil, num, msg): 71 error("{}: {}: {}".format(fil, num, msg)) 72 73 defs_stack = [] 74 with open(inputFile) as f: 75 for lineNumber, line in enumerate(f): 76 line = line.strip() 77 # Skip empty lines 78 if not line: 79 continue 80 # Skip comment lines 81 if line.startswith("#"): 82 continue 83 # Encountered %ifndef DEF 84 m = ifndef.search(line) 85 if m: 86 defs_stack.append(m.group(1) not in definitions) 87 continue 88 # Encountered %ifdef DEF 89 m = ifdef.search(line) 90 if m: 91 defs_stack.append(m.group(1) in definitions) 92 continue 93 # Encountered %endif 94 m = endif.search(line) 95 if m: 96 if not defs_stack: 97 err(inputFile, lineNumber, "orphan %endif directive") 98 defs_stack.pop() 99 continue 100 # Skip lines when not all %ifdef or %ifndef are true 101 if defs_stack and not all(defs_stack): 102 continue 103 # Encountered an export line 104 m = export.search(line) 105 if m: 106 obsolete = m.group(1) is not None 107 entry = m.group(2) 108 rename = m.group(4) 109 ordinal = m.group(6) 110 if entry in dllexports.exports: 111 err( 112 inputFile, 113 lineNumber, 114 "already specified entry: {}".format(entry), 115 ) 116 if rename: 117 entry += "={}".format(rename) 118 # No ordinal number nor DATA specified 119 if not ordinal: 120 ordinal = None 121 is_data = False 122 # DATA ordinal 123 elif ordinal == "DATA": 124 ordinal = None 125 is_data = True 126 # Regular ordinal number 127 else: 128 is_data = False 129 try: 130 ordinal = int(ordinal) 131 except: 132 err( 133 inputFile, 134 lineNumber, 135 "Bad ordinal value: {}".format(ordinal), 136 ) 137 if ordinal >= 1000 and ( 138 entry.startswith("omp_") or entry.startswith("kmp_") 139 ): 140 err( 141 inputFile, 142 lineNumber, 143 "Ordinal of user-callable entry must be < 1000", 144 ) 145 if ordinal >= 1000 and ordinal < 2000: 146 err( 147 inputFile, 148 lineNumber, 149 "Ordinals between 1000 and 1999 are reserved.", 150 ) 151 if ordinal in dllexports.ordinals: 152 err( 153 inputFile, 154 lineNumber, 155 "Ordinal {} has already been used.".format(ordinal), 156 ) 157 dllexports.exports[entry] = { 158 "ordinal": ordinal, 159 "obsolete": obsolete, 160 "is_data": is_data, 161 } 162 continue 163 err( 164 inputFile, 165 lineNumber, 166 'Cannot parse line:{}"{}"'.format(os.linesep, line), 167 ) 168 if defs_stack: 169 error("syntax error: Unterminated %if directive") 170 return dllexports 171 172 173def generate_def(dllexports, f, no_ordinals=False, name=None): 174 """Using dllexports data, write the exports to file, f""" 175 if name: 176 f.write("LIBRARY {}\n".format(name)) 177 f.write("EXPORTS\n") 178 for entry in sorted(list(dllexports.exports.keys())): 179 info = dllexports.exports[entry] 180 if info["obsolete"]: 181 continue 182 f.write(" {:<40} ".format(entry)) 183 if info["is_data"]: 184 f.write("DATA\n") 185 elif no_ordinals or not info["ordinal"]: 186 f.write("\n") 187 else: 188 f.write("@{}\n".format(info["ordinal"])) 189 190 191def main(): 192 parser = argparse.ArgumentParser( 193 description="Reads input file of dllexports, processes conditional" 194 " directives, checks content for consistency, and generates" 195 " output file suitable for linker" 196 ) 197 parser.add_argument( 198 "-D", 199 metavar="DEF", 200 action="append", 201 dest="defs", 202 help="Define a variable. Can specify" " this more than once.", 203 ) 204 parser.add_argument( 205 "--no-ordinals", 206 action="store_true", 207 help="Specify that no ordinal numbers should be generated", 208 ) 209 parser.add_argument( 210 "-n", 211 "--name", 212 dest="name", 213 help="Specify library name for def file LIBRARY statement", 214 ) 215 parser.add_argument( 216 "-o", 217 "--output", 218 metavar="FILE", 219 dest="output", 220 help="Specify output file name. If not specified," " output is sent to stdout", 221 ) 222 parser.add_argument("dllexports", help="The input file describing dllexports") 223 commandArgs = parser.parse_args() 224 defs = set([]) 225 if commandArgs.defs: 226 defs = set(commandArgs.defs) 227 dllexports = DllExports.create(commandArgs.dllexports, defs) 228 dllexports.add_uppercase_entries() 229 try: 230 output = open(commandArgs.output, "w") if commandArgs.output else sys.stdout 231 generate_def(dllexports, output, commandArgs.no_ordinals, commandArgs.name) 232 finally: 233 if commandArgs.output: 234 output.close() 235 236 237if __name__ == "__main__": 238 try: 239 main() 240 except ScriptError as e: 241 print_error_line(str(e)) 242 sys.exit(1) 243 244# end of file 245