diff --git a/helpers/create-abi-bump.py b/helpers/create-abi-bump.py new file mode 100644 --- /dev/null +++ b/helpers/create-abi-bump.py @@ -0,0 +1,155 @@ +#!/usr/bin/python3 + +import argparse +from collections import defaultdict +import os +import re +import subprocess +import tempfile + +from helperslib import Packages + +import logging +logging.basicConfig(level=logging.DEBUG) + +# Parse the command line arguments we've been given +parser = argparse.ArgumentParser(description='Utility to create abi checker tarballs.') +parser.add_argument('--buildlog', type=str, required=True) +parser.add_argument('--environment', type=str, required=True) +arguments = parser.parse_args() + +def cmake_parser(lines): + ret = { + "variables":{}, + "targets":defaultdict(lambda:defaultdict(list)) + } + variables = ret['variables'] + targets = ret['targets'] + + def parse_set(args): + args = args.split() + if len(args) == 2: + variables[args[0]] = args[1] + + def parse_set_target_properties(args): + args = args.split() + target = targets[args[0]] + if not args[1] == "PROPERTIES": + logging.warning("unknown line: %s"%(args)) + + + keywords=["IMPORTED_LINK_DEPENDENT_LIBRARIES_DEBUG", + "IMPORTED_LOCATION_DEBUG", + "IMPORTED_SONAME_DEBUG", + "INTERFACE_INCLUDE_DIRECTORIES", + "INTERFACE_LINK_LIBRARIES", + "INTERFACE_COMPILE_OPTIONS", + ] + + t = None + for arg in args[2:]: + if arg in keywords: + t=target[arg] + continue + t.append(arg) + + keywords={ + "set": parse_set, + "set_target_properties": parse_set_target_properties, + } + RELINE = re.compile("^\s*(?P[^(]+)\s*\(\s*(?P.*)\s*\)\s*$") + for line in lines: + m = RELINE.match(line) + if m and m.group('keyword') in keywords: + keywords[m.group('keyword')](m.group('args')) + + return ret + + +class Library: + def __init__(self, name): + self.name = name + self.p = None + + def __repr__(self): + return "".format(self=self) + + def runCMake(self): + with tempfile.TemporaryDirectory() as d: + with open(d+"/CMakeLists.txt","w") as f: + f.write("find_package({self.name} CONFIG REQUIRED)\n".format(self=self)) + proc = subprocess.Popen(['cmake', '.', '--trace-expand'], cwd=d, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE) + self.mlines = [] + self.libs=defaultdict(dict) + for line in proc.stderr: + theLine = line.decode("utf-8") + m = re.match('.*/{self.name}(Targets[^/]*|Config[^/]*)\.cmake\(\d+\):\s*(.*)$'.format(self=self), theLine) + if m: + mline = m.group(2) + self.mlines.append(mline) + + self.p = cmake_parser(self.mlines) + + self.version = self.p["variables"]["PACKAGE_VERSION"] + self.targets = {} + + def inclDirs(args): + d = [] + for arg in args: + d += arg.split(";") + return d + + for t,value in self.p["targets"].items(): + target={"SONAME": re.search("\.([\d]*)$",value["IMPORTED_SONAME_DEBUG"][0]).group(1), + "path": value["IMPORTED_LOCATION_DEBUG"][0], + "include_dirs": inclDirs(value["INTERFACE_INCLUDE_DIRECTORIES"]), + } + self.targets[t]=target + + def createABIDump(self): + if not self.p: + self.runCMake() + + version = self.version + headers = [] + libs = [] + for target in self.targets.values(): + for i in target['include_dirs']: + if i == '/usr/include' or i.endswith("/KF5"): + continue + if not i in headers: + headers.append(i) + if not target['path'] in libs: + libs.append(target['path']) + + xml = """ +{version} + +{headers} + + +{libs} + +""".format(version=version, headers="\n".join(headers), libs="\n".join(libs)) + with open("{version}.xml".format(version=version),"w") as f: + f.write(xml) + subprocess.call(["abi-compliance-checker", "-gcc-options", "-std=c++11 -fPIC", "-l", self.name, "--dump",f.name]) + +libs = [] + +RELINE = re.compile("^-- (Installing|Up-to-date): .*/([^/]*)Config\.cmake$") +with open(arguments.buildlog) as f: + for line in f.readlines(): + m = RELINE.match(line) + if m: + lib = Library(m.group(2)) + libs.append(lib) + + + +# Initialize the archive manager +ourArchive = Packages.Archive(arguments.environment, 'ABIReference', usingCache = False) + +for lib in libs: + lib.createABIDump() + ourArchive.storePackage(lib.name, "abi_dumps/{name}/{name}_{version}.abi.tar.gz".format(name=lib.name,version=lib.version), max([t['SONAME'] for t in lib.targets.values()]))