|
OLD | NEW |
---|---|
(Empty) | |
1 #!/usr/bin/python | |
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
3 # Use of this source code is governed by a BSD-style license that can be | |
4 # found in the LICENSE file. | |
5 | |
6 import os | |
7 import re | |
8 import sys | |
9 | |
10 """ | |
bradn
2011/09/26 06:21:57
Pull this up onto the line with the """ (per style
noelallen1
2011/09/30 01:12:45
Done.
| |
11 Header Scanner. | |
12 | |
13 This module will scan a set of input sources for include dependencies. Use | |
14 the command-line switch -Ixxxx to add include paths. | |
15 """ | |
16 | |
17 | |
bradn
2011/09/26 06:21:57
Extra cr here.
noelallen1
2011/09/30 01:12:45
Done.
| |
18 | |
19 class Resolver(object): | |
20 """ | |
21 The Resolver object provides a mechanism to to find and convert a source or | |
22 include filename into a relative path based on provided search paths. | |
23 """ | |
24 def __init__(self): | |
25 self.search_dirs = [] | |
26 self.AddDir('.') | |
bradn
2011/09/26 06:21:57
This is adding cwd to the search order (the cwd wh
noelallen1
2011/09/30 01:12:45
Removed, I force you to specify it now.
| |
27 self.cwd = os.path.realpath(os.getcwd()) | |
bradn
2011/09/26 06:21:57
I've noticed you used realpath throughout.
I actua
noelallen1
2011/09/30 01:12:45
I use realpath so that two links to the same locat
| |
28 self.offs = len(self.cwd) | |
29 | |
30 def AddDir(self, pathname): | |
31 """Add an include search path.""" | |
32 pathname = os.path.realpath(pathname) | |
33 if pathname not in self.search_dirs: | |
34 if os.path.isdir(pathname): | |
35 self.search_dirs.append(pathname) | |
36 print 'Added dir: %s' % pathname | |
bradn
2011/09/26 06:21:57
Leftover? I assume these are something you want in
noelallen1
2011/09/30 01:12:45
Not sure what we should do here. We "failed" beca
| |
37 else: | |
38 print 'Not a directory: %s' % pathname | |
39 return False | |
40 return True | |
41 | |
42 def ToRelative(self, filepath): | |
43 """Returns a relative path from CWD to filepath.""" | |
44 filepath = os.path.realpath(filepath) | |
45 basepath = self.cwd | |
46 path_parts = filepath.split(os.sep) | |
47 base_parts = basepath.split(os.sep) | |
48 print "filepath vs basepath" | |
49 print " %s\n %s\n" % (filepath, basepath) | |
bradn
2011/09/26 06:21:57
Leftover?
noelallen1
2011/09/30 01:12:45
Done.
| |
50 while path_parts and base_parts and path_parts[0] == base_parts[0]: | |
51 path_parts = path_parts[1:] | |
52 base_parts = base_parts[1:] | |
53 rel_parts = ['..'] * len(base_parts) + path_parts | |
54 return os.sep.join(rel_parts) | |
55 | |
56 def FindFile(self, filename): | |
57 """Search for <filename> across the search directories, and if found, | |
58 return the filepath relative to the CWD if found or None. """ | |
59 if filename[0] == os.path.sep: | |
bradn
2011/09/26 06:21:57
os.path.isabs(filename)
noelallen1
2011/09/30 01:12:45
Done.
| |
60 if os.path.exists(filename): | |
61 return self.ToRelative(filename) | |
62 return None | |
63 for pathname in self.search_dirs: | |
64 fullname = os.path.join(pathname, filename) | |
65 if os.path.exists(fullname): | |
66 return self.ToRelative(fullname) | |
67 return None | |
68 | |
69 | |
70 class Scanner(object): | |
71 """ | |
bradn
2011/09/26 06:21:57
Give a one line description per the style guide.
| |
72 Scanner does the regular expression work, loading and scanning the source | |
73 files for the key '#include' to find dependencies. | |
74 """ | |
75 def __init__(self, parent): | |
76 regex = r'(?P<inc>\#include [<"].+[>"])' | |
bradn
2011/09/26 06:21:57
You did P<inc> but never used that group name.
bradn
2011/09/26 06:21:57
.+ -> [^>"]+
| |
77 self.work_q = parent | |
bradn
2011/09/26 06:21:57
So include <> vs include "" have different behavio
noelallen1
2011/09/30 01:12:45
Done.
| |
78 self.parser = re.compile(regex) | |
79 | |
80 def GetIncludes(self, data): | |
81 """Generate a list of includes.""" | |
82 out = [] | |
83 for token in self.parser.split(data): | |
bradn
2011/09/26 06:21:57
Split's a little weird, I'd use findall here as if
| |
84 if len(token) > 8 and token[0:8] == '#include': | |
85 filepath = token.split()[1] | |
86 out.append(filepath[1:-1]) | |
87 return out | |
88 | |
89 def Scan(self, filename): | |
90 """Attempt to scan the given file for a list of includes.""" | |
91 try: | |
92 data = open(filename).read() | |
93 return self.GetIncludes(data) | |
94 except: | |
bradn
2011/09/26 06:21:57
Technically the style guide forbids carte-blanc ex
| |
95 return [] | |
96 | |
97 | |
98 class WorkQueue(object): | |
99 """ | |
bradn
2011/09/26 06:21:57
Pull out the first sentence as a one line descript
| |
100 WorkQueue contains provides a queue of files to be processed. The scanner | |
101 will attempt to push new items into the queue, which will be ignored if the | |
102 item is already in the queue. If the item is new, it will be added to the | |
103 work list, which is drained by the scanner. | |
104 """ | |
105 def __init__(self, resolver): | |
106 self.added_set = set() | |
107 self.todo_list = list() | |
108 self.scanner = Scanner(self) | |
109 self.resolver = resolver | |
110 | |
111 def PushIfNew(self, filename): | |
112 """Add this dependency to the list of not already there.""" | |
113 resolved_name = self.resolver.FindFile(filename) | |
114 if not resolved_name: | |
115 return | |
116 if resolved_name in self.added_set: | |
117 return | |
118 self.todo_list.append(resolved_name) | |
119 self.added_set |= set([resolved_name]) | |
bradn
2011/09/26 06:21:57
-> self.added_set.add(resolved_name)
noelallen1
2011/09/30 01:12:45
Done.
| |
120 | |
121 def PopIfAvail(self): | |
122 """Fetch the next dependency to search.""" | |
123 if not self.todo_list: | |
124 return None | |
125 return self.todo_list.pop() | |
126 | |
127 def Run(self): | |
128 """Search through the available dependencies until the list becomes empty. | |
129 The list must be primed with one or more source files to search.""" | |
130 scan_name = self.PopIfAvail() | |
131 while scan_name: | |
132 includes = self.scanner.Scan(scan_name) | |
133 for include_file in includes: | |
134 self.PushIfNew(include_file) | |
135 scan_name = self.PopIfAvail() | |
136 | |
137 sorted_list = sorted(self.added_set) | |
138 for pathname in sorted_list: | |
139 print pathname | |
140 | |
141 | |
142 def Main(argv): | |
143 resolver = Resolver() | |
144 files = [] | |
145 failed = False | |
146 for arg in argv[1:]: | |
147 if len(arg) > 2 and arg[0:2] == '-I': | |
bradn
2011/09/26 06:21:57
Style guide calls for consistent use of single or
bradn
2011/09/26 06:21:57
I'm all for avoiding optparse when possible, but I
noelallen1
2011/09/30 01:12:45
Done.
noelallen1
2011/09/30 01:12:45
Done.
| |
148 if not resolver.AddDir(arg[2:]): | |
149 print "Failed to add path: %s" % arg[2:] | |
150 failed = True | |
151 else: | |
152 files.append(arg) | |
153 | |
154 if failed: return -1 | |
155 workQ = WorkQueue(resolver) | |
156 for filename in files: | |
157 workQ.PushIfNew(filename) | |
158 workQ.Run() | |
159 return 0 | |
160 | |
bradn
2011/09/26 06:21:57
Style guide is a little vague, but most places see
noelallen1
2011/09/30 01:12:45
Done.
| |
161 if __name__ == '__main__': | |
162 sys.exit(Main(sys.argv)) | |
163 | |
bradn
2011/09/26 06:21:57
Drop trailing line.
noelallen1
2011/09/30 01:12:45
Done.
| |
OLD | NEW |