16f5e9bd5SScott Kruger#!/usr/bin/env python 26f5e9bd5SScott Krugerimport fnmatch 36f5e9bd5SScott Krugerimport glob 46f5e9bd5SScott Krugerimport inspect 56f5e9bd5SScott Krugerimport os 66f5e9bd5SScott Krugerimport optparse 76f5e9bd5SScott Krugerimport pickle 86f5e9bd5SScott Krugerimport re 96f5e9bd5SScott Krugerimport sys 106f5e9bd5SScott Kruger 116f5e9bd5SScott Krugerthisfile = os.path.abspath(inspect.getfile(inspect.currentframe())) 126f5e9bd5SScott Krugerpdir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(thisfile))))) 136f5e9bd5SScott Krugersys.path.insert(0, os.path.join(pdir, 'config')) 146f5e9bd5SScott Kruger 156f5e9bd5SScott Krugerimport testparse 166f5e9bd5SScott Krugerfrom gmakegentest import nameSpace 176f5e9bd5SScott Kruger 186f5e9bd5SScott Kruger 196f5e9bd5SScott Kruger""" 206f5e9bd5SScott Kruger Tool for querying the tests. 216f5e9bd5SScott Kruger 226f5e9bd5SScott Kruger Which tests to query? Two options: 236f5e9bd5SScott Kruger 1. Query only the tests that are run for a given configuration. 246f5e9bd5SScott Kruger 2. Query all of the test files in the source directory 256f5e9bd5SScott Kruger For #1: 266f5e9bd5SScott Kruger Use dataDict as written out by gmakegentest.py in $PETSC_ARCH/$TESTBASE 276f5e9bd5SScott Kruger For #2: 286f5e9bd5SScott Kruger Walk the entire tree parsing the files as we go along using testparse. 296f5e9bd5SScott Kruger The tree walker is simpler than what is in gmakegentest.py 306f5e9bd5SScott Kruger 316f5e9bd5SScott Kruger The dataDict follows that generated by testparse. gmakegentest.py does 326f5e9bd5SScott Kruger further manipulations of the dataDict to handle things like for loops 336f5e9bd5SScott Kruger so if using #2, those modifications are not included. 346f5e9bd5SScott Kruger 356f5e9bd5SScott Kruger Querying: 366f5e9bd5SScott Kruger The dataDict dictionary is then "inverted" to create a dictionary with the 376f5e9bd5SScott Kruger range of field values as keys and list test names as the values. This 386f5e9bd5SScott Kruger allows fast searching 396f5e9bd5SScott Kruger 406f5e9bd5SScott Kruger""" 416f5e9bd5SScott Kruger 426f5e9bd5SScott Krugerdef query(invDict,label): 436f5e9bd5SScott Kruger """ 446f5e9bd5SScott Kruger Search the keys using fnmatch to find matching names and return list with 456f5e9bd5SScott Kruger the results 466f5e9bd5SScott Kruger """ 476f5e9bd5SScott Kruger results=[] 48*5b6dee57SScott Kruger if 'name' in invDict: 49*5b6dee57SScott Kruger return fnmatch.filter(invDict['name'],label) 50*5b6dee57SScott Kruger 516f5e9bd5SScott Kruger for key in invDict: 526f5e9bd5SScott Kruger if fnmatch.filter([key],label): 536f5e9bd5SScott Kruger # Do not return values with not unless label itself has not 546f5e9bd5SScott Kruger if label.startswith('!') and not key.startswith('!'): continue 556f5e9bd5SScott Kruger if not label.startswith('!') and key.startswith('!'): continue 566f5e9bd5SScott Kruger results += invDict[key] 576f5e9bd5SScott Kruger 586f5e9bd5SScott Kruger return results 596f5e9bd5SScott Kruger 606f5e9bd5SScott Krugerdef get_inverse_dictionary(dataDict,field,srcdir): 616f5e9bd5SScott Kruger """ 626f5e9bd5SScott Kruger Create a dictionary with the values of field as the keys, and the name of 636f5e9bd5SScott Kruger the tests as the results. 646f5e9bd5SScott Kruger """ 656f5e9bd5SScott Kruger invDict={} 66*5b6dee57SScott Kruger if field == 'name': invDict['name']=[] 676f5e9bd5SScott Kruger for root in dataDict: 686f5e9bd5SScott Kruger for exfile in dataDict[root]: 696f5e9bd5SScott Kruger for test in dataDict[root][exfile]: 706f5e9bd5SScott Kruger defroot = testparse.getDefaultOutputFileRoot(test) 716f5e9bd5SScott Kruger name=nameSpace(defroot,os.path.relpath(root,srcdir)) 72*5b6dee57SScott Kruger if field == 'name': 73*5b6dee57SScott Kruger if 'SKIP' in name: continue 74*5b6dee57SScott Kruger invDict['name'].append(name) 75*5b6dee57SScott Kruger continue 76*5b6dee57SScott Kruger if field not in dataDict[root][exfile][test]: continue 776f5e9bd5SScott Kruger values=dataDict[root][exfile][test][field] 786f5e9bd5SScott Kruger 796f5e9bd5SScott Kruger for val in values.split(): 806f5e9bd5SScott Kruger if val in invDict: 816f5e9bd5SScott Kruger invDict[val].append(name) 826f5e9bd5SScott Kruger else: 836f5e9bd5SScott Kruger invDict[val] = [name] 846f5e9bd5SScott Kruger return invDict 856f5e9bd5SScott Kruger 866f5e9bd5SScott Krugerdef get_gmakegentest_data(testdir): 876f5e9bd5SScott Kruger """ 886f5e9bd5SScott Kruger Write out the dataDict into a pickle file 896f5e9bd5SScott Kruger """ 906f5e9bd5SScott Kruger # This needs to be consistent with gmakegentest.py of course 916f5e9bd5SScott Kruger fd = open(os.path.join(testdir,'datatest.pkl'), 'rb') 926f5e9bd5SScott Kruger dataDict=pickle.load(fd) 936f5e9bd5SScott Kruger fd.close() 946f5e9bd5SScott Kruger return dataDict 956f5e9bd5SScott Kruger 966f5e9bd5SScott Krugerdef walktree(top): 976f5e9bd5SScott Kruger """ 986f5e9bd5SScott Kruger Walk a directory tree, starting from 'top' 996f5e9bd5SScott Kruger """ 1006f5e9bd5SScott Kruger verbose = False 1016f5e9bd5SScott Kruger dataDict = {} 1026f5e9bd5SScott Kruger alldatafiles = [] 1036f5e9bd5SScott Kruger for root, dirs, files in os.walk(top, topdown=False): 1046f5e9bd5SScott Kruger if "examples" not in root: continue 1056f5e9bd5SScott Kruger if root == 'output': continue 1066f5e9bd5SScott Kruger if '.dSYM' in root: continue 1076f5e9bd5SScott Kruger if verbose: print(root) 1086f5e9bd5SScott Kruger 1096f5e9bd5SScott Kruger dataDict[root] = {} 1106f5e9bd5SScott Kruger 1116f5e9bd5SScott Kruger for exfile in files: 1126f5e9bd5SScott Kruger # Ignore emacs files 1136f5e9bd5SScott Kruger if exfile.startswith("#") or exfile.startswith(".#"): continue 1146f5e9bd5SScott Kruger ext=os.path.splitext(exfile)[1] 1156f5e9bd5SScott Kruger if ext[1:] not in ['c','cxx','cpp','cu','F90','F']: continue 1166f5e9bd5SScott Kruger 1176f5e9bd5SScott Kruger # Convenience 1186f5e9bd5SScott Kruger fullex = os.path.join(root, exfile) 1196f5e9bd5SScott Kruger if verbose: print(' --> '+fullex) 1206f5e9bd5SScott Kruger dataDict[root].update(testparse.parseTestFile(fullex, 0)) 1216f5e9bd5SScott Kruger 1226f5e9bd5SScott Kruger return dataDict 1236f5e9bd5SScott Kruger 1246f5e9bd5SScott Krugerdef do_query(use_source, startdir, srcdir, testdir, field, label): 1256f5e9bd5SScott Kruger """ 1266f5e9bd5SScott Kruger Do the actual query 1276f5e9bd5SScott Kruger This part of the code is placed here instead of main() 1286f5e9bd5SScott Kruger to show how one could translate this into ipython/jupyer notebook 1296f5e9bd5SScott Kruger commands for more advanced queries 1306f5e9bd5SScott Kruger """ 1316f5e9bd5SScott Kruger # Get dictionary 1326f5e9bd5SScott Kruger if use_source: 1336f5e9bd5SScott Kruger dataDict=walktree(startdir) 1346f5e9bd5SScott Kruger else: 1356f5e9bd5SScott Kruger dataDict=get_gmakegentest_data(testdir) 1366f5e9bd5SScott Kruger 1376f5e9bd5SScott Kruger # Get inverse dictionary for searching 1386f5e9bd5SScott Kruger invDict=get_inverse_dictionary(dataDict, field, srcdir) 139*5b6dee57SScott Kruger #print(invDict) 1406f5e9bd5SScott Kruger 1416f5e9bd5SScott Kruger # Now do query 1426f5e9bd5SScott Kruger resList=query(invDict, label) 1436f5e9bd5SScott Kruger 1446f5e9bd5SScott Kruger # Print in flat list suitable for use by gmakefile.test 1456f5e9bd5SScott Kruger print(' '.join(resList)) 1466f5e9bd5SScott Kruger 1476f5e9bd5SScott Kruger return 1486f5e9bd5SScott Kruger 1496f5e9bd5SScott Krugerdef main(): 1506f5e9bd5SScott Kruger parser = optparse.OptionParser(usage="%prog [options] field match_pattern") 1516f5e9bd5SScott Kruger parser.add_option('-s', '--startdir', dest='startdir', 1526f5e9bd5SScott Kruger help='Where to start the recursion if not srcdir', 1536f5e9bd5SScott Kruger default='') 1546f5e9bd5SScott Kruger parser.add_option('-p', '--petsc_dir', dest='petsc_dir', 1556f5e9bd5SScott Kruger help='Set PETSC_ARCH different from environment', 1566f5e9bd5SScott Kruger default=os.environ.get('PETSC_DIR')) 1576f5e9bd5SScott Kruger parser.add_option('-a', '--petsc-arch', dest='petsc_arch', 1586f5e9bd5SScott Kruger help='Set PETSC_ARCH different from environment', 1596f5e9bd5SScott Kruger default=os.environ.get('PETSC_ARCH')) 1606f5e9bd5SScott Kruger parser.add_option('--srcdir', dest='srcdir', 1616f5e9bd5SScott Kruger help='Set location of sources different from PETSC_DIR/src. Must be full path.', 1626f5e9bd5SScott Kruger default='src') 1636f5e9bd5SScott Kruger parser.add_option('-t', '--testdir', dest='testdir', 1646f5e9bd5SScott Kruger help='Test directory if not PETSC_ARCH/tests. Must be full path', 1656f5e9bd5SScott Kruger default='tests') 1666f5e9bd5SScott Kruger parser.add_option('-u', '--use-source', action="store_false", 1676f5e9bd5SScott Kruger dest='use_source', 1686f5e9bd5SScott Kruger help='Query all sources rather than those configured in PETSC_ARCH') 1696f5e9bd5SScott Kruger 1706f5e9bd5SScott Kruger opts, args = parser.parse_args() 1716f5e9bd5SScott Kruger 1726f5e9bd5SScott Kruger # Argument Sanity checks 1736f5e9bd5SScott Kruger if len(args) != 2: 1746f5e9bd5SScott Kruger parser.print_usage() 1756f5e9bd5SScott Kruger print('Arguments: ') 1766f5e9bd5SScott Kruger print(' field: Field to search for; e.g., requires') 177*5b6dee57SScott Kruger print(' To just match names, use "name"') 1786f5e9bd5SScott Kruger print(' match_pattern: Matching pattern for field; e.g., cuda') 1796f5e9bd5SScott Kruger return 1806f5e9bd5SScott Kruger 1816f5e9bd5SScott Kruger # Process arguments and options -- mostly just paths here 1826f5e9bd5SScott Kruger field=args[0] 1836f5e9bd5SScott Kruger match=args[1] 1846f5e9bd5SScott Kruger 1856f5e9bd5SScott Kruger petsc_dir = opts.petsc_dir 1866f5e9bd5SScott Kruger petsc_arch = opts.petsc_arch 1876f5e9bd5SScott Kruger petsc_full_arch = os.path.join(petsc_dir, petsc_arch) 1886f5e9bd5SScott Kruger 1896f5e9bd5SScott Kruger if opts.srcdir == 'src': 1906f5e9bd5SScott Kruger petsc_full_src = os.path.join(petsc_dir, 'src') 1916f5e9bd5SScott Kruger else: 1926f5e9bd5SScott Kruger petsc_full_src = opts.srcdir 1936f5e9bd5SScott Kruger if opts.testdir == 'tests': 1946f5e9bd5SScott Kruger petsc_full_test = os.path.join(petsc_full_arch, 'tests') 1956f5e9bd5SScott Kruger else: 1966f5e9bd5SScott Kruger petsc_full_test = opts.testdir 1976f5e9bd5SScott Kruger if opts.startdir: 1986f5e9bd5SScott Kruger startdir=opts.startdir=petsc_full_src 1996f5e9bd5SScott Kruger else: 2006f5e9bd5SScott Kruger startdir=petsc_full_src 2016f5e9bd5SScott Kruger 2026f5e9bd5SScott Kruger # Options Sanity checks 2036f5e9bd5SScott Kruger if not os.path.isdir(petsc_dir): 2046f5e9bd5SScott Kruger print("PETSC_DIR must be a directory") 2056f5e9bd5SScott Kruger return 2066f5e9bd5SScott Kruger 2076f5e9bd5SScott Kruger if not opts.use_source: 2086f5e9bd5SScott Kruger if not os.path.isdir(petsc_full_arch): 2096f5e9bd5SScott Kruger print("PETSC_DIR/PETSC_ARCH must be a directory") 2106f5e9bd5SScott Kruger return 2116f5e9bd5SScott Kruger elif not os.path.isdir(petsc_full_test): 2126f5e9bd5SScott Kruger print("Testdir must be a directory"+petsc_full_test) 2136f5e9bd5SScott Kruger return 2146f5e9bd5SScott Kruger else: 2156f5e9bd5SScott Kruger if not os.path.isdir(petsc_full_src): 2166f5e9bd5SScott Kruger print("Source directory must be a directory"+petsc_full_src) 2176f5e9bd5SScott Kruger return 2186f5e9bd5SScott Kruger 2196f5e9bd5SScott Kruger # Do the actual query 2206f5e9bd5SScott Kruger do_query(opts.use_source, startdir, petsc_full_src, petsc_full_test, field, match) 2216f5e9bd5SScott Kruger 2226f5e9bd5SScott Kruger return 2236f5e9bd5SScott Kruger 2246f5e9bd5SScott Kruger 2256f5e9bd5SScott Krugerif __name__ == "__main__": 2266f5e9bd5SScott Kruger main() 227