xref: /petsc/config/query_tests.py (revision 7b5fd022a6ba26727040df7457b27566b4c6742d)
1df3bd252SSatish Balay#!/usr/bin/env python3
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()))
1284533492SBarry Smithpetscdir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(thisfile)))))
1384533492SBarry Smithsys.path.insert(0, os.path.join(petscdir, 'config'))
146f5e9bd5SScott Kruger
156f5e9bd5SScott Krugerimport testparse
166f5e9bd5SScott Krugerfrom gmakegentest import nameSpace
176f5e9bd5SScott Kruger
186f5e9bd5SScott Kruger"""
1984533492SBarry Smith   This is used by gmakefile.test for the following searches
2084533492SBarry Smith
2184533492SBarry Smith  - make test search=X (or s=X)
2284533492SBarry Smith  - make test query=X (or q=X) queryval=Y (or qv=Y)
2384533492SBarry Smith
246f5e9bd5SScott Kruger  Which tests to query?  Two options:
256f5e9bd5SScott Kruger      1. Query only the tests that are run for a given configuration.
266f5e9bd5SScott Kruger      2. Query all of the test files in the source directory
276f5e9bd5SScott Kruger  For #1:
286f5e9bd5SScott Kruger     Use dataDict as written out by gmakegentest.py in $PETSC_ARCH/$TESTBASE
296f5e9bd5SScott Kruger  For #2:
306f5e9bd5SScott Kruger     Walk the entire tree parsing the files as we go along using testparse.
316f5e9bd5SScott Kruger     The tree walker is simpler than what is in gmakegentest.py
326f5e9bd5SScott Kruger
336f5e9bd5SScott Kruger  The dataDict follows that generated by testparse.  gmakegentest.py does
346f5e9bd5SScott Kruger  further manipulations of the dataDict to handle things like for loops
356f5e9bd5SScott Kruger  so if using #2, those modifications are not included.
366f5e9bd5SScott Kruger
376f5e9bd5SScott Kruger  Querying:
386f5e9bd5SScott Kruger      The dataDict dictionary is then "inverted" to create a dictionary with the
396f5e9bd5SScott Kruger      range of field values as keys and list test names as the values.  This
406f5e9bd5SScott Kruger      allows fast searching
416f5e9bd5SScott Kruger
426f5e9bd5SScott Kruger"""
436f5e9bd5SScott Kruger
4485bc9deeSScott Krugerdef isFile(maybeFile):
4585bc9deeSScott Kruger  ext=os.path.splitext(maybeFile)[1]
4685bc9deeSScott Kruger  if not ext: return False
4785bc9deeSScott Kruger  if ext not in ['.c','.cxx','.cpp','F90','F','cu']: return False
4885bc9deeSScott Kruger  return True
4985bc9deeSScott Kruger
5085bc9deeSScott Krugerdef pathToLabel(path):
5185bc9deeSScott Kruger  """
5285bc9deeSScott Kruger  Because the scripts have a non-unique naming, the pretty-printing
5385bc9deeSScott Kruger  needs to convey the srcdir and srcfile.  There are two ways of doing this.
5485bc9deeSScott Kruger  """
5585bc9deeSScott Kruger  # Strip off any top-level directories or spaces
5684533492SBarry Smith  path=path.strip().replace(petscdir,'')
5785bc9deeSScott Kruger  path=path.replace('src/','')
5885bc9deeSScott Kruger  if isFile(path):
5985bc9deeSScott Kruger    prefix=os.path.dirname(path).replace("/","_")
6085bc9deeSScott Kruger    suffix=os.path.splitext(os.path.basename(path))[0]
6185bc9deeSScott Kruger    label=prefix+"-"+suffix+'_*'
6285bc9deeSScott Kruger  else:
6385bc9deeSScott Kruger    path=path.rstrip('/')
649ea87190SJacob Faibussowitsch    label=path.replace("/","_").replace('tests_','tests-').replace('tutorials_','tutorials-')
6585bc9deeSScott Kruger  return label
6685bc9deeSScott Kruger
6785bc9deeSScott Krugerdef get_value(varset):
6885bc9deeSScott Kruger  """
6985bc9deeSScott Kruger  Searching args is a bit funky:
7085bc9deeSScott Kruger  Consider
7185bc9deeSScott Kruger      args: -ksp_monitor_short -pc_type ml -ksp_max_it 3
7285bc9deeSScott Kruger  Search terms are:
7385bc9deeSScott Kruger    ksp_monitor, 'pc_type ml', ksp_max_it
7485bc9deeSScott Kruger  Also ignore all loops
7585bc9deeSScott Kruger    -pc_fieldsplit_diag_use_amat {{0 1}}
7685bc9deeSScott Kruger  Gives: pc_fieldsplit_diag_use_amat as the search term
7785bc9deeSScott Kruger  Also ignore -f ...  (use matrices from file) because I'll assume
7885bc9deeSScott Kruger   that this kind of information isn't needed for testing.  If it's
7985bc9deeSScott Kruger   a separate search than just grep it
8085bc9deeSScott Kruger  """
8185bc9deeSScott Kruger  if varset.startswith('-f '): return None
8285bc9deeSScott Kruger
8385bc9deeSScott Kruger  # First  remove loops
8485bc9deeSScott Kruger  value=re.sub('{{.*}}','',varset)
8585bc9deeSScott Kruger  # Next remove -
8685bc9deeSScott Kruger  value=varset.lstrip("-")
8785bc9deeSScott Kruger  # Get rid of numbers
8885bc9deeSScott Kruger  value=re.sub(r"[+-]? *(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?",'',value)
8985bc9deeSScott Kruger  # return without spaces
9085bc9deeSScott Kruger  return value.strip()
9185bc9deeSScott Kruger
9285bc9deeSScott Krugerdef query(invDict,fields,labels):
936f5e9bd5SScott Kruger    """
946f5e9bd5SScott Kruger    Search the keys using fnmatch to find matching names and return list with
956f5e9bd5SScott Kruger    the results
966f5e9bd5SScott Kruger    """
97d5b43468SJose E. Roman    setlist=[]  # setlist is a list of lists that set operations will operate on
9885bc9deeSScott Kruger    llist=labels.replace('|',',').split(',')
9985bc9deeSScott Kruger    i=-1
10085bc9deeSScott Kruger    for field in fields.replace('|',',').split(','):
10185bc9deeSScott Kruger        i+=1
10285bc9deeSScott Kruger        label=llist[i]
10385bc9deeSScott Kruger        if field == 'name':
10485bc9deeSScott Kruger            if '/' in label:
10585bc9deeSScott Kruger              label=pathToLabel(label)
106f538e455SScott Kruger            elif label.startswith('src'):
107f538e455SScott Kruger                  label=label.lstrip('src').lstrip('*')
10885bc9deeSScott Kruger            setlist.append(fnmatch.filter(invDict['name'],label))
10985bc9deeSScott Kruger            continue
1105b6dee57SScott Kruger
11185bc9deeSScott Kruger        foundLabel=False   # easy to do if you misspell argument search
112ca84c248SScott Kruger        label=label.lower()
11385bc9deeSScott Kruger        for key in invDict[field]:
114ca84c248SScott Kruger            if fnmatch.filter([key.lower()],label):
11585bc9deeSScott Kruger              foundLabel=True
1166f5e9bd5SScott Kruger              # Do not return values with not unless label itself has not
1176f5e9bd5SScott Kruger              if label.startswith('!') and not key.startswith('!'): continue
1186f5e9bd5SScott Kruger              if not label.startswith('!') and key.startswith('!'): continue
11985bc9deeSScott Kruger              setlist.append(invDict[field][key])
12085bc9deeSScott Kruger        if not foundLabel:
12185bc9deeSScott Kruger          setlist.append([])
1226f5e9bd5SScott Kruger
12385bc9deeSScott Kruger    # Now process the union and intersection operators based on setlist
12485bc9deeSScott Kruger    allresults=[]
12585bc9deeSScott Kruger    # Union
12685bc9deeSScott Kruger    i=-1
12785bc9deeSScott Kruger    for ufield in fields.split(','):
12885bc9deeSScott Kruger       i+=1
12985bc9deeSScott Kruger       if '|' in ufield:
13085bc9deeSScott Kruger         # Intersection
13185bc9deeSScott Kruger         label=llist[i]
13285bc9deeSScott Kruger         results=set(setlist[i])
13385bc9deeSScott Kruger         for field in ufield.split('|')[1:]:
13485bc9deeSScott Kruger             i+=1
13585bc9deeSScott Kruger             label=llist[i]
13685bc9deeSScott Kruger             results=results.intersection(set(setlist[i]))
13785bc9deeSScott Kruger         allresults+=list(results)
13885bc9deeSScott Kruger       else:
13985bc9deeSScott Kruger         allresults+=setlist[i]
1406f5e9bd5SScott Kruger
14185bc9deeSScott Kruger    # remove duplicate entries and sort to give consistent results
14285bc9deeSScott Kruger    uniqlist=list(set(allresults))
14385bc9deeSScott Kruger    uniqlist.sort()
14485bc9deeSScott Kruger    return  uniqlist
14585bc9deeSScott Kruger
14685bc9deeSScott Krugerdef get_inverse_dictionary(dataDict,fields,srcdir):
1476f5e9bd5SScott Kruger    """
1486f5e9bd5SScott Kruger    Create a dictionary with the values of field as the keys, and the name of
1496f5e9bd5SScott Kruger    the tests as the results.
1506f5e9bd5SScott Kruger    """
1516f5e9bd5SScott Kruger    invDict={}
15285bc9deeSScott Kruger    # Comma-delimited lists denote union
15385bc9deeSScott Kruger    for field in fields.replace('|',',').split(','):
15485bc9deeSScott Kruger        if field not in invDict:
15585bc9deeSScott Kruger            if field == 'name':
15685bc9deeSScott Kruger                 invDict[field]=[]   # List for ease
15785bc9deeSScott Kruger            else:
15885bc9deeSScott Kruger                 invDict[field]={}
1596f5e9bd5SScott Kruger        for root in dataDict:
1606f5e9bd5SScott Kruger          for exfile in dataDict[root]:
1616f5e9bd5SScott Kruger            for test in dataDict[root][exfile]:
162aec279ffSScott Kruger              if test in testparse.buildkeys: continue
1636f5e9bd5SScott Kruger              defroot = testparse.getDefaultOutputFileRoot(test)
16485bc9deeSScott Kruger              fname=nameSpace(defroot,os.path.relpath(root,srcdir))
1655b6dee57SScott Kruger              if field == 'name':
16685bc9deeSScott Kruger                  invDict['name'].append(fname)
1675b6dee57SScott Kruger                  continue
1685b6dee57SScott Kruger              if field not in dataDict[root][exfile][test]: continue
1696f5e9bd5SScott Kruger              values=dataDict[root][exfile][test][field]
1706f5e9bd5SScott Kruger
17185bc9deeSScott Kruger              if not field == 'args' and not field == 'diff_args':
1726f5e9bd5SScott Kruger                for val in values.split():
17385bc9deeSScott Kruger                    if val in invDict[field]:
17485bc9deeSScott Kruger                        invDict[field][val].append(fname)
1756f5e9bd5SScott Kruger                    else:
17685bc9deeSScott Kruger                        invDict[field][val] = [fname]
17785bc9deeSScott Kruger              else:
17885bc9deeSScott Kruger                # Args are funky.
1793be2e2fdSJose E. Roman                for varset in re.split(r'(^|\W)-(?=[a-zA-Z])',values):
18085bc9deeSScott Kruger                  val=get_value(varset)
18185bc9deeSScott Kruger                  if not val: continue
18285bc9deeSScott Kruger                  if val in invDict[field]:
18385bc9deeSScott Kruger                    invDict[field][val].append(fname)
18485bc9deeSScott Kruger                  else:
18585bc9deeSScott Kruger                    invDict[field][val] = [fname]
18685bc9deeSScott Kruger        # remove duplicate entries (multiple test/file)
18785bc9deeSScott Kruger        if not field == 'name':
18885bc9deeSScott Kruger          for val in invDict[field]:
18985bc9deeSScott Kruger            invDict[field][val]=list(set(invDict[field][val]))
19085bc9deeSScott Kruger
1916f5e9bd5SScott Kruger    return invDict
1926f5e9bd5SScott Kruger
193*8fd06648SMatthew G. Knepleydef get_gmakegentest_data(srcdir,testdir,petsc_dir,petsc_arch):
1946f5e9bd5SScott Kruger    """
1956f5e9bd5SScott Kruger     Write out the dataDict into a pickle file
1966f5e9bd5SScott Kruger    """
1976f5e9bd5SScott Kruger    # This needs to be consistent with gmakegentest.py of course
1984e028dedSScott Kruger    pkl_file=os.path.join(testdir,'datatest.pkl')
1994e028dedSScott Kruger    # If it doesn't exist, then we need to regenerate
2004e028dedSScott Kruger    if not os.path.exists(pkl_file):
2014e028dedSScott Kruger      startdir=os.path.abspath(os.curdir)
2024e028dedSScott Kruger      os.chdir(petsc_dir)
203*8fd06648SMatthew G. Knepley      args='--petsc-dir='+petsc_dir+' --petsc-arch='+petsc_arch+' --testdir='+testdir+' --srcdir='+srcdir
2044e028dedSScott Kruger      buf = os.popen('config/gmakegentest.py '+args).read()
2054e028dedSScott Kruger      os.chdir(startdir)
2064e028dedSScott Kruger
2074e028dedSScott Kruger    fd = open(pkl_file, 'rb')
2086f5e9bd5SScott Kruger    dataDict=pickle.load(fd)
2096f5e9bd5SScott Kruger    fd.close()
2106f5e9bd5SScott Kruger    return dataDict
2116f5e9bd5SScott Kruger
2126f5e9bd5SScott Krugerdef walktree(top):
2136f5e9bd5SScott Kruger    """
2146f5e9bd5SScott Kruger    Walk a directory tree, starting from 'top'
2156f5e9bd5SScott Kruger    """
2166f5e9bd5SScott Kruger    verbose = False
2176f5e9bd5SScott Kruger    dataDict = {}
2186f5e9bd5SScott Kruger    alldatafiles = []
2196f5e9bd5SScott Kruger    for root, dirs, files in os.walk(top, topdown=False):
2206f5e9bd5SScott Kruger        if root == 'output': continue
2216f5e9bd5SScott Kruger        if '.dSYM' in root: continue
2226f5e9bd5SScott Kruger        if verbose: print(root)
2236f5e9bd5SScott Kruger
2246f5e9bd5SScott Kruger        dataDict[root] = {}
2256f5e9bd5SScott Kruger
2266f5e9bd5SScott Kruger        for exfile in files:
2276f5e9bd5SScott Kruger            # Ignore emacs files
2286f5e9bd5SScott Kruger            if exfile.startswith("#") or exfile.startswith(".#"): continue
2296f5e9bd5SScott Kruger            ext=os.path.splitext(exfile)[1]
2306f5e9bd5SScott Kruger            if ext[1:] not in ['c','cxx','cpp','cu','F90','F']: continue
2316f5e9bd5SScott Kruger
2326f5e9bd5SScott Kruger            # Convenience
2336f5e9bd5SScott Kruger            fullex = os.path.join(root, exfile)
2346f5e9bd5SScott Kruger            if verbose: print('   --> '+fullex)
2356f5e9bd5SScott Kruger            dataDict[root].update(testparse.parseTestFile(fullex, 0))
2366f5e9bd5SScott Kruger
2376f5e9bd5SScott Kruger    return dataDict
2386f5e9bd5SScott Kruger
23985bc9deeSScott Krugerdef do_query(use_source, startdir, srcdir, testdir, petsc_dir, petsc_arch,
24085bc9deeSScott Kruger             fields, labels, searchin):
2416f5e9bd5SScott Kruger    """
2426f5e9bd5SScott Kruger    Do the actual query
2436f5e9bd5SScott Kruger    This part of the code is placed here instead of main()
2446f5e9bd5SScott Kruger    to show how one could translate this into ipython/jupyer notebook
2456f5e9bd5SScott Kruger    commands for more advanced queries
2466f5e9bd5SScott Kruger    """
2476f5e9bd5SScott Kruger    # Get dictionary
2486f5e9bd5SScott Kruger    if use_source:
2496f5e9bd5SScott Kruger        dataDict=walktree(startdir)
2506f5e9bd5SScott Kruger    else:
251*8fd06648SMatthew G. Knepley        dataDict=get_gmakegentest_data(srcdir,testdir, petsc_dir, petsc_arch)
2526f5e9bd5SScott Kruger
2536f5e9bd5SScott Kruger    # Get inverse dictionary for searching
25485bc9deeSScott Kruger    invDict=get_inverse_dictionary(dataDict, fields, srcdir)
2556f5e9bd5SScott Kruger
2566f5e9bd5SScott Kruger    # Now do query
25785bc9deeSScott Kruger    resList=query(invDict, fields, labels)
25885bc9deeSScott Kruger
25985bc9deeSScott Kruger    # Filter results using searchin
26085bc9deeSScott Kruger    newresList=[]
26185bc9deeSScott Kruger    if searchin.strip():
262be89fe9eSScott Kruger        if not searchin.startswith('!'):
26385bc9deeSScott Kruger            for key in resList:
26485bc9deeSScott Kruger                if fnmatch.filter([key],searchin):
26585bc9deeSScott Kruger                  newresList.append(key)
266be89fe9eSScott Kruger        else:
267be89fe9eSScott Kruger            for key in resList:
268be89fe9eSScott Kruger                if not fnmatch.filter([key],searchin[1:]):
269be89fe9eSScott Kruger                  newresList.append(key)
27085bc9deeSScott Kruger        resList=newresList
2716f5e9bd5SScott Kruger
2726f5e9bd5SScott Kruger    # Print in flat list suitable for use by gmakefile.test
2736f5e9bd5SScott Kruger    print(' '.join(resList))
2746f5e9bd5SScott Kruger
2756f5e9bd5SScott Kruger    return
2766f5e9bd5SScott Kruger
2779ea87190SJacob Faibussowitschdef expand_path_like(petscdir,petscarch,pathlike):
2789ea87190SJacob Faibussowitsch    def remove_prefix(text,prefix):
2799ea87190SJacob Faibussowitsch        return text[text.startswith(prefix) and len(prefix):]
2809ea87190SJacob Faibussowitsch
2819ea87190SJacob Faibussowitsch    # expand user second, as expandvars may insert a '~'
2829ea87190SJacob Faibussowitsch    string = os.path.expanduser(os.path.expandvars(pathlike))
2839ea87190SJacob Faibussowitsch    # if the dirname check succeeds then likely we have a glob expression
2849ea87190SJacob Faibussowitsch    pardir = os.path.dirname(string)
2859ea87190SJacob Faibussowitsch    if os.path.exists(pardir):
2869ea87190SJacob Faibussowitsch        suffix   = string.replace(pardir,'') # get whatever is left over
2879ea87190SJacob Faibussowitsch        pathlike = remove_prefix(os.path.relpath(os.path.abspath(pardir),petscdir),'.'+os.path.sep)
2889ea87190SJacob Faibussowitsch        if petscarch == '':
2899ea87190SJacob Faibussowitsch            pathlike = pathlike.replace(os.path.sep.join(('share','petsc','examples'))+'/','')
2909ea87190SJacob Faibussowitsch        pathlike += suffix
29184533492SBarry Smith    pathlike = pathlike.replace('diff-','')
2929ea87190SJacob Faibussowitsch    return pathlike
2939ea87190SJacob Faibussowitsch
2946f5e9bd5SScott Krugerdef main():
2956f5e9bd5SScott Kruger    parser = optparse.OptionParser(usage="%prog [options] field match_pattern")
2966f5e9bd5SScott Kruger    parser.add_option('-s', '--startdir', dest='startdir',
2976f5e9bd5SScott Kruger                      help='Where to start the recursion if not srcdir',
2986f5e9bd5SScott Kruger                      default='')
299aec279ffSScott Kruger    parser.add_option('-p', '--petsc-dir', dest='petsc_dir',
300aec279ffSScott Kruger                      help='Set PETSC_DIR different from environment',
3016f5e9bd5SScott Kruger                      default=os.environ.get('PETSC_DIR'))
3026f5e9bd5SScott Kruger    parser.add_option('-a', '--petsc-arch', dest='petsc_arch',
3036f5e9bd5SScott Kruger                      help='Set PETSC_ARCH different from environment',
3046f5e9bd5SScott Kruger                      default=os.environ.get('PETSC_ARCH'))
3056f5e9bd5SScott Kruger    parser.add_option('--srcdir', dest='srcdir',
3066f5e9bd5SScott Kruger                      help='Set location of sources different from PETSC_DIR/src.  Must be full path.',
3076f5e9bd5SScott Kruger                      default='src')
3086f5e9bd5SScott Kruger    parser.add_option('-t', '--testdir', dest='testdir',
3096f5e9bd5SScott Kruger                      help='Test directory if not PETSC_ARCH/tests.  Must be full path',
3106f5e9bd5SScott Kruger                      default='tests')
3116f5e9bd5SScott Kruger    parser.add_option('-u', '--use-source', action="store_false",
3126f5e9bd5SScott Kruger                      dest='use_source',
3136f5e9bd5SScott Kruger                      help='Query all sources rather than those configured in PETSC_ARCH')
31485bc9deeSScott Kruger    parser.add_option('-i', '--searchin', dest='searchin',
31585bc9deeSScott Kruger                      help='Filter results from the arguments',
31685bc9deeSScott Kruger                      default='')
3176f5e9bd5SScott Kruger
3186f5e9bd5SScott Kruger    opts, args = parser.parse_args()
3196f5e9bd5SScott Kruger
3206f5e9bd5SScott Kruger    # Argument Sanity checks
3216f5e9bd5SScott Kruger    if len(args) != 2:
3226f5e9bd5SScott Kruger        parser.print_usage()
3236f5e9bd5SScott Kruger        print('Arguments: ')
3246f5e9bd5SScott Kruger        print('  field:          Field to search for; e.g., requires')
3255b6dee57SScott Kruger        print('                  To just match names, use "name"')
3266f5e9bd5SScott Kruger        print('  match_pattern:  Matching pattern for field; e.g., cuda')
3276f5e9bd5SScott Kruger        return
3286f5e9bd5SScott Kruger
3293c5439e2SJacob Faibussowitsch    def shell_unquote(string):
3303c5439e2SJacob Faibussowitsch      """
3313c5439e2SJacob Faibussowitsch      Remove quotes from STRING. Useful in the case where you need to bury escaped quotes in a query
3323c5439e2SJacob Faibussowitsch      string in order to escape shell characters. For example:
3333c5439e2SJacob Faibussowitsch
3343c5439e2SJacob Faibussowitsch      $ make test query='foo,bar' queryval='requires|name'
3353c5439e2SJacob Faibussowitsch      /usr/bin/bash: line 1: name: command not found
3363c5439e2SJacob Faibussowitsch
3373c5439e2SJacob Faibussowitsch      While the original shell does not see the pipe character, the actual query is done via a second
3383c5439e2SJacob Faibussowitsch      shell, which is (literally) passed '$(queryval)', i.e. 'queryval='requires|name'' when expanded.
3393c5439e2SJacob Faibussowitsch      Note the fact that the expansion cancels out the quoting!!!
3403c5439e2SJacob Faibussowitsch
3413c5439e2SJacob Faibussowitsch      You can fix this by doing:
3423c5439e2SJacob Faibussowitsch
3433c5439e2SJacob Faibussowitsch      $ make test query='foo,bar' queryval='"requires|name"'
3443c5439e2SJacob Faibussowitsch
3453c5439e2SJacob Faibussowitsch      However this then shows up here as labels = 'queryval="requires|name"'. So we need to remove the
3463c5439e2SJacob Faibussowitsch      '"'. Applying shlex.split() on this returns:
3473c5439e2SJacob Faibussowitsch
3483c5439e2SJacob Faibussowitsch      >>> shlex.split('queryval="requires|name"')
3493c5439e2SJacob Faibussowitsch      ['queryval=requires|name']
3503c5439e2SJacob Faibussowitsch
3513c5439e2SJacob Faibussowitsch      And voila. Note also that:
3523c5439e2SJacob Faibussowitsch
3533c5439e2SJacob Faibussowitsch      >>> shlex.split('queryval=requires|name')
3543c5439e2SJacob Faibussowitsch      ['queryval=requires|name']
3553c5439e2SJacob Faibussowitsch      """
3563c5439e2SJacob Faibussowitsch      import shlex
3573c5439e2SJacob Faibussowitsch
3583c5439e2SJacob Faibussowitsch      if string:
3593c5439e2SJacob Faibussowitsch        ret = shlex.split(string)
3603c5439e2SJacob Faibussowitsch        assert len(ret) == 1, "Dont know what to do if shlex.split() produces more than 1 value?"
3613c5439e2SJacob Faibussowitsch        string = ret[0]
3623c5439e2SJacob Faibussowitsch      return string
3633c5439e2SJacob Faibussowitsch
364168c8686SJacob Faibussowitsch    def alternate_command_preprocess(string):
365168c8686SJacob Faibussowitsch      """
366168c8686SJacob Faibussowitsch      Replace the alternate versions in STRING with the regular variants
367168c8686SJacob Faibussowitsch      """
368168c8686SJacob Faibussowitsch      return string.replace('%OR%', '|').replace('%AND%', ',').replace('%NEG%', '!')
369168c8686SJacob Faibussowitsch
3706f5e9bd5SScott Kruger    # Process arguments and options -- mostly just paths here
371168c8686SJacob Faibussowitsch    field=alternate_command_preprocess(shell_unquote(args[0]))
37284533492SBarry Smith    labels=alternate_command_preprocess(shell_unquote(args[1]))
37385bc9deeSScott Kruger    searchin=opts.searchin
3746f5e9bd5SScott Kruger
3756f5e9bd5SScott Kruger    petsc_dir = opts.petsc_dir
3766f5e9bd5SScott Kruger    petsc_arch = opts.petsc_arch
3776f5e9bd5SScott Kruger    petsc_full_arch = os.path.join(petsc_dir, petsc_arch)
3786f5e9bd5SScott Kruger
37958780e5dSStefano Zampini    if petsc_arch == '':
38058780e5dSStefano Zampini        petsc_full_src = os.path.join(petsc_dir, 'share', 'petsc', 'examples', 'src')
38158780e5dSStefano Zampini    else:
3826f5e9bd5SScott Kruger      if opts.srcdir == 'src':
3836f5e9bd5SScott Kruger        petsc_full_src = os.path.join(petsc_dir, 'src')
3846f5e9bd5SScott Kruger      else:
3856f5e9bd5SScott Kruger        petsc_full_src = opts.srcdir
3866f5e9bd5SScott Kruger    if opts.testdir == 'tests':
3876f5e9bd5SScott Kruger      petsc_full_test = os.path.join(petsc_full_arch, 'tests')
3886f5e9bd5SScott Kruger    else:
3896f5e9bd5SScott Kruger      petsc_full_test = opts.testdir
3906f5e9bd5SScott Kruger    if opts.startdir:
3916f5e9bd5SScott Kruger      startdir=opts.startdir=petsc_full_src
3926f5e9bd5SScott Kruger    else:
3936f5e9bd5SScott Kruger      startdir=petsc_full_src
3946f5e9bd5SScott Kruger
3956f5e9bd5SScott Kruger    # Options Sanity checks
3966f5e9bd5SScott Kruger    if not os.path.isdir(petsc_dir):
3976f5e9bd5SScott Kruger        print("PETSC_DIR must be a directory")
3986f5e9bd5SScott Kruger        return
3996f5e9bd5SScott Kruger
4006f5e9bd5SScott Kruger    if not opts.use_source:
4016f5e9bd5SScott Kruger        if not os.path.isdir(petsc_full_arch):
4026f5e9bd5SScott Kruger            print("PETSC_DIR/PETSC_ARCH must be a directory")
4036f5e9bd5SScott Kruger            return
4046f5e9bd5SScott Kruger        elif not os.path.isdir(petsc_full_test):
4056f5e9bd5SScott Kruger            print("Testdir must be a directory"+petsc_full_test)
4066f5e9bd5SScott Kruger            return
4076f5e9bd5SScott Kruger    else:
4086f5e9bd5SScott Kruger        if not os.path.isdir(petsc_full_src):
4096f5e9bd5SScott Kruger            print("Source directory must be a directory"+petsc_full_src)
4106f5e9bd5SScott Kruger            return
4116f5e9bd5SScott Kruger
41284533492SBarry Smith    labels = expand_path_like(petsc_dir,petsc_arch,labels)
4139ea87190SJacob Faibussowitsch
4146f5e9bd5SScott Kruger    # Do the actual query
4154e028dedSScott Kruger    do_query(opts.use_source, startdir, petsc_full_src, petsc_full_test,
41684533492SBarry Smith             petsc_dir, petsc_arch, field, labels, searchin)
4176f5e9bd5SScott Kruger
4186f5e9bd5SScott Kruger    return
4196f5e9bd5SScott Kruger
4206f5e9bd5SScott Krugerif __name__ == "__main__":
4216f5e9bd5SScott Kruger        main()
422