File indexing completed on 2024-05-12 16:39:34
0001 #!/usr/bin/python -Qwarnall 0002 0003 # 0004 # This script checks the source tree for errors on a file and directory level: 0005 # * File level 0006 # - Checks that the include guard is the same in the #ifndef and #define 0007 # It does not check if the include guard is correct in relation to the filename. 0008 # * Directory level: 0009 # - Checks for duplicate include guards between different files 0010 # - Checks for different export macros and export files in the same directory 0011 # 0012 0013 import sys 0014 import os 0015 import string 0016 import getopt 0017 import re 0018 import fnmatch 0019 0020 0021 # Global variables 0022 dryrun = False # Maybe the default should be True... 0023 pattern = "*" 0024 recursive = True 0025 verbose = False 0026 0027 # Stores include guards in .h files. 0028 includeGuards = {} 0029 0030 # Stores the export macros for each directory 0031 exportMacros = {} 0032 0033 0034 # ---------------------------------------------------------------- 0035 # Individual actions 0036 0037 0038 def checkContents(dirName, filename, contents): 0039 global verbose 0040 global includeGuards 0041 global exportMacros 0042 0043 # Compile a regex that matches an ifndef statement. 0044 ifndefPattern = '^(\\#ifndef\\s*)([^\\s]*)(\\s*)' 0045 ifndefRegex = re.compile(ifndefPattern) 0046 0047 # Compile a regex that matches an define statement. 0048 definePattern = '^(\\#define\\s*)([^\\s]*)(\\s*)' 0049 defineRegex = re.compile(definePattern) 0050 0051 # Compile a regex that matches a class definition with an export 0052 classPattern = '^(class\\s*)([a-zA-Z0-9_]*[Ee][Xx][Pp][Oo][Rr][Tt])?(\\s*)([a-zA-Z0-9_]*)(.*)' 0053 classRegex = re.compile(classPattern) 0054 0055 lineNo = 1 0056 ifndefFound = False 0057 defineFound = False 0058 ifndefLine = -1 0059 defineLine = -1 0060 includeGuard = "" 0061 for line in contents: 0062 #print "input: ", line 0063 0064 # ---------------------------------------------------------------- 0065 # Check include guards 0066 0067 # Check if this line is an #ifndef line. Only check the first one. 0068 # if it is, and it's the first one, assume that it's the include guard and store it. 0069 if not ifndefFound: 0070 ifndefs = ifndefRegex.findall(line) 0071 #print "ifndefs: ", ifndefs 0072 if len(ifndefs) > 0: 0073 ifndefFound = True 0074 ifndefLine = lineNo 0075 0076 ifndef = ifndefs[0] 0077 # Here we know it's an include statement. We should only have found one hit. 0078 # ifndef[0] = "#ifndef " 0079 # ifndef[1] = the symbol the ifndef statement 0080 # ifndef[2] = the rest of the line 0081 includeGuard = ifndef[1] 0082 0083 # Check if this line is a #define line. Only check the first one after the #ifndef is found 0084 if ifndefFound and not defineFound: 0085 defines = defineRegex.findall(line) 0086 #print "defines: ", defines 0087 if len(defines) > 0: 0088 defineFound = True 0089 defineLine = lineNo 0090 0091 define = defines[0] 0092 # Here we know it's an include statement. We should only have found one hit. 0093 # define[0] = "#define " 0094 # define[1] = the symbol the define statement 0095 # define[2] = the rest of the line 0096 if define[1] == includeGuard and lineNo == ifndefLine + 1: 0097 includeGuards[filename] = includeGuard 0098 else: 0099 sys.stderr.write(filename + ":" + str(ifndefLine) + ": Faulty include guard\n") 0100 0101 # ---------------------------------------------------------------- 0102 # Check exports 0103 0104 classes = classRegex.findall(line) 0105 #print "defines: ", defines 0106 if len(classes) > 0: 0107 classLine = classes[0] 0108 # Here we know it's an include statement. We should only have found one hit. 0109 # classLine[0] = "class " 0110 # classLine[1] = export macro 0111 # classLine[2] = spaces 0112 # classLine[3] = class name 0113 # classLine[4] = rest of the line 0114 if classLine[1] != "": 0115 #print classLine 0116 exportMacro = classLine[1] 0117 if exportMacros.has_key(dirName): 0118 # Append the export macro name if it's not already there. 0119 if not exportMacro in exportMacros[dirName]: 0120 exportMacros[dirName].append(exportMacro) 0121 else: 0122 exportMacros[dirName] = [exportMacro] 0123 0124 # end of the loop over the lines 0125 lineNo = lineNo + 1 0126 0127 if not ifndefFound or not defineFound: 0128 sys.stderr.write(filename + ": No include guard\n") 0129 0130 return 0131 0132 0133 # ---------------------------------------------------------------- 0134 0135 0136 def handleFile(dir, name, actions): 0137 global dryrun, verbose 0138 0139 # We only handle .h files for now. 0140 if name[-2:] != ".h": 0141 return 0142 0143 if verbose: 0144 sys.stderr.write(name + ":\n") 0145 0146 #print "doing: ", name 0147 0148 # Read the contents of the file into a list, one item per line. 0149 infile = open(name) 0150 contents = infile.readlines() 0151 infile.close() 0152 0153 checkContents(dir, name, contents) 0154 0155 0156 def traverseTree(dir, actions, names): 0157 global recursive, verbose, pattern 0158 0159 # We could also use os.walk() 0160 for name in names: 0161 if dir == "": 0162 fullname = name 0163 else: 0164 fullname = dir + "/" + name 0165 0166 if not os.path.exists(fullname): 0167 sys.stderr.write(fullname + ": unknown file or directory\n") 0168 continue 0169 0170 if os.path.isdir(fullname): 0171 if recursive: 0172 traverseTree(fullname, actions, os.listdir(fullname)) 0173 # Ignore all directories if not in recursive mode 0174 else: 0175 if fnmatch.fnmatch(name, pattern): 0176 handleFile(dir, fullname, actions) 0177 0178 0179 def report(): 0180 global includeGuards 0181 global exportMacros 0182 0183 print 0184 print "SUMMARY REPORT" 0185 globalProblems = False 0186 0187 # Check for duplicate include guards. 0188 guardFiles = {} 0189 for file in includeGuards.keys(): 0190 #print file 0191 guard = includeGuards[file] 0192 #print "guard:", guard 0193 if guardFiles.has_key(guard): 0194 guardFiles[guard].append(file) 0195 else: 0196 guardFiles[guard] = [file] 0197 #print guardFiles 0198 0199 for guard in guardFiles.keys(): 0200 if len(guardFiles[guard]) > 1: 0201 globalProblems = True 0202 sys.stderr.write('include guard "' + guard + '" is duplicated in the following files:\n') 0203 for file in guardFiles[guard]: 0204 sys.stderr.write(' ' + file + '\n') 0205 0206 #print exportMacros 0207 for key in exportMacros.keys(): 0208 if len(exportMacros[key]) > 1: 0209 globalProblems = True 0210 sys.stderr.write('directory "' + key + '" has the following export macros:\n') 0211 for macro in exportMacros[key]: 0212 sys.stderr.write(' ' + macro + '\n') 0213 0214 0215 if not globalProblems: 0216 print " No global problems" 0217 0218 return 0219 0220 0221 # ================================================================ 0222 0223 0224 def usage(errormsg=""): 0225 if errormsg: 0226 print "Error:", sys.argv[0] + ":", errormsg, "\n" 0227 else: 0228 print "Check for errors in the source tree.\n" 0229 0230 print """usage: chksrc.py [options] [files] 0231 options: 0232 0233 -h --help print this help and exit immediately 0234 -v --verbse print verbose output 0235 0236 files: 0237 directories to be checked 0238 0239 examples: 0240 chksrc.py libs 0241 chksrc.py . 0242 """ 0243 sys.exit(0) 0244 0245 0246 def main(): 0247 global verbose 0248 allActions = ["includeguards", "export", "all"] 0249 0250 try : 0251 opts, params = getopt.getopt(sys.argv[1:], "a:hv" , 0252 ["actions=", "help", "verbose"]) 0253 except getopt.GetoptError: 0254 usage("unknown options") 0255 #print "opts:", opts 0256 #print "params:", params 0257 0258 #actions = [] 0259 actions = ["all"] 0260 for opt, param in opts: 0261 #print opt, param 0262 if False and opt in ("-a" , "--actions"): #disable -a for now 0263 actions = string.split(param, ",") 0264 #print "actions: ", actions 0265 for a in actions: 0266 if not a in allActions: 0267 usage("unknown action: " + a + "\n\n") 0268 if "all" in actions: 0269 actions = allActions[:-1] # Remove "all", which is a meta action. 0270 0271 elif opt in ("-h" , "--help"): 0272 usage() 0273 elif opt in ("-v" , "--verbose"): 0274 verbose = True 0275 0276 if actions == []: 0277 usage("no actions defined") 0278 0279 # Do the actual work 0280 traverseTree("", actions, params) 0281 report() 0282 0283 return 0 0284 0285 if __name__ == '__main__': 0286 main()