1#!/usr/bin/python 2# 3# 4# Check ABI/API compatibility of two PETSc versions (the old and the new). Old and new PETSc should have already been built using GCC and with -g 5# 6# Usage: 7# ./abicheck.py -old_dir <old PETSC_DIR> 8# -old_arch <old PETSC_ARCH> 9# -new_dir <new PETSC_DIR> 10# -new_arch <new PETSC_ARCH> 11# -report_format <format of the report file, either xml or html. Optional with default=html> 12# 13 14import os, sys 15import subprocess 16import argparse 17 18# Copy from config/BuildSystem/config/setCompilers.py 19def isGNU(compiler): 20 '''Returns true if the compiler is a GNU compiler''' 21 try: 22 output = subprocess.check_output([compiler,'--help'], stderr=subprocess.STDOUT) 23 return (any([s in output for s in ['www.gnu.org', 24 'bugzilla.redhat.com', 25 'gcc.gnu.org', 26 'gcc version', 27 '-print-libgcc-file-name', 28 'passed on to the various sub-processes invoked by gcc', 29 'passed on to the various sub-processes invoked by cc', 30 'passed on to the various sub-processes invoked by gfortran', 31 'passed on to the various sub-processes invoked by g++', 32 'passed on to the various sub-processes invoked by c++', 33 ]]) 34 and not any([s in output for s in ['Intel(R)', 35 'Unrecognised option --help passed to ld', # NAG f95 compiler 36 ' clang ' 37 ]])) 38 except: 39 pass 40 return 0 41 42def gen_xml_desc(petsc_dir, petsc_arch, libs, xmlfile): 43 '''Generate an ABI descriptor file for the given PETSC_DIR and PETSC_ARCH''' 44 45 # Get branch and hash of the source used to build the library. We use them as an informative version number of the library 46 petscconf = os.path.join(petsc_dir, petsc_arch, 'include', 'petscconf.h') 47 48 branch = subprocess.check_output(['grep', '^#define PETSC_VERSION_BRANCH_GIT', petscconf]) 49 branch = branch.split("\"")[1] 50 hash = subprocess.check_output(['grep', '^#define PETSC_VERSION_GIT', petscconf]) 51 hash = hash.split("\"")[1] 52 53 petsclibs = '' 54 petscheaders = '' 55 56 for name in libs: 57 name = name.lower() 58 if not name.startswith('petsc'): # Support petsc, petscsys, or sys etc 59 name = 'petsc'+name 60 libname = 'lib'+name 61 libname_so = os.path.join(petsc_dir, petsc_arch, 'lib', libname+'.so') 62 libname_dylib = os.path.join(petsc_dir, petsc_arch, 'lib', libname+'.dylib') 63 libname_a = os.path.join(petsc_dir, petsc_arch, 'lib', libname+'.a') 64 lib = '' 65 if os.path.isfile(libname_so): 66 lib = libname_so 67 elif os.path.isfile(libname_dylib): 68 lib = libname_dylib 69 elif os.path.isfile(libname_a): 70 lib = libname_a 71 72 if lib == '': 73 raise RuntimeError('Could not find library %s for PETSC_DIR=%s PETSC_ARCH=%s. Please configure and build it before doing ABI/API checking\n'% (name,petsc_dir,petsc_arch)) 74 petsclibs += lib+'\n' 75 76 header = os.path.join(petsc_dir,'include',name+'.h') 77 if not os.path.isfile(header): 78 raise RuntimeError('File %s does not exist.\n'% (header)) 79 petscheaders += header+'\n' 80 81 with open(xmlfile, "w") as file: 82 file.write("<version>\n") 83 file.write(branch + ' (' + hash +')') 84 file.write("</version>\n\n") 85 86 file.write("<headers>\n") 87 file.write(petscheaders) 88 file.write("\n") 89 file.write("</headers>\n\n") 90 91 file.write("<libs>\n") 92 file.write(petsclibs) 93 file.write("\n") 94 file.write("</libs>\n\n") 95 96 file.write('<include_paths>\n') 97 file.write(os.path.join(petsc_dir,'include')) 98 file.write("\n") 99 file.write(os.path.join(petsc_dir,petsc_arch,'include')) 100 file.write("\n") 101 file.write("</include_paths>\n") 102 103def run_abi_checker(petsc_dir, petsc_arch, abi_dir, oldxml, newxml, cc, rformat): 104 if rformat == 'html': 105 report = 'report.html' 106 else: 107 report = 'report.xml' 108 reportfile = os.path.join(abi_dir, report) 109 110 abichecker = os.path.join(petsc_dir, 'lib', 'petsc', 'bin', 'maint', 'abi-compliance-checker', 'abi-compliance-checker.pl') 111 ierr = subprocess.call([abichecker, '-l', 'petsc', '--lang=C', '-old', oldxml, '-new', newxml, '--gcc-path', cc, '--report-path', reportfile, '--report-format', rformat]) 112 return ierr 113 114def main(): 115 parser = argparse.ArgumentParser() 116 parser.add_argument("-old_dir", help="Old PETSC_DIR, which defines the PETSc library to compare with", required=True) 117 parser.add_argument("-old_arch", help="Old PETSC_ARCH, which defines the PETSc library to compare with", required=True) 118 parser.add_argument("-new_dir", help="New PETSC_DIR", required=True) 119 parser.add_argument("-new_arch", help="New PETSC_ARCH", required=True) 120 parser.add_argument("-report_format", help="Format of the report file", default='html', required=False) 121 122 args = parser.parse_args() 123 old_dir = args.old_dir 124 old_arch = args.old_arch 125 new_dir = args.new_dir 126 new_arch = args.new_arch 127 rformat = args.report_format 128 129 # Get the compiler and raise error if it is not GNU 130 cc = '' 131 for [petsc_dir, petsc_arch] in [[old_dir, old_arch],[new_dir, new_arch]]: 132 petscvariables = os.path.join(petsc_dir, petsc_arch, 'lib', 'petsc','conf', 'petscvariables') 133 if not os.path.isfile(petscvariables): 134 raise RuntimeError('File %s does not exist.\n'% (petscvariables)) 135 ccstring = subprocess.check_output(['grep', '^CC =', petscvariables]) 136 cc = ccstring.split('=')[1].strip() 137 if not isGNU(cc): 138 raise RuntimeError('The compiler %s is not a GNU compiler.\n'% (cc)) 139 140 # Check the PETSC_USE_SINGLE_LIBRARY status 141 petscconf_h = os.path.join(new_dir, new_arch, 'include', 'petscconf.h') 142 if not os.path.isfile(petscconf_h): 143 raise RuntimeError('File %s does not exist.\n'% (petscconf_h)) 144 145 if 'PETSC_USE_SINGLE_LIBRARY' in open(petscconf_h).read(): 146 libs=['petsc'] 147 else: 148 libs=['sys', 'vec', 'mat', 'dm', 'ksp', 'snes', 'ts', 'tao'] 149 150 # Check report format 151 if rformat != 'html' and rformat != 'xml': 152 raise RuntimeError('Unsupported report format "%s". Only html and xml are supported.\n'% (rformat)) 153 154 # We generate report under new_dir/abi 155 abi_dir = os.path.join(new_dir, new_arch, 'abi') 156 if not os.path.isdir(abi_dir): 157 os.makedirs (abi_dir) 158 159 print("\nChecking libraries %s ..." %(libs)) 160 oldxml = os.path.join(abi_dir, 'old.xml') 161 newxml = os.path.join(abi_dir, 'new.xml') 162 gen_xml_desc(old_dir,old_arch, libs, oldxml) 163 gen_xml_desc(new_dir,new_arch, libs, newxml) 164 ierr = run_abi_checker(new_dir, new_arch, abi_dir, oldxml, newxml, cc, rformat) 165 166 print("=========================================================================================") 167 if (ierr): 168 print("Error: ABI/API compatibility check failed. Open the compatibility report file to see details.") 169 else: 170 print("ABI/API of the two versions are compatible.") 171 172if __name__ == '__main__': 173 main() 174