diff --git a/helpers/create-abi-dump.py b/helpers/create-abi-dump.py --- a/helpers/create-abi-dump.py +++ b/helpers/create-abi-dump.py @@ -1,20 +1,41 @@ #!/usr/bin/python3 -import os -import re +import argparse import logging +import os import pathlib -import argparse -import tempfile +import re import subprocess import sys +import tempfile +import yaml + from collections import defaultdict from typing import Dict, List, Union, Set +from helperslib import CommonUtils, ToolingSettings from helperslib import Packages, EnvironmentHandler # Make sure logging is ready to go #logging.basicConfig(level=logging.DEBUG) +ACCXMLTMPL = """{version} + + {headers} + + + {libs} + + + {skipIncludePaths} + + + {additionalIncludes} + + + {gccOptions} + +""" + def cmake_parser(lines: List) -> Dict: """A small cmake parser, if you search for a better solution think about using a proper one based on ply. @@ -102,9 +123,10 @@ # This class stores information on the library in question and assists in extracting information concerning it class Library: # Make sure we initialize everything we are going to need - def __init__(self, name: str) -> None: + def __init__(self, name: str, accSettings: Dict) -> None: # name of the library self.name = name # type: str + self.accSettings = accSettings # type: Dict # The raw cmake Parser output, available for debugging purposes # see cmake_parser function for the return value @@ -208,7 +230,10 @@ version = self.version headers = set() # type: Set[str] libs = set() # type: Set[str] - additionalIncludes = set() # type: Set[str] + skipIncludePaths = set(self.accSettings['skip_include_paths']) # type: Set[str] + additionalIncludes = set(self.accSettings['add_include_paths']) # type: Set[str] + gccOptions = set(self.accSettings['gcc_options']) # type: Set[str] + # list of general include directories prefixHeaders = [os.path.abspath(i) for i in buildEnvironment.get('CMAKE_PREFIX_PATH').split(":")] @@ -233,32 +258,24 @@ libs.add(target['path']) # Now we can go ahead and generate the XML file for abi-compliance-checker - xml = """ - {version} - - {headers} - - - {libs} - - - /usr/lib/python3.6/site-packages/utils/fake_libc_include - /usr/include/clang/AST - /usr/lib64/clang/6.0.1/include - - - {additionalIncludes} - /usr/lib64/qt5/mkspecs/linux-g++ - - """.format(version=version, headers="\n".join(headers), libs="\n".join(libs), additionalIncludes="\n".join(additionalIncludes)) # replace with f-String in Python 3.6 - + def _(l): + return "\n ".join(l) + + xml = ACCXMLTMPL.format(version=version, + headers=_(headers), + libs=_(libs), + additionalIncludes=_(additionalIncludes), + skipIncludePaths=_(skipIncludePaths), + gccOptions=_(gccOptions), + ) + # Write the generated XML out to a file to pass to abi-compliance-checker # We will give this to abi-compliance-checker using it's --dump parameter with open("{version}.xml".format(version=version),"w") as f: # replace with f-String in python 3.6 f.write(xml) - # acc is compatible for C/C++ as Qt using C++11 and -fPic we need to set the gcc settings explitly - subprocess.check_call(["abi-compliance-checker", "-gcc-options", "-std=c++11 -fPIC", "-l", self.name, "--dump", f.name], env=runtimeEnvironment) + # acc is compatible for C/C++ (but --lang C++ doesn't remove the warning about C++ only settings). + subprocess.check_call(["abi-compliance-checker", "--lang", "C++", "-l", self.name, "--dump", f.name], env=runtimeEnvironment) # Parse the command line arguments we've been given parser = argparse.ArgumentParser(description='Utility to create abi checker tarballs.') @@ -273,6 +290,13 @@ # Make sure we have an environment ready for executing commands buildEnvironment = EnvironmentHandler.generateFor( installPrefix=arguments.usingInstall ) + +# get acc settings +localMetadataPath = os.path.join( CommonUtils.scriptsBaseDirectory(), 'local-metadata', 'abi-compliance-checker.yaml' ) + +accSettings = ToolingSettings.Loader( arguments.project, arguments.platform ) +accSettings.loadSpecificationFile( localMetadataPath ) + # Get ready to start searching for libraries foundLibraries = [] @@ -283,7 +307,7 @@ for line in log.readlines(): match = cmakeConfig.match(line) if match: - foundLibrary = Library( match.group(2) ) + foundLibrary = Library( match.group(2), accSettings ) foundLibraries.append(foundLibrary) # Initialize the archive manager @@ -333,7 +357,7 @@ ourArchive.storePackage(packageName, fileName, scmRevision, extraMetadata) except subprocess.CalledProcessError as e: retval = e.returncode - logging.error("abi-complience-checker exited with {retval}".format(retval=retval)) + logging.error("abi-compliance-checker exited with {retval}".format(retval=retval)) # We had an issue with one of the ABIs if retval != 0: diff --git a/helpers/helperslib/ToolingSettings.py b/helpers/helperslib/ToolingSettings.py new file mode 100644 --- /dev/null +++ b/helpers/helperslib/ToolingSettings.py @@ -0,0 +1,66 @@ +import fnmatch +import copy +import os +import yaml + +from typing import Dict, Union, List + +from helperslib import Buildable, CommonUtils + +# Class to make it convenient to read configs for working with settings for tooling +class Loader(object): + + def __init__(self, project: str, platform: str) -> None: + + self.project = project + self.platform = platform + self.config = dict() + + # Initialise the dependnecy resolver + self.resolver = Buildable.DependencyResolver() + + # Ask the resolver to load the list of projects... + projectsTreeLocation = os.path.join( CommonUtils.scriptsBaseDirectory(), 'repo-metadata', 'projects' ) + self.resolver.loadProjectsFromTree( projectsTreeLocation ) + + self.resolverProject = self.resolver.retrieveProject( project ) + + # Allow fetching our config + def __getitem__(self, name: str) -> Union[List, str, Dict]: + # Do we know about this attribute? + if name in self.config: + # Then return it + return self.config[name] + + # We don't know about it + raise KeyError + + # Load the given specification file + def loadSpecificationFile(self, configFileLocation: str): + # Does the file exist? + # If it does not, then we don't need to do anything else + if not os.path.isfile( configFileLocation ): + return + + # Load the file now + with open( configFileLocation, 'r' ) as configFile: + # Parse the YAML file + projectConfig = yaml.load( configFile ) + + entries = [] + for key in projectConfig: + if fnmatch.fnmatch( self.resolverProject.path, key ): + entries.append( key ) + + # sorting all entries - the entries at the end overwrites the former settings + # len is efficient, but would fail if we have single letter repo names + entries.sort( key=len ) + + self.config = dict() + for entry in entries: + content = projectConfig[entry] + try: + self.config.update( content['general'] ) + self.config.update( content[self.platform] ) # try to load platform specific settings + except KeyError: + pass diff --git a/local-metadata/abi-compliance-checker.yaml b/local-metadata/abi-compliance-checker.yaml new file mode 100644 --- /dev/null +++ b/local-metadata/abi-compliance-checker.yaml @@ -0,0 +1,40 @@ +# This file is used to specify the builds of abi-compliance-checker. +# So far following settings are used: +# +# - gcc_options: +# specify the gcc options to build of headers +# +# - skip_include_paths: +# list pf paths to exclude from searching for headers +# +# - add_include_paths: +# list of paths to search for headers +# +# for more information about the settings: abi-compliance-checker --info +"*": + general: + gcc_options: + - "-std=c++11" + - "-fPIC" + skip_include_paths: + - /usr/lib/python3.6/site-packages/utils/fake_libc_include + - /usr/include/clang/AST + - /usr/lib64/clang/6.0.1/include + add_include_paths: + - /usr/lib64/qt5/mkspecs/linux-g++ +# "SUSEQt5.10": # special settings for one platform +# gcc_options: +# - something +# - nextsetting + +# "kde/*": # special settings for all repos in kde (fnmatch) +# general: +# add_include_paths: +# - special1 + +"kde/pim/akonadi-search": + general: + gcc_options: + - "-std=c++11" + - "-fPIC" + - "-DQT_NO_KEYWORDS"