blob: c1c06a1e6eb3594fe409b4e9033051ca65e2da1f [file] [log] [blame]
David James8c846492011-01-25 17:07:29 -08001#!/usr/bin/python
2# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import datetime
7import multiprocessing
8import optparse
9import os
10import re
11import sys
12import tempfile
13import time
14
Chris Sosa471532a2011-02-01 15:10:06 -080015if __name__ == '__main__':
16 import constants
17 sys.path.append(constants.SOURCE_ROOT)
18
David James8c846492011-01-25 17:07:29 -080019from chromite.lib import cros_build_lib
20from chromite.lib.binpkg import (GrabLocalPackageIndex, GrabRemotePackageIndex,
21 PackageIndex)
22"""
23This script is used to upload host prebuilts as well as board BINHOSTS.
24
25If the URL starts with 'gs://', we upload using gsutil to Google Storage.
26Otherwise, rsync is used.
27
28After a build is successfully uploaded a file is updated with the proper
29BINHOST version as well as the target board. This file is defined in GIT_FILE
30
31
32To read more about prebuilts/binhost binary packages please refer to:
33http://sites/chromeos/for-team-members/engineering/releng/prebuilt-binaries-for-streamlining-the-build-process
34
35
36Example of uploading prebuilt amd64 host files to Google Storage:
37./prebuilt.py -p /b/cbuild/build -s -u gs://chromeos-prebuilt
38
39Example of uploading x86-dogfood binhosts to Google Storage:
40./prebuilt.py -b x86-dogfood -p /b/cbuild/build/ -u gs://chromeos-prebuilt -g
41
42Example of uploading prebuilt amd64 host files using rsync:
43./prebuilt.py -p /b/cbuild/build -s -u codf30.jail:/tmp
44"""
45
46# as per http://crosbug.com/5855 always filter the below packages
47_FILTER_PACKAGES = set()
48_RETRIES = 3
49_GSUTIL_BIN = '/b/build/third_party/gsutil/gsutil'
50_HOST_PACKAGES_PATH = 'chroot/var/lib/portage/pkgs'
David James05bcb2b2011-02-09 09:25:47 -080051_CATEGORIES_PATH = 'chroot/etc/portage/categories'
David James8c846492011-01-25 17:07:29 -080052_HOST_TARGET = 'amd64'
53_BOARD_PATH = 'chroot/build/%(board)s'
54_BOTO_CONFIG = '/home/chrome-bot/external-boto'
55# board/board-target/version/packages/'
56_REL_BOARD_PATH = 'board/%(board)s/%(version)s/packages'
57# host/host-target/version/packages/'
58_REL_HOST_PATH = 'host/%(target)s/%(version)s/packages'
59# Private overlays to look at for builds to filter
60# relative to build path
61_PRIVATE_OVERLAY_DIR = 'src/private-overlays'
62_BINHOST_BASE_URL = 'http://commondatastorage.googleapis.com/chromeos-prebuilt'
63_PREBUILT_BASE_DIR = 'src/third_party/chromiumos-overlay/chromeos/config/'
64# Created in the event of new host targets becoming available
65_PREBUILT_MAKE_CONF = {'amd64': os.path.join(_PREBUILT_BASE_DIR,
66 'make.conf.amd64-host')}
67_BINHOST_CONF_DIR = 'src/third_party/chromiumos-overlay/chromeos/binhost'
68
69
70class FiltersEmpty(Exception):
71 """Raised when filters are used but none are found."""
72 pass
73
74
75class UploadFailed(Exception):
76 """Raised when one of the files uploaded failed."""
77 pass
78
79class UnknownBoardFormat(Exception):
80 """Raised when a function finds an unknown board format."""
81 pass
82
83class GitPushFailed(Exception):
84 """Raised when a git push failed after retry."""
85 pass
86
87
88def UpdateLocalFile(filename, value, key='PORTAGE_BINHOST'):
89 """Update the key in file with the value passed.
90 File format:
91 key="value"
92 Note quotes are added automatically
93
94 Args:
95 filename: Name of file to modify.
96 value: Value to write with the key.
97 key: The variable key to update. (Default: PORTAGE_BINHOST)
98 """
99 if os.path.exists(filename):
100 file_fh = open(filename)
101 else:
102 file_fh = open(filename, 'w+')
103 file_lines = []
104 found = False
105 keyval_str = '%(key)s=%(value)s'
106 for line in file_fh:
107 # Strip newlines from end of line. We already add newlines below.
108 line = line.rstrip("\n")
109
110 if len(line.split('=')) != 2:
111 # Skip any line that doesn't fit key=val.
112 file_lines.append(line)
113 continue
114
115 file_var, file_val = line.split('=')
116 if file_var == key:
117 found = True
118 print 'Updating %s=%s to %s="%s"' % (file_var, file_val, key, value)
119 value = '"%s"' % value
120 file_lines.append(keyval_str % {'key': key, 'value': value})
121 else:
122 file_lines.append(keyval_str % {'key': file_var, 'value': file_val})
123
124 if not found:
125 file_lines.append(keyval_str % {'key': key, 'value': value})
126
127 file_fh.close()
128 # write out new file
129 new_file_fh = open(filename, 'w')
130 new_file_fh.write('\n'.join(file_lines) + '\n')
131 new_file_fh.close()
132
133
134def RevGitPushWithRetry(retries=5):
135 """Repo sync and then push git changes in flight.
136
137 Args:
138 retries: The number of times to retry before giving up, default: 5
139
140 Raises:
141 GitPushFailed if push was unsuccessful after retries
142 """
Chris Sosa471532a2011-02-01 15:10:06 -0800143 for retry in range(1, retries + 1):
David James8c846492011-01-25 17:07:29 -0800144 try:
145 cros_build_lib.RunCommand('repo sync .', shell=True)
146 cros_build_lib.RunCommand('git push', shell=True)
147 break
148 except cros_build_lib.RunCommandError:
149 if retry < retries:
150 print 'Error pushing changes trying again (%s/%s)' % (retry, retries)
Chris Sosa471532a2011-02-01 15:10:06 -0800151 time.sleep(5 * retry)
David James8c846492011-01-25 17:07:29 -0800152 else:
153 raise GitPushFailed('Failed to push change after %s retries' % retries)
154
155
156def RevGitFile(filename, value, retries=5, key='PORTAGE_BINHOST'):
157 """Update and push the git file.
158
159 Args:
160 filename: file to modify that is in a git repo already
161 value: string representing the version of the prebuilt that has been
162 uploaded.
163 retries: The number of times to retry before giving up, default: 5
164 key: The variable key to update in the git file.
165 (Default: PORTAGE_BINHOST)
166 """
167 prebuilt_branch = 'prebuilt_branch'
168 old_cwd = os.getcwd()
169 os.chdir(os.path.dirname(filename))
170
171 cros_build_lib.RunCommand('repo sync .', shell=True)
172 cros_build_lib.RunCommand('repo start %s .' % prebuilt_branch, shell=True)
173 git_ssh_config_cmd = (
174 'git config url.ssh://git@gitrw.chromium.org:9222.pushinsteadof '
175 'http://git.chromium.org/git')
176 cros_build_lib.RunCommand(git_ssh_config_cmd, shell=True)
177 description = 'Update %s="%s" in %s' % (key, value, filename)
178 print description
179 try:
180 UpdateLocalFile(filename, value, key)
181 cros_build_lib.RunCommand('git config push.default tracking', shell=True)
182 cros_build_lib.RunCommand('git commit -am "%s"' % description, shell=True)
183 RevGitPushWithRetry(retries)
184 finally:
185 cros_build_lib.RunCommand('repo abandon %s .' % prebuilt_branch, shell=True)
186 os.chdir(old_cwd)
187
188
189def GetVersion():
190 """Get the version to put in LATEST and update the git version with."""
191 return datetime.datetime.now().strftime('%d.%m.%y.%H%M%S')
192
193
194def LoadPrivateFilters(build_path):
195 """Load private filters based on ebuilds found under _PRIVATE_OVERLAY_DIR.
196
197 This function adds filters to the global set _FILTER_PACKAGES.
198 Args:
199 build_path: Path that _PRIVATE_OVERLAY_DIR is in.
200 """
201 # TODO(scottz): eventually use manifest.xml to find the proper
202 # private overlay path.
203 filter_path = os.path.join(build_path, _PRIVATE_OVERLAY_DIR)
204 files = cros_build_lib.ListFiles(filter_path)
205 filters = []
206 for file in files:
207 if file.endswith('.ebuild'):
208 basename = os.path.basename(file)
209 match = re.match('(.*?)-\d.*.ebuild', basename)
210 if match:
211 filters.append(match.group(1))
212
213 if not filters:
214 raise FiltersEmpty('No filters were returned')
215
216 _FILTER_PACKAGES.update(filters)
217
218
219def ShouldFilterPackage(file_path):
220 """Skip a particular file if it matches a pattern.
221
222 Skip any files that machine the list of packages to filter in
223 _FILTER_PACKAGES.
224
225 Args:
226 file_path: string of a file path to inspect against _FILTER_PACKAGES
227
228 Returns:
229 True if we should filter the package,
230 False otherwise.
231 """
232 for name in _FILTER_PACKAGES:
233 if name in file_path:
234 print 'FILTERING %s' % file_path
235 return True
236
237 return False
238
239
240def _RetryRun(cmd, print_cmd=True, shell=False, cwd=None):
241 """Run the specified command, retrying if necessary.
242
243 Args:
244 cmd: The command to run.
245 print_cmd: Whether to print out the cmd.
246 shell: Whether to treat the command as a shell.
247 cwd: Working directory to run command in.
248
249 Returns:
250 True if the command succeeded. Otherwise, returns False.
251 """
252
253 # TODO(scottz): port to use _Run or similar when it is available in
254 # cros_build_lib.
255 for attempt in range(_RETRIES):
256 try:
257 output = cros_build_lib.RunCommand(cmd, print_cmd=print_cmd, shell=shell,
258 cwd=cwd)
259 return True
260 except cros_build_lib.RunCommandError:
261 print 'Failed to run %s' % cmd
262 else:
263 print 'Retry failed run %s, giving up' % cmd
264 return False
265
266
267def _GsUpload(args):
268 """Upload to GS bucket.
269
270 Args:
David Jamesfd0b0852011-02-23 11:15:36 -0800271 args: a tuple of three arguments that contains local_file, remote_file, and
272 the acl used for uploading the file.
David James8c846492011-01-25 17:07:29 -0800273
274 Returns:
275 Return the arg tuple of two if the upload failed
276 """
David Jamesfd0b0852011-02-23 11:15:36 -0800277 (local_file, remote_file, acl) = args
David James8c846492011-01-25 17:07:29 -0800278
David Jamesfd0b0852011-02-23 11:15:36 -0800279 cmd = '%s cp -a %s %s %s' % (_GSUTIL_BIN, acl, local_file, remote_file)
David James8c846492011-01-25 17:07:29 -0800280 if not _RetryRun(cmd, print_cmd=False, shell=True):
281 return (local_file, remote_file)
282
David Jamesfd0b0852011-02-23 11:15:36 -0800283def RemoteUpload(acl, files, pool=10):
David James8c846492011-01-25 17:07:29 -0800284 """Upload to google storage.
285
286 Create a pool of process and call _GsUpload with the proper arguments.
287
288 Args:
David Jamesfd0b0852011-02-23 11:15:36 -0800289 acl: The canned acl used for uploading. acl can be one of: "public-read",
290 "public-read-write", "authenticated-read", "bucket-owner-read",
291 "bucket-owner-full-control", or "private".
David James8c846492011-01-25 17:07:29 -0800292 files: dictionary with keys to local files and values to remote path.
293 pool: integer of maximum proesses to have at the same time.
294
295 Returns:
296 Return a set of tuple arguments of the failed uploads
297 """
298 # TODO(scottz) port this to use _RunManyParallel when it is available in
299 # cros_build_lib
300 pool = multiprocessing.Pool(processes=pool)
301 workers = []
302 for local_file, remote_path in files.iteritems():
David Jamesfd0b0852011-02-23 11:15:36 -0800303 workers.append((local_file, remote_path, acl))
David James8c846492011-01-25 17:07:29 -0800304
305 result = pool.map_async(_GsUpload, workers, chunksize=1)
306 while True:
307 try:
Chris Sosa471532a2011-02-01 15:10:06 -0800308 return set(result.get(60 * 60))
David James8c846492011-01-25 17:07:29 -0800309 except multiprocessing.TimeoutError:
310 pass
311
312
313def GenerateUploadDict(base_local_path, base_remote_path, pkgs):
314 """Build a dictionary of local remote file key pairs to upload.
315
316 Args:
317 base_local_path: The base path to the files on the local hard drive.
318 remote_path: The base path to the remote paths.
319 pkgs: The packages to upload.
320
321 Returns:
322 Returns a dictionary of local_path/remote_path pairs
323 """
324 upload_files = {}
325 for pkg in pkgs:
326 suffix = pkg['CPV'] + '.tbz2'
327 local_path = os.path.join(base_local_path, suffix)
328 assert os.path.exists(local_path)
329 remote_path = '%s/%s' % (base_remote_path.rstrip('/'), suffix)
330 upload_files[local_path] = remote_path
331
332 return upload_files
333
334def GetBoardPathFromCrosOverlayList(build_path, target):
335 """Use the cros_overlay_list to determine the path to the board overlay
336 Args:
337 build_path: The path to the root of the build directory
338 target: The target that we are looking for, could consist of board and
339 board_variant, we handle that properly
340 Returns:
341 The last line from cros_overlay_list as a string
342 """
Chris Sosa471532a2011-02-01 15:10:06 -0800343 script_dir = os.path.join(build_path, 'src/platform/dev/host')
David James8c846492011-01-25 17:07:29 -0800344 cmd = ['./cros_overlay_list']
345 if re.match('.*?_.*', target):
346 (board, variant) = target.split('_')
347 cmd += ['--board', board, '--variant', variant]
348 elif re.match('.*?-\w+', target):
349 cmd += ['--board', target]
350 else:
351 raise UnknownBoardFormat('Unknown format: %s' % target)
352
353 cmd_output = cros_build_lib.RunCommand(cmd, redirect_stdout=True,
354 cwd=script_dir)
355 # We only care about the last entry
356 return cmd_output.output.splitlines().pop()
357
358
359def DeterminePrebuiltConfFile(build_path, target):
360 """Determine the prebuilt.conf file that needs to be updated for prebuilts.
361
362 Args:
363 build_path: The path to the root of the build directory
364 target: String representation of the board. This includes host and board
365 targets
366
367 Returns
368 A string path to a prebuilt.conf file to be updated.
369 """
370 if _HOST_TARGET == target:
371 # We are host.
372 # Without more examples of hosts this is a kludge for now.
373 # TODO(Scottz): as new host targets come online expand this to
374 # work more like boards.
Chris Sosa471532a2011-02-01 15:10:06 -0800375 make_path = _PREBUILT_MAKE_CONF[target]
David James8c846492011-01-25 17:07:29 -0800376 else:
377 # We are a board
378 board = GetBoardPathFromCrosOverlayList(build_path, target)
379 make_path = os.path.join(board, 'prebuilt.conf')
380
381 return make_path
382
383
384def UpdateBinhostConfFile(path, key, value):
385 """Update binhost config file file with key=value.
386
387 Args:
388 path: Filename to update.
389 key: Key to update.
390 value: New value for key.
391 """
392 cwd = os.path.dirname(os.path.abspath(path))
393 filename = os.path.basename(path)
394 if not os.path.isdir(cwd):
395 os.makedirs(cwd)
396 if not os.path.isfile(path):
397 config_file = file(path, 'w')
David James8c846492011-01-25 17:07:29 -0800398 config_file.close()
399 UpdateLocalFile(path, value, key)
400 cros_build_lib.RunCommand('git add %s' % filename, cwd=cwd, shell=True)
401 description = 'Update %s=%s in %s' % (key, value, filename)
402 cros_build_lib.RunCommand('git commit -m "%s"' % description, cwd=cwd,
403 shell=True)
404
405
David Jamesce093af2011-02-23 15:21:58 -0800406def _GrabAllRemotePackageIndexes(binhost_urls):
David James05bcb2b2011-02-09 09:25:47 -0800407 """Grab all of the packages files associated with a list of binhost_urls.
408
David James05bcb2b2011-02-09 09:25:47 -0800409 Args:
410 binhost_urls: The URLs for the directories containing the Packages files we
411 want to grab.
David James05bcb2b2011-02-09 09:25:47 -0800412
413 Returns:
414 A list of PackageIndex objects.
415 """
416 pkg_indexes = []
417 for url in binhost_urls:
418 pkg_index = GrabRemotePackageIndex(url)
419 if pkg_index:
420 pkg_indexes.append(pkg_index)
David James05bcb2b2011-02-09 09:25:47 -0800421 return pkg_indexes
422
423
David James05bcb2b2011-02-09 09:25:47 -0800424
David Jamesc0f158a2011-02-22 16:07:29 -0800425class PrebuiltUploader(object):
426 """Synchronize host and board prebuilts."""
David James8c846492011-01-25 17:07:29 -0800427
David Jamesfd0b0852011-02-23 11:15:36 -0800428 def __init__(self, upload_location, acl, binhost_base_url, pkg_indexes):
David Jamesc0f158a2011-02-22 16:07:29 -0800429 """Constructor for prebuilt uploader object.
David James8c846492011-01-25 17:07:29 -0800430
David Jamesc0f158a2011-02-22 16:07:29 -0800431 This object can upload host or prebuilt files to Google Storage.
David James8c846492011-01-25 17:07:29 -0800432
David Jamesc0f158a2011-02-22 16:07:29 -0800433 Args:
434 upload_location: The upload location.
David Jamesfd0b0852011-02-23 11:15:36 -0800435 acl: The canned acl used for uploading to Google Storage. acl can be one
436 of: "public-read", "public-read-write", "authenticated-read",
437 "bucket-owner-read", "bucket-owner-full-control", or "private". If
438 we are not uploading to Google Storage, this parameter is unused.
439 binhost_base_url: The URL used for downloading the prebuilts.
David Jamesc0f158a2011-02-22 16:07:29 -0800440 pkg_indexes: Old uploaded prebuilts to compare against. Instead of
441 uploading duplicate files, we just link to the old files.
442 """
443 self._upload_location = upload_location
David Jamesfd0b0852011-02-23 11:15:36 -0800444 self._acl = acl
David Jamesc0f158a2011-02-22 16:07:29 -0800445 self._binhost_base_url = binhost_base_url
446 self._pkg_indexes = pkg_indexes
David James8c846492011-01-25 17:07:29 -0800447
David Jamesc0f158a2011-02-22 16:07:29 -0800448 def _UploadPrebuilt(self, package_path, url_suffix):
449 """Upload host or board prebuilt files to Google Storage space.
David James8c846492011-01-25 17:07:29 -0800450
David Jamesc0f158a2011-02-22 16:07:29 -0800451 Args:
452 package_path: The path to the packages dir.
David Jamesce093af2011-02-23 15:21:58 -0800453 url_suffix: The remote subdirectory where we should upload the packages.
David Jamesc0f158a2011-02-22 16:07:29 -0800454 """
David James8c846492011-01-25 17:07:29 -0800455
David Jamesc0f158a2011-02-22 16:07:29 -0800456 # Process Packages file, removing duplicates and filtered packages.
457 pkg_index = GrabLocalPackageIndex(package_path)
458 pkg_index.SetUploadLocation(self._binhost_base_url, url_suffix)
459 pkg_index.RemoveFilteredPackages(ShouldFilterPackage)
460 uploads = pkg_index.ResolveDuplicateUploads(self._pkg_indexes)
David James05bcb2b2011-02-09 09:25:47 -0800461
David Jamesc0f158a2011-02-22 16:07:29 -0800462 # Write Packages file.
463 tmp_packages_file = pkg_index.WriteToNamedTemporaryFile()
David James05bcb2b2011-02-09 09:25:47 -0800464
David Jamesc0f158a2011-02-22 16:07:29 -0800465 remote_location = '%s/%s' % (self._upload_location.rstrip('/'), url_suffix)
466 if remote_location.startswith('gs://'):
467 # Build list of files to upload.
468 upload_files = GenerateUploadDict(package_path, remote_location, uploads)
469 remote_file = '%s/Packages' % remote_location.rstrip('/')
470 upload_files[tmp_packages_file.name] = remote_file
David James05bcb2b2011-02-09 09:25:47 -0800471
David Jamesfd0b0852011-02-23 11:15:36 -0800472 failed_uploads = RemoteUpload(self._acl, upload_files)
David Jamesc0f158a2011-02-22 16:07:29 -0800473 if len(failed_uploads) > 1 or (None not in failed_uploads):
474 error_msg = ['%s -> %s\n' % args for args in failed_uploads]
475 raise UploadFailed('Error uploading:\n%s' % error_msg)
476 else:
477 pkgs = ' '.join(p['CPV'] + '.tbz2' for p in uploads)
478 ssh_server, remote_path = remote_location.split(':', 1)
479 d = { 'pkg_index': tmp_packages_file.name,
480 'pkgs': pkgs,
481 'remote_packages': '%s/Packages' % remote_location.rstrip('/'),
482 'remote_path': remote_path.rstrip('/'),
483 'remote_location': remote_location.rstrip('/'),
484 'ssh_server': ssh_server }
485 cmds = ['ssh %(ssh_server)s mkdir -p %(remote_path)s' % d,
486 'rsync -av --chmod=a+r %(pkg_index)s %(remote_packages)s' % d]
487 if pkgs:
488 cmds.append('rsync -Rav %(pkgs)s %(remote_location)s/' % d)
489 for cmd in cmds:
490 if not _RetryRun(cmd, shell=True, cwd=package_path):
491 raise UploadFailed('Could not run %s' % cmd)
David James8c846492011-01-25 17:07:29 -0800492
David Jamesc0f158a2011-02-22 16:07:29 -0800493 def _SyncHostPrebuilts(self, build_path, version, key, git_sync,
494 sync_binhost_conf):
495 """Synchronize host prebuilt files.
David James05bcb2b2011-02-09 09:25:47 -0800496
David Jamesc0f158a2011-02-22 16:07:29 -0800497 This function will sync both the standard host packages, plus the host
498 packages associated with all targets that have been "setup" with the
499 current host's chroot. For instance, if this host has been used to build
500 x86-generic, it will sync the host packages associated with
501 'i686-pc-linux-gnu'. If this host has also been used to build arm-generic,
502 it will also sync the host packages associated with
503 'armv7a-cros-linux-gnueabi'.
David James05bcb2b2011-02-09 09:25:47 -0800504
David Jamesc0f158a2011-02-22 16:07:29 -0800505 Args:
506 build_path: The path to the directory containing the chroot.
507 version: A unique string, intended to be included in the upload path,
508 which identifies the version number of the uploaded prebuilts.
509 key: The variable key to update in the git file.
510 git_sync: If set, update make.conf of target to reference the latest
511 prebuilt packages generated here.
512 sync_binhost_conf: If set, update binhost config file in
513 chromiumos-overlay for the host.
514 """
515 # Upload prebuilts.
516 package_path = os.path.join(build_path, _HOST_PACKAGES_PATH)
517 url_suffix = _REL_HOST_PATH % {'version': version, 'target': _HOST_TARGET}
David Jamesc0f158a2011-02-22 16:07:29 -0800518 self._UploadPrebuilt(package_path, url_suffix)
David James05bcb2b2011-02-09 09:25:47 -0800519
David Jamesc0f158a2011-02-22 16:07:29 -0800520 # Record URL where prebuilts were uploaded.
521 url_value = '%s/%s/' % (self._binhost_base_url.rstrip('/'),
522 url_suffix.rstrip('/'))
523 if git_sync:
524 git_file = os.path.join(build_path, _PREBUILT_MAKE_CONF[_HOST_TARGET])
525 RevGitFile(git_file, url_value, key=key)
526 if sync_binhost_conf:
527 binhost_conf = os.path.join(build_path, _BINHOST_CONF_DIR, 'host',
528 '%s-%s.conf' % (_HOST_TARGET, key))
529 UpdateBinhostConfFile(binhost_conf, key, url_value)
530
531 def _SyncBoardPrebuilts(self, board, build_path, version, key, git_sync,
532 sync_binhost_conf):
533 """Synchronize board prebuilt files.
534
535 Args:
536 board: The board to upload to Google Storage.
537 build_path: The path to the directory containing the chroot.
538 version: A unique string, intended to be included in the upload path,
539 which identifies the version number of the uploaded prebuilts.
540 key: The variable key to update in the git file.
541 git_sync: If set, update make.conf of target to reference the latest
542 prebuilt packages generated here.
543 sync_binhost_conf: If set, update binhost config file in
544 chromiumos-overlay for the current board.
545 """
546 # Upload prebuilts.
547 board_path = os.path.join(build_path, _BOARD_PATH % {'board': board})
548 package_path = os.path.join(board_path, 'packages')
549 url_suffix = _REL_BOARD_PATH % {'board': board, 'version': version}
550 self._UploadPrebuilt(package_path, url_suffix)
551
552 # Record URL where prebuilts were uploaded.
553 url_value = '%s/%s/' % (self._binhost_base_url.rstrip('/'),
554 url_suffix.rstrip('/'))
555 if git_sync:
556 git_file = DeterminePrebuiltConfFile(build_path, board)
557 RevGitFile(git_file, url_value, key=key)
558 if sync_binhost_conf:
559 binhost_conf = os.path.join(build_path, _BINHOST_CONF_DIR, 'target',
560 '%s-%s.conf' % (board, key))
561 UpdateBinhostConfFile(binhost_conf, key, url_value)
David James05bcb2b2011-02-09 09:25:47 -0800562
563
David James8c846492011-01-25 17:07:29 -0800564def usage(parser, msg):
565 """Display usage message and parser help then exit with 1."""
566 print >> sys.stderr, msg
567 parser.print_help()
568 sys.exit(1)
569
David Jamesc0f158a2011-02-22 16:07:29 -0800570def ParseOptions():
David James8c846492011-01-25 17:07:29 -0800571 parser = optparse.OptionParser()
572 parser.add_option('-H', '--binhost-base-url', dest='binhost_base_url',
573 default=_BINHOST_BASE_URL,
574 help='Base URL to use for binhost in make.conf updates')
575 parser.add_option('', '--previous-binhost-url', action='append',
576 default=[], dest='previous_binhost_url',
577 help='Previous binhost URL')
578 parser.add_option('-b', '--board', dest='board', default=None,
579 help='Board type that was built on this machine')
580 parser.add_option('-p', '--build-path', dest='build_path',
David James05bcb2b2011-02-09 09:25:47 -0800581 help='Path to the directory containing the chroot')
David James8c846492011-01-25 17:07:29 -0800582 parser.add_option('-s', '--sync-host', dest='sync_host',
583 default=False, action='store_true',
584 help='Sync host prebuilts')
585 parser.add_option('-g', '--git-sync', dest='git_sync',
586 default=False, action='store_true',
587 help='Enable git version sync (This commits to a repo)')
588 parser.add_option('-u', '--upload', dest='upload',
589 default=None,
590 help='Upload location')
591 parser.add_option('-V', '--prepend-version', dest='prepend_version',
592 default=None,
593 help='Add an identifier to the front of the version')
594 parser.add_option('-f', '--filters', dest='filters', action='store_true',
595 default=False,
596 help='Turn on filtering of private ebuild packages')
597 parser.add_option('-k', '--key', dest='key',
598 default='PORTAGE_BINHOST',
599 help='Key to update in make.conf / binhost.conf')
600 parser.add_option('', '--sync-binhost-conf', dest='sync_binhost_conf',
601 default=False, action='store_true',
602 help='Update binhost.conf')
David Jamesfd0b0852011-02-23 11:15:36 -0800603 parser.add_option('-P', '--private', dest='private', action='store_true',
604 default=False, help='Mark gs:// uploads as private.')
David James8c846492011-01-25 17:07:29 -0800605
606 options, args = parser.parse_args()
David James8c846492011-01-25 17:07:29 -0800607 if not options.build_path:
608 usage(parser, 'Error: you need provide a chroot path')
David James8c846492011-01-25 17:07:29 -0800609 if not options.upload:
610 usage(parser, 'Error: you need to provide an upload location using -u')
David Jamesfd0b0852011-02-23 11:15:36 -0800611 if options.private and not (options.binhost_base_url.startswith('gs://') and
612 options.upload.startswith('gs://')):
613 usage(parser, 'Error: --private is only valid for gs:// URLs.\n'
614 'Both --binhost-base-url and --upload must be gs:// URLs.')
David Jamesc0f158a2011-02-22 16:07:29 -0800615 return options
616
617def main():
618 options = ParseOptions()
619
620 # Setup boto environment for gsutil to use
621 os.environ['BOTO_CONFIG'] = _BOTO_CONFIG
David James8c846492011-01-25 17:07:29 -0800622
623 if options.filters:
624 LoadPrivateFilters(options.build_path)
625
David Jamesfd0b0852011-02-23 11:15:36 -0800626 acl = 'public-read'
627 if options.private:
628 acl = 'private'
629
David James05bcb2b2011-02-09 09:25:47 -0800630 # Calculate a list of Packages index files to compare against. Whenever we
631 # upload a package, we check to make sure it's not already stored in one of
632 # the packages files we uploaded. This list of packages files might contain
633 # both board and host packages.
David Jamesce093af2011-02-23 15:21:58 -0800634 pkg_indexes = _GrabAllRemotePackageIndexes(options.previous_binhost_url)
David James8c846492011-01-25 17:07:29 -0800635
David Jamesc0f158a2011-02-22 16:07:29 -0800636 version = GetVersion()
637 if options.prepend_version:
638 version = '%s-%s' % (options.prepend_version, version)
639
David Jamesfd0b0852011-02-23 11:15:36 -0800640 uploader = PrebuiltUploader(options.upload, acl, options.binhost_base_url,
David Jamesc0f158a2011-02-22 16:07:29 -0800641 pkg_indexes)
642
David James8c846492011-01-25 17:07:29 -0800643 if options.sync_host:
David Jamesc0f158a2011-02-22 16:07:29 -0800644 uploader._SyncHostPrebuilts(options.build_path, version, options.key,
645 options.git_sync, options.sync_binhost_conf)
David James8c846492011-01-25 17:07:29 -0800646
647 if options.board:
David Jamesc0f158a2011-02-22 16:07:29 -0800648 uploader._SyncBoardPrebuilts(options.board, options.build_path, version,
649 options.key, options.git_sync,
650 options.sync_binhost_conf)
David James8c846492011-01-25 17:07:29 -0800651
652if __name__ == '__main__':
653 main()