xref: /aosp_15_r20/external/jsoncpp/devtools/antglob.py (revision 4484440890e2bc6e07362b4feaf15601abfe0071)
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