aboutsummaryrefslogtreecommitdiff
path: root/catapult/common/py_vulcanize/py_vulcanize/project.py
diff options
context:
space:
mode:
Diffstat (limited to 'catapult/common/py_vulcanize/py_vulcanize/project.py')
-rw-r--r--catapult/common/py_vulcanize/py_vulcanize/project.py231
1 files changed, 231 insertions, 0 deletions
diff --git a/catapult/common/py_vulcanize/py_vulcanize/project.py b/catapult/common/py_vulcanize/py_vulcanize/project.py
new file mode 100644
index 00000000..d8f9ca47
--- /dev/null
+++ b/catapult/common/py_vulcanize/py_vulcanize/project.py
@@ -0,0 +1,231 @@
+# Copyright 2013 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 collections
+import os
+import cStringIO
+
+from py_vulcanize import resource_loader
+
+
+def _FindAllFilesRecursive(source_paths):
+ all_filenames = set()
+ for source_path in source_paths:
+ for dirpath, _, filenames in os.walk(source_path):
+ for f in filenames:
+ if f.startswith('.'):
+ continue
+ x = os.path.abspath(os.path.join(dirpath, f))
+ all_filenames.add(x)
+ return all_filenames
+
+
+class AbsFilenameList(object):
+
+ def __init__(self, willDirtyCallback):
+ self._willDirtyCallback = willDirtyCallback
+ self._filenames = []
+ self._filenames_set = set()
+
+ def _WillBecomeDirty(self):
+ if self._willDirtyCallback:
+ self._willDirtyCallback()
+
+ def append(self, filename):
+ assert os.path.isabs(filename)
+ self._WillBecomeDirty()
+ self._filenames.append(filename)
+ self._filenames_set.add(filename)
+
+ def extend(self, iterable):
+ self._WillBecomeDirty()
+ for filename in iterable:
+ assert os.path.isabs(filename)
+ self._filenames.append(filename)
+ self._filenames_set.add(filename)
+
+ def appendRel(self, basedir, filename):
+ assert os.path.isabs(basedir)
+ self._WillBecomeDirty()
+ n = os.path.abspath(os.path.join(basedir, filename))
+ self._filenames.append(n)
+ self._filenames_set.add(n)
+
+ def extendRel(self, basedir, iterable):
+ self._WillBecomeDirty()
+ assert os.path.isabs(basedir)
+ for filename in iterable:
+ n = os.path.abspath(os.path.join(basedir, filename))
+ self._filenames.append(n)
+ self._filenames_set.add(n)
+
+ def __contains__(self, x):
+ return x in self._filenames_set
+
+ def __len__(self):
+ return self._filenames.__len__()
+
+ def __iter__(self):
+ return iter(self._filenames)
+
+ def __repr__(self):
+ return repr(self._filenames)
+
+ def __str__(self):
+ return str(self._filenames)
+
+
+class Project(object):
+
+ py_vulcanize_path = os.path.abspath(os.path.join(
+ os.path.dirname(__file__), '..'))
+
+ def __init__(self, source_paths=None):
+ """
+ source_paths: A list of top-level directories in which modules and raw
+ scripts can be found. Module paths are relative to these directories.
+ """
+ self._loader = None
+ self._frozen = False
+ self.source_paths = AbsFilenameList(self._WillPartOfPathChange)
+
+ if source_paths is not None:
+ self.source_paths.extend(source_paths)
+
+ def Freeze(self):
+ self._frozen = True
+
+ def _WillPartOfPathChange(self):
+ if self._frozen:
+ raise Exception('The project is frozen. You cannot edit it now')
+ self._loader = None
+
+ @staticmethod
+ def FromDict(d):
+ return Project(d['source_paths'])
+
+ def AsDict(self):
+ return {
+ 'source_paths': list(self.source_paths)
+ }
+
+ def __repr__(self):
+ return "Project(%s)" % repr(self.source_paths)
+
+ def AddSourcePath(self, path):
+ self.source_paths.append(path)
+
+ @property
+ def loader(self):
+ if self._loader is None:
+ self._loader = resource_loader.ResourceLoader(self)
+ return self._loader
+
+ def ResetLoader(self):
+ self._loader = None
+
+ def _Load(self, filenames):
+ return [self.loader.LoadModule(module_filename=filename) for
+ filename in filenames]
+
+ def LoadModule(self, module_name=None, module_filename=None):
+ return self.loader.LoadModule(module_name=module_name,
+ module_filename=module_filename)
+
+ def CalcLoadSequenceForModuleNames(self, module_names,
+ excluded_scripts=None):
+ modules = [self.loader.LoadModule(module_name=name,
+ excluded_scripts=excluded_scripts) for
+ name in module_names]
+ return self.CalcLoadSequenceForModules(modules)
+
+ def CalcLoadSequenceForModules(self, modules):
+ already_loaded_set = set()
+ load_sequence = []
+ for m in modules:
+ m.ComputeLoadSequenceRecursive(load_sequence, already_loaded_set)
+ return load_sequence
+
+ def GetDepsGraphFromModuleNames(self, module_names):
+ modules = [self.loader.LoadModule(module_name=name) for
+ name in module_names]
+ return self.GetDepsGraphFromModules(modules)
+
+ def GetDepsGraphFromModules(self, modules):
+ load_sequence = self.CalcLoadSequenceForModules(modules)
+ g = _Graph()
+ for m in load_sequence:
+ g.AddModule(m)
+
+ for dep in m.dependent_modules:
+ g.AddEdge(m, dep.id)
+
+ # FIXME: _GetGraph is not defined. Maybe `return g` is intended?
+ return _GetGraph(load_sequence)
+
+ def GetDominatorGraphForModulesNamed(self, module_names, load_sequence):
+ modules = [self.loader.LoadModule(module_name=name)
+ for name in module_names]
+ return self.GetDominatorGraphForModules(modules, load_sequence)
+
+ def GetDominatorGraphForModules(self, start_modules, load_sequence):
+ modules_by_id = {}
+ for m in load_sequence:
+ modules_by_id[m.id] = m
+
+ module_referrers = collections.defaultdict(list)
+ for m in load_sequence:
+ for dep in m.dependent_modules:
+ module_referrers[dep].append(m)
+
+ # Now start at the top module and reverse.
+ visited = set()
+ g = _Graph()
+
+ pending = collections.deque()
+ pending.extend(start_modules)
+ while len(pending):
+ cur = pending.pop()
+
+ g.AddModule(cur)
+ visited.add(cur)
+
+ for out_dep in module_referrers[cur]:
+ if out_dep in visited:
+ continue
+ g.AddEdge(out_dep, cur)
+ visited.add(out_dep)
+ pending.append(out_dep)
+
+ # Visited -> Dot
+ return g.GetDot()
+
+
+class _Graph(object):
+
+ def __init__(self):
+ self.nodes = []
+ self.edges = []
+
+ def AddModule(self, m):
+ f = cStringIO.StringIO()
+ m.AppendJSContentsToFile(f, False, None)
+
+ attrs = {
+ 'label': '%s (%i)' % (m.name, f.tell())
+ }
+
+ f.close()
+
+ attr_items = ['%s="%s"' % (x, y) for x, y in attrs.iteritems()]
+ node = 'M%i [%s];' % (m.id, ','.join(attr_items))
+ self.nodes.append(node)
+
+ def AddEdge(self, mFrom, mTo):
+ edge = 'M%i -> M%i;' % (mFrom.id, mTo.id)
+ self.edges.append(edge)
+
+ def GetDot(self):
+ return 'digraph deps {\n\n%s\n\n%s\n}\n' % (
+ '\n'.join(self.nodes), '\n'.join(self.edges))