From 536fba3256d008f0e24fe49be4cd8932a51e04d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Wagner?= Date: Thu, 3 Feb 2022 08:02:09 +0100 Subject: build: add abi-check to check for abi stability --- .gitlab-ci/check-abi | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100755 .gitlab-ci/check-abi (limited to '.gitlab-ci') diff --git a/.gitlab-ci/check-abi b/.gitlab-ci/check-abi new file mode 100755 index 0000000..fc2296d --- /dev/null +++ b/.gitlab-ci/check-abi @@ -0,0 +1,113 @@ +#!/usr/bin/python3 + + +import argparse +import contextlib +import os +import shutil +import subprocess +import sys + + +def format_title(title): + box = { + 'tl': '╔', 'tr': '╗', 'bl': '╚', 'br': '╝', 'h': '═', 'v': '║', + } + hline = box['h'] * (len(title) + 2) + + return '\n'.join([ + f"{box['tl']}{hline}{box['tr']}", + f"{box['v']} {title} {box['v']}", + f"{box['bl']}{hline}{box['br']}", + ]) + + +def rm_rf(path): + try: + shutil.rmtree(path) + except FileNotFoundError: + pass + + +def sanitize_path(name): + return name.replace('/', '-') + + +def get_current_revision(): + revision = subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD'], + encoding='utf-8').strip() + + if revision == 'HEAD': + # This is a detached HEAD, get the commit hash + revision = subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip().decode('utf-8') + + return revision + + +@contextlib.contextmanager +def checkout_git_revision(revision): + current_revision = get_current_revision() + subprocess.check_call(['git', 'checkout', '-q', revision]) + + try: + yield + finally: + subprocess.check_call(['git', 'checkout', '-q', current_revision]) + + +def build_install(revision): + build_dir = '_build' + dest_dir = os.path.abspath(sanitize_path(revision)) + print(format_title(f'# Building and installing {revision} in {dest_dir}'), + end='\n\n', flush=True) + + with checkout_git_revision(revision): + rm_rf(build_dir) + rm_rf(revision) + + subprocess.check_call(['meson', build_dir, + '--prefix=/usr', '--libdir=lib', + '-Db_coverage=false', '-Dgtk_doc=false', '-Dtests=false', '-Dexamples=false']) + subprocess.check_call(['ninja', '-v', '-C', build_dir]) + subprocess.check_call(['ninja', '-v', '-C', build_dir, 'install'], + env={'DESTDIR': dest_dir}) + + return dest_dir + + +def compare(old_tree, new_tree): + print(format_title(f'# Comparing the two ABIs'), end='\n\n', flush=True) + + old_headers = os.path.join(old_tree, 'usr', 'include') + old_lib = os.path.join(old_tree, 'usr', 'lib', 'librest-1.0.so') + + new_headers = os.path.join(new_tree, 'usr', 'include') + new_lib = os.path.join(new_tree, 'usr', 'lib', 'librest-1.0.so') + + subprocess.check_call([ + 'abidiff', '--headers-dir1', old_headers, '--headers-dir2', new_headers, + '--drop-private-types', '--fail-no-debug-info', '--no-added-syms', old_lib, new_lib]) + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + + parser.add_argument('old', help='the previous revision, considered the reference') + parser.add_argument('new', help='the new revision, to compare to the reference') + + args = parser.parse_args() + + if args.old == args.new: + print("Let's not waste time comparing something to itself") + sys.exit(0) + + old_tree = build_install(args.old) + new_tree = build_install(args.new) + + try: + compare(old_tree, new_tree) + + except Exception as e: + print ('ABI comparison failed: '+ str(e)) + sys.exit(1) + + print(f'Hurray! {args.old} and {args.new} are ABI-compatible!') -- cgit v1.2.1