-
Notifications
You must be signed in to change notification settings - Fork 96
/
generate_prerelease_script.py
executable file
·301 lines (261 loc) · 11.7 KB
/
generate_prerelease_script.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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
#!/usr/bin/env python
# Copyright 2014-2016 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import print_function
import argparse
from copy import deepcopy
import os
import stat
import sys
from em import BANGPATH_OPT
from em import Hook
from ros_buildfarm.argument import add_argument_arch
from ros_buildfarm.argument import add_argument_build_name
from ros_buildfarm.argument import add_argument_config_url
from ros_buildfarm.argument import add_argument_os_code_name
from ros_buildfarm.argument import add_argument_os_name
from ros_buildfarm.argument import add_argument_output_dir
from ros_buildfarm.argument import add_argument_rosdistro_name
from ros_buildfarm.config import get_index as get_config_index
from ros_buildfarm.config import get_release_build_files
from ros_buildfarm.config import get_source_build_files
from ros_buildfarm.devel_job import configure_devel_job
from ros_buildfarm.prerelease import add_overlay_arguments
from ros_buildfarm.templates import expand_template
from rosdistro import get_distribution_cache
from rosdistro import get_index
from rosdistro.repository_specification import RepositorySpecification
def main(argv=sys.argv[1:]):
parser = argparse.ArgumentParser(
description="Generate a 'prerelease' script")
add_argument_config_url(parser)
add_argument_rosdistro_name(parser)
add_argument_build_name(parser, 'source')
add_argument_os_name(parser)
add_argument_os_code_name(parser)
add_argument_arch(parser)
add_argument_output_dir(parser, required=True)
group = parser.add_argument_group(
'Repositories in underlay workspace',
description='The repositories in the underlay workspace will be ' +
'built and installed as well as built and tested. ' +
'Dependencies will be provided by binary packages.')
group.add_argument(
'source_repos',
nargs='*',
default=[],
metavar='REPO_NAME',
help="A name of a 'repository' from the distribution file")
group.add_argument(
'--custom-branch',
nargs='*',
type=_repository_name_and_branch,
default=[],
metavar='REPO_NAME:BRANCH_OR_TAG_NAME',
help="A name of a 'repository' from the distribution file followed " +
'by a colon and a branch / tag name')
group.add_argument(
'--custom-repo',
nargs='*',
type=_repository_name_and_type_and_url_and_branch,
default=[],
metavar='REPO_NAME:REPO_TYPE:REPO_URL:BRANCH_OR_TAG_NAME',
help='The name, type, url and branch / tag name of a repository, '
'e.g. "common_tutorials:git:https://github.com/ros/common_tutorials:pullrequest-1"')
add_overlay_arguments(parser)
args = parser.parse_args(argv)
print('Fetching buildfarm configuration...')
config = get_config_index(args.config_url)
build_files = get_source_build_files(config, args.rosdistro_name)
build_file = build_files[args.source_build_name]
print('Fetching rosdistro cache...')
# Targets defined by source build file are subset of targets
# defined by release build files. To increase the number of supported
# pre-release targets, we combine all targets defined by all release
# build files and use that when configuring the devel job.
release_build_files = get_release_build_files(config, args.rosdistro_name)
release_targets_combined = {}
if release_build_files:
release_targets_combined[args.os_name] = {}
for build_name, rel_obj in release_build_files.items():
if args.os_name not in rel_obj.targets:
continue
for dist_name, targets in rel_obj.targets[args.os_name].items():
if dist_name not in release_targets_combined[args.os_name]:
release_targets_combined[args.os_name][dist_name] = {}
release_targets_combined[args.os_name][dist_name].update(targets)
index = get_index(config.rosdistro_index_url)
dist_cache = get_distribution_cache(index, args.rosdistro_name)
dist_file = dist_cache.distribution_file
# determine source repositories for underlay workspace
repositories = {}
for repo_name in args.source_repos:
if repo_name in repositories:
print("The repository '%s' appears multiple times" % repo_name,
file=sys.stderr)
return 1
try:
repositories[repo_name] = \
dist_file.repositories[repo_name].source_repository
except KeyError:
print(("The repository '%s' was not found in the distribution " +
"file") % repo_name, file=sys.stderr)
return 1
for repo_name, custom_version in args.custom_branch:
if repo_name in repositories:
print("The repository '%s' appears multiple times" % repo_name,
file=sys.stderr)
return 1
try:
source_repo = dist_file.repositories[repo_name].source_repository
except KeyError:
print(("The repository '%s' was not found in the distribution " +
"file") % repo_name, file=sys.stderr)
return 1
source_repo = deepcopy(source_repo)
source_repo.version = custom_version
repositories[repo_name] = source_repo
for repo_name, repo_type, repo_url, version in args.custom_repo:
if repo_name in repositories and repositories[repo_name]:
print("custom_repos option overriding '%s' to pull via '%s' "
"from '%s' with version '%s'. " %
(repo_name, repo_type, repo_url, version),
file=sys.stderr)
source_repo = RepositorySpecification(
repo_name, {
'type': repo_type,
'url': repo_url,
'version': version,
})
repositories[repo_name] = source_repo
scms = [(repositories[k], 'catkin_workspace/src/%s' % k)
for k in sorted(repositories.keys())]
# collect all template snippets of specific types
class IncludeHook(Hook):
def __init__(self):
Hook.__init__(self)
self.scripts = []
def beforeInclude(self, *args, **kwargs):
template_path = kwargs['file'].name
if template_path.endswith('/snippet/builder_shell.xml.em'):
self.scripts.append(kwargs['locals']['script'])
hook = IncludeHook()
from ros_buildfarm import templates
templates.template_hooks = [hook]
# use random source repo to pass to devel job template
source_repository = deepcopy(list(repositories.values())[0])
if not source_repository:
print(("The repository '%s' does not have a source entry in the distribution " +
'file. We cannot generate a prerelease without a source entry.') % repo_name,
file=sys.stderr)
return 1
source_repository.name = 'prerelease'
print('Evaluating job templates...')
configure_devel_job(
args.config_url, args.rosdistro_name, args.source_build_name,
None, args.os_name, args.os_code_name, args.arch,
config=config, build_file=build_file,
index=index, dist_file=dist_file, dist_cache=dist_cache,
jenkins=False, views=False,
source_repository=source_repository,
build_targets=release_targets_combined)
templates.template_hooks = None
# derive scripts for overlay workspace from underlay
overlay_scripts = []
for script in hook.scripts:
# skip cloning of ros_buildfarm repository
if 'git clone' in script and '.git ros_buildfarm' in script:
continue
# skip build-and-install step
if 'build and install' in script:
continue
# add prerelease overlay flag
run_devel_job = '/run_devel_job.py'
if run_devel_job in script:
script = script.replace(
run_devel_job, run_devel_job + ' --prerelease-overlay')
# replace mounted workspace volume with overlay and underlay
# used by:
# - create_devel_task_generator.py needs to find packages in both
# the underlay as well as the overlay workspace
# - catkin_make_isolated_and_test.py needs to source the environment of
# the underlay before building the overlay
mount_volume = '-v $WORKSPACE/catkin_workspace:/tmp/catkin_workspace'
if mount_volume in script:
script = script.replace(
mount_volume, mount_volume + ':ro ' + '-v $WORKSPACE/' +
'catkin_workspace_overlay:/tmp/catkin_workspace_overlay')
# relocate all docker files
docker_path = '$WORKSPACE/docker_'
if docker_path in script:
script = script.replace(
docker_path, docker_path + 'overlay_')
# rename all docker images
name_suffix = '_prerelease'
if name_suffix in script:
script = script.replace(
name_suffix, name_suffix + '_overlay')
overlay_scripts.append(script)
from ros_buildfarm import __file__ as ros_buildfarm_file
data = deepcopy(args.__dict__)
data.update({
'scms': scms,
'scripts': hook.scripts,
'overlay_scripts': overlay_scripts,
'ros_buildfarm_python_path': os.path.dirname(
os.path.dirname(os.path.abspath(ros_buildfarm_file))),
'python_executable': sys.executable,
'prerelease_script_path': os.path.dirname(os.path.abspath(__file__))})
if not os.path.exists(args.output_dir):
os.makedirs(args.output_dir)
# generate multiple scripts
for script_name in [
'prerelease',
'prerelease_build_overlay',
'prerelease_build_underlay',
'prerelease_clone_overlay',
'prerelease_clone_underlay']:
content = expand_template(
'prerelease/%s_script.sh.em' % script_name, data,
options={BANGPATH_OPT: False})
script_file = os.path.join(args.output_dir, script_name + '.sh')
with open(script_file, 'w') as h:
h.write(content)
os.chmod(script_file, os.stat(script_file).st_mode | stat.S_IEXEC)
print('')
print('Generated prerelease script - to execute it run:')
if os.path.abspath(args.output_dir) != os.path.abspath(os.curdir):
print(' cd %s' % args.output_dir)
print(' ./prerelease.sh')
def _repository_name_and_branch(arg):
if arg.count(':') != 1:
msg = "'%s' is not a repository name and a branch / tag name " + \
"separated by a colon" % arg
raise argparse.ArgumentTypeError(msg)
return arg.split(':')
def _repository_name_and_type_and_url_and_branch(arg):
if arg.count(':') < 4:
msg = ("'%s' is not a name, repository type, url and branch / tag " +
"name separated by colons") % arg
raise argparse.ArgumentTypeError(msg)
first_index = arg.index(':')
second_index = arg.index(':', first_index + 1)
last_index = arg.rindex(':')
return \
arg[:first_index], \
arg[first_index + 1:second_index], \
arg[second_index + 1: last_index], \
arg[last_index + 1:]
if __name__ == '__main__':
sys.exit(main())