1*2d9fd380Sjfb8856606#!/usr/bin/env python3 24418919fSjohnjiang# SPDX-License-Identifier: BSD-3-Clause 34418919fSjohnjiang# Copyright(c) 2019 Intel Corporation 44418919fSjohnjiang 54418919fSjohnjiang""" 64418919fSjohnjiangA Python program that updates and merges all available stable ABI versions into 74418919fSjohnjiangone ABI version, while leaving experimental ABI exactly as it is. The intended 84418919fSjohnjiangABI version is supplied via command-line parameter. This script is to be called 94418919fSjohnjiangfrom the devtools/update-abi.sh utility. 104418919fSjohnjiang""" 114418919fSjohnjiang 124418919fSjohnjiangimport argparse 134418919fSjohnjiangimport sys 144418919fSjohnjiangimport re 154418919fSjohnjiang 164418919fSjohnjiang 174418919fSjohnjiangdef __parse_map_file(f_in): 184418919fSjohnjiang # match function name, followed by semicolon, followed by EOL, optionally 194418919fSjohnjiang # with whitespace in between each item 204418919fSjohnjiang func_line_regex = re.compile(r"\s*" 214418919fSjohnjiang r"(?P<func>[a-zA-Z_0-9]+)" 224418919fSjohnjiang r"\s*" 234418919fSjohnjiang r";" 244418919fSjohnjiang r"\s*" 254418919fSjohnjiang r"$") 264418919fSjohnjiang # match section name, followed by opening bracked, followed by EOL, 274418919fSjohnjiang # optionally with whitespace in between each item 284418919fSjohnjiang section_begin_regex = re.compile(r"\s*" 294418919fSjohnjiang r"(?P<version>[a-zA-Z0-9_\.]+)" 304418919fSjohnjiang r"\s*" 314418919fSjohnjiang r"{" 324418919fSjohnjiang r"\s*" 334418919fSjohnjiang r"$") 344418919fSjohnjiang # match closing bracket, optionally followed by section name (for when we 354418919fSjohnjiang # inherit from another ABI version), followed by semicolon, followed by 364418919fSjohnjiang # EOL, optionally with whitespace in between each item 374418919fSjohnjiang section_end_regex = re.compile(r"\s*" 384418919fSjohnjiang r"}" 394418919fSjohnjiang r"\s*" 404418919fSjohnjiang r"(?P<parent>[a-zA-Z0-9_\.]+)?" 414418919fSjohnjiang r"\s*" 424418919fSjohnjiang r";" 434418919fSjohnjiang r"\s*" 444418919fSjohnjiang r"$") 454418919fSjohnjiang 464418919fSjohnjiang # for stable ABI, we don't care about which version introduced which 474418919fSjohnjiang # function, we just flatten the list. there are dupes in certain files, so 484418919fSjohnjiang # use a set instead of a list 494418919fSjohnjiang stable_lines = set() 504418919fSjohnjiang # copy experimental section as is 514418919fSjohnjiang experimental_lines = [] 52*2d9fd380Sjfb8856606 # copy internal section as is 53*2d9fd380Sjfb8856606 internal_lines = [] 544418919fSjohnjiang in_experimental = False 55*2d9fd380Sjfb8856606 in_internal = False 564418919fSjohnjiang has_stable = False 574418919fSjohnjiang 584418919fSjohnjiang # gather all functions 594418919fSjohnjiang for line in f_in: 604418919fSjohnjiang # clean up the line 614418919fSjohnjiang line = line.strip('\n').strip() 624418919fSjohnjiang 634418919fSjohnjiang # is this an end of section? 644418919fSjohnjiang match = section_end_regex.match(line) 654418919fSjohnjiang if match: 664418919fSjohnjiang # whatever section this was, it's not active any more 674418919fSjohnjiang in_experimental = False 68*2d9fd380Sjfb8856606 in_internal = False 694418919fSjohnjiang continue 704418919fSjohnjiang 714418919fSjohnjiang # if we're in the middle of experimental section, we need to copy 724418919fSjohnjiang # the section verbatim, so just add the line 734418919fSjohnjiang if in_experimental: 744418919fSjohnjiang experimental_lines += [line] 754418919fSjohnjiang continue 764418919fSjohnjiang 77*2d9fd380Sjfb8856606 # if we're in the middle of internal section, we need to copy 78*2d9fd380Sjfb8856606 # the section verbatim, so just add the line 79*2d9fd380Sjfb8856606 if in_internal: 80*2d9fd380Sjfb8856606 internal_lines += [line] 81*2d9fd380Sjfb8856606 continue 82*2d9fd380Sjfb8856606 834418919fSjohnjiang # skip empty lines 844418919fSjohnjiang if not line: 854418919fSjohnjiang continue 864418919fSjohnjiang 874418919fSjohnjiang # is this a beginning of a new section? 884418919fSjohnjiang match = section_begin_regex.match(line) 894418919fSjohnjiang if match: 904418919fSjohnjiang cur_section = match.group("version") 914418919fSjohnjiang # is it experimental? 924418919fSjohnjiang in_experimental = cur_section == "EXPERIMENTAL" 93*2d9fd380Sjfb8856606 # is it internal? 94*2d9fd380Sjfb8856606 in_internal = cur_section == "INTERNAL" 95*2d9fd380Sjfb8856606 if not in_experimental and not in_internal: 964418919fSjohnjiang has_stable = True 974418919fSjohnjiang continue 984418919fSjohnjiang 994418919fSjohnjiang # is this a function? 1004418919fSjohnjiang match = func_line_regex.match(line) 1014418919fSjohnjiang if match: 1024418919fSjohnjiang stable_lines.add(match.group("func")) 1034418919fSjohnjiang 104*2d9fd380Sjfb8856606 return has_stable, stable_lines, experimental_lines, internal_lines 1054418919fSjohnjiang 1064418919fSjohnjiang 107*2d9fd380Sjfb8856606def __generate_stable_abi(f_out, abi_major, lines): 1084418919fSjohnjiang # print ABI version header 109*2d9fd380Sjfb8856606 print("DPDK_{} {{".format(abi_major), file=f_out) 1104418919fSjohnjiang 1114418919fSjohnjiang # print global section if it exists 1124418919fSjohnjiang if lines: 1134418919fSjohnjiang print("\tglobal:", file=f_out) 1144418919fSjohnjiang # blank line 1154418919fSjohnjiang print(file=f_out) 1164418919fSjohnjiang 1174418919fSjohnjiang # print all stable lines, alphabetically sorted 1184418919fSjohnjiang for line in sorted(lines): 1194418919fSjohnjiang print("\t{};".format(line), file=f_out) 1204418919fSjohnjiang 1214418919fSjohnjiang # another blank line 1224418919fSjohnjiang print(file=f_out) 1234418919fSjohnjiang 1244418919fSjohnjiang # print local section 1254418919fSjohnjiang print("\tlocal: *;", file=f_out) 1264418919fSjohnjiang 1274418919fSjohnjiang # end stable version 1284418919fSjohnjiang print("};", file=f_out) 1294418919fSjohnjiang 1304418919fSjohnjiang 1314418919fSjohnjiangdef __generate_experimental_abi(f_out, lines): 1324418919fSjohnjiang # start experimental section 1334418919fSjohnjiang print("EXPERIMENTAL {", file=f_out) 1344418919fSjohnjiang 1354418919fSjohnjiang # print all experimental lines as they were 1364418919fSjohnjiang for line in lines: 1374418919fSjohnjiang # don't print empty whitespace 1384418919fSjohnjiang if not line: 1394418919fSjohnjiang print("", file=f_out) 1404418919fSjohnjiang else: 1414418919fSjohnjiang print("\t{}".format(line), file=f_out) 1424418919fSjohnjiang 1434418919fSjohnjiang # end section 1444418919fSjohnjiang print("};", file=f_out) 1454418919fSjohnjiang 146*2d9fd380Sjfb8856606def __generate_internal_abi(f_out, lines): 147*2d9fd380Sjfb8856606 # start internal section 148*2d9fd380Sjfb8856606 print("INTERNAL {", file=f_out) 149*2d9fd380Sjfb8856606 150*2d9fd380Sjfb8856606 # print all internal lines as they were 151*2d9fd380Sjfb8856606 for line in lines: 152*2d9fd380Sjfb8856606 # don't print empty whitespace 153*2d9fd380Sjfb8856606 if not line: 154*2d9fd380Sjfb8856606 print("", file=f_out) 155*2d9fd380Sjfb8856606 else: 156*2d9fd380Sjfb8856606 print("\t{}".format(line), file=f_out) 157*2d9fd380Sjfb8856606 158*2d9fd380Sjfb8856606 # end section 159*2d9fd380Sjfb8856606 print("};", file=f_out) 1604418919fSjohnjiang 1614418919fSjohnjiangdef __main(): 1624418919fSjohnjiang arg_parser = argparse.ArgumentParser( 1634418919fSjohnjiang description='Merge versions in linker version script.') 1644418919fSjohnjiang 1654418919fSjohnjiang arg_parser.add_argument("map_file", type=str, 1664418919fSjohnjiang help='path to linker version script file ' 167*2d9fd380Sjfb8856606 '(pattern: version.map)') 1684418919fSjohnjiang arg_parser.add_argument("abi_version", type=str, 1694418919fSjohnjiang help='target ABI version (pattern: MAJOR.MINOR)') 1704418919fSjohnjiang 1714418919fSjohnjiang parsed = arg_parser.parse_args() 1724418919fSjohnjiang 1734418919fSjohnjiang if not parsed.map_file.endswith('version.map'): 1744418919fSjohnjiang print("Invalid input file: {}".format(parsed.map_file), 1754418919fSjohnjiang file=sys.stderr) 1764418919fSjohnjiang arg_parser.print_help() 1774418919fSjohnjiang sys.exit(1) 1784418919fSjohnjiang 1794418919fSjohnjiang if not re.match(r"\d{1,2}\.\d{1,2}", parsed.abi_version): 1804418919fSjohnjiang print("Invalid ABI version: {}".format(parsed.abi_version), 1814418919fSjohnjiang file=sys.stderr) 1824418919fSjohnjiang arg_parser.print_help() 1834418919fSjohnjiang sys.exit(1) 184*2d9fd380Sjfb8856606 abi_major = parsed.abi_version.split('.')[0] 1854418919fSjohnjiang 1864418919fSjohnjiang with open(parsed.map_file) as f_in: 187*2d9fd380Sjfb8856606 has_stable, stable_lines, experimental_lines, internal_lines = __parse_map_file(f_in) 1884418919fSjohnjiang 1894418919fSjohnjiang with open(parsed.map_file, 'w') as f_out: 1904418919fSjohnjiang need_newline = has_stable and experimental_lines 1914418919fSjohnjiang if has_stable: 192*2d9fd380Sjfb8856606 __generate_stable_abi(f_out, abi_major, stable_lines) 1934418919fSjohnjiang if need_newline: 1944418919fSjohnjiang # separate sections with a newline 1954418919fSjohnjiang print(file=f_out) 1964418919fSjohnjiang if experimental_lines: 1974418919fSjohnjiang __generate_experimental_abi(f_out, experimental_lines) 198*2d9fd380Sjfb8856606 if internal_lines: 199*2d9fd380Sjfb8856606 if has_stable or experimental_lines: 200*2d9fd380Sjfb8856606 # separate sections with a newline 201*2d9fd380Sjfb8856606 print(file=f_out) 202*2d9fd380Sjfb8856606 __generate_internal_abi(f_out, internal_lines) 2034418919fSjohnjiang 2044418919fSjohnjiang 2054418919fSjohnjiangif __name__ == "__main__": 2064418919fSjohnjiang __main() 207