1*62c56f98SSadaf Ebrahimi#!/usr/bin/env python3 2*62c56f98SSadaf Ebrahimi"""This script compares the interfaces of two versions of Mbed TLS, looking 3*62c56f98SSadaf Ebrahimifor backward incompatibilities between two different Git revisions within 4*62c56f98SSadaf Ebrahimian Mbed TLS repository. It must be run from the root of a Git working tree. 5*62c56f98SSadaf Ebrahimi 6*62c56f98SSadaf Ebrahimi### How the script works ### 7*62c56f98SSadaf Ebrahimi 8*62c56f98SSadaf EbrahimiFor the source (API) and runtime (ABI) interface compatibility, this script 9*62c56f98SSadaf Ebrahimiis a small wrapper around the abi-compliance-checker and abi-dumper tools, 10*62c56f98SSadaf Ebrahimiapplying them to compare the header and library files. 11*62c56f98SSadaf Ebrahimi 12*62c56f98SSadaf EbrahimiFor the storage format, this script compares the automatically generated 13*62c56f98SSadaf Ebrahimistorage tests and the manual read tests, and complains if there is a 14*62c56f98SSadaf Ebrahimireduction in coverage. A change in test data will be signaled as a 15*62c56f98SSadaf Ebrahimicoverage reduction since the old test data is no longer present. A change in 16*62c56f98SSadaf Ebrahimihow test data is presented will be signaled as well; this would be a false 17*62c56f98SSadaf Ebrahimipositive. 18*62c56f98SSadaf Ebrahimi 19*62c56f98SSadaf EbrahimiThe results of the API/ABI comparison are either formatted as HTML and stored 20*62c56f98SSadaf Ebrahimiat a configurable location, or are given as a brief list of problems. 21*62c56f98SSadaf EbrahimiReturns 0 on success, 1 on non-compliance, and 2 if there is an error 22*62c56f98SSadaf Ebrahimiwhile running the script. 23*62c56f98SSadaf Ebrahimi 24*62c56f98SSadaf Ebrahimi### How to interpret non-compliance ### 25*62c56f98SSadaf Ebrahimi 26*62c56f98SSadaf EbrahimiThis script has relatively common false positives. In many scenarios, it only 27*62c56f98SSadaf Ebrahimireports a pass if there is a strict textual match between the old version and 28*62c56f98SSadaf Ebrahimithe new version, and it reports problems where there is a sufficient semantic 29*62c56f98SSadaf Ebrahimimatch but not a textual match. This section lists some common false positives. 30*62c56f98SSadaf EbrahimiThis is not an exhaustive list: in the end what matters is whether we are 31*62c56f98SSadaf Ebrahimibreaking a backward compatibility goal. 32*62c56f98SSadaf Ebrahimi 33*62c56f98SSadaf Ebrahimi**API**: the goal is that if an application works with the old version of the 34*62c56f98SSadaf Ebrahimilibrary, it can be recompiled against the new version and will still work. 35*62c56f98SSadaf EbrahimiThis is normally validated by comparing the declarations in `include/*/*.h`. 36*62c56f98SSadaf EbrahimiA failure is a declaration that has disappeared or that now has a different 37*62c56f98SSadaf Ebrahimitype. 38*62c56f98SSadaf Ebrahimi 39*62c56f98SSadaf Ebrahimi * It's ok to change or remove macros and functions that are documented as 40*62c56f98SSadaf Ebrahimi for internal use only or as experimental. 41*62c56f98SSadaf Ebrahimi * It's ok to rename function or macro parameters as long as the semantics 42*62c56f98SSadaf Ebrahimi has not changed. 43*62c56f98SSadaf Ebrahimi * It's ok to change or remove structure fields that are documented as 44*62c56f98SSadaf Ebrahimi private. 45*62c56f98SSadaf Ebrahimi * It's ok to add fields to a structure that already had private fields 46*62c56f98SSadaf Ebrahimi or was documented as extensible. 47*62c56f98SSadaf Ebrahimi 48*62c56f98SSadaf Ebrahimi**ABI**: the goal is that if an application was built against the old version 49*62c56f98SSadaf Ebrahimiof the library, the same binary will work when linked against the new version. 50*62c56f98SSadaf EbrahimiThis is normally validated by comparing the symbols exported by `libmbed*.so`. 51*62c56f98SSadaf EbrahimiA failure is a symbol that is no longer exported by the same library or that 52*62c56f98SSadaf Ebrahiminow has a different type. 53*62c56f98SSadaf Ebrahimi 54*62c56f98SSadaf Ebrahimi * All ABI changes are acceptable if the library version is bumped 55*62c56f98SSadaf Ebrahimi (see `scripts/bump_version.sh`). 56*62c56f98SSadaf Ebrahimi * ABI changes that concern functions which are declared only inside the 57*62c56f98SSadaf Ebrahimi library directory, and not in `include/*/*.h`, are acceptable only if 58*62c56f98SSadaf Ebrahimi the function was only ever used inside the same library (libmbedcrypto, 59*62c56f98SSadaf Ebrahimi libmbedx509, libmbedtls). As a counter example, if the old version 60*62c56f98SSadaf Ebrahimi of libmbedtls calls mbedtls_foo() from libmbedcrypto, and the new version 61*62c56f98SSadaf Ebrahimi of libmbedcrypto no longer has a compatible mbedtls_foo(), this does 62*62c56f98SSadaf Ebrahimi require a version bump for libmbedcrypto. 63*62c56f98SSadaf Ebrahimi 64*62c56f98SSadaf Ebrahimi**Storage format**: the goal is to check that persistent keys stored by the 65*62c56f98SSadaf Ebrahimiold version can be read by the new version. This is normally validated by 66*62c56f98SSadaf Ebrahimicomparing the `*read*` test cases in `test_suite*storage_format*.data`. 67*62c56f98SSadaf EbrahimiA failure is a storage read test case that is no longer present with the same 68*62c56f98SSadaf Ebrahimifunction name and parameter list. 69*62c56f98SSadaf Ebrahimi 70*62c56f98SSadaf Ebrahimi * It's ok if the same test data is present, but its presentation has changed, 71*62c56f98SSadaf Ebrahimi for example if a test function is renamed or has different parameters. 72*62c56f98SSadaf Ebrahimi * It's ok if redundant tests are removed. 73*62c56f98SSadaf Ebrahimi 74*62c56f98SSadaf Ebrahimi**Generated test coverage**: the goal is to check that automatically 75*62c56f98SSadaf Ebrahimigenerated tests have as much coverage as before. This is normally validated 76*62c56f98SSadaf Ebrahimiby comparing the test cases that are automatically generated by a script. 77*62c56f98SSadaf EbrahimiA failure is a generated test case that is no longer present with the same 78*62c56f98SSadaf Ebrahimifunction name and parameter list. 79*62c56f98SSadaf Ebrahimi 80*62c56f98SSadaf Ebrahimi * It's ok if the same test data is present, but its presentation has changed, 81*62c56f98SSadaf Ebrahimi for example if a test function is renamed or has different parameters. 82*62c56f98SSadaf Ebrahimi * It's ok if redundant tests are removed. 83*62c56f98SSadaf Ebrahimi 84*62c56f98SSadaf Ebrahimi""" 85*62c56f98SSadaf Ebrahimi 86*62c56f98SSadaf Ebrahimi# Copyright The Mbed TLS Contributors 87*62c56f98SSadaf Ebrahimi# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later 88*62c56f98SSadaf Ebrahimi 89*62c56f98SSadaf Ebrahimiimport glob 90*62c56f98SSadaf Ebrahimiimport os 91*62c56f98SSadaf Ebrahimiimport re 92*62c56f98SSadaf Ebrahimiimport sys 93*62c56f98SSadaf Ebrahimiimport traceback 94*62c56f98SSadaf Ebrahimiimport shutil 95*62c56f98SSadaf Ebrahimiimport subprocess 96*62c56f98SSadaf Ebrahimiimport argparse 97*62c56f98SSadaf Ebrahimiimport logging 98*62c56f98SSadaf Ebrahimiimport tempfile 99*62c56f98SSadaf Ebrahimiimport fnmatch 100*62c56f98SSadaf Ebrahimifrom types import SimpleNamespace 101*62c56f98SSadaf Ebrahimi 102*62c56f98SSadaf Ebrahimiimport xml.etree.ElementTree as ET 103*62c56f98SSadaf Ebrahimi 104*62c56f98SSadaf Ebrahimifrom mbedtls_dev import build_tree 105*62c56f98SSadaf Ebrahimi 106*62c56f98SSadaf Ebrahimi 107*62c56f98SSadaf Ebrahimiclass AbiChecker: 108*62c56f98SSadaf Ebrahimi """API and ABI checker.""" 109*62c56f98SSadaf Ebrahimi 110*62c56f98SSadaf Ebrahimi def __init__(self, old_version, new_version, configuration): 111*62c56f98SSadaf Ebrahimi """Instantiate the API/ABI checker. 112*62c56f98SSadaf Ebrahimi 113*62c56f98SSadaf Ebrahimi old_version: RepoVersion containing details to compare against 114*62c56f98SSadaf Ebrahimi new_version: RepoVersion containing details to check 115*62c56f98SSadaf Ebrahimi configuration.report_dir: directory for output files 116*62c56f98SSadaf Ebrahimi configuration.keep_all_reports: if false, delete old reports 117*62c56f98SSadaf Ebrahimi configuration.brief: if true, output shorter report to stdout 118*62c56f98SSadaf Ebrahimi configuration.check_abi: if true, compare ABIs 119*62c56f98SSadaf Ebrahimi configuration.check_api: if true, compare APIs 120*62c56f98SSadaf Ebrahimi configuration.check_storage: if true, compare storage format tests 121*62c56f98SSadaf Ebrahimi configuration.skip_file: path to file containing symbols and types to skip 122*62c56f98SSadaf Ebrahimi """ 123*62c56f98SSadaf Ebrahimi self.repo_path = "." 124*62c56f98SSadaf Ebrahimi self.log = None 125*62c56f98SSadaf Ebrahimi self.verbose = configuration.verbose 126*62c56f98SSadaf Ebrahimi self._setup_logger() 127*62c56f98SSadaf Ebrahimi self.report_dir = os.path.abspath(configuration.report_dir) 128*62c56f98SSadaf Ebrahimi self.keep_all_reports = configuration.keep_all_reports 129*62c56f98SSadaf Ebrahimi self.can_remove_report_dir = not (os.path.exists(self.report_dir) or 130*62c56f98SSadaf Ebrahimi self.keep_all_reports) 131*62c56f98SSadaf Ebrahimi self.old_version = old_version 132*62c56f98SSadaf Ebrahimi self.new_version = new_version 133*62c56f98SSadaf Ebrahimi self.skip_file = configuration.skip_file 134*62c56f98SSadaf Ebrahimi self.check_abi = configuration.check_abi 135*62c56f98SSadaf Ebrahimi self.check_api = configuration.check_api 136*62c56f98SSadaf Ebrahimi if self.check_abi != self.check_api: 137*62c56f98SSadaf Ebrahimi raise Exception('Checking API without ABI or vice versa is not supported') 138*62c56f98SSadaf Ebrahimi self.check_storage_tests = configuration.check_storage 139*62c56f98SSadaf Ebrahimi self.brief = configuration.brief 140*62c56f98SSadaf Ebrahimi self.git_command = "git" 141*62c56f98SSadaf Ebrahimi self.make_command = "make" 142*62c56f98SSadaf Ebrahimi 143*62c56f98SSadaf Ebrahimi def _setup_logger(self): 144*62c56f98SSadaf Ebrahimi self.log = logging.getLogger() 145*62c56f98SSadaf Ebrahimi if self.verbose: 146*62c56f98SSadaf Ebrahimi self.log.setLevel(logging.DEBUG) 147*62c56f98SSadaf Ebrahimi else: 148*62c56f98SSadaf Ebrahimi self.log.setLevel(logging.INFO) 149*62c56f98SSadaf Ebrahimi self.log.addHandler(logging.StreamHandler()) 150*62c56f98SSadaf Ebrahimi 151*62c56f98SSadaf Ebrahimi @staticmethod 152*62c56f98SSadaf Ebrahimi def check_abi_tools_are_installed(): 153*62c56f98SSadaf Ebrahimi for command in ["abi-dumper", "abi-compliance-checker"]: 154*62c56f98SSadaf Ebrahimi if not shutil.which(command): 155*62c56f98SSadaf Ebrahimi raise Exception("{} not installed, aborting".format(command)) 156*62c56f98SSadaf Ebrahimi 157*62c56f98SSadaf Ebrahimi def _get_clean_worktree_for_git_revision(self, version): 158*62c56f98SSadaf Ebrahimi """Make a separate worktree with version.revision checked out. 159*62c56f98SSadaf Ebrahimi Do not modify the current worktree.""" 160*62c56f98SSadaf Ebrahimi git_worktree_path = tempfile.mkdtemp() 161*62c56f98SSadaf Ebrahimi if version.repository: 162*62c56f98SSadaf Ebrahimi self.log.debug( 163*62c56f98SSadaf Ebrahimi "Checking out git worktree for revision {} from {}".format( 164*62c56f98SSadaf Ebrahimi version.revision, version.repository 165*62c56f98SSadaf Ebrahimi ) 166*62c56f98SSadaf Ebrahimi ) 167*62c56f98SSadaf Ebrahimi fetch_output = subprocess.check_output( 168*62c56f98SSadaf Ebrahimi [self.git_command, "fetch", 169*62c56f98SSadaf Ebrahimi version.repository, version.revision], 170*62c56f98SSadaf Ebrahimi cwd=self.repo_path, 171*62c56f98SSadaf Ebrahimi stderr=subprocess.STDOUT 172*62c56f98SSadaf Ebrahimi ) 173*62c56f98SSadaf Ebrahimi self.log.debug(fetch_output.decode("utf-8")) 174*62c56f98SSadaf Ebrahimi worktree_rev = "FETCH_HEAD" 175*62c56f98SSadaf Ebrahimi else: 176*62c56f98SSadaf Ebrahimi self.log.debug("Checking out git worktree for revision {}".format( 177*62c56f98SSadaf Ebrahimi version.revision 178*62c56f98SSadaf Ebrahimi )) 179*62c56f98SSadaf Ebrahimi worktree_rev = version.revision 180*62c56f98SSadaf Ebrahimi worktree_output = subprocess.check_output( 181*62c56f98SSadaf Ebrahimi [self.git_command, "worktree", "add", "--detach", 182*62c56f98SSadaf Ebrahimi git_worktree_path, worktree_rev], 183*62c56f98SSadaf Ebrahimi cwd=self.repo_path, 184*62c56f98SSadaf Ebrahimi stderr=subprocess.STDOUT 185*62c56f98SSadaf Ebrahimi ) 186*62c56f98SSadaf Ebrahimi self.log.debug(worktree_output.decode("utf-8")) 187*62c56f98SSadaf Ebrahimi version.commit = subprocess.check_output( 188*62c56f98SSadaf Ebrahimi [self.git_command, "rev-parse", "HEAD"], 189*62c56f98SSadaf Ebrahimi cwd=git_worktree_path, 190*62c56f98SSadaf Ebrahimi stderr=subprocess.STDOUT 191*62c56f98SSadaf Ebrahimi ).decode("ascii").rstrip() 192*62c56f98SSadaf Ebrahimi self.log.debug("Commit is {}".format(version.commit)) 193*62c56f98SSadaf Ebrahimi return git_worktree_path 194*62c56f98SSadaf Ebrahimi 195*62c56f98SSadaf Ebrahimi def _update_git_submodules(self, git_worktree_path, version): 196*62c56f98SSadaf Ebrahimi """If the crypto submodule is present, initialize it. 197*62c56f98SSadaf Ebrahimi if version.crypto_revision exists, update it to that revision, 198*62c56f98SSadaf Ebrahimi otherwise update it to the default revision""" 199*62c56f98SSadaf Ebrahimi update_output = subprocess.check_output( 200*62c56f98SSadaf Ebrahimi [self.git_command, "submodule", "update", "--init", '--recursive'], 201*62c56f98SSadaf Ebrahimi cwd=git_worktree_path, 202*62c56f98SSadaf Ebrahimi stderr=subprocess.STDOUT 203*62c56f98SSadaf Ebrahimi ) 204*62c56f98SSadaf Ebrahimi self.log.debug(update_output.decode("utf-8")) 205*62c56f98SSadaf Ebrahimi if not (os.path.exists(os.path.join(git_worktree_path, "crypto")) 206*62c56f98SSadaf Ebrahimi and version.crypto_revision): 207*62c56f98SSadaf Ebrahimi return 208*62c56f98SSadaf Ebrahimi 209*62c56f98SSadaf Ebrahimi if version.crypto_repository: 210*62c56f98SSadaf Ebrahimi fetch_output = subprocess.check_output( 211*62c56f98SSadaf Ebrahimi [self.git_command, "fetch", version.crypto_repository, 212*62c56f98SSadaf Ebrahimi version.crypto_revision], 213*62c56f98SSadaf Ebrahimi cwd=os.path.join(git_worktree_path, "crypto"), 214*62c56f98SSadaf Ebrahimi stderr=subprocess.STDOUT 215*62c56f98SSadaf Ebrahimi ) 216*62c56f98SSadaf Ebrahimi self.log.debug(fetch_output.decode("utf-8")) 217*62c56f98SSadaf Ebrahimi crypto_rev = "FETCH_HEAD" 218*62c56f98SSadaf Ebrahimi else: 219*62c56f98SSadaf Ebrahimi crypto_rev = version.crypto_revision 220*62c56f98SSadaf Ebrahimi 221*62c56f98SSadaf Ebrahimi checkout_output = subprocess.check_output( 222*62c56f98SSadaf Ebrahimi [self.git_command, "checkout", crypto_rev], 223*62c56f98SSadaf Ebrahimi cwd=os.path.join(git_worktree_path, "crypto"), 224*62c56f98SSadaf Ebrahimi stderr=subprocess.STDOUT 225*62c56f98SSadaf Ebrahimi ) 226*62c56f98SSadaf Ebrahimi self.log.debug(checkout_output.decode("utf-8")) 227*62c56f98SSadaf Ebrahimi 228*62c56f98SSadaf Ebrahimi def _build_shared_libraries(self, git_worktree_path, version): 229*62c56f98SSadaf Ebrahimi """Build the shared libraries in the specified worktree.""" 230*62c56f98SSadaf Ebrahimi my_environment = os.environ.copy() 231*62c56f98SSadaf Ebrahimi my_environment["CFLAGS"] = "-g -Og" 232*62c56f98SSadaf Ebrahimi my_environment["SHARED"] = "1" 233*62c56f98SSadaf Ebrahimi if os.path.exists(os.path.join(git_worktree_path, "crypto")): 234*62c56f98SSadaf Ebrahimi my_environment["USE_CRYPTO_SUBMODULE"] = "1" 235*62c56f98SSadaf Ebrahimi make_output = subprocess.check_output( 236*62c56f98SSadaf Ebrahimi [self.make_command, "lib"], 237*62c56f98SSadaf Ebrahimi env=my_environment, 238*62c56f98SSadaf Ebrahimi cwd=git_worktree_path, 239*62c56f98SSadaf Ebrahimi stderr=subprocess.STDOUT 240*62c56f98SSadaf Ebrahimi ) 241*62c56f98SSadaf Ebrahimi self.log.debug(make_output.decode("utf-8")) 242*62c56f98SSadaf Ebrahimi for root, _dirs, files in os.walk(git_worktree_path): 243*62c56f98SSadaf Ebrahimi for file in fnmatch.filter(files, "*.so"): 244*62c56f98SSadaf Ebrahimi version.modules[os.path.splitext(file)[0]] = ( 245*62c56f98SSadaf Ebrahimi os.path.join(root, file) 246*62c56f98SSadaf Ebrahimi ) 247*62c56f98SSadaf Ebrahimi 248*62c56f98SSadaf Ebrahimi @staticmethod 249*62c56f98SSadaf Ebrahimi def _pretty_revision(version): 250*62c56f98SSadaf Ebrahimi if version.revision == version.commit: 251*62c56f98SSadaf Ebrahimi return version.revision 252*62c56f98SSadaf Ebrahimi else: 253*62c56f98SSadaf Ebrahimi return "{} ({})".format(version.revision, version.commit) 254*62c56f98SSadaf Ebrahimi 255*62c56f98SSadaf Ebrahimi def _get_abi_dumps_from_shared_libraries(self, version): 256*62c56f98SSadaf Ebrahimi """Generate the ABI dumps for the specified git revision. 257*62c56f98SSadaf Ebrahimi The shared libraries must have been built and the module paths 258*62c56f98SSadaf Ebrahimi present in version.modules.""" 259*62c56f98SSadaf Ebrahimi for mbed_module, module_path in version.modules.items(): 260*62c56f98SSadaf Ebrahimi output_path = os.path.join( 261*62c56f98SSadaf Ebrahimi self.report_dir, "{}-{}-{}.dump".format( 262*62c56f98SSadaf Ebrahimi mbed_module, version.revision, version.version 263*62c56f98SSadaf Ebrahimi ) 264*62c56f98SSadaf Ebrahimi ) 265*62c56f98SSadaf Ebrahimi abi_dump_command = [ 266*62c56f98SSadaf Ebrahimi "abi-dumper", 267*62c56f98SSadaf Ebrahimi module_path, 268*62c56f98SSadaf Ebrahimi "-o", output_path, 269*62c56f98SSadaf Ebrahimi "-lver", self._pretty_revision(version), 270*62c56f98SSadaf Ebrahimi ] 271*62c56f98SSadaf Ebrahimi abi_dump_output = subprocess.check_output( 272*62c56f98SSadaf Ebrahimi abi_dump_command, 273*62c56f98SSadaf Ebrahimi stderr=subprocess.STDOUT 274*62c56f98SSadaf Ebrahimi ) 275*62c56f98SSadaf Ebrahimi self.log.debug(abi_dump_output.decode("utf-8")) 276*62c56f98SSadaf Ebrahimi version.abi_dumps[mbed_module] = output_path 277*62c56f98SSadaf Ebrahimi 278*62c56f98SSadaf Ebrahimi @staticmethod 279*62c56f98SSadaf Ebrahimi def _normalize_storage_test_case_data(line): 280*62c56f98SSadaf Ebrahimi """Eliminate cosmetic or irrelevant details in storage format test cases.""" 281*62c56f98SSadaf Ebrahimi line = re.sub(r'\s+', r'', line) 282*62c56f98SSadaf Ebrahimi return line 283*62c56f98SSadaf Ebrahimi 284*62c56f98SSadaf Ebrahimi def _read_storage_tests(self, 285*62c56f98SSadaf Ebrahimi directory, 286*62c56f98SSadaf Ebrahimi filename, 287*62c56f98SSadaf Ebrahimi is_generated, 288*62c56f98SSadaf Ebrahimi storage_tests): 289*62c56f98SSadaf Ebrahimi """Record storage tests from the given file. 290*62c56f98SSadaf Ebrahimi 291*62c56f98SSadaf Ebrahimi Populate the storage_tests dictionary with test cases read from 292*62c56f98SSadaf Ebrahimi filename under directory. 293*62c56f98SSadaf Ebrahimi """ 294*62c56f98SSadaf Ebrahimi at_paragraph_start = True 295*62c56f98SSadaf Ebrahimi description = None 296*62c56f98SSadaf Ebrahimi full_path = os.path.join(directory, filename) 297*62c56f98SSadaf Ebrahimi with open(full_path) as fd: 298*62c56f98SSadaf Ebrahimi for line_number, line in enumerate(fd, 1): 299*62c56f98SSadaf Ebrahimi line = line.strip() 300*62c56f98SSadaf Ebrahimi if not line: 301*62c56f98SSadaf Ebrahimi at_paragraph_start = True 302*62c56f98SSadaf Ebrahimi continue 303*62c56f98SSadaf Ebrahimi if line.startswith('#'): 304*62c56f98SSadaf Ebrahimi continue 305*62c56f98SSadaf Ebrahimi if at_paragraph_start: 306*62c56f98SSadaf Ebrahimi description = line.strip() 307*62c56f98SSadaf Ebrahimi at_paragraph_start = False 308*62c56f98SSadaf Ebrahimi continue 309*62c56f98SSadaf Ebrahimi if line.startswith('depends_on:'): 310*62c56f98SSadaf Ebrahimi continue 311*62c56f98SSadaf Ebrahimi # We've reached a test case data line 312*62c56f98SSadaf Ebrahimi test_case_data = self._normalize_storage_test_case_data(line) 313*62c56f98SSadaf Ebrahimi if not is_generated: 314*62c56f98SSadaf Ebrahimi # In manual test data, only look at read tests. 315*62c56f98SSadaf Ebrahimi function_name = test_case_data.split(':', 1)[0] 316*62c56f98SSadaf Ebrahimi if 'read' not in function_name.split('_'): 317*62c56f98SSadaf Ebrahimi continue 318*62c56f98SSadaf Ebrahimi metadata = SimpleNamespace( 319*62c56f98SSadaf Ebrahimi filename=filename, 320*62c56f98SSadaf Ebrahimi line_number=line_number, 321*62c56f98SSadaf Ebrahimi description=description 322*62c56f98SSadaf Ebrahimi ) 323*62c56f98SSadaf Ebrahimi storage_tests[test_case_data] = metadata 324*62c56f98SSadaf Ebrahimi 325*62c56f98SSadaf Ebrahimi @staticmethod 326*62c56f98SSadaf Ebrahimi def _list_generated_test_data_files(git_worktree_path): 327*62c56f98SSadaf Ebrahimi """List the generated test data files.""" 328*62c56f98SSadaf Ebrahimi output = subprocess.check_output( 329*62c56f98SSadaf Ebrahimi ['tests/scripts/generate_psa_tests.py', '--list'], 330*62c56f98SSadaf Ebrahimi cwd=git_worktree_path, 331*62c56f98SSadaf Ebrahimi ).decode('ascii') 332*62c56f98SSadaf Ebrahimi return [line for line in output.split('\n') if line] 333*62c56f98SSadaf Ebrahimi 334*62c56f98SSadaf Ebrahimi def _get_storage_format_tests(self, version, git_worktree_path): 335*62c56f98SSadaf Ebrahimi """Record the storage format tests for the specified git version. 336*62c56f98SSadaf Ebrahimi 337*62c56f98SSadaf Ebrahimi The storage format tests are the test suite data files whose name 338*62c56f98SSadaf Ebrahimi contains "storage_format". 339*62c56f98SSadaf Ebrahimi 340*62c56f98SSadaf Ebrahimi The version must be checked out at git_worktree_path. 341*62c56f98SSadaf Ebrahimi 342*62c56f98SSadaf Ebrahimi This function creates or updates the generated data files. 343*62c56f98SSadaf Ebrahimi """ 344*62c56f98SSadaf Ebrahimi # Existing test data files. This may be missing some automatically 345*62c56f98SSadaf Ebrahimi # generated files if they haven't been generated yet. 346*62c56f98SSadaf Ebrahimi storage_data_files = set(glob.glob( 347*62c56f98SSadaf Ebrahimi 'tests/suites/test_suite_*storage_format*.data' 348*62c56f98SSadaf Ebrahimi )) 349*62c56f98SSadaf Ebrahimi # Discover and (re)generate automatically generated data files. 350*62c56f98SSadaf Ebrahimi to_be_generated = set() 351*62c56f98SSadaf Ebrahimi for filename in self._list_generated_test_data_files(git_worktree_path): 352*62c56f98SSadaf Ebrahimi if 'storage_format' in filename: 353*62c56f98SSadaf Ebrahimi storage_data_files.add(filename) 354*62c56f98SSadaf Ebrahimi to_be_generated.add(filename) 355*62c56f98SSadaf Ebrahimi subprocess.check_call( 356*62c56f98SSadaf Ebrahimi ['tests/scripts/generate_psa_tests.py'] + sorted(to_be_generated), 357*62c56f98SSadaf Ebrahimi cwd=git_worktree_path, 358*62c56f98SSadaf Ebrahimi ) 359*62c56f98SSadaf Ebrahimi for test_file in sorted(storage_data_files): 360*62c56f98SSadaf Ebrahimi self._read_storage_tests(git_worktree_path, 361*62c56f98SSadaf Ebrahimi test_file, 362*62c56f98SSadaf Ebrahimi test_file in to_be_generated, 363*62c56f98SSadaf Ebrahimi version.storage_tests) 364*62c56f98SSadaf Ebrahimi 365*62c56f98SSadaf Ebrahimi def _cleanup_worktree(self, git_worktree_path): 366*62c56f98SSadaf Ebrahimi """Remove the specified git worktree.""" 367*62c56f98SSadaf Ebrahimi shutil.rmtree(git_worktree_path) 368*62c56f98SSadaf Ebrahimi worktree_output = subprocess.check_output( 369*62c56f98SSadaf Ebrahimi [self.git_command, "worktree", "prune"], 370*62c56f98SSadaf Ebrahimi cwd=self.repo_path, 371*62c56f98SSadaf Ebrahimi stderr=subprocess.STDOUT 372*62c56f98SSadaf Ebrahimi ) 373*62c56f98SSadaf Ebrahimi self.log.debug(worktree_output.decode("utf-8")) 374*62c56f98SSadaf Ebrahimi 375*62c56f98SSadaf Ebrahimi def _get_abi_dump_for_ref(self, version): 376*62c56f98SSadaf Ebrahimi """Generate the interface information for the specified git revision.""" 377*62c56f98SSadaf Ebrahimi git_worktree_path = self._get_clean_worktree_for_git_revision(version) 378*62c56f98SSadaf Ebrahimi self._update_git_submodules(git_worktree_path, version) 379*62c56f98SSadaf Ebrahimi if self.check_abi: 380*62c56f98SSadaf Ebrahimi self._build_shared_libraries(git_worktree_path, version) 381*62c56f98SSadaf Ebrahimi self._get_abi_dumps_from_shared_libraries(version) 382*62c56f98SSadaf Ebrahimi if self.check_storage_tests: 383*62c56f98SSadaf Ebrahimi self._get_storage_format_tests(version, git_worktree_path) 384*62c56f98SSadaf Ebrahimi self._cleanup_worktree(git_worktree_path) 385*62c56f98SSadaf Ebrahimi 386*62c56f98SSadaf Ebrahimi def _remove_children_with_tag(self, parent, tag): 387*62c56f98SSadaf Ebrahimi children = parent.getchildren() 388*62c56f98SSadaf Ebrahimi for child in children: 389*62c56f98SSadaf Ebrahimi if child.tag == tag: 390*62c56f98SSadaf Ebrahimi parent.remove(child) 391*62c56f98SSadaf Ebrahimi else: 392*62c56f98SSadaf Ebrahimi self._remove_children_with_tag(child, tag) 393*62c56f98SSadaf Ebrahimi 394*62c56f98SSadaf Ebrahimi def _remove_extra_detail_from_report(self, report_root): 395*62c56f98SSadaf Ebrahimi for tag in ['test_info', 'test_results', 'problem_summary', 396*62c56f98SSadaf Ebrahimi 'added_symbols', 'affected']: 397*62c56f98SSadaf Ebrahimi self._remove_children_with_tag(report_root, tag) 398*62c56f98SSadaf Ebrahimi 399*62c56f98SSadaf Ebrahimi for report in report_root: 400*62c56f98SSadaf Ebrahimi for problems in report.getchildren()[:]: 401*62c56f98SSadaf Ebrahimi if not problems.getchildren(): 402*62c56f98SSadaf Ebrahimi report.remove(problems) 403*62c56f98SSadaf Ebrahimi 404*62c56f98SSadaf Ebrahimi def _abi_compliance_command(self, mbed_module, output_path): 405*62c56f98SSadaf Ebrahimi """Build the command to run to analyze the library mbed_module. 406*62c56f98SSadaf Ebrahimi The report will be placed in output_path.""" 407*62c56f98SSadaf Ebrahimi abi_compliance_command = [ 408*62c56f98SSadaf Ebrahimi "abi-compliance-checker", 409*62c56f98SSadaf Ebrahimi "-l", mbed_module, 410*62c56f98SSadaf Ebrahimi "-old", self.old_version.abi_dumps[mbed_module], 411*62c56f98SSadaf Ebrahimi "-new", self.new_version.abi_dumps[mbed_module], 412*62c56f98SSadaf Ebrahimi "-strict", 413*62c56f98SSadaf Ebrahimi "-report-path", output_path, 414*62c56f98SSadaf Ebrahimi ] 415*62c56f98SSadaf Ebrahimi if self.skip_file: 416*62c56f98SSadaf Ebrahimi abi_compliance_command += ["-skip-symbols", self.skip_file, 417*62c56f98SSadaf Ebrahimi "-skip-types", self.skip_file] 418*62c56f98SSadaf Ebrahimi if self.brief: 419*62c56f98SSadaf Ebrahimi abi_compliance_command += ["-report-format", "xml", 420*62c56f98SSadaf Ebrahimi "-stdout"] 421*62c56f98SSadaf Ebrahimi return abi_compliance_command 422*62c56f98SSadaf Ebrahimi 423*62c56f98SSadaf Ebrahimi def _is_library_compatible(self, mbed_module, compatibility_report): 424*62c56f98SSadaf Ebrahimi """Test if the library mbed_module has remained compatible. 425*62c56f98SSadaf Ebrahimi Append a message regarding compatibility to compatibility_report.""" 426*62c56f98SSadaf Ebrahimi output_path = os.path.join( 427*62c56f98SSadaf Ebrahimi self.report_dir, "{}-{}-{}.html".format( 428*62c56f98SSadaf Ebrahimi mbed_module, self.old_version.revision, 429*62c56f98SSadaf Ebrahimi self.new_version.revision 430*62c56f98SSadaf Ebrahimi ) 431*62c56f98SSadaf Ebrahimi ) 432*62c56f98SSadaf Ebrahimi try: 433*62c56f98SSadaf Ebrahimi subprocess.check_output( 434*62c56f98SSadaf Ebrahimi self._abi_compliance_command(mbed_module, output_path), 435*62c56f98SSadaf Ebrahimi stderr=subprocess.STDOUT 436*62c56f98SSadaf Ebrahimi ) 437*62c56f98SSadaf Ebrahimi except subprocess.CalledProcessError as err: 438*62c56f98SSadaf Ebrahimi if err.returncode != 1: 439*62c56f98SSadaf Ebrahimi raise err 440*62c56f98SSadaf Ebrahimi if self.brief: 441*62c56f98SSadaf Ebrahimi self.log.info( 442*62c56f98SSadaf Ebrahimi "Compatibility issues found for {}".format(mbed_module) 443*62c56f98SSadaf Ebrahimi ) 444*62c56f98SSadaf Ebrahimi report_root = ET.fromstring(err.output.decode("utf-8")) 445*62c56f98SSadaf Ebrahimi self._remove_extra_detail_from_report(report_root) 446*62c56f98SSadaf Ebrahimi self.log.info(ET.tostring(report_root).decode("utf-8")) 447*62c56f98SSadaf Ebrahimi else: 448*62c56f98SSadaf Ebrahimi self.can_remove_report_dir = False 449*62c56f98SSadaf Ebrahimi compatibility_report.append( 450*62c56f98SSadaf Ebrahimi "Compatibility issues found for {}, " 451*62c56f98SSadaf Ebrahimi "for details see {}".format(mbed_module, output_path) 452*62c56f98SSadaf Ebrahimi ) 453*62c56f98SSadaf Ebrahimi return False 454*62c56f98SSadaf Ebrahimi compatibility_report.append( 455*62c56f98SSadaf Ebrahimi "No compatibility issues for {}".format(mbed_module) 456*62c56f98SSadaf Ebrahimi ) 457*62c56f98SSadaf Ebrahimi if not (self.keep_all_reports or self.brief): 458*62c56f98SSadaf Ebrahimi os.remove(output_path) 459*62c56f98SSadaf Ebrahimi return True 460*62c56f98SSadaf Ebrahimi 461*62c56f98SSadaf Ebrahimi @staticmethod 462*62c56f98SSadaf Ebrahimi def _is_storage_format_compatible(old_tests, new_tests, 463*62c56f98SSadaf Ebrahimi compatibility_report): 464*62c56f98SSadaf Ebrahimi """Check whether all tests present in old_tests are also in new_tests. 465*62c56f98SSadaf Ebrahimi 466*62c56f98SSadaf Ebrahimi Append a message regarding compatibility to compatibility_report. 467*62c56f98SSadaf Ebrahimi """ 468*62c56f98SSadaf Ebrahimi missing = frozenset(old_tests.keys()).difference(new_tests.keys()) 469*62c56f98SSadaf Ebrahimi for test_data in sorted(missing): 470*62c56f98SSadaf Ebrahimi metadata = old_tests[test_data] 471*62c56f98SSadaf Ebrahimi compatibility_report.append( 472*62c56f98SSadaf Ebrahimi 'Test case from {} line {} "{}" has disappeared: {}'.format( 473*62c56f98SSadaf Ebrahimi metadata.filename, metadata.line_number, 474*62c56f98SSadaf Ebrahimi metadata.description, test_data 475*62c56f98SSadaf Ebrahimi ) 476*62c56f98SSadaf Ebrahimi ) 477*62c56f98SSadaf Ebrahimi compatibility_report.append( 478*62c56f98SSadaf Ebrahimi 'FAIL: {}/{} storage format test cases have changed or disappeared.'.format( 479*62c56f98SSadaf Ebrahimi len(missing), len(old_tests) 480*62c56f98SSadaf Ebrahimi ) if missing else 481*62c56f98SSadaf Ebrahimi 'PASS: All {} storage format test cases are preserved.'.format( 482*62c56f98SSadaf Ebrahimi len(old_tests) 483*62c56f98SSadaf Ebrahimi ) 484*62c56f98SSadaf Ebrahimi ) 485*62c56f98SSadaf Ebrahimi compatibility_report.append( 486*62c56f98SSadaf Ebrahimi 'Info: number of storage format tests cases: {} -> {}.'.format( 487*62c56f98SSadaf Ebrahimi len(old_tests), len(new_tests) 488*62c56f98SSadaf Ebrahimi ) 489*62c56f98SSadaf Ebrahimi ) 490*62c56f98SSadaf Ebrahimi return not missing 491*62c56f98SSadaf Ebrahimi 492*62c56f98SSadaf Ebrahimi def get_abi_compatibility_report(self): 493*62c56f98SSadaf Ebrahimi """Generate a report of the differences between the reference ABI 494*62c56f98SSadaf Ebrahimi and the new ABI. ABI dumps from self.old_version and self.new_version 495*62c56f98SSadaf Ebrahimi must be available.""" 496*62c56f98SSadaf Ebrahimi compatibility_report = ["Checking evolution from {} to {}".format( 497*62c56f98SSadaf Ebrahimi self._pretty_revision(self.old_version), 498*62c56f98SSadaf Ebrahimi self._pretty_revision(self.new_version) 499*62c56f98SSadaf Ebrahimi )] 500*62c56f98SSadaf Ebrahimi compliance_return_code = 0 501*62c56f98SSadaf Ebrahimi 502*62c56f98SSadaf Ebrahimi if self.check_abi: 503*62c56f98SSadaf Ebrahimi shared_modules = list(set(self.old_version.modules.keys()) & 504*62c56f98SSadaf Ebrahimi set(self.new_version.modules.keys())) 505*62c56f98SSadaf Ebrahimi for mbed_module in shared_modules: 506*62c56f98SSadaf Ebrahimi if not self._is_library_compatible(mbed_module, 507*62c56f98SSadaf Ebrahimi compatibility_report): 508*62c56f98SSadaf Ebrahimi compliance_return_code = 1 509*62c56f98SSadaf Ebrahimi 510*62c56f98SSadaf Ebrahimi if self.check_storage_tests: 511*62c56f98SSadaf Ebrahimi if not self._is_storage_format_compatible( 512*62c56f98SSadaf Ebrahimi self.old_version.storage_tests, 513*62c56f98SSadaf Ebrahimi self.new_version.storage_tests, 514*62c56f98SSadaf Ebrahimi compatibility_report): 515*62c56f98SSadaf Ebrahimi compliance_return_code = 1 516*62c56f98SSadaf Ebrahimi 517*62c56f98SSadaf Ebrahimi for version in [self.old_version, self.new_version]: 518*62c56f98SSadaf Ebrahimi for mbed_module, mbed_module_dump in version.abi_dumps.items(): 519*62c56f98SSadaf Ebrahimi os.remove(mbed_module_dump) 520*62c56f98SSadaf Ebrahimi if self.can_remove_report_dir: 521*62c56f98SSadaf Ebrahimi os.rmdir(self.report_dir) 522*62c56f98SSadaf Ebrahimi self.log.info("\n".join(compatibility_report)) 523*62c56f98SSadaf Ebrahimi return compliance_return_code 524*62c56f98SSadaf Ebrahimi 525*62c56f98SSadaf Ebrahimi def check_for_abi_changes(self): 526*62c56f98SSadaf Ebrahimi """Generate a report of ABI differences 527*62c56f98SSadaf Ebrahimi between self.old_rev and self.new_rev.""" 528*62c56f98SSadaf Ebrahimi build_tree.check_repo_path() 529*62c56f98SSadaf Ebrahimi if self.check_api or self.check_abi: 530*62c56f98SSadaf Ebrahimi self.check_abi_tools_are_installed() 531*62c56f98SSadaf Ebrahimi self._get_abi_dump_for_ref(self.old_version) 532*62c56f98SSadaf Ebrahimi self._get_abi_dump_for_ref(self.new_version) 533*62c56f98SSadaf Ebrahimi return self.get_abi_compatibility_report() 534*62c56f98SSadaf Ebrahimi 535*62c56f98SSadaf Ebrahimi 536*62c56f98SSadaf Ebrahimidef run_main(): 537*62c56f98SSadaf Ebrahimi try: 538*62c56f98SSadaf Ebrahimi parser = argparse.ArgumentParser( 539*62c56f98SSadaf Ebrahimi description=__doc__ 540*62c56f98SSadaf Ebrahimi ) 541*62c56f98SSadaf Ebrahimi parser.add_argument( 542*62c56f98SSadaf Ebrahimi "-v", "--verbose", action="store_true", 543*62c56f98SSadaf Ebrahimi help="set verbosity level", 544*62c56f98SSadaf Ebrahimi ) 545*62c56f98SSadaf Ebrahimi parser.add_argument( 546*62c56f98SSadaf Ebrahimi "-r", "--report-dir", type=str, default="reports", 547*62c56f98SSadaf Ebrahimi help="directory where reports are stored, default is reports", 548*62c56f98SSadaf Ebrahimi ) 549*62c56f98SSadaf Ebrahimi parser.add_argument( 550*62c56f98SSadaf Ebrahimi "-k", "--keep-all-reports", action="store_true", 551*62c56f98SSadaf Ebrahimi help="keep all reports, even if there are no compatibility issues", 552*62c56f98SSadaf Ebrahimi ) 553*62c56f98SSadaf Ebrahimi parser.add_argument( 554*62c56f98SSadaf Ebrahimi "-o", "--old-rev", type=str, help="revision for old version.", 555*62c56f98SSadaf Ebrahimi required=True, 556*62c56f98SSadaf Ebrahimi ) 557*62c56f98SSadaf Ebrahimi parser.add_argument( 558*62c56f98SSadaf Ebrahimi "-or", "--old-repo", type=str, help="repository for old version." 559*62c56f98SSadaf Ebrahimi ) 560*62c56f98SSadaf Ebrahimi parser.add_argument( 561*62c56f98SSadaf Ebrahimi "-oc", "--old-crypto-rev", type=str, 562*62c56f98SSadaf Ebrahimi help="revision for old crypto submodule." 563*62c56f98SSadaf Ebrahimi ) 564*62c56f98SSadaf Ebrahimi parser.add_argument( 565*62c56f98SSadaf Ebrahimi "-ocr", "--old-crypto-repo", type=str, 566*62c56f98SSadaf Ebrahimi help="repository for old crypto submodule." 567*62c56f98SSadaf Ebrahimi ) 568*62c56f98SSadaf Ebrahimi parser.add_argument( 569*62c56f98SSadaf Ebrahimi "-n", "--new-rev", type=str, help="revision for new version", 570*62c56f98SSadaf Ebrahimi required=True, 571*62c56f98SSadaf Ebrahimi ) 572*62c56f98SSadaf Ebrahimi parser.add_argument( 573*62c56f98SSadaf Ebrahimi "-nr", "--new-repo", type=str, help="repository for new version." 574*62c56f98SSadaf Ebrahimi ) 575*62c56f98SSadaf Ebrahimi parser.add_argument( 576*62c56f98SSadaf Ebrahimi "-nc", "--new-crypto-rev", type=str, 577*62c56f98SSadaf Ebrahimi help="revision for new crypto version" 578*62c56f98SSadaf Ebrahimi ) 579*62c56f98SSadaf Ebrahimi parser.add_argument( 580*62c56f98SSadaf Ebrahimi "-ncr", "--new-crypto-repo", type=str, 581*62c56f98SSadaf Ebrahimi help="repository for new crypto submodule." 582*62c56f98SSadaf Ebrahimi ) 583*62c56f98SSadaf Ebrahimi parser.add_argument( 584*62c56f98SSadaf Ebrahimi "-s", "--skip-file", type=str, 585*62c56f98SSadaf Ebrahimi help=("path to file containing symbols and types to skip " 586*62c56f98SSadaf Ebrahimi "(typically \"-s identifiers\" after running " 587*62c56f98SSadaf Ebrahimi "\"tests/scripts/list-identifiers.sh --internal\")") 588*62c56f98SSadaf Ebrahimi ) 589*62c56f98SSadaf Ebrahimi parser.add_argument( 590*62c56f98SSadaf Ebrahimi "--check-abi", 591*62c56f98SSadaf Ebrahimi action='store_true', default=True, 592*62c56f98SSadaf Ebrahimi help="Perform ABI comparison (default: yes)" 593*62c56f98SSadaf Ebrahimi ) 594*62c56f98SSadaf Ebrahimi parser.add_argument("--no-check-abi", action='store_false', dest='check_abi') 595*62c56f98SSadaf Ebrahimi parser.add_argument( 596*62c56f98SSadaf Ebrahimi "--check-api", 597*62c56f98SSadaf Ebrahimi action='store_true', default=True, 598*62c56f98SSadaf Ebrahimi help="Perform API comparison (default: yes)" 599*62c56f98SSadaf Ebrahimi ) 600*62c56f98SSadaf Ebrahimi parser.add_argument("--no-check-api", action='store_false', dest='check_api') 601*62c56f98SSadaf Ebrahimi parser.add_argument( 602*62c56f98SSadaf Ebrahimi "--check-storage", 603*62c56f98SSadaf Ebrahimi action='store_true', default=True, 604*62c56f98SSadaf Ebrahimi help="Perform storage tests comparison (default: yes)" 605*62c56f98SSadaf Ebrahimi ) 606*62c56f98SSadaf Ebrahimi parser.add_argument("--no-check-storage", action='store_false', dest='check_storage') 607*62c56f98SSadaf Ebrahimi parser.add_argument( 608*62c56f98SSadaf Ebrahimi "-b", "--brief", action="store_true", 609*62c56f98SSadaf Ebrahimi help="output only the list of issues to stdout, instead of a full report", 610*62c56f98SSadaf Ebrahimi ) 611*62c56f98SSadaf Ebrahimi abi_args = parser.parse_args() 612*62c56f98SSadaf Ebrahimi if os.path.isfile(abi_args.report_dir): 613*62c56f98SSadaf Ebrahimi print("Error: {} is not a directory".format(abi_args.report_dir)) 614*62c56f98SSadaf Ebrahimi parser.exit() 615*62c56f98SSadaf Ebrahimi old_version = SimpleNamespace( 616*62c56f98SSadaf Ebrahimi version="old", 617*62c56f98SSadaf Ebrahimi repository=abi_args.old_repo, 618*62c56f98SSadaf Ebrahimi revision=abi_args.old_rev, 619*62c56f98SSadaf Ebrahimi commit=None, 620*62c56f98SSadaf Ebrahimi crypto_repository=abi_args.old_crypto_repo, 621*62c56f98SSadaf Ebrahimi crypto_revision=abi_args.old_crypto_rev, 622*62c56f98SSadaf Ebrahimi abi_dumps={}, 623*62c56f98SSadaf Ebrahimi storage_tests={}, 624*62c56f98SSadaf Ebrahimi modules={} 625*62c56f98SSadaf Ebrahimi ) 626*62c56f98SSadaf Ebrahimi new_version = SimpleNamespace( 627*62c56f98SSadaf Ebrahimi version="new", 628*62c56f98SSadaf Ebrahimi repository=abi_args.new_repo, 629*62c56f98SSadaf Ebrahimi revision=abi_args.new_rev, 630*62c56f98SSadaf Ebrahimi commit=None, 631*62c56f98SSadaf Ebrahimi crypto_repository=abi_args.new_crypto_repo, 632*62c56f98SSadaf Ebrahimi crypto_revision=abi_args.new_crypto_rev, 633*62c56f98SSadaf Ebrahimi abi_dumps={}, 634*62c56f98SSadaf Ebrahimi storage_tests={}, 635*62c56f98SSadaf Ebrahimi modules={} 636*62c56f98SSadaf Ebrahimi ) 637*62c56f98SSadaf Ebrahimi configuration = SimpleNamespace( 638*62c56f98SSadaf Ebrahimi verbose=abi_args.verbose, 639*62c56f98SSadaf Ebrahimi report_dir=abi_args.report_dir, 640*62c56f98SSadaf Ebrahimi keep_all_reports=abi_args.keep_all_reports, 641*62c56f98SSadaf Ebrahimi brief=abi_args.brief, 642*62c56f98SSadaf Ebrahimi check_abi=abi_args.check_abi, 643*62c56f98SSadaf Ebrahimi check_api=abi_args.check_api, 644*62c56f98SSadaf Ebrahimi check_storage=abi_args.check_storage, 645*62c56f98SSadaf Ebrahimi skip_file=abi_args.skip_file 646*62c56f98SSadaf Ebrahimi ) 647*62c56f98SSadaf Ebrahimi abi_check = AbiChecker(old_version, new_version, configuration) 648*62c56f98SSadaf Ebrahimi return_code = abi_check.check_for_abi_changes() 649*62c56f98SSadaf Ebrahimi sys.exit(return_code) 650*62c56f98SSadaf Ebrahimi except Exception: # pylint: disable=broad-except 651*62c56f98SSadaf Ebrahimi # Print the backtrace and exit explicitly so as to exit with 652*62c56f98SSadaf Ebrahimi # status 2, not 1. 653*62c56f98SSadaf Ebrahimi traceback.print_exc() 654*62c56f98SSadaf Ebrahimi sys.exit(2) 655*62c56f98SSadaf Ebrahimi 656*62c56f98SSadaf Ebrahimi 657*62c56f98SSadaf Ebrahimiif __name__ == "__main__": 658*62c56f98SSadaf Ebrahimi run_main() 659