xref: /llvm-project/openmp/runtime/tools/generate-def.py (revision 88dae3d5d0230747f3cbabdde9ac5ae9e5dc3f8d)
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