Chromium Code Reviews| Index: build/scan_sources.py |
| =================================================================== |
| --- build/scan_sources.py (revision 0) |
| +++ build/scan_sources.py (revision 0) |
| @@ -0,0 +1,163 @@ |
| +#!/usr/bin/python |
| +# Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| + |
| +import os |
| +import re |
| +import sys |
| + |
| +""" |
|
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.
|
| + Header Scanner. |
| + |
| +This module will scan a set of input sources for include dependencies. Use |
| +the command-line switch -Ixxxx to add include paths. |
| +""" |
| + |
| + |
|
bradn
2011/09/26 06:21:57
Extra cr here.
noelallen1
2011/09/30 01:12:45
Done.
|
| + |
| +class Resolver(object): |
| + """ |
| + The Resolver object provides a mechanism to to find and convert a source or |
| + include filename into a relative path based on provided search paths. |
| + """ |
| + def __init__(self): |
| + self.search_dirs = [] |
| + 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.
|
| + 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
|
| + self.offs = len(self.cwd) |
| + |
| + def AddDir(self, pathname): |
| + """Add an include search path.""" |
| + pathname = os.path.realpath(pathname) |
| + if pathname not in self.search_dirs: |
| + if os.path.isdir(pathname): |
| + self.search_dirs.append(pathname) |
| + 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
|
| + else: |
| + print 'Not a directory: %s' % pathname |
| + return False |
| + return True |
| + |
| + def ToRelative(self, filepath): |
| + """Returns a relative path from CWD to filepath.""" |
| + filepath = os.path.realpath(filepath) |
| + basepath = self.cwd |
| + path_parts = filepath.split(os.sep) |
| + base_parts = basepath.split(os.sep) |
| + print "filepath vs basepath" |
| + print " %s\n %s\n" % (filepath, basepath) |
|
bradn
2011/09/26 06:21:57
Leftover?
noelallen1
2011/09/30 01:12:45
Done.
|
| + while path_parts and base_parts and path_parts[0] == base_parts[0]: |
| + path_parts = path_parts[1:] |
| + base_parts = base_parts[1:] |
| + rel_parts = ['..'] * len(base_parts) + path_parts |
| + return os.sep.join(rel_parts) |
| + |
| + def FindFile(self, filename): |
| + """Search for <filename> across the search directories, and if found, |
| + return the filepath relative to the CWD if found or None. """ |
| + 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.
|
| + if os.path.exists(filename): |
| + return self.ToRelative(filename) |
| + return None |
| + for pathname in self.search_dirs: |
| + fullname = os.path.join(pathname, filename) |
| + if os.path.exists(fullname): |
| + return self.ToRelative(fullname) |
| + return None |
| + |
| + |
| +class Scanner(object): |
| + """ |
|
bradn
2011/09/26 06:21:57
Give a one line description per the style guide.
|
| + Scanner does the regular expression work, loading and scanning the source |
| + files for the key '#include' to find dependencies. |
| + """ |
| + def __init__(self, parent): |
| + 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
.+ -> [^>"]+
|
| + 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.
|
| + self.parser = re.compile(regex) |
| + |
| + def GetIncludes(self, data): |
| + """Generate a list of includes.""" |
| + out = [] |
| + 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
|
| + if len(token) > 8 and token[0:8] == '#include': |
| + filepath = token.split()[1] |
| + out.append(filepath[1:-1]) |
| + return out |
| + |
| + def Scan(self, filename): |
| + """Attempt to scan the given file for a list of includes.""" |
| + try: |
| + data = open(filename).read() |
| + return self.GetIncludes(data) |
| + except: |
|
bradn
2011/09/26 06:21:57
Technically the style guide forbids carte-blanc ex
|
| + return [] |
| + |
| + |
| +class WorkQueue(object): |
| + """ |
|
bradn
2011/09/26 06:21:57
Pull out the first sentence as a one line descript
|
| + WorkQueue contains provides a queue of files to be processed. The scanner |
| + will attempt to push new items into the queue, which will be ignored if the |
| + item is already in the queue. If the item is new, it will be added to the |
| + work list, which is drained by the scanner. |
| + """ |
| + def __init__(self, resolver): |
| + self.added_set = set() |
| + self.todo_list = list() |
| + self.scanner = Scanner(self) |
| + self.resolver = resolver |
| + |
| + def PushIfNew(self, filename): |
| + """Add this dependency to the list of not already there.""" |
| + resolved_name = self.resolver.FindFile(filename) |
| + if not resolved_name: |
| + return |
| + if resolved_name in self.added_set: |
| + return |
| + self.todo_list.append(resolved_name) |
| + 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.
|
| + |
| + def PopIfAvail(self): |
| + """Fetch the next dependency to search.""" |
| + if not self.todo_list: |
| + return None |
| + return self.todo_list.pop() |
| + |
| + def Run(self): |
| + """Search through the available dependencies until the list becomes empty. |
| + The list must be primed with one or more source files to search.""" |
| + scan_name = self.PopIfAvail() |
| + while scan_name: |
| + includes = self.scanner.Scan(scan_name) |
| + for include_file in includes: |
| + self.PushIfNew(include_file) |
| + scan_name = self.PopIfAvail() |
| + |
| + sorted_list = sorted(self.added_set) |
| + for pathname in sorted_list: |
| + print pathname |
| + |
| + |
| +def Main(argv): |
| + resolver = Resolver() |
| + files = [] |
| + failed = False |
| + for arg in argv[1:]: |
| + 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.
|
| + if not resolver.AddDir(arg[2:]): |
| + print "Failed to add path: %s" % arg[2:] |
| + failed = True |
| + else: |
| + files.append(arg) |
| + |
| + if failed: return -1 |
| + workQ = WorkQueue(resolver) |
| + for filename in files: |
| + workQ.PushIfNew(filename) |
| + workQ.Run() |
| + return 0 |
| + |
|
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.
|
| +if __name__ == '__main__': |
| + sys.exit(Main(sys.argv)) |
| + |
|
bradn
2011/09/26 06:21:57
Drop trailing line.
noelallen1
2011/09/30 01:12:45
Done.
|
| Property changes on: build/scan_sources.py |
| ___________________________________________________________________ |
| Added: svn:executable |
| + * |