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