blob: c8dad27d882495551cd84a86c1db06af74d64d03 [file] [log] [blame]
Darryl Green7c2dd582018-03-01 14:53:49 +00001#!/usr/bin/env python3
Darryl Green78696802018-04-06 11:23:22 +01002"""
Darryl Green78696802018-04-06 11:23:22 +01003Purpose
4
5This script is a small wrapper around the abi-compliance-checker and
6abi-dumper tools, applying them to compare the ABI and API of the library
7files from two different Git revisions within an Mbed TLS repository.
Darryl Greene62f9bb2019-02-21 13:09:26 +00008The results of the comparison are either formatted as HTML and stored at
Darryl Green4cde8a02019-03-05 15:21:32 +00009a configurable location, or are given as a brief list of problems.
Darryl Greene62f9bb2019-02-21 13:09:26 +000010Returns 0 on success, 1 on ABI/API non-compliance, and 2 if there is an error
11while running the script. Note: must be run from Mbed TLS root.
Darryl Green78696802018-04-06 11:23:22 +010012"""
Darryl Green7c2dd582018-03-01 14:53:49 +000013
Bence Szépkúti1e148272020-08-07 13:07:28 +020014# Copyright The Mbed TLS Contributors
Bence Szépkútic7da1fe2020-05-26 01:54:15 +020015# SPDX-License-Identifier: Apache-2.0
16#
17# Licensed under the Apache License, Version 2.0 (the "License"); you may
18# not use this file except in compliance with the License.
19# You may obtain a copy of the License at
20#
21# http://www.apache.org/licenses/LICENSE-2.0
22#
23# Unless required by applicable law or agreed to in writing, software
24# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
25# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
26# See the License for the specific language governing permissions and
27# limitations under the License.
Bence Szépkútic7da1fe2020-05-26 01:54:15 +020028
Darryl Green7c2dd582018-03-01 14:53:49 +000029import os
30import sys
31import traceback
32import shutil
33import subprocess
34import argparse
35import logging
36import tempfile
Darryl Green9f357d62019-02-25 11:35:05 +000037import fnmatch
Darryl Green0d1ca512019-04-09 09:14:17 +010038from types import SimpleNamespace
Darryl Green7c2dd582018-03-01 14:53:49 +000039
Darryl Greene62f9bb2019-02-21 13:09:26 +000040import xml.etree.ElementTree as ET
41
Darryl Green7c2dd582018-03-01 14:53:49 +000042
Gilles Peskine184c0962020-03-24 18:25:17 +010043class AbiChecker:
Gilles Peskine712afa72019-02-25 20:36:52 +010044 """API and ABI checker."""
Darryl Green7c2dd582018-03-01 14:53:49 +000045
Darryl Green0d1ca512019-04-09 09:14:17 +010046 def __init__(self, old_version, new_version, configuration):
Gilles Peskine712afa72019-02-25 20:36:52 +010047 """Instantiate the API/ABI checker.
48
Darryl Green7c1a7332019-03-05 16:25:38 +000049 old_version: RepoVersion containing details to compare against
50 new_version: RepoVersion containing details to check
Darryl Greenf67e3492019-04-12 15:17:02 +010051 configuration.report_dir: directory for output files
52 configuration.keep_all_reports: if false, delete old reports
53 configuration.brief: if true, output shorter report to stdout
Gilles Peskinec76ab852021-04-23 16:32:32 +020054 configuration.check_api: if true, compare ABIs
55 configuration.check_api: if true, compare APIs
Darryl Greenf67e3492019-04-12 15:17:02 +010056 configuration.skip_file: path to file containing symbols and types to skip
Gilles Peskine712afa72019-02-25 20:36:52 +010057 """
Darryl Green7c2dd582018-03-01 14:53:49 +000058 self.repo_path = "."
59 self.log = None
Darryl Green0d1ca512019-04-09 09:14:17 +010060 self.verbose = configuration.verbose
Darryl Green3a5f6c82019-03-05 16:30:39 +000061 self._setup_logger()
Darryl Green0d1ca512019-04-09 09:14:17 +010062 self.report_dir = os.path.abspath(configuration.report_dir)
63 self.keep_all_reports = configuration.keep_all_reports
Darryl Green492bc402019-04-11 15:50:41 +010064 self.can_remove_report_dir = not (os.path.exists(self.report_dir) or
Darryl Green0d1ca512019-04-09 09:14:17 +010065 self.keep_all_reports)
Darryl Green7c1a7332019-03-05 16:25:38 +000066 self.old_version = old_version
67 self.new_version = new_version
Darryl Green0d1ca512019-04-09 09:14:17 +010068 self.skip_file = configuration.skip_file
Gilles Peskinec76ab852021-04-23 16:32:32 +020069 self.check_abi = configuration.check_abi
70 self.check_api = configuration.check_api
71 if self.check_abi != self.check_api:
72 raise Exception('Checking API without ABI or vice versa is not supported')
Darryl Green0d1ca512019-04-09 09:14:17 +010073 self.brief = configuration.brief
Darryl Green7c2dd582018-03-01 14:53:49 +000074 self.git_command = "git"
75 self.make_command = "make"
76
Gilles Peskine712afa72019-02-25 20:36:52 +010077 @staticmethod
78 def check_repo_path():
Gilles Peskine6aa32cc2019-07-04 18:59:36 +020079 if not all(os.path.isdir(d) for d in ["include", "library", "tests"]):
Darryl Green7c2dd582018-03-01 14:53:49 +000080 raise Exception("Must be run from Mbed TLS root")
81
Darryl Green3a5f6c82019-03-05 16:30:39 +000082 def _setup_logger(self):
Darryl Green7c2dd582018-03-01 14:53:49 +000083 self.log = logging.getLogger()
Darryl Green3c3da792019-03-08 11:30:04 +000084 if self.verbose:
85 self.log.setLevel(logging.DEBUG)
86 else:
87 self.log.setLevel(logging.INFO)
Darryl Green7c2dd582018-03-01 14:53:49 +000088 self.log.addHandler(logging.StreamHandler())
89
Gilles Peskine712afa72019-02-25 20:36:52 +010090 @staticmethod
91 def check_abi_tools_are_installed():
Darryl Green7c2dd582018-03-01 14:53:49 +000092 for command in ["abi-dumper", "abi-compliance-checker"]:
93 if not shutil.which(command):
94 raise Exception("{} not installed, aborting".format(command))
95
Darryl Green3a5f6c82019-03-05 16:30:39 +000096 def _get_clean_worktree_for_git_revision(self, version):
Darryl Green7c1a7332019-03-05 16:25:38 +000097 """Make a separate worktree with version.revision checked out.
Gilles Peskine712afa72019-02-25 20:36:52 +010098 Do not modify the current worktree."""
Darryl Green7c2dd582018-03-01 14:53:49 +000099 git_worktree_path = tempfile.mkdtemp()
Darryl Green7c1a7332019-03-05 16:25:38 +0000100 if version.repository:
Darryl Green3c3da792019-03-08 11:30:04 +0000101 self.log.debug(
Darryl Greenda84e322019-02-19 16:59:33 +0000102 "Checking out git worktree for revision {} from {}".format(
Darryl Green7c1a7332019-03-05 16:25:38 +0000103 version.revision, version.repository
Darryl Greenda84e322019-02-19 16:59:33 +0000104 )
105 )
Darryl Greenb2ee0b82019-04-12 16:24:25 +0100106 fetch_output = subprocess.check_output(
Darryl Green7c1a7332019-03-05 16:25:38 +0000107 [self.git_command, "fetch",
108 version.repository, version.revision],
Darryl Greenda84e322019-02-19 16:59:33 +0000109 cwd=self.repo_path,
Darryl Greenda84e322019-02-19 16:59:33 +0000110 stderr=subprocess.STDOUT
111 )
Darryl Green3c3da792019-03-08 11:30:04 +0000112 self.log.debug(fetch_output.decode("utf-8"))
Darryl Greenda84e322019-02-19 16:59:33 +0000113 worktree_rev = "FETCH_HEAD"
114 else:
Darryl Green3c3da792019-03-08 11:30:04 +0000115 self.log.debug("Checking out git worktree for revision {}".format(
Darryl Green7c1a7332019-03-05 16:25:38 +0000116 version.revision
117 ))
118 worktree_rev = version.revision
Darryl Greenb2ee0b82019-04-12 16:24:25 +0100119 worktree_output = subprocess.check_output(
Darryl Greenda84e322019-02-19 16:59:33 +0000120 [self.git_command, "worktree", "add", "--detach",
121 git_worktree_path, worktree_rev],
Darryl Green7c2dd582018-03-01 14:53:49 +0000122 cwd=self.repo_path,
Darryl Green7c2dd582018-03-01 14:53:49 +0000123 stderr=subprocess.STDOUT
124 )
Darryl Green3c3da792019-03-08 11:30:04 +0000125 self.log.debug(worktree_output.decode("utf-8"))
Gilles Peskine3e2da4a2019-07-04 19:01:22 +0200126 version.commit = subprocess.check_output(
Darryl Green762351b2019-07-25 14:33:33 +0100127 [self.git_command, "rev-parse", "HEAD"],
Gilles Peskine3e2da4a2019-07-04 19:01:22 +0200128 cwd=git_worktree_path,
129 stderr=subprocess.STDOUT
130 ).decode("ascii").rstrip()
131 self.log.debug("Commit is {}".format(version.commit))
Darryl Green7c2dd582018-03-01 14:53:49 +0000132 return git_worktree_path
133
Darryl Green3a5f6c82019-03-05 16:30:39 +0000134 def _update_git_submodules(self, git_worktree_path, version):
Darryl Green8184df52019-04-05 17:06:17 +0100135 """If the crypto submodule is present, initialize it.
136 if version.crypto_revision exists, update it to that revision,
137 otherwise update it to the default revision"""
Darryl Greenb2ee0b82019-04-12 16:24:25 +0100138 update_output = subprocess.check_output(
Jaeden Ameroffeb1b82018-11-02 16:35:09 +0000139 [self.git_command, "submodule", "update", "--init", '--recursive'],
140 cwd=git_worktree_path,
Jaeden Ameroffeb1b82018-11-02 16:35:09 +0000141 stderr=subprocess.STDOUT
142 )
Darryl Greenb2ee0b82019-04-12 16:24:25 +0100143 self.log.debug(update_output.decode("utf-8"))
Darryl Greene29ce702019-03-05 15:23:25 +0000144 if not (os.path.exists(os.path.join(git_worktree_path, "crypto"))
Darryl Green7c1a7332019-03-05 16:25:38 +0000145 and version.crypto_revision):
Darryl Greene29ce702019-03-05 15:23:25 +0000146 return
147
Darryl Green7c1a7332019-03-05 16:25:38 +0000148 if version.crypto_repository:
Darryl Greenb2ee0b82019-04-12 16:24:25 +0100149 fetch_output = subprocess.check_output(
Darryl Green1d95c532019-03-08 11:12:19 +0000150 [self.git_command, "fetch", version.crypto_repository,
151 version.crypto_revision],
Darryl Greene29ce702019-03-05 15:23:25 +0000152 cwd=os.path.join(git_worktree_path, "crypto"),
Darryl Greene29ce702019-03-05 15:23:25 +0000153 stderr=subprocess.STDOUT
154 )
Darryl Green3c3da792019-03-08 11:30:04 +0000155 self.log.debug(fetch_output.decode("utf-8"))
Darryl Green1d95c532019-03-08 11:12:19 +0000156 crypto_rev = "FETCH_HEAD"
157 else:
158 crypto_rev = version.crypto_revision
159
Darryl Greenb2ee0b82019-04-12 16:24:25 +0100160 checkout_output = subprocess.check_output(
Darryl Green1d95c532019-03-08 11:12:19 +0000161 [self.git_command, "checkout", crypto_rev],
162 cwd=os.path.join(git_worktree_path, "crypto"),
Darryl Green1d95c532019-03-08 11:12:19 +0000163 stderr=subprocess.STDOUT
164 )
Darryl Green3c3da792019-03-08 11:30:04 +0000165 self.log.debug(checkout_output.decode("utf-8"))
Jaeden Ameroffeb1b82018-11-02 16:35:09 +0000166
Darryl Green3a5f6c82019-03-05 16:30:39 +0000167 def _build_shared_libraries(self, git_worktree_path, version):
Gilles Peskine712afa72019-02-25 20:36:52 +0100168 """Build the shared libraries in the specified worktree."""
Darryl Green7c2dd582018-03-01 14:53:49 +0000169 my_environment = os.environ.copy()
170 my_environment["CFLAGS"] = "-g -Og"
171 my_environment["SHARED"] = "1"
Darryl Greend2dba362019-05-09 13:03:05 +0100172 if os.path.exists(os.path.join(git_worktree_path, "crypto")):
173 my_environment["USE_CRYPTO_SUBMODULE"] = "1"
Darryl Greenb2ee0b82019-04-12 16:24:25 +0100174 make_output = subprocess.check_output(
Darryl Greenddf25a62019-02-28 11:52:39 +0000175 [self.make_command, "lib"],
Darryl Green7c2dd582018-03-01 14:53:49 +0000176 env=my_environment,
177 cwd=git_worktree_path,
Darryl Green7c2dd582018-03-01 14:53:49 +0000178 stderr=subprocess.STDOUT
179 )
Darryl Green3c3da792019-03-08 11:30:04 +0000180 self.log.debug(make_output.decode("utf-8"))
Darryl Greenf025d532019-04-12 15:18:02 +0100181 for root, _dirs, files in os.walk(git_worktree_path):
Darryl Green9f357d62019-02-25 11:35:05 +0000182 for file in fnmatch.filter(files, "*.so"):
Darryl Green7c1a7332019-03-05 16:25:38 +0000183 version.modules[os.path.splitext(file)[0]] = (
Darryl Green3e7a9802019-02-27 16:53:40 +0000184 os.path.join(root, file)
Darryl Green9f357d62019-02-25 11:35:05 +0000185 )
Darryl Green7c2dd582018-03-01 14:53:49 +0000186
Gilles Peskine3e2da4a2019-07-04 19:01:22 +0200187 @staticmethod
188 def _pretty_revision(version):
189 if version.revision == version.commit:
190 return version.revision
191 else:
192 return "{} ({})".format(version.revision, version.commit)
193
Darryl Green8184df52019-04-05 17:06:17 +0100194 def _get_abi_dumps_from_shared_libraries(self, version):
Gilles Peskine712afa72019-02-25 20:36:52 +0100195 """Generate the ABI dumps for the specified git revision.
Darryl Green8184df52019-04-05 17:06:17 +0100196 The shared libraries must have been built and the module paths
197 present in version.modules."""
Darryl Green7c1a7332019-03-05 16:25:38 +0000198 for mbed_module, module_path in version.modules.items():
Darryl Green7c2dd582018-03-01 14:53:49 +0000199 output_path = os.path.join(
Darryl Greenfe9a6752019-04-04 14:39:33 +0100200 self.report_dir, "{}-{}-{}.dump".format(
201 mbed_module, version.revision, version.version
Darryl Green3e7a9802019-02-27 16:53:40 +0000202 )
Darryl Green7c2dd582018-03-01 14:53:49 +0000203 )
204 abi_dump_command = [
205 "abi-dumper",
Darryl Green9f357d62019-02-25 11:35:05 +0000206 module_path,
Darryl Green7c2dd582018-03-01 14:53:49 +0000207 "-o", output_path,
Gilles Peskine3e2da4a2019-07-04 19:01:22 +0200208 "-lver", self._pretty_revision(version),
Darryl Green7c2dd582018-03-01 14:53:49 +0000209 ]
Darryl Greenb2ee0b82019-04-12 16:24:25 +0100210 abi_dump_output = subprocess.check_output(
Darryl Green7c2dd582018-03-01 14:53:49 +0000211 abi_dump_command,
Darryl Green7c2dd582018-03-01 14:53:49 +0000212 stderr=subprocess.STDOUT
213 )
Darryl Green3c3da792019-03-08 11:30:04 +0000214 self.log.debug(abi_dump_output.decode("utf-8"))
Darryl Green7c1a7332019-03-05 16:25:38 +0000215 version.abi_dumps[mbed_module] = output_path
Darryl Green7c2dd582018-03-01 14:53:49 +0000216
Darryl Green3a5f6c82019-03-05 16:30:39 +0000217 def _cleanup_worktree(self, git_worktree_path):
Gilles Peskine712afa72019-02-25 20:36:52 +0100218 """Remove the specified git worktree."""
Darryl Green7c2dd582018-03-01 14:53:49 +0000219 shutil.rmtree(git_worktree_path)
Darryl Greenb2ee0b82019-04-12 16:24:25 +0100220 worktree_output = subprocess.check_output(
Darryl Green7c2dd582018-03-01 14:53:49 +0000221 [self.git_command, "worktree", "prune"],
222 cwd=self.repo_path,
Darryl Green7c2dd582018-03-01 14:53:49 +0000223 stderr=subprocess.STDOUT
224 )
Darryl Green3c3da792019-03-08 11:30:04 +0000225 self.log.debug(worktree_output.decode("utf-8"))
Darryl Green7c2dd582018-03-01 14:53:49 +0000226
Darryl Green3a5f6c82019-03-05 16:30:39 +0000227 def _get_abi_dump_for_ref(self, version):
Gilles Peskine712afa72019-02-25 20:36:52 +0100228 """Generate the ABI dumps for the specified git revision."""
Darryl Green3a5f6c82019-03-05 16:30:39 +0000229 git_worktree_path = self._get_clean_worktree_for_git_revision(version)
230 self._update_git_submodules(git_worktree_path, version)
Gilles Peskinec76ab852021-04-23 16:32:32 +0200231 if self.check_abi:
232 self._build_shared_libraries(git_worktree_path, version)
233 self._get_abi_dumps_from_shared_libraries(version)
Darryl Green3a5f6c82019-03-05 16:30:39 +0000234 self._cleanup_worktree(git_worktree_path)
Darryl Green7c2dd582018-03-01 14:53:49 +0000235
Darryl Green3a5f6c82019-03-05 16:30:39 +0000236 def _remove_children_with_tag(self, parent, tag):
Darryl Greene62f9bb2019-02-21 13:09:26 +0000237 children = parent.getchildren()
238 for child in children:
239 if child.tag == tag:
240 parent.remove(child)
241 else:
Darryl Green3a5f6c82019-03-05 16:30:39 +0000242 self._remove_children_with_tag(child, tag)
Darryl Greene62f9bb2019-02-21 13:09:26 +0000243
Darryl Green3a5f6c82019-03-05 16:30:39 +0000244 def _remove_extra_detail_from_report(self, report_root):
Darryl Greene62f9bb2019-02-21 13:09:26 +0000245 for tag in ['test_info', 'test_results', 'problem_summary',
Darryl Greenc6f874b2019-06-05 12:57:50 +0100246 'added_symbols', 'affected']:
Darryl Green3a5f6c82019-03-05 16:30:39 +0000247 self._remove_children_with_tag(report_root, tag)
Darryl Greene62f9bb2019-02-21 13:09:26 +0000248
249 for report in report_root:
250 for problems in report.getchildren()[:]:
251 if not problems.getchildren():
252 report.remove(problems)
253
Gilles Peskineada828f2019-07-04 19:17:40 +0200254 def _abi_compliance_command(self, mbed_module, output_path):
255 """Build the command to run to analyze the library mbed_module.
256 The report will be placed in output_path."""
257 abi_compliance_command = [
258 "abi-compliance-checker",
259 "-l", mbed_module,
260 "-old", self.old_version.abi_dumps[mbed_module],
261 "-new", self.new_version.abi_dumps[mbed_module],
262 "-strict",
263 "-report-path", output_path,
264 ]
265 if self.skip_file:
266 abi_compliance_command += ["-skip-symbols", self.skip_file,
267 "-skip-types", self.skip_file]
268 if self.brief:
269 abi_compliance_command += ["-report-format", "xml",
270 "-stdout"]
271 return abi_compliance_command
272
273 def _is_library_compatible(self, mbed_module, compatibility_report):
274 """Test if the library mbed_module has remained compatible.
275 Append a message regarding compatibility to compatibility_report."""
276 output_path = os.path.join(
277 self.report_dir, "{}-{}-{}.html".format(
278 mbed_module, self.old_version.revision,
279 self.new_version.revision
280 )
281 )
282 try:
283 subprocess.check_output(
284 self._abi_compliance_command(mbed_module, output_path),
285 stderr=subprocess.STDOUT
286 )
287 except subprocess.CalledProcessError as err:
288 if err.returncode != 1:
289 raise err
290 if self.brief:
291 self.log.info(
292 "Compatibility issues found for {}".format(mbed_module)
293 )
294 report_root = ET.fromstring(err.output.decode("utf-8"))
295 self._remove_extra_detail_from_report(report_root)
296 self.log.info(ET.tostring(report_root).decode("utf-8"))
297 else:
298 self.can_remove_report_dir = False
299 compatibility_report.append(
300 "Compatibility issues found for {}, "
301 "for details see {}".format(mbed_module, output_path)
302 )
303 return False
304 compatibility_report.append(
305 "No compatibility issues for {}".format(mbed_module)
306 )
307 if not (self.keep_all_reports or self.brief):
308 os.remove(output_path)
309 return True
310
Darryl Green7c2dd582018-03-01 14:53:49 +0000311 def get_abi_compatibility_report(self):
Gilles Peskine712afa72019-02-25 20:36:52 +0100312 """Generate a report of the differences between the reference ABI
Darryl Green8184df52019-04-05 17:06:17 +0100313 and the new ABI. ABI dumps from self.old_version and self.new_version
314 must be available."""
Gilles Peskineada828f2019-07-04 19:17:40 +0200315 compatibility_report = ["Checking evolution from {} to {}".format(
Gilles Peskine3e2da4a2019-07-04 19:01:22 +0200316 self._pretty_revision(self.old_version),
317 self._pretty_revision(self.new_version)
Gilles Peskineada828f2019-07-04 19:17:40 +0200318 )]
Darryl Green7c2dd582018-03-01 14:53:49 +0000319 compliance_return_code = 0
Gilles Peskinec76ab852021-04-23 16:32:32 +0200320 if self.check_abi:
321 shared_modules = list(set(self.old_version.modules.keys()) &
322 set(self.new_version.modules.keys()))
323 for mbed_module in shared_modules:
324 if not self._is_library_compatible(mbed_module,
325 compatibility_report):
326 compliance_return_code = 1
327
Gilles Peskineada828f2019-07-04 19:17:40 +0200328 compliance_return_code = 1
Darryl Greenf2688e22019-05-29 11:29:08 +0100329 for version in [self.old_version, self.new_version]:
330 for mbed_module, mbed_module_dump in version.abi_dumps.items():
331 os.remove(mbed_module_dump)
Darryl Green3d3d5522019-02-25 17:01:55 +0000332 if self.can_remove_report_dir:
Darryl Green7c2dd582018-03-01 14:53:49 +0000333 os.rmdir(self.report_dir)
Gilles Peskineada828f2019-07-04 19:17:40 +0200334 self.log.info("\n".join(compatibility_report))
Darryl Green7c2dd582018-03-01 14:53:49 +0000335 return compliance_return_code
336
337 def check_for_abi_changes(self):
Gilles Peskine712afa72019-02-25 20:36:52 +0100338 """Generate a report of ABI differences
339 between self.old_rev and self.new_rev."""
Darryl Green7c2dd582018-03-01 14:53:49 +0000340 self.check_repo_path()
341 self.check_abi_tools_are_installed()
Darryl Green3a5f6c82019-03-05 16:30:39 +0000342 self._get_abi_dump_for_ref(self.old_version)
343 self._get_abi_dump_for_ref(self.new_version)
Darryl Green7c2dd582018-03-01 14:53:49 +0000344 return self.get_abi_compatibility_report()
345
346
347def run_main():
348 try:
349 parser = argparse.ArgumentParser(
350 description=(
Darryl Green418527b2018-04-16 12:02:29 +0100351 """This script is a small wrapper around the
352 abi-compliance-checker and abi-dumper tools, applying them
353 to compare the ABI and API of the library files from two
354 different Git revisions within an Mbed TLS repository.
Darryl Greene62f9bb2019-02-21 13:09:26 +0000355 The results of the comparison are either formatted as HTML and
Darryl Green4cde8a02019-03-05 15:21:32 +0000356 stored at a configurable location, or are given as a brief list
357 of problems. Returns 0 on success, 1 on ABI/API non-compliance,
358 and 2 if there is an error while running the script.
359 Note: must be run from Mbed TLS root."""
Darryl Green7c2dd582018-03-01 14:53:49 +0000360 )
361 )
362 parser.add_argument(
Darryl Green3c3da792019-03-08 11:30:04 +0000363 "-v", "--verbose", action="store_true",
364 help="set verbosity level",
365 )
366 parser.add_argument(
Darryl Green418527b2018-04-16 12:02:29 +0100367 "-r", "--report-dir", type=str, default="reports",
Darryl Green7c2dd582018-03-01 14:53:49 +0000368 help="directory where reports are stored, default is reports",
369 )
370 parser.add_argument(
Darryl Green418527b2018-04-16 12:02:29 +0100371 "-k", "--keep-all-reports", action="store_true",
Darryl Green7c2dd582018-03-01 14:53:49 +0000372 help="keep all reports, even if there are no compatibility issues",
373 )
374 parser.add_argument(
Darryl Greenc5132ff2019-03-01 09:54:44 +0000375 "-o", "--old-rev", type=str, help="revision for old version.",
376 required=True,
Darryl Green7c2dd582018-03-01 14:53:49 +0000377 )
378 parser.add_argument(
Darryl Greenc5132ff2019-03-01 09:54:44 +0000379 "-or", "--old-repo", type=str, help="repository for old version."
Darryl Green9f357d62019-02-25 11:35:05 +0000380 )
381 parser.add_argument(
Darryl Greenc5132ff2019-03-01 09:54:44 +0000382 "-oc", "--old-crypto-rev", type=str,
383 help="revision for old crypto submodule."
Darryl Green7c2dd582018-03-01 14:53:49 +0000384 )
Darryl Greenc2883a22019-02-20 15:01:56 +0000385 parser.add_argument(
Darryl Greenc5132ff2019-03-01 09:54:44 +0000386 "-ocr", "--old-crypto-repo", type=str,
387 help="repository for old crypto submodule."
388 )
389 parser.add_argument(
390 "-n", "--new-rev", type=str, help="revision for new version",
391 required=True,
392 )
393 parser.add_argument(
394 "-nr", "--new-repo", type=str, help="repository for new version."
395 )
396 parser.add_argument(
397 "-nc", "--new-crypto-rev", type=str,
398 help="revision for new crypto version"
399 )
400 parser.add_argument(
401 "-ncr", "--new-crypto-repo", type=str,
402 help="repository for new crypto submodule."
Darryl Green9f357d62019-02-25 11:35:05 +0000403 )
404 parser.add_argument(
Darryl Greenc2883a22019-02-20 15:01:56 +0000405 "-s", "--skip-file", type=str,
Gilles Peskineb6ce2342019-07-04 19:00:31 +0200406 help=("path to file containing symbols and types to skip "
407 "(typically \"-s identifiers\" after running "
408 "\"tests/scripts/list-identifiers.sh --internal\")")
Darryl Greenc2883a22019-02-20 15:01:56 +0000409 )
Darryl Greene62f9bb2019-02-21 13:09:26 +0000410 parser.add_argument(
Gilles Peskinec76ab852021-04-23 16:32:32 +0200411 "--check-abi",
412 action='store_true', default=True,
413 help="Perform ABI comparison (default: yes)"
414 )
415 parser.add_argument("--no-check-abi", action='store_false', dest='check_abi')
416 parser.add_argument(
417 "--check-api",
418 action='store_true', default=True,
419 help="Perform API comparison (default: yes)"
420 )
421 parser.add_argument("--no-check-api", action='store_false', dest='check_api')
422 parser.add_argument(
Darryl Greene62f9bb2019-02-21 13:09:26 +0000423 "-b", "--brief", action="store_true",
424 help="output only the list of issues to stdout, instead of a full report",
425 )
Darryl Green7c2dd582018-03-01 14:53:49 +0000426 abi_args = parser.parse_args()
Darryl Green492bc402019-04-11 15:50:41 +0100427 if os.path.isfile(abi_args.report_dir):
428 print("Error: {} is not a directory".format(abi_args.report_dir))
429 parser.exit()
Darryl Green0d1ca512019-04-09 09:14:17 +0100430 old_version = SimpleNamespace(
431 version="old",
432 repository=abi_args.old_repo,
433 revision=abi_args.old_rev,
Gilles Peskine3e2da4a2019-07-04 19:01:22 +0200434 commit=None,
Darryl Green0d1ca512019-04-09 09:14:17 +0100435 crypto_repository=abi_args.old_crypto_repo,
436 crypto_revision=abi_args.old_crypto_rev,
437 abi_dumps={},
438 modules={}
Darryl Green8184df52019-04-05 17:06:17 +0100439 )
Darryl Green0d1ca512019-04-09 09:14:17 +0100440 new_version = SimpleNamespace(
441 version="new",
442 repository=abi_args.new_repo,
443 revision=abi_args.new_rev,
Gilles Peskine3e2da4a2019-07-04 19:01:22 +0200444 commit=None,
Darryl Green0d1ca512019-04-09 09:14:17 +0100445 crypto_repository=abi_args.new_crypto_repo,
446 crypto_revision=abi_args.new_crypto_rev,
447 abi_dumps={},
448 modules={}
Darryl Green8184df52019-04-05 17:06:17 +0100449 )
Darryl Green0d1ca512019-04-09 09:14:17 +0100450 configuration = SimpleNamespace(
451 verbose=abi_args.verbose,
452 report_dir=abi_args.report_dir,
453 keep_all_reports=abi_args.keep_all_reports,
454 brief=abi_args.brief,
Gilles Peskinec76ab852021-04-23 16:32:32 +0200455 check_abi=abi_args.check_abi,
456 check_api=abi_args.check_api,
Darryl Green0d1ca512019-04-09 09:14:17 +0100457 skip_file=abi_args.skip_file
Darryl Green7c2dd582018-03-01 14:53:49 +0000458 )
Darryl Green0d1ca512019-04-09 09:14:17 +0100459 abi_check = AbiChecker(old_version, new_version, configuration)
Darryl Green7c2dd582018-03-01 14:53:49 +0000460 return_code = abi_check.check_for_abi_changes()
461 sys.exit(return_code)
Gilles Peskinee915d532019-02-25 21:39:42 +0100462 except Exception: # pylint: disable=broad-except
463 # Print the backtrace and exit explicitly so as to exit with
464 # status 2, not 1.
Darryl Greena6f430f2018-03-15 10:12:06 +0000465 traceback.print_exc()
Darryl Green7c2dd582018-03-01 14:53:49 +0000466 sys.exit(2)
467
468
469if __name__ == "__main__":
470 run_main()