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

Side by Side Diff: build/scan_sources.py

Issue 8440004: This CL is based on the patch: (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 1 month 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 | « DEPS ('k') | chrome/nacl.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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. All filenames and paths
15 are expected and returned with POSIX separators.
16 """
17
18
19 debug = False
20
21
22 def DebugPrint(txt):
23 if debug: print txt
24
25
26 class PathConverter(object):
27 """PathConverter does path manipulates using Posix style pathnames.
28
29 Regardless of the native path type, all inputs and outputs to the path
30 functions are with POSIX style separators.
31 """
32 def ToNativePath(self, pathname):
33 return os.path.sep.join(pathname.split('/'))
34
35 def ToPosixPath(self, pathname):
36 return '/'.join(pathname.split(os.path.sep))
37
38 def exists(self, pathname):
39 ospath = self.ToNativePath(pathname)
40 return os.path.exists(ospath)
41
42 def getcwd(self):
43 return self.ToPosixPath(os.getcwd())
44
45 def isabs(self, pathname):
46 ospath = self.ToNativePath(pathname)
47 return os.path.isabs(ospath)
48
49 def isdir(self, pathname):
50 ospath = self.ToNativePath(pathname)
51 return os.path.isdir(ospath)
52
53 def open(self, pathname):
54 ospath = self.ToNativePath(pathname)
55 return open(ospath)
56
57 def realpath(self, pathname):
58 ospath = self.ToNativePath(pathname)
59 ospath = os.path.realpath(ospath)
60 return self.ToPosixPath(ospath)
61
62
63 class Resolver(object):
64 """Resolver finds and generates relative paths for include files.
65
66 The Resolver object provides a mechanism to to find and convert a source or
67 include filename into a relative path based on provided search paths. All
68 paths use POSIX style separator.
69 """
70 def __init__(self, pathobj=PathConverter()):
71 self.search_dirs = []
72 self.pathobj = pathobj
73 self.cwd = self.pathobj.getcwd()
74 self.offs = len(self.cwd)
75
76 def AddOneDirectory(self, pathname):
77 """Add an include search path."""
78 pathname = self.pathobj.realpath(pathname)
79 DebugPrint('Adding DIR: %s' % pathname)
80 if pathname not in self.search_dirs:
81 if self.pathobj.isdir(pathname):
82 self.search_dirs.append(pathname)
83 else:
84 sys.stderr.write('Not a directory: %s\n' % pathname)
85 return False
86 return True
87
88 def AddDirectories(self, pathlist):
89 """Add list of space separated directories."""
90 failed = False
91 dirlist = ' '.join(pathlist)
92 for dirname in dirlist.split(' '):
93 if not self.AddOneDirectory(dirname):
94 failed = True
95 return not failed
96
97 def GetDirectories(self):
98 return self.search_dirs
99
100 def RealToRelative(self, filepath, basepath):
101 """Returns a relative path from an absolute basepath and filepath."""
102 path_parts = filepath.split('/')
103 base_parts = basepath.split('/')
104 while path_parts and base_parts and path_parts[0] == base_parts[0]:
105 path_parts = path_parts[1:]
106 base_parts = base_parts[1:]
107 rel_parts = ['..'] * len(base_parts) + path_parts
108 return '/'.join(rel_parts)
109
110 def FilenameToRelative(self, filepath):
111 """Returns a relative path from CWD to filepath."""
112 filepath = self.pathobj.realpath(filepath)
113 basepath = self.cwd
114 return self.RealToRelative(filepath, basepath)
115
116 def FindFile(self, filename):
117 """Search for <filename> across the search directories, if the path is not
118 absolute. Return the filepath relative to the CWD or None. """
119 if self.pathobj.isabs(filename):
120 if self.pathobj.exists(filename):
121 return self.FilenameToRelative(filename)
122 return None
123 for pathname in self.search_dirs:
124 fullname = '%s/%s' % (pathname, filename)
125 if self.pathobj.exists(fullname):
126 return self.FilenameToRelative(fullname)
127 return None
128
129
130 def LoadFile(filename):
131 # Catch cases where the file does not exist
132 try:
133 fd = PathConverter().open(filename)
134 except IOError:
135 DebugPrint('Exception on file: %s' % filename)
136 return ''
137 # Go ahead and throw if you fail to read
138 return fd.read()
139
140
141 class Scanner(object):
142 """Scanner searches for '#include' to find dependencies."""
143
144 def __init__(self, loader=None):
145 regex = r'\#[ \t]*include[ \t]*[<"]([^>^"]+)[>"]'
146 self.parser = re.compile(regex)
147 self.loader = loader
148 if not loader:
149 self.loader = LoadFile
150
151 def ScanData(self, data):
152 """Generate a list of includes from this text block."""
153 return self.parser.findall(data)
154
155 def ScanFile(self, filename):
156 """Generate a list of includes from this filename."""
157 includes = self.ScanData(self.loader(filename))
158 DebugPrint('Source %s contains:\n\t%s' % (filename, '\n\t'.join(includes)))
159 return includes
160
161
162 class WorkQueue(object):
163 """WorkQueue contains the list of files to be scanned.
164
165 WorkQueue contains provides a queue of files to be processed. The scanner
166 will attempt to push new items into the queue, which will be ignored if the
167 item is already in the queue. If the item is new, it will be added to the
168 work list, which is drained by the scanner.
169 """
170 def __init__(self, resolver, scanner=Scanner()):
171 self.added_set = set()
172 self.todo_list = list()
173 self.scanner = scanner
174 self.resolver = resolver
175
176 def PushIfNew(self, filename):
177 """Add this dependency to the list of not already there."""
178 DebugPrint('Adding %s' % filename)
179 resolved_name = self.resolver.FindFile(filename)
180 if not resolved_name:
181 DebugPrint('Failed to resolve %s' % filename)
182 return
183 DebugPrint('Resolvd as %s' % resolved_name)
184 if resolved_name in self.added_set:
185 return
186 self.todo_list.append(resolved_name)
187 self.added_set.add(resolved_name)
188
189 def PopIfAvail(self):
190 """Fetch the next dependency to search."""
191 if not self.todo_list:
192 return None
193 return self.todo_list.pop()
194
195 def Run(self):
196 """Search through the available dependencies until the list becomes empty.
197 The list must be primed with one or more source files to search."""
198 scan_name = self.PopIfAvail()
199 while scan_name:
200 includes = self.scanner.ScanFile(scan_name)
201 for include_file in includes:
202 self.PushIfNew(include_file)
203 scan_name = self.PopIfAvail()
204 return sorted(self.added_set)
205
206
207 def Main(argv):
208 global debug
209 parser = OptionParser()
210 parser.add_option('-I', dest='includes', action='append',
211 help='Set include path.')
212 parser.add_option('-D', dest='debug', action='store_true',
213 help='Enable debugging output.', default=False)
214 (options, files) = parser.parse_args(argv[1:])
215
216 if options.debug:
217 debug = Trueglobal_var_name,
218
219 resolver = Resolver()
220 if options.includes:
221 if not resolver.AddDirectories(options.includes):
222 return -1
223
224 workQ = WorkQueue(resolver)
225 for filename in files:
226 workQ.PushIfNew(filename)
227
228 sorted_list = workQ.Run()
229 for pathname in sorted_list:
230 sys.stderr.write(pathname + '\n')
231 return 0
232
233
234 if __name__ == '__main__':
235 sys.exit(Main(sys.argv))
236
OLDNEW
« no previous file with comments | « DEPS ('k') | chrome/nacl.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698