Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(123)

Side by Side Diff: build/scan_sources.py

Issue 8037013: Create scanning script to determine header dependencies. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | build/scan_sources_test.py » ('j') | build/scan_sources_test.py » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Property Changes:
Added: svn:executable
+ *
OLDNEW
(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 from optparse import OptionParser
7 import os
8 import re
9 import sys
10
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 debug = False
bradn 2011/09/29 20:25:30 If you're gonna leave this in, maybe put options u
noelallen1 2011/09/30 01:12:45 Done.
18
bradn 2011/09/29 20:25:30 cr
noelallen1 2011/09/30 01:12:45 Done.
19 def DebugPrint(txt):
20 if debug: print txt
21
bradn 2011/09/29 20:25:30 cr
noelallen1 2011/09/30 01:12:45 Done.
22 class Resolver(object):
23 """Resolver finds and generates relative paths for include files.
24
25 The Resolver object provides a mechanism to to find and convert a source or
26 include filename into a relative path based on provided search paths.
27 """
28 def __init__(self, pathobj=os.path):
29 self.search_dirs = []
30 self.pathobj = pathobj
31 self.cwd = self.pathobj.realpath(os.getcwd())
32 self.offs = len(self.cwd)
33
34 def AddOneDirectory(self, pathname):
35 """Add an include search path."""
36 pathname = self.pathobj.realpath(pathname)
37 DebugPrint('Adding DIR: %s' % pathname)
38 if pathname not in self.search_dirs:
39 if self.pathobj.isdir(pathname):
40 self.search_dirs.append(pathname)
41 else:
42 sys.stderr.write('Not a directory: %s\n' % pathname)
43 return False
44 return True
45
46 def AddDirectories(self, pathlist):
47 """Add list of space separated directories."""
48 failed = False
49 dirlist = ' '.join(pathlist)
50 for dirname in dirlist.split(' '):
51 if not self.AddOneDirectory(dirname):
52 failed = True
53 return not failed
54
55 def GetDirectories(self):
56 return self.search_dirs
57
58 def RealToRelative(self, filepath, basepath):
59 """Returns a relative path from an absolute basepath and filepath."""
60 path_parts = filepath.split(os.sep)
61 base_parts = basepath.split(os.sep)
62 while path_parts and base_parts and path_parts[0] == base_parts[0]:
63 path_parts = path_parts[1:]
64 base_parts = base_parts[1:]
65 rel_parts = ['..'] * len(base_parts) + path_parts
66 return os.sep.join(rel_parts)
bradn 2011/09/29 20:25:30 Maybe make this use '/' instead of os.sep, and or
noelallen1 2011/09/30 01:12:45 Done.
67
68 def FilenameToRelative(self, filepath):
69 """Returns a relative path from CWD to filepath."""
70 filepath = self.pathobj.realpath(filepath)
71 basepath = self.cwd
72 return self.RealToRelative(filepath, basepath)
73
74 def FindFile(self, filename):
75 """Search for <filename> across the search directories, if the path is not
76 absolute. Return the filepath relative to the CWD or None. """
77 if self.pathobj.isabs(filename):
78 if self.pathobj.exists(filename):
79 return self.FilenameToRelative(filename)
80 return None
81 for pathname in self.search_dirs:
82 fullname = os.path.join(pathname, filename)
83 if self.pathobj.exists(fullname):
84 return self.FilenameToRelative(fullname)
85 return None
86
87
88 def LoadFile(filename):
89 # Catch cases where the file does not exist
90 try:
91 fd = open(filename)
92 except IOError:
93 DebugPrint('Exception on file: %s' % filename)
94 return ''
95 # Go ahead and throw if you fail to read
96 return fd.read()
97
98
99 class Scanner(object):
100 """Scanner searches for '#include' to find dependencies."""
101
102 def __init__(self, loader=None):
103 regex = r'\#[ \t]*include[ \t]*[<"]([^>^"]+)[>"]'
104 self.parser = re.compile(regex)
105 self.loader = loader
106 if not loader:
107 self.loader = LoadFile
108
109 def ScanData(self, data):
110 """Generate a list of includes from this text block."""
111 return self.parser.findall(data)
112
113 def ScanFile(self, filename):
114 """Generate a list of includes from this filename."""
115 includes = self.ScanData(self.loader(filename))
116 DebugPrint('Source %s contains:\n\t%s' % (filename, '\n\t'.join(includes)))
117 return includes
118
119
120 class WorkQueue(object):
121 """WorkQueue contains the list of files to be scanned.
122
123 WorkQueue contains provides a queue of files to be processed. The scanner
124 will attempt to push new items into the queue, which will be ignored if the
125 item is already in the queue. If the item is new, it will be added to the
126 work list, which is drained by the scanner.
127 """
128 def __init__(self, resolver, scanner=Scanner()):
129 self.added_set = set()
130 self.todo_list = list()
131 self.scanner = scanner
132 self.resolver = resolver
133
134 def PushIfNew(self, filename):
135 """Add this dependency to the list of not already there."""
136 DebugPrint('Adding %s' % filename)
137 resolved_name = self.resolver.FindFile(filename)
138 if not resolved_name:
139 DebugPrint('Failed to resolve %s' % filename)
140 return
141 DebugPrint('Resolvd as %s' % resolved_name)
142 if resolved_name in self.added_set:
143 return
144 self.todo_list.append(resolved_name)
145 self.added_set.add(resolved_name)
146
147 def PopIfAvail(self):
148 """Fetch the next dependency to search."""
149 if not self.todo_list:
150 return None
151 return self.todo_list.pop()
152
153 def Run(self):
154 """Search through the available dependencies until the list becomes empty.
155 The list must be primed with one or more source files to search."""
156 scan_name = self.PopIfAvail()
157 while scan_name:
158 includes = self.scanner.ScanFile(scan_name)
159 for include_file in includes:
160 self.PushIfNew(include_file)
161 scan_name = self.PopIfAvail()
162 return sorted(self.added_set)
163
164
165 def Main(argv):
166 parser = OptionParser()
167 parser.add_option('-I', dest='includes', action="append",
168 help='Set include path')
169 (options, files) = parser.parse_args(argv[1:])
170
171 resolver = Resolver()
172 if options.includes:
173 if not resolver.AddDirectories(options.includes):
174 return -1
175
176 workQ = WorkQueue(resolver)
177 for filename in files:
178 workQ.PushIfNew(filename)
179
180 sorted_list = workQ.Run()
181 for pathname in sorted_list:
182 sys.stderr.write(pathname + '\n')
183 return 0
184
185
186 if __name__ == '__main__':
187 sys.exit(Main(sys.argv))
188
OLDNEW
« no previous file with comments | « no previous file | build/scan_sources_test.py » ('j') | build/scan_sources_test.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698