def test_sort_decorated_packages(self): projects = {} sprojects = _sort_decorated_packages(projects) self.assertEqual([], sprojects) mock1 = Mock() mock1.depends_for_topological_order = set() mock1.message_generator = True mock2 = Mock() mock2.depends_for_topological_order = set() mock2.message_generator = False mock3 = Mock() mock3.depends_for_topological_order = set() mock3.message_generator = False projects = {'baz': mock3, 'bar': mock2, 'foo': mock1} sprojects = _sort_decorated_packages(projects) # mock1 has message generator, come first # mock2 before mock3 because of alphabetical ordering self.assertEqual([[mock1.path, mock1.package], [mock2.path, mock2.package], [mock3.path, mock3.package]], sprojects)
def test_sort_decorated_packages_cycles(self): def create_mock(path, depend): m = Mock() m.path = path m.depends_for_topological_order = set([depend]) m.message_generator = False return m # creating a cycle for cycle detection mock1 = create_mock('mock1', 'mock2') mock2 = create_mock('mock2', 'mock3') mock3 = create_mock('mock3', 'mock4') mock4 = create_mock('mock4', 'mock2') projects = {'mock3': mock3, 'mock2': mock2, 'mock1': mock1, 'mock4': mock4} sprojects = _sort_decorated_packages(projects) self.assertEqual([[None, 'mock2, mock3, mock4']], sprojects) # remove cycle mock4.depends_for_topological_order = set() sprojects = _sort_decorated_packages(projects) # mock4 first since it has no dependencies # than mock3 since it only had mock4 as a dependency # than mock2 since it only had mock3 as a dependency # than mock1 since it only had mock2 as a dependency self.assertEqual(['mock4', 'mock3', 'mock2', 'mock1'], [path for path, _ in sprojects])
def test_sort_decorated_packages(self): projects = {} sprojects = _sort_decorated_packages(projects) self.assertEqual([], sprojects) mock1 = Mock() mock1.depends_for_topological_order = set() mock1.message_generator = True mock2 = Mock() mock2.depends_for_topological_order = set() mock2.message_generator = False mock3 = Mock() mock3.depends_for_topological_order = set() mock3.message_generator = False projects = {'baz': mock3, 'bar': mock2, 'foo': mock1} sprojects = _sort_decorated_packages(projects) # mock1 has message generator, come first # mock2 before mock3 because of alphabetical ordering self.assertEqual( [[mock1.path, mock1.package], [mock2.path, mock2.package], [mock3.path, mock3.package]], sprojects)
def test_sort_decorated_packages_cycles(self): def create_mock(path, depend): m = Mock() m.path = path m.depends_for_topological_order = set([depend]) m.message_generator = False return m # creating a cycle for cycle detection mock1 = create_mock('mock1', 'mock2') mock2 = create_mock('mock2', 'mock3') mock3 = create_mock('mock3', 'mock4') mock4 = create_mock('mock4', 'mock2') projects = {'mock3': mock3, 'mock2': mock2, 'mock1': mock1, 'mock4': mock4} sprojects = _sort_decorated_packages(projects) self.assertEqual([[None, 'mock2, mock3, mock4']], sprojects) # remove cycle mock4.depends_for_topological_order = set() sprojects = _sort_decorated_packages(projects) # mock4 first since it has no dependencies # than mock3 since it only had mock4 as a dependency # than mock2 since it only had mock3 as a dependency # than mock1 since it only had mock2 as a dependency self.assertEqual(['mock4', 'mock3', 'mock2', 'mock1'], [path for path, _ in sprojects])
def topological_order_packages(packages): """ Order packages topologically. First returning packages which have message generators and then the rest based on all direct depends and indirect recursive run_depends. :param packages: A dict mapping relative paths to ``Package`` objects ``dict`` :returns: A list of tuples containing the relative path and a ``Package`` object, ``list`` """ from catkin_pkg.topological_order import _PackageDecorator from catkin_pkg.topological_order import _sort_decorated_packages decorators_by_name = {} for path, package in packages.items(): decorators_by_name[package.name] = _PackageDecorator(package, path) # calculate transitive dependencies for decorator in decorators_by_name.values(): decorator.depends_for_topological_order = set([]) all_depends = \ decorator.package.build_depends + decorator.package.buildtool_depends + \ decorator.package.run_depends + decorator.package.test_depends # skip external dependencies, meaning names that are not known packages for name in [d.name for d in all_depends if d.name in decorators_by_name.keys()]: decorators_by_name[name]._add_recursive_run_depends( decorators_by_name, decorator.depends_for_topological_order) ordered_pkg_tuples = _sort_decorated_packages(decorators_by_name) for pkg_path, pkg in ordered_pkg_tuples: if pkg_path is None: raise RuntimeError('Circular dependency in: %s' % pkg) return ordered_pkg_tuples
def test_sort_decorated_packages_cycles(self): mock1 = Mock() mock2 = Mock() mock3 = Mock() mock4 = Mock() # creating a cycle for cycle detection mock1.depends_for_topological_order = set(['mock3']) mock2.depends_for_topological_order = set(['mock1']) mock3.depends_for_topological_order = set(['mock2']) mock4.depends_for_topological_order = set(['mock3']) projects = {'mock3': mock3, 'mock2': mock2, 'mock1': mock1, 'mock4': mock4} sprojects = _sort_decorated_packages(projects) self.assertEqual([[None, 'mock1, mock2, mock3']], sprojects) # remove cycle mock1.depends_for_topological_order = set() sprojects = _sort_decorated_packages(projects) # mock1 has message generator, come first # mock2 before mock3 because of alphabetical ordering self.assertEqual([[mock1.path, mock1.package], [mock2.path, mock2.package], [mock3.path, mock3.package], [mock4.path, mock4.package]], sprojects)
def test_sort_decorated_packages(self): projects = {} sprojects = _sort_decorated_packages(projects) self.assertEqual([], sprojects) def create_mock(path): m = Mock() m.path = path m.depends_for_topological_order = set() m.message_generator = False return m mock1 = create_mock('mock1') mock2 = create_mock('mock2') mock3 = create_mock('mock3') mock3.message_generator = True projects = {'mock3': mock3, 'mock2': mock2, 'mock1': mock1} sprojects = _sort_decorated_packages(projects) # mock3 first since it is a message generator # mock1 before mock2 due to alphabetic order self.assertEqual(['mock3', 'mock1', 'mock2'], [path for path, _ in sprojects])
def test_sort_decorated_packages(self): projects = {} sprojects = _sort_decorated_packages(projects) self.assertEqual([], sprojects) def create_mock(path): m = Mock() m.path = path m.depends_for_topological_order = set() m.message_generator = False return m mock1 = create_mock('mock1') mock2 = create_mock('mock2') mock3 = create_mock('mock3') mock3.message_generator = True projects = {'mock3': mock3, 'mock2': mock2, 'mock1': mock1} sprojects = _sort_decorated_packages(projects) # mock3 first since it is a message generator # mock1 before mock2 due to alphabetic order self.assertEqual(['mock3', 'mock1', 'mock2'], [path for path, _ in sprojects])
def topological_order_packages(packages): """ Order packages topologically. First returning packages which have message generators and then the rest based on all direct depends and indirect recursive run_depends. :param packages: A dict mapping relative paths to ``Package`` objects ``dict`` :returns: A list of tuples containing the relative path and a ``Package`` object, ``list`` """ from catkin_pkg.topological_order import _PackageDecorator from catkin_pkg.topological_order import _sort_decorated_packages decorators_by_name = {} for path, package in packages.items(): decorators_by_name[package.name] = _PackageDecorator(package, path) # calculate transitive dependencies for decorator in decorators_by_name.values(): decorator.depends_for_topological_order = set([]) all_depends = \ decorator.package.build_depends + decorator.package.buildtool_depends + \ decorator.package.run_depends + decorator.package.test_depends # skip external dependencies, meaning names that are not known packages unique_depend_names = set([ d.name for d in all_depends if d.name in decorators_by_name.keys() and d.evaluated_condition is not False ]) unique_depend_names.update([ m for d in decorator.package.group_depends for m in d.members if d.evaluated_condition is not False ]) for name in unique_depend_names: if name in decorator.depends_for_topological_order: # avoid function call to improve performance # check within the loop since the set changes every cycle continue decorators_by_name[name]._add_recursive_run_depends( decorators_by_name, decorator.depends_for_topological_order) ordered_pkg_tuples = _sort_decorated_packages(decorators_by_name) for pkg_path, pkg in ordered_pkg_tuples: if pkg_path is None: raise RuntimeError('Circular dependency in: %s' % pkg) return ordered_pkg_tuples
def test_sort_decorated_packages_favoring_message_generators(self): def create_mock(path): m = Mock() m.path = path m.depends_for_topological_order = set() m.message_generator = False return m mock1 = create_mock('mock1') mock2 = create_mock('mock2') mock3 = create_mock('mock3') mock3.depends_for_topological_order = set(['mock2']) mock3.message_generator = True projects = {'mock3': mock3, 'mock2': mock2, 'mock1': mock1} sprojects = _sort_decorated_packages(projects) # mock2 first since it is the dependency of a message generator # mock3 since it is a message generator # mock1 last, although having no dependencies and being first in alphabetic order self.assertEqual(['mock2', 'mock3', 'mock1'], [path for path, _ in sprojects])
def test_sort_decorated_packages_favoring_message_generators(self): def create_mock(path): m = Mock() m.path = path m.depends_for_topological_order = set() m.message_generator = False return m mock1 = create_mock('mock1') mock2 = create_mock('mock2') mock3 = create_mock('mock3') mock3.depends_for_topological_order = set(['mock2']) mock3.message_generator = True projects = {'mock3': mock3, 'mock2': mock2, 'mock1': mock1} sprojects = _sort_decorated_packages(projects) # mock2 first since it is the dependency of a message generator # mock3 since it is a message generator # mock1 last, although having no dependencies and being first in alphabetic order self.assertEqual(['mock2', 'mock3', 'mock1'], [path for path, _ in sprojects])
def topological_order_packages(packages): """ Order packages topologically. First returning packages which have message generators and then the rest based on all direct depends and indirect recursive run_depends. :param packages: A dict mapping relative paths to ``Package`` objects ``dict`` :returns: A list of tuples containing the relative path and a ``Package`` object, ``list`` """ from catkin_pkg.topological_order import _PackageDecorator from catkin_pkg.topological_order import _sort_decorated_packages decorators_by_name = {} for path, package in packages.items(): decorators_by_name[package.name] = _PackageDecorator(package, path) # calculate transitive dependencies for decorator in decorators_by_name.values(): decorator.depends_for_topological_order = set([]) all_depends = \ decorator.package.build_depends + decorator.package.buildtool_depends + \ decorator.package.run_depends + decorator.package.test_depends # skip external dependencies, meaning names that are not known packages for name in [ d.name for d in all_depends if d.name in decorators_by_name.keys() ]: decorators_by_name[name]._add_recursive_run_depends( decorators_by_name, decorator.depends_for_topological_order) ordered_pkg_tuples = _sort_decorated_packages(decorators_by_name) for pkg_path, pkg in ordered_pkg_tuples: if pkg_path is None: raise RuntimeError('Circular dependency in: %s' % pkg) return ordered_pkg_tuples