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