xref: /aosp_15_r20/external/mbedtls/scripts/abi_check.py (revision 62c56f9862f102b96d72393aff6076c951fb8148)
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