1*44844408SAndroid Build Coastguard Worker#!/usr/bin/env python 2*44844408SAndroid Build Coastguard Worker# encoding: utf-8 3*44844408SAndroid Build Coastguard Worker# Copyright 2009 Baptiste Lepilleur and The JsonCpp Authors 4*44844408SAndroid Build Coastguard Worker# Distributed under MIT license, or public domain if desired and 5*44844408SAndroid Build Coastguard Worker# recognized in your jurisdiction. 6*44844408SAndroid Build Coastguard Worker# See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 7*44844408SAndroid Build Coastguard Worker 8*44844408SAndroid Build Coastguard Workerfrom __future__ import print_function 9*44844408SAndroid Build Coastguard Workerfrom dircache import listdir 10*44844408SAndroid Build Coastguard Workerimport re 11*44844408SAndroid Build Coastguard Workerimport fnmatch 12*44844408SAndroid Build Coastguard Workerimport os.path 13*44844408SAndroid Build Coastguard Worker 14*44844408SAndroid Build Coastguard Worker 15*44844408SAndroid Build Coastguard Worker# These fnmatch expressions are used by default to prune the directory tree 16*44844408SAndroid Build Coastguard Worker# while doing the recursive traversal in the glob_impl method of glob function. 17*44844408SAndroid Build Coastguard Workerprune_dirs = '.git .bzr .hg .svn _MTN _darcs CVS SCCS ' 18*44844408SAndroid Build Coastguard Worker 19*44844408SAndroid Build Coastguard Worker# These fnmatch expressions are used by default to exclude files and dirs 20*44844408SAndroid Build Coastguard Worker# while doing the recursive traversal in the glob_impl method of glob function. 21*44844408SAndroid Build Coastguard Worker##exclude_pats = prune_pats + '*~ #*# .#* %*% ._* .gitignore .cvsignore vssver.scc .DS_Store'.split() 22*44844408SAndroid Build Coastguard Worker 23*44844408SAndroid Build Coastguard Worker# These ant_glob expressions are used by default to exclude files and dirs and also prune the directory tree 24*44844408SAndroid Build Coastguard Worker# while doing the recursive traversal in the glob_impl method of glob function. 25*44844408SAndroid Build Coastguard Workerdefault_excludes = ''' 26*44844408SAndroid Build Coastguard Worker**/*~ 27*44844408SAndroid Build Coastguard Worker**/#*# 28*44844408SAndroid Build Coastguard Worker**/.#* 29*44844408SAndroid Build Coastguard Worker**/%*% 30*44844408SAndroid Build Coastguard Worker**/._* 31*44844408SAndroid Build Coastguard Worker**/CVS 32*44844408SAndroid Build Coastguard Worker**/CVS/** 33*44844408SAndroid Build Coastguard Worker**/.cvsignore 34*44844408SAndroid Build Coastguard Worker**/SCCS 35*44844408SAndroid Build Coastguard Worker**/SCCS/** 36*44844408SAndroid Build Coastguard Worker**/vssver.scc 37*44844408SAndroid Build Coastguard Worker**/.svn 38*44844408SAndroid Build Coastguard Worker**/.svn/** 39*44844408SAndroid Build Coastguard Worker**/.git 40*44844408SAndroid Build Coastguard Worker**/.git/** 41*44844408SAndroid Build Coastguard Worker**/.gitignore 42*44844408SAndroid Build Coastguard Worker**/.bzr 43*44844408SAndroid Build Coastguard Worker**/.bzr/** 44*44844408SAndroid Build Coastguard Worker**/.hg 45*44844408SAndroid Build Coastguard Worker**/.hg/** 46*44844408SAndroid Build Coastguard Worker**/_MTN 47*44844408SAndroid Build Coastguard Worker**/_MTN/** 48*44844408SAndroid Build Coastguard Worker**/_darcs 49*44844408SAndroid Build Coastguard Worker**/_darcs/** 50*44844408SAndroid Build Coastguard Worker**/.DS_Store ''' 51*44844408SAndroid Build Coastguard Worker 52*44844408SAndroid Build Coastguard WorkerDIR = 1 53*44844408SAndroid Build Coastguard WorkerFILE = 2 54*44844408SAndroid Build Coastguard WorkerDIR_LINK = 4 55*44844408SAndroid Build Coastguard WorkerFILE_LINK = 8 56*44844408SAndroid Build Coastguard WorkerLINKS = DIR_LINK | FILE_LINK 57*44844408SAndroid Build Coastguard WorkerALL_NO_LINK = DIR | FILE 58*44844408SAndroid Build Coastguard WorkerALL = DIR | FILE | LINKS 59*44844408SAndroid Build Coastguard Worker 60*44844408SAndroid Build Coastguard Worker_ANT_RE = re.compile(r'(/\*\*/)|(\*\*/)|(/\*\*)|(\*)|(/)|([^\*/]*)') 61*44844408SAndroid Build Coastguard Worker 62*44844408SAndroid Build Coastguard Workerdef ant_pattern_to_re(ant_pattern): 63*44844408SAndroid Build Coastguard Worker """Generates a regular expression from the ant pattern. 64*44844408SAndroid Build Coastguard Worker Matching convention: 65*44844408SAndroid Build Coastguard Worker **/a: match 'a', 'dir/a', 'dir1/dir2/a' 66*44844408SAndroid Build Coastguard Worker a/**/b: match 'a/b', 'a/c/b', 'a/d/c/b' 67*44844408SAndroid Build Coastguard Worker *.py: match 'script.py' but not 'a/script.py' 68*44844408SAndroid Build Coastguard Worker """ 69*44844408SAndroid Build Coastguard Worker rex = ['^'] 70*44844408SAndroid Build Coastguard Worker next_pos = 0 71*44844408SAndroid Build Coastguard Worker sep_rex = r'(?:/|%s)' % re.escape(os.path.sep) 72*44844408SAndroid Build Coastguard Worker## print 'Converting', ant_pattern 73*44844408SAndroid Build Coastguard Worker for match in _ANT_RE.finditer(ant_pattern): 74*44844408SAndroid Build Coastguard Worker## print 'Matched', match.group() 75*44844408SAndroid Build Coastguard Worker## print match.start(0), next_pos 76*44844408SAndroid Build Coastguard Worker if match.start(0) != next_pos: 77*44844408SAndroid Build Coastguard Worker raise ValueError("Invalid ant pattern") 78*44844408SAndroid Build Coastguard Worker if match.group(1): # /**/ 79*44844408SAndroid Build Coastguard Worker rex.append(sep_rex + '(?:.*%s)?' % sep_rex) 80*44844408SAndroid Build Coastguard Worker elif match.group(2): # **/ 81*44844408SAndroid Build Coastguard Worker rex.append('(?:.*%s)?' % sep_rex) 82*44844408SAndroid Build Coastguard Worker elif match.group(3): # /** 83*44844408SAndroid Build Coastguard Worker rex.append(sep_rex + '.*') 84*44844408SAndroid Build Coastguard Worker elif match.group(4): # * 85*44844408SAndroid Build Coastguard Worker rex.append('[^/%s]*' % re.escape(os.path.sep)) 86*44844408SAndroid Build Coastguard Worker elif match.group(5): # / 87*44844408SAndroid Build Coastguard Worker rex.append(sep_rex) 88*44844408SAndroid Build Coastguard Worker else: # somepath 89*44844408SAndroid Build Coastguard Worker rex.append(re.escape(match.group(6))) 90*44844408SAndroid Build Coastguard Worker next_pos = match.end() 91*44844408SAndroid Build Coastguard Worker rex.append('$') 92*44844408SAndroid Build Coastguard Worker return re.compile(''.join(rex)) 93*44844408SAndroid Build Coastguard Worker 94*44844408SAndroid Build Coastguard Workerdef _as_list(l): 95*44844408SAndroid Build Coastguard Worker if isinstance(l, basestring): 96*44844408SAndroid Build Coastguard Worker return l.split() 97*44844408SAndroid Build Coastguard Worker return l 98*44844408SAndroid Build Coastguard Worker 99*44844408SAndroid Build Coastguard Workerdef glob(dir_path, 100*44844408SAndroid Build Coastguard Worker includes = '**/*', 101*44844408SAndroid Build Coastguard Worker excludes = default_excludes, 102*44844408SAndroid Build Coastguard Worker entry_type = FILE, 103*44844408SAndroid Build Coastguard Worker prune_dirs = prune_dirs, 104*44844408SAndroid Build Coastguard Worker max_depth = 25): 105*44844408SAndroid Build Coastguard Worker include_filter = [ant_pattern_to_re(p) for p in _as_list(includes)] 106*44844408SAndroid Build Coastguard Worker exclude_filter = [ant_pattern_to_re(p) for p in _as_list(excludes)] 107*44844408SAndroid Build Coastguard Worker prune_dirs = [p.replace('/',os.path.sep) for p in _as_list(prune_dirs)] 108*44844408SAndroid Build Coastguard Worker dir_path = dir_path.replace('/',os.path.sep) 109*44844408SAndroid Build Coastguard Worker entry_type_filter = entry_type 110*44844408SAndroid Build Coastguard Worker 111*44844408SAndroid Build Coastguard Worker def is_pruned_dir(dir_name): 112*44844408SAndroid Build Coastguard Worker for pattern in prune_dirs: 113*44844408SAndroid Build Coastguard Worker if fnmatch.fnmatch(dir_name, pattern): 114*44844408SAndroid Build Coastguard Worker return True 115*44844408SAndroid Build Coastguard Worker return False 116*44844408SAndroid Build Coastguard Worker 117*44844408SAndroid Build Coastguard Worker def apply_filter(full_path, filter_rexs): 118*44844408SAndroid Build Coastguard Worker """Return True if at least one of the filter regular expression match full_path.""" 119*44844408SAndroid Build Coastguard Worker for rex in filter_rexs: 120*44844408SAndroid Build Coastguard Worker if rex.match(full_path): 121*44844408SAndroid Build Coastguard Worker return True 122*44844408SAndroid Build Coastguard Worker return False 123*44844408SAndroid Build Coastguard Worker 124*44844408SAndroid Build Coastguard Worker def glob_impl(root_dir_path): 125*44844408SAndroid Build Coastguard Worker child_dirs = [root_dir_path] 126*44844408SAndroid Build Coastguard Worker while child_dirs: 127*44844408SAndroid Build Coastguard Worker dir_path = child_dirs.pop() 128*44844408SAndroid Build Coastguard Worker for entry in listdir(dir_path): 129*44844408SAndroid Build Coastguard Worker full_path = os.path.join(dir_path, entry) 130*44844408SAndroid Build Coastguard Worker## print 'Testing:', full_path, 131*44844408SAndroid Build Coastguard Worker is_dir = os.path.isdir(full_path) 132*44844408SAndroid Build Coastguard Worker if is_dir and not is_pruned_dir(entry): # explore child directory ? 133*44844408SAndroid Build Coastguard Worker## print '===> marked for recursion', 134*44844408SAndroid Build Coastguard Worker child_dirs.append(full_path) 135*44844408SAndroid Build Coastguard Worker included = apply_filter(full_path, include_filter) 136*44844408SAndroid Build Coastguard Worker rejected = apply_filter(full_path, exclude_filter) 137*44844408SAndroid Build Coastguard Worker if not included or rejected: # do not include entry ? 138*44844408SAndroid Build Coastguard Worker## print '=> not included or rejected' 139*44844408SAndroid Build Coastguard Worker continue 140*44844408SAndroid Build Coastguard Worker link = os.path.islink(full_path) 141*44844408SAndroid Build Coastguard Worker is_file = os.path.isfile(full_path) 142*44844408SAndroid Build Coastguard Worker if not is_file and not is_dir: 143*44844408SAndroid Build Coastguard Worker## print '=> unknown entry type' 144*44844408SAndroid Build Coastguard Worker continue 145*44844408SAndroid Build Coastguard Worker if link: 146*44844408SAndroid Build Coastguard Worker entry_type = is_file and FILE_LINK or DIR_LINK 147*44844408SAndroid Build Coastguard Worker else: 148*44844408SAndroid Build Coastguard Worker entry_type = is_file and FILE or DIR 149*44844408SAndroid Build Coastguard Worker## print '=> type: %d' % entry_type, 150*44844408SAndroid Build Coastguard Worker if (entry_type & entry_type_filter) != 0: 151*44844408SAndroid Build Coastguard Worker## print ' => KEEP' 152*44844408SAndroid Build Coastguard Worker yield os.path.join(dir_path, entry) 153*44844408SAndroid Build Coastguard Worker## else: 154*44844408SAndroid Build Coastguard Worker## print ' => TYPE REJECTED' 155*44844408SAndroid Build Coastguard Worker return list(glob_impl(dir_path)) 156*44844408SAndroid Build Coastguard Worker 157*44844408SAndroid Build Coastguard Worker 158*44844408SAndroid Build Coastguard Workerif __name__ == "__main__": 159*44844408SAndroid Build Coastguard Worker import unittest 160*44844408SAndroid Build Coastguard Worker 161*44844408SAndroid Build Coastguard Worker class AntPatternToRETest(unittest.TestCase): 162*44844408SAndroid Build Coastguard Worker## def test_conversion(self): 163*44844408SAndroid Build Coastguard Worker## self.assertEqual('^somepath$', ant_pattern_to_re('somepath').pattern) 164*44844408SAndroid Build Coastguard Worker 165*44844408SAndroid Build Coastguard Worker def test_matching(self): 166*44844408SAndroid Build Coastguard Worker test_cases = [ ('path', 167*44844408SAndroid Build Coastguard Worker ['path'], 168*44844408SAndroid Build Coastguard Worker ['somepath', 'pathsuffix', '/path', '/path']), 169*44844408SAndroid Build Coastguard Worker ('*.py', 170*44844408SAndroid Build Coastguard Worker ['source.py', 'source.ext.py', '.py'], 171*44844408SAndroid Build Coastguard Worker ['path/source.py', '/.py', 'dir.py/z', 'z.pyc', 'z.c']), 172*44844408SAndroid Build Coastguard Worker ('**/path', 173*44844408SAndroid Build Coastguard Worker ['path', '/path', '/a/path', 'c:/a/path', '/a/b/path', '//a/path', '/a/path/b/path'], 174*44844408SAndroid Build Coastguard Worker ['path/', 'a/path/b', 'dir.py/z', 'somepath', 'pathsuffix', 'a/somepath']), 175*44844408SAndroid Build Coastguard Worker ('path/**', 176*44844408SAndroid Build Coastguard Worker ['path/a', 'path/path/a', 'path//'], 177*44844408SAndroid Build Coastguard Worker ['path', 'somepath/a', 'a/path', 'a/path/a', 'pathsuffix/a']), 178*44844408SAndroid Build Coastguard Worker ('/**/path', 179*44844408SAndroid Build Coastguard Worker ['/path', '/a/path', '/a/b/path/path', '/path/path'], 180*44844408SAndroid Build Coastguard Worker ['path', 'path/', 'a/path', '/pathsuffix', '/somepath']), 181*44844408SAndroid Build Coastguard Worker ('a/b', 182*44844408SAndroid Build Coastguard Worker ['a/b'], 183*44844408SAndroid Build Coastguard Worker ['somea/b', 'a/bsuffix', 'a/b/c']), 184*44844408SAndroid Build Coastguard Worker ('**/*.py', 185*44844408SAndroid Build Coastguard Worker ['script.py', 'src/script.py', 'a/b/script.py', '/a/b/script.py'], 186*44844408SAndroid Build Coastguard Worker ['script.pyc', 'script.pyo', 'a.py/b']), 187*44844408SAndroid Build Coastguard Worker ('src/**/*.py', 188*44844408SAndroid Build Coastguard Worker ['src/a.py', 'src/dir/a.py'], 189*44844408SAndroid Build Coastguard Worker ['a/src/a.py', '/src/a.py']), 190*44844408SAndroid Build Coastguard Worker ] 191*44844408SAndroid Build Coastguard Worker for ant_pattern, accepted_matches, rejected_matches in list(test_cases): 192*44844408SAndroid Build Coastguard Worker def local_path(paths): 193*44844408SAndroid Build Coastguard Worker return [ p.replace('/',os.path.sep) for p in paths ] 194*44844408SAndroid Build Coastguard Worker test_cases.append((ant_pattern, local_path(accepted_matches), local_path(rejected_matches))) 195*44844408SAndroid Build Coastguard Worker for ant_pattern, accepted_matches, rejected_matches in test_cases: 196*44844408SAndroid Build Coastguard Worker rex = ant_pattern_to_re(ant_pattern) 197*44844408SAndroid Build Coastguard Worker print('ant_pattern:', ant_pattern, ' => ', rex.pattern) 198*44844408SAndroid Build Coastguard Worker for accepted_match in accepted_matches: 199*44844408SAndroid Build Coastguard Worker print('Accepted?:', accepted_match) 200*44844408SAndroid Build Coastguard Worker self.assertTrue(rex.match(accepted_match) is not None) 201*44844408SAndroid Build Coastguard Worker for rejected_match in rejected_matches: 202*44844408SAndroid Build Coastguard Worker print('Rejected?:', rejected_match) 203*44844408SAndroid Build Coastguard Worker self.assertTrue(rex.match(rejected_match) is None) 204*44844408SAndroid Build Coastguard Worker 205*44844408SAndroid Build Coastguard Worker unittest.main() 206