#!/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