forked from ros/rosdistro
-
Notifications
You must be signed in to change notification settings - Fork 0
/
check_blocking_repos.py
executable file
·205 lines (166 loc) · 7.53 KB
/
check_blocking_repos.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#!/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)