123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- #!/usr/bin/env python
- from __future__ import print_function
- import argparse
- import sys
- import rosdistro
- from rosdistro.dependency_walker import DependencyWalker
- def is_released(repository, dist_file):
- return repository in dist_file.repositories and \
- dist_file.repositories[repository].release_repository is not None and \
- dist_file.repositories[repository].release_repository.version is not None
- parser = argparse.ArgumentParser(
- description='Get unreleased repos and their dependencies.',
- formatter_class=argparse.ArgumentDefaultsHelpFormatter)
- parser.add_argument(
- '--rosdistro', metavar='ROS_DISTRO',
- help='The ROS distribution to check packages for')
- # If not specified, check for all repositories in the previous distribution
- parser.add_argument(
- '--repositories',
- metavar='REPOSITORY_NAME', nargs='*',
- help='Unreleased repositories to check dependencies for')
- parser.add_argument(
- '--depth',
- metavar='depth', type=int,
- help='Maxmium depth to crawl the dependency tree')
- parser.add_argument(
- '--comparison-rosdistro',
- metavar='ROS_DISTRO',
- dest='comparison',
- help='The rosdistro with which to compare')
- args = parser.parse_args()
- distro_key = args.rosdistro
- repo_names_argument = args.repositories
- prev_distro_key = None
- index = rosdistro.get_index(rosdistro.get_index_url())
- valid_distro_keys = index.distributions.keys()
- valid_distro_keys.sort()
- if distro_key is None:
- distro_key = valid_distro_keys[-1]
- # Find the previous distribution to the current one
- try:
- i = valid_distro_keys.index(distro_key)
- except ValueError:
- print('Distribution key (%s) not found in list of valid distributions.' % distro_key, file=sys.stderr)
- print('Valid rosdistros are %s.' % valid_distro_keys, file=sys.stderr)
- exit(-1)
- if i == 0 and not args.comparison:
- print('No previous distribution found.', file=sys.stderr)
- exit(-1)
- if args.comparison:
- valid_comparison_keys = valid_distro_keys[:]
- valid_comparison_keys.remove(distro_key)
- if args.comparison not in valid_comparison_keys:
- print('Invalid rosdistro [%s] selected for comparison to [%s].' % (args.comparison, distro_key),
- file=sys.stderr)
- print('Valid rosdistros are %s.' % valid_comparison_keys, file=sys.stderr)
- exit(-1)
- prev_distro_key = args.comparison
- else:
- prev_distro_key = valid_distro_keys[i - 1]
- cache = rosdistro.get_distribution_cache(index, distro_key)
- distro_file = cache.distribution_file
- prev_cache = rosdistro.get_distribution_cache(index, prev_distro_key)
- prev_distribution = rosdistro.get_cached_distribution(
- index, prev_distro_key, cache=prev_cache)
- prev_distro_file = prev_cache.distribution_file
- dependency_walker = DependencyWalker(prev_distribution)
- if repo_names_argument is None:
- # Check missing dependencies for packages that were in the previous
- # distribution that have not yet been released in the current distribution
- # Filter repos without a version or a release repository
- repo_names_argument = prev_distro_file.repositories.keys()
- prev_repo_names = set(
- repo for repo in repo_names_argument if is_released(repo, prev_distro_file))
- keys = distro_file.repositories.keys()
- current_repo_names = set(
- repo for repo in keys if is_released(repo, distro_file))
- # Print the repositories that will be eliminated from the input
- eliminated_repositories = prev_repo_names.intersection(
- current_repo_names)
- if len(eliminated_repositories) > 0:
- print('Ignoring inputs which have already been released:')
- print('\n'.join(
- sorted('\t{0}'.format(repo) for repo in eliminated_repositories)))
- repo_names_set = prev_repo_names.difference(
- current_repo_names)
- invalid_names = set(repo_names_argument).difference(prev_repo_names)
- if len(repo_names_set) == 0:
- print('All inputs are invalid or were already released in {0}.'.format(
- distro_key))
- if invalid_names:
- print('Could no resolve: %s in %s' % (list(invalid_names), prev_distro_key), file=sys.stderr)
- exit(1)
- print('Exiting without checking any dependencies.')
- exit(0)
- repo_names = list(repo_names_set)
- # Get a list of currently released packages
- current_package_names = set(
- pkg for repo in current_repo_names
- for pkg in distro_file.repositories[repo].release_repository.package_names)
- # Construct a dictionary where keys are repository names and values are a list
- # of the missing dependencies for that repository
- blocked_repos = {}
- unblocked_repos = set()
- total_blocking_repos = set()
- for repository_name in repo_names:
- repo = prev_distro_file.repositories[repository_name]
- release_repo = repo.release_repository
- package_dependencies = set()
- packages = release_repo.package_names
- # Accumulate all dependencies for those packages
- for package in packages:
- recursive_dependencies = dependency_walker.get_recursive_depends(
- package, ['build', 'run', 'buildtool'], ros_packages_only=True,
- limit_depth=args.depth)
- package_dependencies = package_dependencies.union(
- recursive_dependencies)
- # For all package dependencies, check if they are released yet
- unreleased_pkgs = package_dependencies.difference(
- current_package_names)
- # remove the packages which this repo provides.
- unreleased_pkgs = unreleased_pkgs.difference(packages)
- # Now get the repositories for these packages.
- blocking_repos = set(prev_distro_file.release_packages[pkg].repository_name
- for pkg in unreleased_pkgs)
- if len(blocking_repos) == 0:
- unblocked_repos.add(repository_name)
- else:
- # Get the repository for the unreleased packages
- blocked_repos[repository_name] = blocking_repos
- total_blocking_repos |= blocking_repos
- unblocked_blocking_repos = total_blocking_repos.intersection(unblocked_repos)
- unblocked_leaf_repos = unblocked_repos.difference(unblocked_blocking_repos)
- # Double-check repositories that we think are leaf repos
- for repo in unblocked_leaf_repos:
- # Check only one level of depends_on
- depends_on = dependency_walker.get_depends_on(package, 'build') | \
- dependency_walker.get_depends_on(package, 'run') | \
- dependency_walker.get_depends_on(package, 'buildtool')
- if len(depends_on) != 0:
- # There are packages that depend on this "leaf", but we didn't find
- # them initially because they weren't related to our inputs
- unblocked_blocking_repos.add(repo)
- unblocked_leaf_repos = unblocked_leaf_repos.difference(
- unblocked_blocking_repos)
- if len(blocked_repos.keys()) > 0:
- print('The following repos cannot be released because of unreleased '
- 'dependencies:')
- for blocked_repo_name in sorted(blocked_repos.keys()):
- unreleased_repos = blocked_repos[blocked_repo_name]
- print('\t{0}:'.format(blocked_repo_name))
- print('\n'.join(
- sorted('\t\t{0}'.format(repo) for repo in unreleased_repos)))
- if len(unblocked_leaf_repos) > 0:
- print('The following repos can be released, but do not block other repos:')
- print('\n'.join(
- sorted('\t{0}'.format(repo) for repo in unblocked_leaf_repos)))
- if len(unblocked_blocking_repos) > 0:
- print('The following repos can be released, and are blocking other repos:')
- print('\n'.join(
- sorted('\t{0}'.format(repo) for repo in unblocked_blocking_repos)))
- if len(invalid_names):
- print('Could no resolve the following arguments in %s: ' % prev_distro_key, file=sys.stderr)
- print('\n'.join(
- sorted('\t{0}'.format(repo) for repo in invalid_names)), file=sys.stderr)
- exit(1)
|