| #!/usr/bin/env python3 |
| |
| # |
| # //===----------------------------------------------------------------------===// |
| # // |
| # // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| # // See https://llvm.org/LICENSE.txt for license information. |
| # // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| # // |
| # //===----------------------------------------------------------------------===// |
| # |
| |
| import argparse |
| import os |
| import re |
| import sys |
| from libomputils import error, ScriptError, print_error_line |
| |
| |
| class DllExports(object): |
| def __init__(self): |
| self.filename = None |
| self.exports = {} |
| self.ordinals = set([]) |
| |
| def add_uppercase_entries(self): |
| # Ignored entries are C/C++ only functions |
| ignores = [ |
| "omp_alloc", |
| "omp_free", |
| "omp_calloc", |
| "omp_realloc", |
| "omp_aligned_alloc", |
| "omp_aligned_calloc", |
| ] |
| keys = list(self.exports.keys()) |
| for entry in keys: |
| info = self.exports[entry] |
| if info["obsolete"] or info["is_data"] or entry in ignores: |
| continue |
| if entry.startswith("omp_") or entry.startswith("kmp_"): |
| newentry = entry.upper() |
| if info["ordinal"]: |
| newordinal = info["ordinal"] + 1000 |
| else: |
| newordinal = None |
| self.exports[newentry] = { |
| "obsolete": False, |
| "is_data": False, |
| "ordinal": newordinal, |
| } |
| |
| @staticmethod |
| def create(inputFile, defs=None): |
| """Creates DllExports object from inputFile""" |
| dllexports = DllExports() |
| dllexports.filename = inputFile |
| # Create a (possibly empty) list of definitions |
| if defs: |
| definitions = set(list(defs)) |
| else: |
| definitions = set([]) |
| # Different kinds of lines to parse |
| kw = r"[a-zA-Z_][a-zA-Z0-9_]*" |
| ifndef = re.compile(r"%ifndef\s+({})".format(kw)) |
| ifdef = re.compile(r"%ifdef\s+({})".format(kw)) |
| endif = re.compile(r"%endif") |
| export = re.compile(r"(-)?\s*({0})(=({0}))?(\s+([0-9]+|DATA))?".format(kw)) |
| |
| def err(fil, num, msg): |
| error("{}: {}: {}".format(fil, num, msg)) |
| |
| defs_stack = [] |
| with open(inputFile) as f: |
| for lineNumber, line in enumerate(f): |
| line = line.strip() |
| # Skip empty lines |
| if not line: |
| continue |
| # Skip comment lines |
| if line.startswith("#"): |
| continue |
| # Encountered %ifndef DEF |
| m = ifndef.search(line) |
| if m: |
| defs_stack.append(m.group(1) not in definitions) |
| continue |
| # Encountered %ifdef DEF |
| m = ifdef.search(line) |
| if m: |
| defs_stack.append(m.group(1) in definitions) |
| continue |
| # Encountered %endif |
| m = endif.search(line) |
| if m: |
| if not defs_stack: |
| err(inputFile, lineNumber, "orphan %endif directive") |
| defs_stack.pop() |
| continue |
| # Skip lines when not all %ifdef or %ifndef are true |
| if defs_stack and not all(defs_stack): |
| continue |
| # Encountered an export line |
| m = export.search(line) |
| if m: |
| obsolete = m.group(1) is not None |
| entry = m.group(2) |
| rename = m.group(4) |
| ordinal = m.group(6) |
| if entry in dllexports.exports: |
| err( |
| inputFile, |
| lineNumber, |
| "already specified entry: {}".format(entry), |
| ) |
| if rename: |
| entry += "={}".format(rename) |
| # No ordinal number nor DATA specified |
| if not ordinal: |
| ordinal = None |
| is_data = False |
| # DATA ordinal |
| elif ordinal == "DATA": |
| ordinal = None |
| is_data = True |
| # Regular ordinal number |
| else: |
| is_data = False |
| try: |
| ordinal = int(ordinal) |
| except: |
| err( |
| inputFile, |
| lineNumber, |
| "Bad ordinal value: {}".format(ordinal), |
| ) |
| if ordinal >= 1000 and ( |
| entry.startswith("omp_") or entry.startswith("kmp_") |
| ): |
| err( |
| inputFile, |
| lineNumber, |
| "Ordinal of user-callable entry must be < 1000", |
| ) |
| if ordinal >= 1000 and ordinal < 2000: |
| err( |
| inputFile, |
| lineNumber, |
| "Ordinals between 1000 and 1999 are reserved.", |
| ) |
| if ordinal in dllexports.ordinals: |
| err( |
| inputFile, |
| lineNumber, |
| "Ordinal {} has already been used.".format(ordinal), |
| ) |
| dllexports.exports[entry] = { |
| "ordinal": ordinal, |
| "obsolete": obsolete, |
| "is_data": is_data, |
| } |
| continue |
| err( |
| inputFile, |
| lineNumber, |
| 'Cannot parse line:{}"{}"'.format(os.linesep, line), |
| ) |
| if defs_stack: |
| error("syntax error: Unterminated %if directive") |
| return dllexports |
| |
| |
| def generate_def(dllexports, f, no_ordinals=False, name=None): |
| """Using dllexports data, write the exports to file, f""" |
| if name: |
| f.write("LIBRARY {}\n".format(name)) |
| f.write("EXPORTS\n") |
| for entry in sorted(list(dllexports.exports.keys())): |
| info = dllexports.exports[entry] |
| if info["obsolete"]: |
| continue |
| f.write(" {:<40} ".format(entry)) |
| if info["is_data"]: |
| f.write("DATA\n") |
| elif no_ordinals or not info["ordinal"]: |
| f.write("\n") |
| else: |
| f.write("@{}\n".format(info["ordinal"])) |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser( |
| description="Reads input file of dllexports, processes conditional" |
| " directives, checks content for consistency, and generates" |
| " output file suitable for linker" |
| ) |
| parser.add_argument( |
| "-D", |
| metavar="DEF", |
| action="append", |
| dest="defs", |
| help="Define a variable. Can specify" " this more than once.", |
| ) |
| parser.add_argument( |
| "--no-ordinals", |
| action="store_true", |
| help="Specify that no ordinal numbers should be generated", |
| ) |
| parser.add_argument( |
| "-n", |
| "--name", |
| dest="name", |
| help="Specify library name for def file LIBRARY statement", |
| ) |
| parser.add_argument( |
| "-o", |
| "--output", |
| metavar="FILE", |
| dest="output", |
| help="Specify output file name. If not specified," " output is sent to stdout", |
| ) |
| parser.add_argument("dllexports", help="The input file describing dllexports") |
| commandArgs = parser.parse_args() |
| defs = set([]) |
| if commandArgs.defs: |
| defs = set(commandArgs.defs) |
| dllexports = DllExports.create(commandArgs.dllexports, defs) |
| dllexports.add_uppercase_entries() |
| try: |
| output = open(commandArgs.output, "w") if commandArgs.output else sys.stdout |
| generate_def(dllexports, output, commandArgs.no_ordinals, commandArgs.name) |
| finally: |
| if commandArgs.output: |
| output.close() |
| |
| |
| if __name__ == "__main__": |
| try: |
| main() |
| except ScriptError as e: |
| print_error_line(str(e)) |
| sys.exit(1) |
| |
| # end of file |