diff --git a/helpers/check-abi.py b/helpers/check-abi.py --- a/helpers/check-abi.py +++ b/helpers/check-abi.py @@ -12,11 +12,14 @@ import argparse import collections +import decimal import logging import os +import re import subprocess import sys import time +import yaml from helperslib import Packages from helperslib.Version import Version @@ -59,6 +62,25 @@ return candidate +def parseACCOutputToDict(stdout): + """Parse output of abi-compliance-checker for further processing and returning a dict. + extract binary/source compatibility from acc + and calculate a simple bool for the compatibibility. + """ + checkBlock = re.compile(br"""^Binary compatibility: (?P[0-9.]+)%\s* +Source compatibility: (?P[0-9.]+)%\s*$""", re.M) + m = checkBlock.search(stdout).groupdict() + + m['binary'] = decimal.Decimal(m['binary'].decode()) + m['source'] = decimal.Decimal(m['source'].decode()) + compatibility = m['binary'] == 100 and m['source'] == 100 + + return { + 'binaryCompatibility': float(m['binary']), + 'sourceCompatibility': float(m['source']), + 'compatibility': compatibility, + } + # Make sure logging is ready to go logging.basicConfig(level=logging.DEBUG) @@ -166,6 +188,9 @@ # Safe the overall retval state retval = 0 +# the dictonary that will be written to abi-compatibility-results.yaml +resultsYamlFile = {} + for l in libraries: library = l.library libname = library['libname'] @@ -181,17 +206,45 @@ logging.info("check %s(old) -> %s(new)", candidate['scmRevision'], library['scmRevision']) + reportPath = "compat_reports/{libname}_compat_report.html".format(libname=libname) + + # Basic result yml information + yml = { + 'reportPath': reportPath, + 'ownCommit': scmRevision, + 'otherCommit': candidate['scmRevision'], + } + resultsYamlFile[libname] = yml + + if candidate['scmRevision'] in HASH2TAG: + yml['tag'] = HASH2TAG[candidate['scmRevision']].version + # check ABI and write compat reports - cmd = ["abi-compliance-checker", - "-report-path", "compat_reports/{libname}_compat_report.html".format(libname=libname), + cmd = [ + "abi-compliance-checker", + "-report-path", reportPath, "-l", libname, "--old", oldLibraryPath, - "--new", newLibraryPath] - ret = subprocess.call(cmd) - - if ret != 0: - logging.error("abi-compliance-checker exited with %s", ret) - retval = ret + "--new", newLibraryPath, + ] + logging.debug(" ".join(cmd)) + try: + prog = subprocess.run(cmd, check=True, capture_output=True) + except subprocess.CalledProcessError as e: + if e.returncode == 1: # that means that we are not compatible, but still valid output. + logging.warning("abi-compliance-checker exited with 1:\n%s", prog.stdout.decode()) + + yml.update(parseACCOutputToDict(e.stdout)) + else: + logging.error("abi-compliance-checker exited with %s:\nstdout:\n\ลง%s\nstderr:\n\t%s", e.returncode, e.stdout.decode(), e.stderr.decode()) + retval = e.returncode + yml['error'] = e.returncode + else: + logging.debug(prog.stdout.decode()) + yml.update(parseACCOutputToDict(prog.stdout)) + +with open('abi-compatibility-results.yaml', 'w') as f: + f.write(yaml.dump(resultsYamlFile, default_flow_style=False)) # We had an issue with one of the ABIs if retval != 0: diff --git a/pipeline-templates/SUSEQt5.9.template b/pipeline-templates/SUSEQt5.9.template --- a/pipeline-templates/SUSEQt5.9.template +++ b/pipeline-templates/SUSEQt5.9.template @@ -133,6 +133,9 @@ // Save the ABI Compatibility reports for developers to review if necessary archiveArtifacts artifacts: 'compat_reports/*_compat_report.html', onlyIfSuccessful: false, allowEmptyArchive: true + // Save the ABI Compatibility results yaml file + archiveArtifacts artifacts: 'abi-compatibility-results.yaml', onlyIfSuccessful: false, allowEmptyArchive: true + // cppcheck is not supported by Pipeline at the moment, so we don't run that for now // See https://issues.jenkins-ci.org/browse/JENKINS-35096