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_version, lines): 109 # print ABI version header 110 print("DPDK_{} {{".format(abi_version), 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 arg_parser = argparse.ArgumentParser( 164 description='Merge versions in linker version script.') 165 166 arg_parser.add_argument("map_file", type=str, 167 help='path to linker version script file ' 168 '(pattern: *version.map)') 169 arg_parser.add_argument("abi_version", type=str, 170 help='target ABI version (pattern: MAJOR.MINOR)') 171 172 parsed = arg_parser.parse_args() 173 174 if not parsed.map_file.endswith('version.map'): 175 print("Invalid input file: {}".format(parsed.map_file), 176 file=sys.stderr) 177 arg_parser.print_help() 178 sys.exit(1) 179 180 if not re.match(r"\d{1,2}\.\d{1,2}", parsed.abi_version): 181 print("Invalid ABI version: {}".format(parsed.abi_version), 182 file=sys.stderr) 183 arg_parser.print_help() 184 sys.exit(1) 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, parsed.abi_version, 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