blob: 3c9f6821ff339bcb568a592f45e251140bb7c906 [file] [log] [blame]
Kuang-che Wu41e8b592018-09-25 17:01:30 +08001#!/usr/bin/env python2
2# -*- coding: utf-8 -*-
3# Copyright 2018 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6"""Helper script to prepare source trees for ChromeOS bisection.
7
8Typical usage:
9
10 Initial setup:
11 $ %(prog)s init --chromeos
12 $ %(prog)s init --chrome
13 $ %(prog)s init --android=pi-arc-dev
14
15 Sync code if necessary:
16 $ %(prog)s sync
17
18 Create source trees for bisection
19 $ %(prog)s new --session=12345
20
21 After bisection finished, delete trees
22 $ %(prog)s delete --session=12345
23"""
24from __future__ import print_function
25import argparse
26import csv
27import logging
28import os
Kuang-che Wu22f207e2019-02-23 12:53:53 +080029import subprocess
Kuang-che Wu67be74b2018-10-15 14:17:26 +080030import time
Kuang-che Wu7d0c7592019-09-16 09:59:28 +080031import xml.etree.ElementTree
Kuang-che Wu41e8b592018-09-25 17:01:30 +080032
Kuang-che Wua7ddf9b2019-11-25 18:59:57 +080033from six.moves import urllib
34
Kuang-che Wu41e8b592018-09-25 17:01:30 +080035from bisect_kit import common
36from bisect_kit import configure
37from bisect_kit import gclient_util
38from bisect_kit import git_util
Kuang-che Wudc714412018-10-17 16:06:39 +080039from bisect_kit import locking
Kuang-che Wu41e8b592018-09-25 17:01:30 +080040from bisect_kit import repo_util
41from bisect_kit import util
42
43DEFAULT_MIRROR_BASE = os.path.expanduser('~/git-mirrors')
44DEFAULT_WORK_BASE = os.path.expanduser('~/bisect-workdir')
45CHECKOUT_TEMPLATE_NAME = 'template'
46
47logger = logging.getLogger(__name__)
48
49
50class DefaultProjectPathFactory(object):
51 """Factory for chromeos/chrome/android source tree paths."""
52
53 def __init__(self, mirror_base, work_base, session):
54 self.mirror_base = mirror_base
55 self.work_base = work_base
56 self.session = session
57
58 def get_chromeos_mirror(self):
59 return os.path.join(self.mirror_base, 'chromeos')
60
61 def get_chromeos_tree(self):
62 return os.path.join(self.work_base, self.session, 'chromeos')
63
64 def get_android_mirror(self, branch):
65 return os.path.join(self.mirror_base, 'android.%s' % branch)
66
67 def get_android_tree(self, branch):
68 return os.path.join(self.work_base, self.session, 'android.%s' % branch)
69
70 def get_chrome_cache(self):
71 return os.path.join(self.mirror_base, 'chrome')
72
73 def get_chrome_tree(self):
74 return os.path.join(self.work_base, self.session, 'chrome')
75
76
77def subvolume_or_makedirs(opts, path):
78 if os.path.exists(path):
79 return
80
81 path = os.path.abspath(path)
82 if opts.btrfs:
83 dirname, basename = os.path.split(path)
84 if not os.path.exists(dirname):
85 os.makedirs(dirname)
86 util.check_call('btrfs', 'subvolume', 'create', basename, cwd=dirname)
87 else:
88 os.makedirs(path)
89
90
91def is_btrfs_subvolume(path):
92 if util.check_output('stat', '-f', '--format=%T', path).strip() != 'btrfs':
93 return False
94 return util.check_output('stat', '--format=%i', path).strip() == '256'
95
96
97def snapshot_or_copytree(src, dst):
98 assert os.path.isdir(src), '%s does not exist' % src
99 assert os.path.isdir(os.path.dirname(dst))
100
101 # Make sure dst do not exist, otherwise it becomes "dst/name" (one extra
102 # depth) instead of "dst".
103 assert not os.path.exists(dst)
104
105 if is_btrfs_subvolume(src):
106 util.check_call('btrfs', 'subvolume', 'snapshot', src, dst)
107 else:
108 # -a for recursion and preserve all attributes.
109 util.check_call('cp', '-a', src, dst)
110
111
Kuang-che Wubfa64482018-10-16 11:49:49 +0800112def collect_removed_manifest_repos(repo_dir, last_sync_time, only_branch=None):
Kuang-che Wu67be74b2018-10-15 14:17:26 +0800113 manifest_dir = os.path.join(repo_dir, '.repo', 'manifests')
Kuang-che Wu67be74b2018-10-15 14:17:26 +0800114 manifest_path = 'default.xml'
115 manifest_full_path = os.path.join(manifest_dir, manifest_path)
116 # hack for chromeos symlink
117 if os.path.islink(manifest_full_path):
118 manifest_path = os.readlink(manifest_full_path)
119
120 parser = repo_util.ManifestParser(manifest_dir)
121 latest = None
122 removed = {}
123 for _, git_rev in reversed(
124 parser.enumerate_manifest_commits(last_sync_time, None, manifest_path)):
Kuang-che Wu7d0c7592019-09-16 09:59:28 +0800125 try:
126 root = parser.parse_xml_recursive(git_rev, manifest_path)
127 except xml.etree.ElementTree.ParseError:
128 logger.warning('%s %s@%s syntax error, skip', manifest_dir, manifest_path,
129 git_rev[:12])
130 continue
Kuang-che Wubfa64482018-10-16 11:49:49 +0800131 if (only_branch and root.find('default') is not None and
132 root.find('default').get('revision') != only_branch):
133 break
Kuang-che Wu67be74b2018-10-15 14:17:26 +0800134 entries = parser.process_parsed_result(root)
135 if latest is None:
136 assert entries is not None
137 latest = entries
138 continue
139
140 for path, path_spec in entries.items():
141 if path in latest:
142 continue
143 if path in removed:
144 continue
145 removed[path] = path_spec
146
147 return removed
148
149
Kuang-che Wu41e8b592018-09-25 17:01:30 +0800150def setup_chromeos_repos(opts, path_factory):
151 chromeos_mirror = path_factory.get_chromeos_mirror()
152 chromeos_tree = path_factory.get_chromeos_tree()
153 subvolume_or_makedirs(opts, chromeos_mirror)
154 subvolume_or_makedirs(opts, chromeos_tree)
155
156 manifest_url = (
157 'https://chrome-internal.googlesource.com/chromeos/manifest-internal')
158 repo_url = 'https://chromium.googlesource.com/external/repo.git'
159
160 if os.path.exists(os.path.join(chromeos_mirror, '.repo', 'manifests')):
161 logger.warning(
162 '%s has already been initialized, assume it is setup properly',
163 chromeos_mirror)
164 else:
165 logger.info('repo init for chromeos mirror')
166 repo_util.init(
167 chromeos_mirror,
168 manifest_url=manifest_url,
169 repo_url=repo_url,
170 mirror=True)
171
172 local_manifest_dir = os.path.join(chromeos_mirror, '.repo',
173 'local_manifests')
174 os.mkdir(local_manifest_dir)
175 with open(os.path.join(local_manifest_dir, 'manifest-versions.xml'),
176 'w') as f:
Kuang-che Wuae6824b2019-08-27 22:20:01 +0800177 f.write("""<?xml version="1.0" encoding="UTF-8"?>
Kuang-che Wu41e8b592018-09-25 17:01:30 +0800178 <manifest>
179 <project name="chromeos/manifest-versions" remote="cros-internal" />
180 </manifest>
Kuang-che Wuae6824b2019-08-27 22:20:01 +0800181 """)
Kuang-che Wu41e8b592018-09-25 17:01:30 +0800182
183 logger.info('repo init for chromeos tree')
184 repo_util.init(
185 chromeos_tree,
186 manifest_url=manifest_url,
187 repo_url=repo_url,
188 reference=chromeos_mirror)
189
Kuang-che Wudc714412018-10-17 16:06:39 +0800190 with locking.lock_file(
191 os.path.join(chromeos_mirror, locking.LOCK_FILE_FOR_MIRROR_SYNC)):
192 logger.info('repo sync for chromeos mirror (this takes hours; be patient)')
193 repo_util.sync(chromeos_mirror)
Kuang-che Wu41e8b592018-09-25 17:01:30 +0800194
195 logger.info('repo sync for chromeos tree')
196 repo_util.sync(chromeos_tree)
197
198
Kuang-che Wu67be74b2018-10-15 14:17:26 +0800199def read_last_sync_time(repo_dir):
200 timestamp_path = os.path.join(repo_dir, 'last_sync_time')
201 if os.path.exists(timestamp_path):
202 with open(timestamp_path) as f:
203 return int(f.read())
204 else:
205 # 4 months should be enough for most bisect cases.
206 return int(time.time()) - 86400 * 120
207
208
209def write_sync_time(repo_dir, sync_time):
210 timestamp_path = os.path.join(repo_dir, 'last_sync_time')
211 with open(timestamp_path, 'w') as f:
212 f.write('%d\n' % sync_time)
213
214
215def write_extra_manifest_to_mirror(repo_dir, removed):
216 local_manifest_dir = os.path.join(repo_dir, '.repo', 'local_manifests')
217 if not os.path.exists(local_manifest_dir):
218 os.mkdir(local_manifest_dir)
219 with open(os.path.join(local_manifest_dir, 'deleted-repos.xml'), 'w') as f:
Kuang-che Wuae6824b2019-08-27 22:20:01 +0800220 f.write("""<?xml version="1.0" encoding="UTF-8"?>\n<manifest>\n""")
Kuang-che Wu67be74b2018-10-15 14:17:26 +0800221 remotes = {}
222 for path_spec in removed.values():
Kuang-che Wua7ddf9b2019-11-25 18:59:57 +0800223 scheme, netloc, remote_path = urllib.parse.urlsplit(
224 path_spec.repo_url)[:3]
Kuang-che Wu67be74b2018-10-15 14:17:26 +0800225 assert remote_path[0] == '/'
226 remote_path = remote_path[1:]
227 if (scheme, netloc) not in remotes:
228 remote_name = 'remote_for_deleted_repo_%s' % (scheme + netloc)
229 remotes[scheme, netloc] = remote_name
Kuang-che Wuae6824b2019-08-27 22:20:01 +0800230 f.write(""" <remote name="%s" fetch="%s" />\n""" %
Kuang-che Wu67be74b2018-10-15 14:17:26 +0800231 (remote_name, '%s://%s' % (scheme, netloc)))
Kuang-che Wubfa64482018-10-16 11:49:49 +0800232 f.write(
Kuang-che Wuae6824b2019-08-27 22:20:01 +0800233 """ <project name="%s" path="%s" remote="%s" revision="%s" />\n""" %
Kuang-che Wubfa64482018-10-16 11:49:49 +0800234 (remote_path, path_spec.path, remotes[scheme, netloc], path_spec.at))
Kuang-che Wuae6824b2019-08-27 22:20:01 +0800235 f.write("""</manifest>\n""")
Kuang-che Wu67be74b2018-10-15 14:17:26 +0800236
237
Kuang-che Wubfa64482018-10-16 11:49:49 +0800238def generate_extra_manifest_for_deleted_repo(repo_dir, only_branch=None):
239 last_sync_time = read_last_sync_time(repo_dir)
240 removed = collect_removed_manifest_repos(
241 repo_dir, last_sync_time, only_branch=only_branch)
242 write_extra_manifest_to_mirror(repo_dir, removed)
243 logger.info('since last sync, %d repo got removed', len(removed))
Kuang-che Wub76b7f62019-09-16 10:06:18 +0800244 return len(removed)
Kuang-che Wubfa64482018-10-16 11:49:49 +0800245
246
Kuang-che Wu41e8b592018-09-25 17:01:30 +0800247def sync_chromeos_code(opts, path_factory):
248 del opts # unused
249
Kuang-che Wu67be74b2018-10-15 14:17:26 +0800250 start_sync_time = int(time.time())
Kuang-che Wu41e8b592018-09-25 17:01:30 +0800251 chromeos_mirror = path_factory.get_chromeos_mirror()
Kuang-che Wu67be74b2018-10-15 14:17:26 +0800252
Kuang-che Wu67be74b2018-10-15 14:17:26 +0800253 logger.info('repo sync for chromeos mirror')
Kuang-che Wu41e8b592018-09-25 17:01:30 +0800254 repo_util.sync(chromeos_mirror)
Kuang-che Wub76b7f62019-09-16 10:06:18 +0800255 # If there are repos deleted after last sync, generate custom manifest and
256 # sync again for those repos. So we can mirror commits just before the repo
257 # deletion.
258 if generate_extra_manifest_for_deleted_repo(chromeos_mirror) != 0:
259 logger.info('repo sync again')
260 repo_util.sync(chromeos_mirror)
Kuang-che Wu67be74b2018-10-15 14:17:26 +0800261 write_sync_time(chromeos_mirror, start_sync_time)
Kuang-che Wu41e8b592018-09-25 17:01:30 +0800262
263 logger.info('repo sync for chromeos tree')
264 chromeos_tree = path_factory.get_chromeos_tree()
265 repo_util.sync(chromeos_tree)
266
Kuang-che Wu41e8b592018-09-25 17:01:30 +0800267
268def query_chrome_latest_branch():
269 result = None
Kuang-che Wua7ddf9b2019-11-25 18:59:57 +0800270 r = urllib.request.urlopen('https://omahaproxy.appspot.com/all')
Kuang-che Wu41e8b592018-09-25 17:01:30 +0800271 for row in csv.DictReader(r):
272 if row['true_branch'].isdigit():
273 result = max(result, int(row['true_branch']))
274 return result
275
276
277def setup_chrome_repos(opts, path_factory):
278 chrome_cache = path_factory.get_chrome_cache()
279 subvolume_or_makedirs(opts, chrome_cache)
280 chrome_tree = path_factory.get_chrome_tree()
281 subvolume_or_makedirs(opts, chrome_tree)
282
283 latest_branch = query_chrome_latest_branch()
284 logger.info('latest chrome branch is %d', latest_branch)
285 assert latest_branch
Kuang-che Wuae6824b2019-08-27 22:20:01 +0800286 spec = """
Kuang-che Wu41e8b592018-09-25 17:01:30 +0800287solutions = [
288 { "name" : "buildspec",
289 "url" : "https://chrome-internal.googlesource.com/a/chrome/tools/buildspec.git",
290 "deps_file" : "branches/%d/DEPS",
291 "custom_deps" : {
292 },
293 "custom_vars": {'checkout_src_internal': True},
294 },
295]
296target_os = ['chromeos']
297cache_dir = %r
Kuang-che Wuae6824b2019-08-27 22:20:01 +0800298""" % (latest_branch, chrome_cache)
Kuang-che Wu41e8b592018-09-25 17:01:30 +0800299
Kuang-che Wudc714412018-10-17 16:06:39 +0800300 with locking.lock_file(
301 os.path.join(chrome_cache, locking.LOCK_FILE_FOR_MIRROR_SYNC)):
302 logger.info('gclient config for chrome')
303 gclient_util.config(chrome_tree, spec=spec)
Kuang-che Wu41e8b592018-09-25 17:01:30 +0800304
Kuang-che Wudc714412018-10-17 16:06:39 +0800305 is_first_sync = not os.listdir(chrome_cache)
306 if is_first_sync:
307 logger.info('gclient sync for chrome (this takes hours; be patient)')
308 else:
309 logger.info('gclient sync for chrome')
310 gclient_util.sync(
311 chrome_tree, with_branch_heads=True, with_tags=True, ignore_locks=True)
Kuang-che Wu41e8b592018-09-25 17:01:30 +0800312
Kuang-che Wudc714412018-10-17 16:06:39 +0800313 # It's possible that some repos are removed from latest branch and thus
314 # their commit history is not fetched in recent gclient sync. So we call
315 # 'git fetch' for all existing git mirrors.
316 # TODO(kcwu): only sync repos not in DEPS files of latest branch
317 logger.info('additional sync for chrome mirror')
318 for git_repo_name in os.listdir(chrome_cache):
319 # another gclient is running or leftover of previous run; skip
320 if git_repo_name.startswith('_cache_tmp'):
321 continue
322 git_repo = os.path.join(chrome_cache, git_repo_name)
Kuang-che Wu08366542019-01-12 12:37:49 +0800323 if not git_util.is_git_bare_dir(git_repo):
Kuang-che Wudc714412018-10-17 16:06:39 +0800324 continue
Kuang-che Wu2b1286b2019-05-20 20:37:26 +0800325 git_util.fetch(git_repo)
Kuang-che Wu41e8b592018-09-25 17:01:30 +0800326
Kuang-che Wu1e49f512018-12-06 15:27:42 +0800327 # Some repos were removed from the DEPS and won't be synced here. They will
328 # be synced during DEPS file processing because the necessary information
329 # requires full DEPS parsing. (crbug.com/902238)
330
Kuang-che Wu41e8b592018-09-25 17:01:30 +0800331
332def sync_chrome_code(opts, path_factory):
333 # The sync step is identical to the initial gclient config step.
334 setup_chrome_repos(opts, path_factory)
335
336
337def setup_android_repos(opts, path_factory, branch):
338 android_mirror = path_factory.get_android_mirror(branch)
339 android_tree = path_factory.get_android_tree(branch)
340 subvolume_or_makedirs(opts, android_mirror)
341 subvolume_or_makedirs(opts, android_tree)
342
343 manifest_url = ('persistent-https://googleplex-android.git.corp.google.com'
344 '/platform/manifest')
345 repo_url = 'https://gerrit.googlesource.com/git-repo'
346
347 if os.path.exists(os.path.join(android_mirror, '.repo', 'manifests')):
348 logger.warning(
349 '%s has already been initialized, assume it is setup properly',
350 android_mirror)
351 else:
352 logger.info('repo init for android mirror branch=%s', branch)
353 repo_util.init(
354 android_mirror,
355 manifest_url=manifest_url,
356 repo_url=repo_url,
357 manifest_branch=branch,
358 mirror=True)
359
360 logger.info('repo init for android tree branch=%s', branch)
361 repo_util.init(
362 android_tree,
363 manifest_url=manifest_url,
364 repo_url=repo_url,
365 manifest_branch=branch,
366 reference=android_mirror)
367
368 logger.info('repo sync for android mirror (this takes hours; be patient)')
369 repo_util.sync(android_mirror, current_branch=True)
370
371 logger.info('repo sync for android tree branch=%s', branch)
372 repo_util.sync(android_tree, current_branch=True)
373
374
375def sync_android_code(opts, path_factory, branch):
376 del opts # unused
Kuang-che Wu67be74b2018-10-15 14:17:26 +0800377 start_sync_time = int(time.time())
Kuang-che Wu41e8b592018-09-25 17:01:30 +0800378 android_mirror = path_factory.get_android_mirror(branch)
379 android_tree = path_factory.get_android_tree(branch)
380
Kuang-che Wudc714412018-10-17 16:06:39 +0800381 with locking.lock_file(
382 os.path.join(android_mirror, locking.LOCK_FILE_FOR_MIRROR_SYNC)):
383 logger.info('repo sync for android mirror branch=%s', branch)
Kuang-che Wub76b7f62019-09-16 10:06:18 +0800384 repo_util.sync(android_mirror, current_branch=True)
Kuang-che Wudc714412018-10-17 16:06:39 +0800385 # Android usually big jump between milestone releases and add/delete lots of
386 # repos when switch releases. Because it's infeasible to bisect between such
387 # big jump, the deleted repo is useless. In order to save disk, do not sync
388 # repos deleted in other branches.
Kuang-che Wub76b7f62019-09-16 10:06:18 +0800389 if generate_extra_manifest_for_deleted_repo(
390 android_mirror, only_branch=branch) != 0:
391 logger.info('repo sync again')
392 repo_util.sync(android_mirror, current_branch=True)
Kuang-che Wudc714412018-10-17 16:06:39 +0800393 write_sync_time(android_mirror, start_sync_time)
Kuang-che Wu41e8b592018-09-25 17:01:30 +0800394
395 logger.info('repo sync for android tree branch=%s', branch)
396 repo_util.sync(android_tree, current_branch=True)
397
398
399def cmd_init(opts):
400 path_factory = DefaultProjectPathFactory(opts.mirror_base, opts.work_base,
401 CHECKOUT_TEMPLATE_NAME)
402
403 if opts.chromeos:
404 setup_chromeos_repos(opts, path_factory)
405 if opts.chrome:
406 setup_chrome_repos(opts, path_factory)
407 for branch in opts.android:
408 setup_android_repos(opts, path_factory, branch)
409
410
411def enumerate_android_branches_available(base):
412 branches = []
413 for name in os.listdir(base):
414 if name.startswith('android.'):
415 branches.append(name.partition('.')[2])
416 return branches
417
418
Kuang-che Wu22f207e2019-02-23 12:53:53 +0800419def do_sync(opts):
Kuang-che Wu41e8b592018-09-25 17:01:30 +0800420 path_factory = DefaultProjectPathFactory(opts.mirror_base, opts.work_base,
421 CHECKOUT_TEMPLATE_NAME)
422
423 sync_all = False
424 if not opts.chromeos and not opts.chrome and not opts.android:
425 logger.info('sync trees for all')
426 sync_all = True
427
428 if sync_all or opts.chromeos:
429 sync_chromeos_code(opts, path_factory)
430 if sync_all or opts.chrome:
431 sync_chrome_code(opts, path_factory)
432
433 if sync_all:
434 android_branches = enumerate_android_branches_available(opts.mirror_base)
435 else:
436 android_branches = opts.android
437 for branch in android_branches:
438 sync_android_code(opts, path_factory, branch)
439
440
Kuang-che Wu22f207e2019-02-23 12:53:53 +0800441def cmd_sync(opts):
442 try:
443 do_sync(opts)
444 except subprocess.CalledProcessError:
445 # Sync may fail due to network or server issues.
446 logger.exception('do_sync failed, will retry one minute later')
447 time.sleep(60)
448 do_sync(opts)
449
450
Kuang-che Wu41e8b592018-09-25 17:01:30 +0800451def cmd_new(opts):
452 work_dir = os.path.join(opts.work_base, opts.session)
453 if not os.path.exists(work_dir):
454 os.makedirs(work_dir)
455
456 template_factory = DefaultProjectPathFactory(opts.mirror_base, opts.work_base,
457 CHECKOUT_TEMPLATE_NAME)
458 path_factory = DefaultProjectPathFactory(opts.mirror_base, opts.work_base,
459 opts.session)
460
461 prepare_all = False
462 if not opts.chromeos and not opts.chrome and not opts.android:
463 logger.info('prepare trees for all')
464 prepare_all = True
465
466 chromeos_template = template_factory.get_chromeos_tree()
467 if (prepare_all and os.path.exists(chromeos_template)) or opts.chromeos:
468 logger.info('prepare tree for chromeos, %s',
469 path_factory.get_chromeos_tree())
470 snapshot_or_copytree(chromeos_template, path_factory.get_chromeos_tree())
471
472 chrome_template = template_factory.get_chrome_tree()
473 if (prepare_all and os.path.exists(chrome_template)) or opts.chrome:
474 logger.info('prepare tree for chrome, %s', path_factory.get_chrome_tree())
475 snapshot_or_copytree(chrome_template, path_factory.get_chrome_tree())
476
477 if prepare_all:
478 android_branches = enumerate_android_branches_available(opts.mirror_base)
479 else:
480 android_branches = opts.android
481 for branch in android_branches:
482 logger.info('prepare tree for android branch=%s, %s', branch,
483 path_factory.get_android_tree(branch))
484 snapshot_or_copytree(
485 template_factory.get_android_tree(branch),
486 path_factory.get_android_tree(branch))
487
488
489def delete_tree(path):
490 if is_btrfs_subvolume(path):
Kuang-che Wu9d3ccde2019-01-03 17:06:09 +0800491 # btrfs should be mounted with 'user_subvol_rm_allowed' option and thus
492 # normal user permission is enough.
Kuang-che Wu41e8b592018-09-25 17:01:30 +0800493 util.check_call('btrfs', 'subvolume', 'delete', path)
494 else:
Kuang-che Wu9d3ccde2019-01-03 17:06:09 +0800495 util.check_call('sudo', 'rm', '-rf', path)
Kuang-che Wu41e8b592018-09-25 17:01:30 +0800496
497
498def cmd_list(opts):
499 print('%-20s %s' % ('Session', 'Path'))
500 for name in os.listdir(opts.work_base):
501 if name == CHECKOUT_TEMPLATE_NAME:
502 continue
503 path = os.path.join(opts.work_base, name)
504 print('%-20s %s' % (name, path))
505
506
507def cmd_delete(opts):
508 assert opts.session
509 path_factory = DefaultProjectPathFactory(opts.mirror_base, opts.work_base,
510 opts.session)
511
512 chromeos_tree = path_factory.get_chromeos_tree()
513 if os.path.exists(chromeos_tree):
514 if os.path.exists(os.path.join(chromeos_tree, 'chromite')):
515 # ignore error
516 util.call('cros_sdk', '--unmount', cwd=chromeos_tree)
517 delete_tree(chromeos_tree)
518
519 chrome_tree = path_factory.get_chrome_tree()
520 if os.path.exists(chrome_tree):
521 delete_tree(chrome_tree)
522
523 android_branches = enumerate_android_branches_available(opts.mirror_base)
524 for branch in android_branches:
525 android_tree = path_factory.get_android_tree(branch)
526 if os.path.exists(android_tree):
527 delete_tree(android_tree)
528
529 os.rmdir(os.path.join(opts.work_base, opts.session))
530
531
532def create_parser():
533 parser = argparse.ArgumentParser(
534 formatter_class=argparse.RawDescriptionHelpFormatter, description=__doc__)
535 parser.add_argument(
536 '--mirror_base',
537 metavar='MIRROR_BASE',
538 default=configure.get('MIRROR_BASE', DEFAULT_MIRROR_BASE),
539 help='Directory for mirrors (default: %(default)s)')
540 parser.add_argument(
541 '--work_base',
542 metavar='WORK_BASE',
543 default=configure.get('WORK_BASE', DEFAULT_WORK_BASE),
544 help='Directory for bisection working directories (default: %(default)s)')
545 common.add_common_arguments(parser)
546 subparsers = parser.add_subparsers(
547 dest='command', title='commands', metavar='<command>')
548
549 parser_init = subparsers.add_parser(
550 'init', help='Mirror source trees and create template checkout')
551 parser_init.add_argument(
552 '--chrome', action='store_true', help='init chrome mirror and tree')
553 parser_init.add_argument(
554 '--chromeos', action='store_true', help='init chromeos mirror and tree')
555 parser_init.add_argument(
556 '--android',
557 metavar='BRANCH',
558 action='append',
559 default=[],
560 help='init android mirror and tree of BRANCH')
561 parser_init.add_argument(
562 '--btrfs',
563 action='store_true',
564 help='create btrfs subvolume for source tree')
565 parser_init.set_defaults(func=cmd_init)
566
567 parser_sync = subparsers.add_parser(
568 'sync',
569 help='Sync source trees',
570 description='Sync all if no projects are specified '
571 '(--chrome, --chromeos, or --android)')
572 parser_sync.add_argument(
573 '--chrome', action='store_true', help='sync chrome mirror and tree')
574 parser_sync.add_argument(
575 '--chromeos', action='store_true', help='sync chromeos mirror and tree')
576 parser_sync.add_argument(
577 '--android',
578 metavar='BRANCH',
579 action='append',
580 default=[],
581 help='sync android mirror and tree of BRANCH')
582 parser_sync.set_defaults(func=cmd_sync)
583
584 parser_new = subparsers.add_parser(
585 'new',
586 help='Create new source checkout for bisect',
587 description='Create for all if no projects are specified '
588 '(--chrome, --chromeos, or --android)')
589 parser_new.add_argument('--session', required=True)
590 parser_new.add_argument(
591 '--chrome', action='store_true', help='create chrome checkout')
592 parser_new.add_argument(
593 '--chromeos', action='store_true', help='create chromeos checkout')
594 parser_new.add_argument(
595 '--android',
596 metavar='BRANCH',
597 action='append',
598 default=[],
599 help='create android checkout of BRANCH')
600 parser_new.set_defaults(func=cmd_new)
601
602 parser_list = subparsers.add_parser(
603 'list', help='List existing sessions with source checkout')
604 parser_list.set_defaults(func=cmd_list)
605
606 parser_delete = subparsers.add_parser('delete', help='Delete source checkout')
607 parser_delete.add_argument('--session', required=True)
608 parser_delete.set_defaults(func=cmd_delete)
609
610 return parser
611
612
613def main():
614 common.init()
615 parser = create_parser()
616 opts = parser.parse_args()
617 common.config_logging(opts)
618
619 opts.func(opts)
620
621
622if __name__ == '__main__':
623 main()