diff --git a/archive-configs/production.yaml b/archive-configs/production.yaml --- a/archive-configs/production.yaml +++ b/archive-configs/production.yaml @@ -16,3 +16,4 @@ SUSEQt5.11: "/srv/archives/production/SUSEQt5.11/" FreeBSDQt5.11: "/usr/home/jenkins/archives/production/" AndroidQt5.11: "/srv/archives/production/AndroidQt5.11/" + ABIReference: "/srv/archives/production/ABIReference" diff --git a/helpers/check-abi.py b/helpers/check-abi.py new file mode 100755 --- /dev/null +++ b/helpers/check-abi.py @@ -0,0 +1,106 @@ +#!/usr/bin/python3 + +# check the ABIs against a earlier state. + +# abi-compliance-checker creates a html file with the report. +# it can be multiple libraries in one repository (see messagelib) +# so we have multiple html files for one repository +# in order to store tham as artifacts in jenkins add: +# +# archiveArtifacts artifacts: 'compat_reports/*_compat_reports.html', onlyIfSuccessful: false + +import os +import logging +import argparse +import subprocess +import sys + +from helperslib import Packages + +# Make sure logging is ready to go +logging.basicConfig(level=logging.DEBUG) + +# Parse the command line arguments we've been given +parser = argparse.ArgumentParser(description='Utility to check ABI.') +parser.add_argument('--project', type=str, required=True) +parser.add_argument('--environment', type=str, required=True) +parser.add_argument('--branchGroup', type=str, required=True) +arguments = parser.parse_args() + +# Initialize the archive manager +ourArchive = Packages.Archive(arguments.environment, 'ABIReference', usingCache = True, contentsSuffix = ".abidump") + +# Determine which SCM revision we are storing +# This will be embedded into the package metadata which might help someone doing some debugging +# GIT_COMMIT is set by Jenkins Git plugin, so we can rely on that for most of our builds +scmRevision = '' +if os.getenv('GIT_COMMIT') != '': + scmRevision = os.getenv('GIT_COMMIT') + +if not scmRevision: + scmRevision = subprocess.check_output(["git", "log", "--format=%H", "-n 1", "HEAD"]).strip().decode() + +# Find all libaries, that are build with the same git commit +# TODO: if we have project in the metadata, we should check that, too. Otherwise we do strange things if we have the same git hashes in different repositories. +libraries = [] + +for key, entry in ourArchive.serverManifest.items(): + if "scmRevision" in entry and entry["scmRevision"] == scmRevision: + entry["key"] = key + libraries.append(entry) + +# Find the reference dump +# the reference dump, needs to have: +# * same libname +# * same SONAME otherwise we have a ABI bump and than it is safe to break ABI +# * lowest timestamp possible, ABI should not change the whole life of a library's SONAME +# TODO: The interessting part is, how to handle the pre release versions when the stable branch get's updated but it is not released. + +referenceLibraries = {i['libname']:i for i in libraries} +for library in libraries: + libname = library["libname"] + soname = library["SONAME"] + for key, entry in ourArchive.serverManifest.items(): + # We whant to so search for the library + if "libname" in entry and entry["libname"] == libname: + # only interessed, for builds with the same SONAME + if entry['SONAME'] == soname: + # find the oldest build + if entry["timestamp"] < referenceLibraries[libname]["timestamp"]: + entry["key"] = key + referenceLibraries[libname] = entry + if entry['SONAME'] > soname: + # TODO: if we are in a stable build only check for entries in stable branch + logging.warning("We searched for SONAME = {}, but found a newer SONAME = {} in the builds, that should not happen, as SONAMEs should only rise and never go lower!".format(soname, entry['SONAME'])) + +# Check every libraries ABI and do not fail, if one is not fine. +# Safe the overall retval state +retval = 0 + +for library in libraries: + libname = library["libname"] + if referenceLibraries[libname]['timestamp'] == library["timestamp"]: + logging.info("Did not found any older build for {}, nothing to check ABI against.".format(libname)) + continue + + # get the packages, we want to test against each other + newLibraryPath, _ = ourArchive.retrievePackage(library['key']) + oldLibraryPath, _ = ourArchive.retrievePackage(referenceLibraries[libname]['key']) + + logging.info("Let's do a ABI check {} against {}".format(library['scmRevision'], referenceLibraries[libname]['scmRevision'])) + + # check ABI and write compat reports + cmd = ["abi-compliance-checker", + "-report-path", "$WORKSPACE/compat_reports/{libname}_compat_report.html".format(libname=libname), + "-l", libname, + "--old", oldLibraryPath, + "--new", newLibraryPath] + ret = subprocess.call(cmd) + + if ret != 0: + logging.error("abi-compliance-checker exited with {ret}".format(ret=ret)) + retval = ret + +# We had an issue with one of the ABIs +if retval != 0: + sys.exit(retval)