1#!/usr/bin/env python 2import fnmatch 3import glob 4import inspect 5import os 6import optparse 7import pickle 8import re 9import sys 10 11thisfile = os.path.abspath(inspect.getfile(inspect.currentframe())) 12pdir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(thisfile))))) 13sys.path.insert(0, os.path.join(pdir, 'config')) 14 15import testparse 16from gmakegentest import nameSpace 17 18 19""" 20 Tool for querying the tests. 21 22 Which tests to query? Two options: 23 1. Query only the tests that are run for a given configuration. 24 2. Query all of the test files in the source directory 25 For #1: 26 Use dataDict as written out by gmakegentest.py in $PETSC_ARCH/$TESTBASE 27 For #2: 28 Walk the entire tree parsing the files as we go along using testparse. 29 The tree walker is simpler than what is in gmakegentest.py 30 31 The dataDict follows that generated by testparse. gmakegentest.py does 32 further manipulations of the dataDict to handle things like for loops 33 so if using #2, those modifications are not included. 34 35 Querying: 36 The dataDict dictionary is then "inverted" to create a dictionary with the 37 range of field values as keys and list test names as the values. This 38 allows fast searching 39 40""" 41 42def query(invDict,label): 43 """ 44 Search the keys using fnmatch to find matching names and return list with 45 the results 46 """ 47 results=[] 48 if 'name' in invDict: 49 return fnmatch.filter(invDict['name'],label) 50 51 for key in invDict: 52 if fnmatch.filter([key],label): 53 # Do not return values with not unless label itself has not 54 if label.startswith('!') and not key.startswith('!'): continue 55 if not label.startswith('!') and key.startswith('!'): continue 56 results += invDict[key] 57 58 return results 59 60def get_inverse_dictionary(dataDict,field,srcdir): 61 """ 62 Create a dictionary with the values of field as the keys, and the name of 63 the tests as the results. 64 """ 65 invDict={} 66 if field == 'name': invDict['name']=[] 67 for root in dataDict: 68 for exfile in dataDict[root]: 69 for test in dataDict[root][exfile]: 70 if test in testparse.buildkeys: continue 71 defroot = testparse.getDefaultOutputFileRoot(test) 72 name=nameSpace(defroot,os.path.relpath(root,srcdir)) 73 if field == 'name': 74 invDict['name'].append(name) 75 continue 76 if field not in dataDict[root][exfile][test]: continue 77 values=dataDict[root][exfile][test][field] 78 79 for val in values.split(): 80 if val in invDict: 81 invDict[val].append(name) 82 else: 83 invDict[val] = [name] 84 return invDict 85 86def get_gmakegentest_data(testdir): 87 """ 88 Write out the dataDict into a pickle file 89 """ 90 # This needs to be consistent with gmakegentest.py of course 91 fd = open(os.path.join(testdir,'datatest.pkl'), 'rb') 92 dataDict=pickle.load(fd) 93 fd.close() 94 return dataDict 95 96def walktree(top): 97 """ 98 Walk a directory tree, starting from 'top' 99 """ 100 verbose = False 101 dataDict = {} 102 alldatafiles = [] 103 for root, dirs, files in os.walk(top, topdown=False): 104 if "examples" not in root: continue 105 if root == 'output': continue 106 if '.dSYM' in root: continue 107 if verbose: print(root) 108 109 dataDict[root] = {} 110 111 for exfile in files: 112 # Ignore emacs files 113 if exfile.startswith("#") or exfile.startswith(".#"): continue 114 ext=os.path.splitext(exfile)[1] 115 if ext[1:] not in ['c','cxx','cpp','cu','F90','F']: continue 116 117 # Convenience 118 fullex = os.path.join(root, exfile) 119 if verbose: print(' --> '+fullex) 120 dataDict[root].update(testparse.parseTestFile(fullex, 0)) 121 122 return dataDict 123 124def do_query(use_source, startdir, srcdir, testdir, field, label): 125 """ 126 Do the actual query 127 This part of the code is placed here instead of main() 128 to show how one could translate this into ipython/jupyer notebook 129 commands for more advanced queries 130 """ 131 # Get dictionary 132 if use_source: 133 dataDict=walktree(startdir) 134 else: 135 dataDict=get_gmakegentest_data(testdir) 136 137 # Get inverse dictionary for searching 138 invDict=get_inverse_dictionary(dataDict, field, srcdir) 139 #print(invDict) 140 141 # Now do query 142 resList=query(invDict, label) 143 144 # Print in flat list suitable for use by gmakefile.test 145 print(' '.join(resList)) 146 147 return 148 149def main(): 150 parser = optparse.OptionParser(usage="%prog [options] field match_pattern") 151 parser.add_option('-s', '--startdir', dest='startdir', 152 help='Where to start the recursion if not srcdir', 153 default='') 154 parser.add_option('-p', '--petsc-dir', dest='petsc_dir', 155 help='Set PETSC_DIR different from environment', 156 default=os.environ.get('PETSC_DIR')) 157 parser.add_option('-a', '--petsc-arch', dest='petsc_arch', 158 help='Set PETSC_ARCH different from environment', 159 default=os.environ.get('PETSC_ARCH')) 160 parser.add_option('--srcdir', dest='srcdir', 161 help='Set location of sources different from PETSC_DIR/src. Must be full path.', 162 default='src') 163 parser.add_option('-t', '--testdir', dest='testdir', 164 help='Test directory if not PETSC_ARCH/tests. Must be full path', 165 default='tests') 166 parser.add_option('-u', '--use-source', action="store_false", 167 dest='use_source', 168 help='Query all sources rather than those configured in PETSC_ARCH') 169 170 opts, args = parser.parse_args() 171 172 # Argument Sanity checks 173 if len(args) != 2: 174 parser.print_usage() 175 print('Arguments: ') 176 print(' field: Field to search for; e.g., requires') 177 print(' To just match names, use "name"') 178 print(' match_pattern: Matching pattern for field; e.g., cuda') 179 return 180 181 # Process arguments and options -- mostly just paths here 182 field=args[0] 183 match=args[1] 184 185 petsc_dir = opts.petsc_dir 186 petsc_arch = opts.petsc_arch 187 petsc_full_arch = os.path.join(petsc_dir, petsc_arch) 188 189 if opts.srcdir == 'src': 190 petsc_full_src = os.path.join(petsc_dir, 'src') 191 else: 192 petsc_full_src = opts.srcdir 193 if opts.testdir == 'tests': 194 petsc_full_test = os.path.join(petsc_full_arch, 'tests') 195 else: 196 petsc_full_test = opts.testdir 197 if opts.startdir: 198 startdir=opts.startdir=petsc_full_src 199 else: 200 startdir=petsc_full_src 201 202 # Options Sanity checks 203 if not os.path.isdir(petsc_dir): 204 print("PETSC_DIR must be a directory") 205 return 206 207 if not opts.use_source: 208 if not os.path.isdir(petsc_full_arch): 209 print("PETSC_DIR/PETSC_ARCH must be a directory") 210 return 211 elif not os.path.isdir(petsc_full_test): 212 print("Testdir must be a directory"+petsc_full_test) 213 return 214 else: 215 if not os.path.isdir(petsc_full_src): 216 print("Source directory must be a directory"+petsc_full_src) 217 return 218 219 # Do the actual query 220 do_query(opts.use_source, startdir, petsc_full_src, petsc_full_test, field, match) 221 222 return 223 224 225if __name__ == "__main__": 226 main() 227