xref: /dpdk/devtools/update_version_map_abi.py (revision f35e5b3e07b2e7999f7d3085236cc366c9cb4da6)
1#!/usr/bin/env python
2# SPDX-License-Identifier: BSD-3-Clause
3# Copyright(c) 2019 Intel Corporation
4
5"""
6A Python program that updates and merges all available stable ABI versions into
7one ABI version, while leaving experimental ABI exactly as it is. The intended
8ABI version is supplied via command-line parameter. This script is to be called
9from the devtools/update-abi.sh utility.
10"""
11
12from __future__ import print_function
13import argparse
14import sys
15import re
16
17
18def __parse_map_file(f_in):
19    # match function name, followed by semicolon, followed by EOL, optionally
20    # with whitespace in between each item
21    func_line_regex = re.compile(r"\s*"
22                                 r"(?P<func>[a-zA-Z_0-9]+)"
23                                 r"\s*"
24                                 r";"
25                                 r"\s*"
26                                 r"$")
27    # match section name, followed by opening bracked, followed by EOL,
28    # optionally with whitespace in between each item
29    section_begin_regex = re.compile(r"\s*"
30                                     r"(?P<version>[a-zA-Z0-9_\.]+)"
31                                     r"\s*"
32                                     r"{"
33                                     r"\s*"
34                                     r"$")
35    # match closing bracket, optionally followed by section name (for when we
36    # inherit from another ABI version), followed by semicolon, followed by
37    # EOL, optionally with whitespace in between each item
38    section_end_regex = re.compile(r"\s*"
39                                   r"}"
40                                   r"\s*"
41                                   r"(?P<parent>[a-zA-Z0-9_\.]+)?"
42                                   r"\s*"
43                                   r";"
44                                   r"\s*"
45                                   r"$")
46
47    # for stable ABI, we don't care about which version introduced which
48    # function, we just flatten the list. there are dupes in certain files, so
49    # use a set instead of a list
50    stable_lines = set()
51    # copy experimental section as is
52    experimental_lines = []
53    in_experimental = False
54    has_stable = False
55
56    # gather all functions
57    for line in f_in:
58        # clean up the line
59        line = line.strip('\n').strip()
60
61        # is this an end of section?
62        match = section_end_regex.match(line)
63        if match:
64            # whatever section this was, it's not active any more
65            in_experimental = False
66            continue
67
68        # if we're in the middle of experimental section, we need to copy
69        # the section verbatim, so just add the line
70        if in_experimental:
71            experimental_lines += [line]
72            continue
73
74        # skip empty lines
75        if not line:
76            continue
77
78        # is this a beginning of a new section?
79        match = section_begin_regex.match(line)
80        if match:
81            cur_section = match.group("version")
82            # is it experimental?
83            in_experimental = cur_section == "EXPERIMENTAL"
84            if not in_experimental:
85                has_stable = True
86            continue
87
88        # is this a function?
89        match = func_line_regex.match(line)
90        if match:
91            stable_lines.add(match.group("func"))
92
93    return has_stable, stable_lines, experimental_lines
94
95
96def __generate_stable_abi(f_out, abi_version, lines):
97    # print ABI version header
98    print("DPDK_{} {{".format(abi_version), file=f_out)
99
100    # print global section if it exists
101    if lines:
102        print("\tglobal:", file=f_out)
103        # blank line
104        print(file=f_out)
105
106        # print all stable lines, alphabetically sorted
107        for line in sorted(lines):
108            print("\t{};".format(line), file=f_out)
109
110        # another blank line
111        print(file=f_out)
112
113    # print local section
114    print("\tlocal: *;", file=f_out)
115
116    # end stable version
117    print("};", file=f_out)
118
119
120def __generate_experimental_abi(f_out, lines):
121    # start experimental section
122    print("EXPERIMENTAL {", file=f_out)
123
124    # print all experimental lines as they were
125    for line in lines:
126        # don't print empty whitespace
127        if not line:
128            print("", file=f_out)
129        else:
130            print("\t{}".format(line), file=f_out)
131
132    # end section
133    print("};", file=f_out)
134
135
136def __main():
137    arg_parser = argparse.ArgumentParser(
138        description='Merge versions in linker version script.')
139
140    arg_parser.add_argument("map_file", type=str,
141                            help='path to linker version script file '
142                                 '(pattern: *version.map)')
143    arg_parser.add_argument("abi_version", type=str,
144                            help='target ABI version (pattern: MAJOR.MINOR)')
145
146    parsed = arg_parser.parse_args()
147
148    if not parsed.map_file.endswith('version.map'):
149        print("Invalid input file: {}".format(parsed.map_file),
150              file=sys.stderr)
151        arg_parser.print_help()
152        sys.exit(1)
153
154    if not re.match(r"\d{1,2}\.\d{1,2}", parsed.abi_version):
155        print("Invalid ABI version: {}".format(parsed.abi_version),
156              file=sys.stderr)
157        arg_parser.print_help()
158        sys.exit(1)
159
160    with open(parsed.map_file) as f_in:
161        has_stable, stable_lines, experimental_lines = __parse_map_file(f_in)
162
163    with open(parsed.map_file, 'w') as f_out:
164        need_newline = has_stable and experimental_lines
165        if has_stable:
166            __generate_stable_abi(f_out, parsed.abi_version, stable_lines)
167        if need_newline:
168            # separate sections with a newline
169            print(file=f_out)
170        if experimental_lines:
171            __generate_experimental_abi(f_out, experimental_lines)
172
173
174if __name__ == "__main__":
175    __main()
176