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

Side by Side Diff: build/scan_sources.py

Issue 2455783004: Remove old tools used by gyp build (Closed)
Patch Set: . Created 4 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
« no previous file with comments | « build/detect_nacl_host_arch.py ('k') | build/test_build.py » ('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) 2012 The Native Client 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 """Header Scanner.
11
12 This module will scan a set of input sources for include dependencies. Use
13 the command-line switch -Ixxxx to add include paths. All filenames and paths
14 are expected and returned with POSIX separators.
15 """
16
17
18 debug = False
19
20
21 def DebugPrint(txt):
22 if debug: print txt
23
24
25 class PathConverter(object):
26 """PathConverter does path manipulates using Posix style pathnames.
27
28 Regardless of the native path type, all inputs and outputs to the path
29 functions are with POSIX style separators.
30 """
31 def ToNativePath(self, pathname):
32 return os.path.sep.join(pathname.split('/'))
33
34 def ToPosixPath(self, pathname):
35 return '/'.join(pathname.split(os.path.sep))
36
37 def isfile(self, pathname):
38 ospath = self.ToNativePath(pathname)
39 return os.path.isfile(ospath)
40
41 def getcwd(self):
42 return self.ToPosixPath(os.getcwd())
43
44 def isabs(self, pathname):
45 ospath = self.ToNativePath(pathname)
46 return os.path.isabs(ospath)
47
48 def isdir(self, pathname):
49 ospath = self.ToNativePath(pathname)
50 return os.path.isdir(ospath)
51
52 def open(self, pathname):
53 ospath = self.ToNativePath(pathname)
54 return open(ospath)
55
56 def abspath(self, pathname):
57 ospath = self.ToNativePath(pathname)
58 ospath = os.path.abspath(ospath)
59 return self.ToPosixPath(ospath)
60
61 def dirname(self, pathname):
62 ospath = self.ToNativePath(pathname)
63 ospath = os.path.dirname(ospath)
64 return self.ToPosixPath(ospath)
65
66
67 filename_to_relative_cache = {} # (filepath, basepath) -> relpath
68 findfile_cache = {} # (tuple(searchdirs), cwd, file) -> filename/None
69 pathisfile_cache = {} # abspath -> boolean, works because fs is static
70 # during a run.
71
72
73 class Resolver(object):
74 """Resolver finds and generates relative paths for include files.
75
76 The Resolver object provides a mechanism to to find and convert a source or
77 include filename into a relative path based on provided search paths. All
78 paths use POSIX style separator.
79 """
80 def __init__(self, pathobj=PathConverter()):
81 self.search_dirs = []
82 self.pathobj = pathobj
83 self.cwd = self.pathobj.getcwd()
84 self.offs = len(self.cwd)
85
86 def AddOneDirectory(self, pathname):
87 """Add an include search path."""
88 pathname = self.pathobj.abspath(pathname)
89 DebugPrint('Adding DIR: %s' % pathname)
90 if pathname not in self.search_dirs:
91 if self.pathobj.isdir(pathname):
92 self.search_dirs.append(pathname)
93 else:
94 # We can end up here when using the gyp generator analyzer. To avoid
95 # spamming only log if debug enabled.
96 DebugPrint('Not a directory: %s\n' % pathname)
97 return False
98 return True
99
100 def RemoveOneDirectory(self, pathname):
101 """Remove an include search path."""
102 pathname = self.pathobj.abspath(pathname)
103 DebugPrint('Removing DIR: %s' % pathname)
104 if pathname in self.search_dirs:
105 self.search_dirs.remove(pathname)
106 return True
107
108 def AddDirectories(self, pathlist):
109 """Add list of space separated directories."""
110 failed = False
111 dirlist = ' '.join(pathlist)
112 for dirname in dirlist.split(' '):
113 if not self.AddOneDirectory(dirname):
114 failed = True
115 return not failed
116
117 def GetDirectories(self):
118 return self.search_dirs
119
120 def RealToRelative(self, filepath, basepath):
121 """Returns a relative path from an absolute basepath and filepath."""
122 cache_key = (filepath, basepath)
123 cache_result = None
124 if cache_key in filename_to_relative_cache:
125 cache_result = filename_to_relative_cache[cache_key]
126 return cache_result
127 def SlowRealToRelative(filepath, basepath):
128 path_parts = filepath.split('/')
129 base_parts = basepath.split('/')
130 while path_parts and base_parts and path_parts[0] == base_parts[0]:
131 path_parts = path_parts[1:]
132 base_parts = base_parts[1:]
133 rel_parts = ['..'] * len(base_parts) + path_parts
134 rel_path = '/'.join(rel_parts)
135 return rel_path
136 rel_path = SlowRealToRelative(filepath, basepath)
137 filename_to_relative_cache[cache_key] = rel_path
138 return rel_path
139
140 def FilenameToRelative(self, filepath):
141 """Returns a relative path from CWD to filepath."""
142 filepath = self.pathobj.abspath(filepath)
143 basepath = self.cwd
144 return self.RealToRelative(filepath, basepath)
145
146 def FindFile(self, filename):
147 """Search for <filename> across the search directories, if the path is not
148 absolute. Return the filepath relative to the CWD or None. """
149 cache_key = (tuple(self.search_dirs), self.cwd, filename)
150 if cache_key in findfile_cache:
151 cache_result = findfile_cache[cache_key]
152 return cache_result
153 result = None
154 def isfile(absname):
155 res = pathisfile_cache.get(absname)
156 if res is None:
157 res = self.pathobj.isfile(absname)
158 pathisfile_cache[absname] = res
159 return res
160
161 if self.pathobj.isabs(filename):
162 if isfile(filename):
163 result = self.FilenameToRelative(filename)
164 else:
165 for pathname in self.search_dirs:
166 fullname = '%s/%s' % (pathname, filename)
167 if isfile(fullname):
168 result = self.FilenameToRelative(fullname)
169 break
170 findfile_cache[cache_key] = result
171 return result
172
173
174 def LoadFile(filename):
175 # Catch cases where the file does not exist
176 try:
177 fd = PathConverter().open(filename)
178 except IOError:
179 DebugPrint('Exception on file: %s' % filename)
180 return ''
181 # Go ahead and throw if you fail to read
182 return fd.read()
183
184
185 scan_cache = {} # cache (abs_filename -> include_list)
186
187
188 class Scanner(object):
189 """Scanner searches for '#include' to find dependencies."""
190
191 def __init__(self, loader=None):
192 regex = r'^\s*\#[ \t]*include[ \t]*[<"]([^>"]+)[>"]'
193 self.parser = re.compile(regex, re.M)
194 self.loader = loader
195 if not loader:
196 self.loader = LoadFile
197
198 def ScanData(self, data):
199 """Generate a list of includes from this text block."""
200 return self.parser.findall(data)
201
202 def ScanFile(self, filename):
203 """Generate a list of includes from this filename."""
204 abs_filename = os.path.abspath(filename)
205 if abs_filename in scan_cache:
206 return scan_cache[abs_filename]
207 includes = self.ScanData(self.loader(filename))
208 scan_cache[abs_filename] = includes
209 DebugPrint('Source %s contains:\n\t%s' % (filename, '\n\t'.join(includes)))
210 return includes
211
212
213 class WorkQueue(object):
214 """WorkQueue contains the list of files to be scanned.
215
216 WorkQueue contains provides a queue of files to be processed. The scanner
217 will attempt to push new items into the queue, which will be ignored if the
218 item is already in the queue. If the item is new, it will be added to the
219 work list, which is drained by the scanner.
220 """
221 def __init__(self, resolver, scanner=Scanner()):
222 self.added_set = set()
223 self.todo_list = list()
224 self.scanner = scanner
225 self.resolver = resolver
226
227 def PushIfNew(self, filename):
228 """Add this dependency to the list of not already there."""
229 DebugPrint('Adding %s' % filename)
230 resolved_name = self.resolver.FindFile(filename)
231 if not resolved_name:
232 DebugPrint('Failed to resolve %s' % filename)
233 return
234 DebugPrint('Resolvd as %s' % resolved_name)
235 if resolved_name in self.added_set:
236 return
237 self.todo_list.append(resolved_name)
238 self.added_set.add(resolved_name)
239
240 def PopIfAvail(self):
241 """Fetch the next dependency to search."""
242 if not self.todo_list:
243 return None
244 return self.todo_list.pop()
245
246 def Run(self):
247 """Search through the available dependencies until the list becomes empty.
248 The list must be primed with one or more source files to search."""
249 scan_name = self.PopIfAvail()
250 while scan_name:
251 includes = self.scanner.ScanFile(scan_name)
252 # Add the directory of the current scanned file for resolving includes
253 # while processing includes for this file.
254 scan_dir = PathConverter().dirname(scan_name)
255 added_dir = not self.resolver.AddOneDirectory(scan_dir)
256 for include_file in includes:
257 self.PushIfNew(include_file)
258 if added_dir:
259 self.resolver.RemoveOneDirectory(scan_dir)
260 scan_name = self.PopIfAvail()
261 return self.added_set
262
263
264 def DoMain(argv):
265 """Entry point used by gyp's pymod_do_main feature."""
266 global debug
267
268 resolver = Resolver()
269 files = []
270
271 arg_type = ''
272 for arg in argv:
273 if arg in ['-I', '-S']:
274 arg_type = arg
275 elif arg == '-D':
276 debug = True
277 elif arg_type == '-I':
278 # Skip generated include directories. These files may not exist and
279 # there should be explicit dependency on the target that generates
280 # these files.
281 if arg.startswith('$!PRODUCT_DIR'):
282 continue
283 resolver.AddDirectories([arg])
284 elif arg_type == '-S':
285 files.append(arg)
286
287 workQ = WorkQueue(resolver)
288 for filename in files:
289 workQ.PushIfNew(filename)
290
291 sources_set = workQ.Run()
292
293 # If any of the original files requested aren't found, add them anyway.
294 # This is so that source files that will be generated are still returned in
295 # the program output.
296 sources_set = sources_set.union(set(files))
297
298 sorted_list = sorted(sources_set)
299 return '\n'.join(sorted_list) + '\n'
300
301
302 def Main():
303 result = DoMain(sys.argv[1:])
304 sys.stdout.write(result)
305
306
307 if __name__ == '__main__':
308 Main()
OLDNEW
« no previous file with comments | « build/detect_nacl_host_arch.py ('k') | build/test_build.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698