1#!/usr/bin/env python3 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 12import argparse 13import sys 14import re 15 16 17def __parse_map_file(f_in): 18 # match function name, followed by semicolon, followed by EOL, optionally 19 # with whitespace in between each item 20 func_line_regex = re.compile(r"\s*" 21 r"(?P<func>[a-zA-Z_0-9]+)" 22 r"\s*" 23 r";" 24 r"\s*" 25 r"$") 26 # match section name, followed by opening bracked, followed by EOL, 27 # optionally with whitespace in between each item 28 section_begin_regex = re.compile(r"\s*" 29 r"(?P<version>[a-zA-Z0-9_\.]+)" 30 r"\s*" 31 r"{" 32 r"\s*" 33 r"$") 34 # match closing bracket, optionally followed by section name (for when we 35 # inherit from another ABI version), followed by semicolon, followed by 36 # EOL, optionally with whitespace in between each item 37 section_end_regex = re.compile(r"\s*" 38 r"}" 39 r"\s*" 40 r"(?P<parent>[a-zA-Z0-9_\.]+)?" 41 r"\s*" 42 r";" 43 r"\s*" 44 r"$") 45 46 # for stable ABI, we don't care about which version introduced which 47 # function, we just flatten the list. there are dupes in certain files, so 48 # use a set instead of a list 49 stable_lines = set() 50 # copy experimental section as is 51 experimental_lines = [] 52 # copy internal section as is 53 internal_lines = [] 54 in_experimental = False 55 in_internal = False 56 has_stable = False 57 58 # gather all functions 59 for line in f_in: 60 # clean up the line 61 line = line.strip('\n').strip() 62 63 # is this an end of section? 64 match = section_end_regex.match(line) 65 if match: 66 # whatever section this was, it's not active any more 67 in_experimental = False 68 in_internal = False 69 continue 70 71 # if we're in the middle of experimental section, we need to copy 72 # the section verbatim, so just add the line 73 if in_experimental: 74 experimental_lines += [line] 75 continue 76 77 # if we're in the middle of internal section, we need to copy 78 # the section verbatim, so just add the line 79 if in_internal: 80 internal_lines += [line] 81 continue 82 83 # skip empty lines 84 if not line: 85 continue 86 87 # is this a beginning of a new section? 88 match = section_begin_regex.match(line) 89 if match: 90 cur_section = match.group("version") 91 # is it experimental? 92 in_experimental = cur_section == "EXPERIMENTAL" 93 # is it internal? 94 in_internal = cur_section == "INTERNAL" 95 if not in_experimental and not in_internal: 96 has_stable = True 97 continue 98 99 # is this a function? 100 match = func_line_regex.match(line) 101 if match: 102 stable_lines.add(match.group("func")) 103 104 return has_stable, stable_lines, experimental_lines, internal_lines 105 106 107def __generate_stable_abi(f_out, abi_major, lines): 108 # print ABI version header 109 print("DPDK_{} {{".format(abi_major), file=f_out) 110 111 # print global section if it exists 112 if lines: 113 print("\tglobal:", file=f_out) 114 # blank line 115 print(file=f_out) 116 117 # print all stable lines, alphabetically sorted 118 for line in sorted(lines): 119 print("\t{};".format(line), file=f_out) 120 121 # another blank line 122 print(file=f_out) 123 124 # print local section 125 print("\tlocal: *;", file=f_out) 126 127 # end stable version 128 print("};", file=f_out) 129 130 131def __generate_experimental_abi(f_out, lines): 132 # start experimental section 133 print("EXPERIMENTAL {", file=f_out) 134 135 # print all experimental lines as they were 136 for line in lines: 137 # don't print empty whitespace 138 if not line: 139 print("", file=f_out) 140 else: 141 print("\t{}".format(line), file=f_out) 142 143 # end section 144 print("};", file=f_out) 145 146def __generate_internal_abi(f_out, lines): 147 # start internal section 148 print("INTERNAL {", file=f_out) 149 150 # print all internal lines as they were 151 for line in lines: 152 # don't print empty whitespace 153 if not line: 154 print("", file=f_out) 155 else: 156 print("\t{}".format(line), file=f_out) 157 158 # end section 159 print("};", file=f_out) 160 161def __main(): 162 arg_parser = argparse.ArgumentParser( 163 description='Merge versions in linker version script.') 164 165 arg_parser.add_argument("map_file", type=str, 166 help='path to linker version script file ' 167 '(pattern: *version.map)') 168 arg_parser.add_argument("abi_version", type=str, 169 help='target ABI version (pattern: MAJOR.MINOR)') 170 171 parsed = arg_parser.parse_args() 172 173 if not parsed.map_file.endswith('version.map'): 174 print("Invalid input file: {}".format(parsed.map_file), 175 file=sys.stderr) 176 arg_parser.print_help() 177 sys.exit(1) 178 179 if not re.match(r"\d{1,2}\.\d{1,2}", parsed.abi_version): 180 print("Invalid ABI version: {}".format(parsed.abi_version), 181 file=sys.stderr) 182 arg_parser.print_help() 183 sys.exit(1) 184 abi_major = parsed.abi_version.split('.')[0] 185 186 with open(parsed.map_file) as f_in: 187 has_stable, stable_lines, experimental_lines, internal_lines = __parse_map_file(f_in) 188 189 with open(parsed.map_file, 'w') as f_out: 190 need_newline = has_stable and experimental_lines 191 if has_stable: 192 __generate_stable_abi(f_out, abi_major, stable_lines) 193 if need_newline: 194 # separate sections with a newline 195 print(file=f_out) 196 if experimental_lines: 197 __generate_experimental_abi(f_out, experimental_lines) 198 if internal_lines: 199 if has_stable or experimental_lines: 200 # separate sections with a newline 201 print(file=f_out) 202 __generate_internal_abi(f_out, internal_lines) 203 204 205if __name__ == "__main__": 206 __main() 207