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 # copy internal section as is 54 internal_lines = [] 55 in_experimental = False 56 in_internal = False 57 has_stable = False 58 59 # gather all functions 60 for line in f_in: 61 # clean up the line 62 line = line.strip('\n').strip() 63 64 # is this an end of section? 65 match = section_end_regex.match(line) 66 if match: 67 # whatever section this was, it's not active any more 68 in_experimental = False 69 in_internal = False 70 continue 71 72 # if we're in the middle of experimental section, we need to copy 73 # the section verbatim, so just add the line 74 if in_experimental: 75 experimental_lines += [line] 76 continue 77 78 # if we're in the middle of internal section, we need to copy 79 # the section verbatim, so just add the line 80 if in_internal: 81 internal_lines += [line] 82 continue 83 84 # skip empty lines 85 if not line: 86 continue 87 88 # is this a beginning of a new section? 89 match = section_begin_regex.match(line) 90 if match: 91 cur_section = match.group("version") 92 # is it experimental? 93 in_experimental = cur_section == "EXPERIMENTAL" 94 # is it internal? 95 in_internal = cur_section == "INTERNAL" 96 if not in_experimental and not in_internal: 97 has_stable = True 98 continue 99 100 # is this a function? 101 match = func_line_regex.match(line) 102 if match: 103 stable_lines.add(match.group("func")) 104 105 return has_stable, stable_lines, experimental_lines, internal_lines 106 107 108def __generate_stable_abi(f_out, abi_major, lines): 109 # print ABI version header 110 print("DPDK_{} {{".format(abi_major), file=f_out) 111 112 # print global section if it exists 113 if lines: 114 print("\tglobal:", file=f_out) 115 # blank line 116 print(file=f_out) 117 118 # print all stable lines, alphabetically sorted 119 for line in sorted(lines): 120 print("\t{};".format(line), file=f_out) 121 122 # another blank line 123 print(file=f_out) 124 125 # print local section 126 print("\tlocal: *;", file=f_out) 127 128 # end stable version 129 print("};", file=f_out) 130 131 132def __generate_experimental_abi(f_out, lines): 133 # start experimental section 134 print("EXPERIMENTAL {", file=f_out) 135 136 # print all experimental lines as they were 137 for line in lines: 138 # don't print empty whitespace 139 if not line: 140 print("", file=f_out) 141 else: 142 print("\t{}".format(line), file=f_out) 143 144 # end section 145 print("};", file=f_out) 146 147def __generate_internal_abi(f_out, lines): 148 # start internal section 149 print("INTERNAL {", file=f_out) 150 151 # print all internal lines as they were 152 for line in lines: 153 # don't print empty whitespace 154 if not line: 155 print("", file=f_out) 156 else: 157 print("\t{}".format(line), file=f_out) 158 159 # end section 160 print("};", file=f_out) 161 162def __main(): 163 if sys.version_info.major < 3: 164 print("WARNING: Python 2 is deprecated for use in DPDK, and will not work in future releases.", file=sys.stderr) 165 print("Please use Python 3 instead", file=sys.stderr) 166 167 arg_parser = argparse.ArgumentParser( 168 description='Merge versions in linker version script.') 169 170 arg_parser.add_argument("map_file", type=str, 171 help='path to linker version script file ' 172 '(pattern: *version.map)') 173 arg_parser.add_argument("abi_version", type=str, 174 help='target ABI version (pattern: MAJOR.MINOR)') 175 176 parsed = arg_parser.parse_args() 177 178 if not parsed.map_file.endswith('version.map'): 179 print("Invalid input file: {}".format(parsed.map_file), 180 file=sys.stderr) 181 arg_parser.print_help() 182 sys.exit(1) 183 184 if not re.match(r"\d{1,2}\.\d{1,2}", parsed.abi_version): 185 print("Invalid ABI version: {}".format(parsed.abi_version), 186 file=sys.stderr) 187 arg_parser.print_help() 188 sys.exit(1) 189 abi_major = parsed.abi_version.split('.')[0] 190 191 with open(parsed.map_file) as f_in: 192 has_stable, stable_lines, experimental_lines, internal_lines = __parse_map_file(f_in) 193 194 with open(parsed.map_file, 'w') as f_out: 195 need_newline = has_stable and experimental_lines 196 if has_stable: 197 __generate_stable_abi(f_out, abi_major, stable_lines) 198 if need_newline: 199 # separate sections with a newline 200 print(file=f_out) 201 if experimental_lines: 202 __generate_experimental_abi(f_out, experimental_lines) 203 if internal_lines: 204 if has_stable or experimental_lines: 205 # separate sections with a newline 206 print(file=f_out) 207 __generate_internal_abi(f_out, internal_lines) 208 209 210if __name__ == "__main__": 211 __main() 212