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

Side by Side Diff: pylib/gyp/input.py

Issue 664253005: Simplify and optimize FindCycles (Closed) Base URL: https://chromium.googlesource.com/external/gyp.git@master
Patch Set: Fix cycle output ordering Created 6 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2012 Google Inc. All rights reserved. 1 # Copyright (c) 2012 Google Inc. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 from compiler.ast import Const 5 from compiler.ast import Const
6 from compiler.ast import Dict 6 from compiler.ast import Dict
7 from compiler.ast import Discard 7 from compiler.ast import Discard
8 from compiler.ast import List 8 from compiler.ast import List
9 from compiler.ast import Module 9 from compiler.ast import Module
10 from compiler.ast import Node 10 from compiler.ast import Node
(...skipping 1538 matching lines...) Expand 10 before | Expand all | Expand 10 after
1549 break 1549 break
1550 1550
1551 if is_in_degree_zero: 1551 if is_in_degree_zero:
1552 # All of the dependent's dependencies are already in flat_list. Add 1552 # All of the dependent's dependencies are already in flat_list. Add
1553 # it to in_degree_zeros where it will be processed in a future 1553 # it to in_degree_zeros where it will be processed in a future
1554 # iteration of the outer loop. 1554 # iteration of the outer loop.
1555 in_degree_zeros.add(node_dependent) 1555 in_degree_zeros.add(node_dependent)
1556 1556
1557 return list(flat_list) 1557 return list(flat_list)
1558 1558
1559 def FindCycles(self, path=None): 1559 def FindCycles(self):
1560 """ 1560 """
1561 Returns a list of cycles in the graph, where each cycle is its own list. 1561 Returns a list of cycles in the graph, where each cycle is its own list.
1562 """ 1562 """
1563 if path is None: 1563 results = []
1564 path = [self] 1564 visited = set()
1565 1565
1566 results = [] 1566 def Visit(node, path):
1567 for node in self.dependents: 1567 for child in node.dependents:
1568 if node in path: 1568 if child in path:
1569 cycle = [node] 1569 results.append(tuple([child] + path[:path.index(child) + 1]))
scottmg 2014/10/21 02:53:07 Does this have to make a tuple() out of the list?
cjhopman 2014/10/21 18:22:23 Done. This needed the tuple to convert results to
1570 for part in path: 1570 elif not child in visited:
1571 cycle.append(part) 1571 visited.add(child)
1572 if part == node: 1572 Visit(child, [child] + path)
scottmg 2014/10/21 03:22:42 I'm probably reading this wrong, but... given A->B
cjhopman 2014/10/21 18:22:23 Ehh, I had the same problem. So the key is that th
1573 break 1573
1574 results.append(tuple(cycle)) 1574 visited.add(self)
1575 else: 1575 Visit(self, [self])
1576 results.extend(node.FindCycles([node] + path))
1577 1576
1578 return list(set(results)) 1577 return list(set(results))
1579 1578
1580 def DirectDependencies(self, dependencies=None): 1579 def DirectDependencies(self, dependencies=None):
1581 """Returns a list of just direct dependencies.""" 1580 """Returns a list of just direct dependencies."""
1582 if dependencies == None: 1581 if dependencies == None:
1583 dependencies = [] 1582 dependencies = []
1584 1583
1585 for dependency in self.dependencies: 1584 for dependency in self.dependencies:
1586 # Check for None, corresponding to the root node. 1585 # Check for None, corresponding to the root node.
(...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after
1785 dependency_node = dependency_nodes.get(dependency) 1784 dependency_node = dependency_nodes.get(dependency)
1786 if not dependency_node: 1785 if not dependency_node:
1787 raise GypError("Dependency '%s' not found while " 1786 raise GypError("Dependency '%s' not found while "
1788 "trying to load target %s" % (dependency, target)) 1787 "trying to load target %s" % (dependency, target))
1789 target_node.dependencies.append(dependency_node) 1788 target_node.dependencies.append(dependency_node)
1790 dependency_node.dependents.append(target_node) 1789 dependency_node.dependents.append(target_node)
1791 1790
1792 flat_list = root_node.FlattenToList() 1791 flat_list = root_node.FlattenToList()
1793 1792
1794 # If there's anything left unvisited, there must be a circular dependency 1793 # If there's anything left unvisited, there must be a circular dependency
1795 # (cycle). If you need to figure out what's wrong, look for elements of 1794 # (cycle).
1796 # targets that are not in flat_list.
1797 if len(flat_list) != len(targets): 1795 if len(flat_list) != len(targets):
1798 raise DependencyGraphNode.CircularException( 1796 cycles = []
1799 'Some targets not reachable, cycle in dependency graph detected: ' + 1797 for cycle in root_node.FindCycles():
1800 ' '.join(set(flat_list) ^ set(targets))) 1798 paths = []
1799 for node in cycle:
1800 paths.append(node.ref)
1801 cycles.append('Cycle: %s' % ' -> '.join(paths))
1802 raise DependencyGraphNode.CircularException, \
scottmg 2014/10/21 02:50:23 Why not CircularException(...)?
cjhopman 2014/10/21 18:22:23 Done. I just did what was used for raising this b
1803 'Cycles in dependency graph detected:\n' + '\n'.join(cycles)
scottmg 2014/10/21 02:50:23 Could you add a test for this improved output form
cjhopman 2014/10/21 18:22:23 Done.
1801 1804
1802 return [dependency_nodes, flat_list] 1805 return [dependency_nodes, flat_list]
1803 1806
1804 1807
1805 def VerifyNoGYPFileCircularDependencies(targets): 1808 def VerifyNoGYPFileCircularDependencies(targets):
1806 # Create a DependencyGraphNode for each gyp file containing a target. Put 1809 # Create a DependencyGraphNode for each gyp file containing a target. Put
1807 # it into a dict for easy access. 1810 # it into a dict for easy access.
1808 dependency_nodes = {} 1811 dependency_nodes = {}
1809 for target in targets.iterkeys(): 1812 for target in targets.iterkeys():
1810 build_file = gyp.common.BuildFile(target) 1813 build_file = gyp.common.BuildFile(target)
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
1843 root_node.dependents.append(build_file_node) 1846 root_node.dependents.append(build_file_node)
1844 1847
1845 flat_list = root_node.FlattenToList() 1848 flat_list = root_node.FlattenToList()
1846 1849
1847 # If there's anything left unvisited, there must be a circular dependency 1850 # If there's anything left unvisited, there must be a circular dependency
1848 # (cycle). 1851 # (cycle).
1849 if len(flat_list) != len(dependency_nodes): 1852 if len(flat_list) != len(dependency_nodes):
1850 bad_files = [] 1853 bad_files = []
1851 for file in dependency_nodes.iterkeys(): 1854 for file in dependency_nodes.iterkeys():
1852 if not file in flat_list: 1855 if not file in flat_list:
1853 bad_files.append(file) 1856 bad_files.append(file)
cjhopman 2014/10/21 18:22:23 bad_files was unused
1854 common_path_prefix = os.path.commonprefix(dependency_nodes) 1857 common_path_prefix = os.path.commonprefix(dependency_nodes)
cjhopman 2014/10/21 18:22:23 This doesn't really seem helpful since the prefix
1855 cycles = [] 1858 cycles = []
1856 for cycle in root_node.FindCycles(): 1859 for cycle in root_node.FindCycles():
1857 simplified_paths = [] 1860 simplified_paths = []
1858 for node in cycle: 1861 for node in cycle:
1859 assert(node.ref.startswith(common_path_prefix)) 1862 assert(node.ref.startswith(common_path_prefix))
1860 simplified_paths.append(node.ref[len(common_path_prefix):]) 1863 simplified_paths.append(node.ref[len(common_path_prefix):])
1861 cycles.append('Cycle: %s' % ' -> '.join(simplified_paths)) 1864 cycles.append('Cycle: %s' % ' -> '.join(simplified_paths))
1862 raise DependencyGraphNode.CircularException, \ 1865 raise DependencyGraphNode.CircularException, \
1863 'Cycles in .gyp file dependency graph detected:\n' + '\n'.join(cycles) 1866 'Cycles in .gyp file dependency graph detected:\n' + '\n'.join(cycles)
1864 1867
(...skipping 999 matching lines...) Expand 10 before | Expand all | Expand 10 after
2864 ValidateRunAsInTarget(target, target_dict, build_file) 2867 ValidateRunAsInTarget(target, target_dict, build_file)
2865 ValidateActionsInTarget(target, target_dict, build_file) 2868 ValidateActionsInTarget(target, target_dict, build_file)
2866 2869
2867 # Generators might not expect ints. Turn them into strs. 2870 # Generators might not expect ints. Turn them into strs.
2868 TurnIntIntoStrInDict(data) 2871 TurnIntIntoStrInDict(data)
2869 2872
2870 # TODO(mark): Return |data| for now because the generator needs a list of 2873 # TODO(mark): Return |data| for now because the generator needs a list of
2871 # build files that came in. In the future, maybe it should just accept 2874 # build files that came in. In the future, maybe it should just accept
2872 # a list, and not the whole data dict. 2875 # a list, and not the whole data dict.
2873 return [flat_list, targets, data] 2876 return [flat_list, targets, data]
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698