xref: /aosp_15_r20/external/perfetto/tools/test_data (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*6dbdd20aSAndroid Build Coastguard Worker"""
3*6dbdd20aSAndroid Build Coastguard WorkerScript to synchronize (local>remote and viceversa) test data files from/to GCS.
4*6dbdd20aSAndroid Build Coastguard Worker
5*6dbdd20aSAndroid Build Coastguard Worker//test/data files are not checked in the codebase because they are large binary
6*6dbdd20aSAndroid Build Coastguard Workerfile and change frequently. Instead we check-in only xxx.sha256 files, which
7*6dbdd20aSAndroid Build Coastguard Workercontain the SHA-256 of the actual binary file, and sync them from a GCS bucket.
8*6dbdd20aSAndroid Build Coastguard Worker
9*6dbdd20aSAndroid Build Coastguard WorkerFile in the GCS bucket are content-indexed as gs://bucket/file_name-a1b2c3f4 .
10*6dbdd20aSAndroid Build Coastguard Worker
11*6dbdd20aSAndroid Build Coastguard WorkerUsage:
12*6dbdd20aSAndroid Build Coastguard Worker./test_data status     # Prints the status of new & modified files.
13*6dbdd20aSAndroid Build Coastguard Worker./test_data download   # To sync remote>local (used by install-build-deps).
14*6dbdd20aSAndroid Build Coastguard Worker./test_data upload     # To upload newly created and modified files.
15*6dbdd20aSAndroid Build Coastguard Worker"""
16*6dbdd20aSAndroid Build Coastguard Worker
17*6dbdd20aSAndroid Build Coastguard Workerimport argparse
18*6dbdd20aSAndroid Build Coastguard Workerimport logging
19*6dbdd20aSAndroid Build Coastguard Workerimport os
20*6dbdd20aSAndroid Build Coastguard Workerimport sys
21*6dbdd20aSAndroid Build Coastguard Workerimport hashlib
22*6dbdd20aSAndroid Build Coastguard Workerimport subprocess
23*6dbdd20aSAndroid Build Coastguard Worker
24*6dbdd20aSAndroid Build Coastguard Workerfrom multiprocessing.pool import ThreadPool
25*6dbdd20aSAndroid Build Coastguard Workerfrom collections import namedtuple, defaultdict
26*6dbdd20aSAndroid Build Coastguard Worker
27*6dbdd20aSAndroid Build Coastguard WorkerROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
28*6dbdd20aSAndroid Build Coastguard WorkerBUCKET = 'gs://perfetto/test_data'
29*6dbdd20aSAndroid Build Coastguard WorkerSUFFIX = '.sha256'
30*6dbdd20aSAndroid Build Coastguard Worker
31*6dbdd20aSAndroid Build Coastguard WorkerFS_MATCH = 'matches'
32*6dbdd20aSAndroid Build Coastguard WorkerFS_NEW_FILE = 'needs upload'
33*6dbdd20aSAndroid Build Coastguard WorkerFS_MODIFIED = 'modified'
34*6dbdd20aSAndroid Build Coastguard WorkerFS_MISSING = 'needs download'
35*6dbdd20aSAndroid Build Coastguard Worker
36*6dbdd20aSAndroid Build Coastguard WorkerFileStat = namedtuple('FileStat',
37*6dbdd20aSAndroid Build Coastguard Worker                      ['path', 'status', 'actual_digest', 'expected_digest'])
38*6dbdd20aSAndroid Build Coastguard Workerargs = None
39*6dbdd20aSAndroid Build Coastguard Worker
40*6dbdd20aSAndroid Build Coastguard Worker
41*6dbdd20aSAndroid Build Coastguard Workerdef relpath(path):
42*6dbdd20aSAndroid Build Coastguard Worker  return os.path.relpath(path, ROOT_DIR)
43*6dbdd20aSAndroid Build Coastguard Worker
44*6dbdd20aSAndroid Build Coastguard Worker
45*6dbdd20aSAndroid Build Coastguard Workerdef download(url, out_file):
46*6dbdd20aSAndroid Build Coastguard Worker  subprocess.check_call(['curl', '-L', '-s', '-o', out_file, url])
47*6dbdd20aSAndroid Build Coastguard Worker
48*6dbdd20aSAndroid Build Coastguard Worker
49*6dbdd20aSAndroid Build Coastguard Workerdef list_files(path, scan_new_files=False):
50*6dbdd20aSAndroid Build Coastguard Worker  """ List files recursively in path.
51*6dbdd20aSAndroid Build Coastguard Worker
52*6dbdd20aSAndroid Build Coastguard Worker  If scan_new_files=False, returns only files with a maching xxx.sha256 tracker.
53*6dbdd20aSAndroid Build Coastguard Worker  If scan_new_files=True returns all files including untracked ones.
54*6dbdd20aSAndroid Build Coastguard Worker  """
55*6dbdd20aSAndroid Build Coastguard Worker  seen = set()
56*6dbdd20aSAndroid Build Coastguard Worker  for root, _, files in os.walk(path):
57*6dbdd20aSAndroid Build Coastguard Worker    for fname in files:
58*6dbdd20aSAndroid Build Coastguard Worker      if fname.endswith('.swp'):
59*6dbdd20aSAndroid Build Coastguard Worker        continue  # Temporary files left around if CTRL-C-ing while downloading.
60*6dbdd20aSAndroid Build Coastguard Worker      if fname in ["OWNERS", "README.md"]:
61*6dbdd20aSAndroid Build Coastguard Worker        continue  # OWNERS or README.md file should not be uploaded.
62*6dbdd20aSAndroid Build Coastguard Worker      fpath = os.path.join(root, fname)
63*6dbdd20aSAndroid Build Coastguard Worker      if not os.path.isfile(fpath) or fname.startswith('.'):
64*6dbdd20aSAndroid Build Coastguard Worker        continue
65*6dbdd20aSAndroid Build Coastguard Worker      if fpath.endswith(SUFFIX):
66*6dbdd20aSAndroid Build Coastguard Worker        fpath = fpath[:-len(SUFFIX)]
67*6dbdd20aSAndroid Build Coastguard Worker      elif not scan_new_files:
68*6dbdd20aSAndroid Build Coastguard Worker        continue
69*6dbdd20aSAndroid Build Coastguard Worker      if fpath not in seen:
70*6dbdd20aSAndroid Build Coastguard Worker        seen.add(fpath)
71*6dbdd20aSAndroid Build Coastguard Worker        yield fpath
72*6dbdd20aSAndroid Build Coastguard Worker
73*6dbdd20aSAndroid Build Coastguard Worker
74*6dbdd20aSAndroid Build Coastguard Workerdef hash_file(fpath):
75*6dbdd20aSAndroid Build Coastguard Worker  hasher = hashlib.sha256()
76*6dbdd20aSAndroid Build Coastguard Worker  with open(fpath, 'rb') as f:
77*6dbdd20aSAndroid Build Coastguard Worker    for chunk in iter(lambda: f.read(32768), b''):
78*6dbdd20aSAndroid Build Coastguard Worker      hasher.update(chunk)
79*6dbdd20aSAndroid Build Coastguard Worker  return hasher.hexdigest()
80*6dbdd20aSAndroid Build Coastguard Worker
81*6dbdd20aSAndroid Build Coastguard Worker
82*6dbdd20aSAndroid Build Coastguard Workerdef map_concurrently(fn, files):
83*6dbdd20aSAndroid Build Coastguard Worker  done = 0
84*6dbdd20aSAndroid Build Coastguard Worker  for fs in ThreadPool(args.jobs).imap_unordered(fn, files):
85*6dbdd20aSAndroid Build Coastguard Worker    assert (isinstance(fs, FileStat))
86*6dbdd20aSAndroid Build Coastguard Worker    done += 1
87*6dbdd20aSAndroid Build Coastguard Worker    if not args.quiet:
88*6dbdd20aSAndroid Build Coastguard Worker      print(
89*6dbdd20aSAndroid Build Coastguard Worker          '[%d/%d] %-60s' % (done, len(files), relpath(fs.path)[-60:]),
90*6dbdd20aSAndroid Build Coastguard Worker          end='\r')
91*6dbdd20aSAndroid Build Coastguard Worker  if not args.quiet:
92*6dbdd20aSAndroid Build Coastguard Worker    print('')
93*6dbdd20aSAndroid Build Coastguard Worker
94*6dbdd20aSAndroid Build Coastguard Worker
95*6dbdd20aSAndroid Build Coastguard Workerdef get_file_status(fpath):
96*6dbdd20aSAndroid Build Coastguard Worker  sha_file = fpath + SUFFIX
97*6dbdd20aSAndroid Build Coastguard Worker  sha_exists = os.path.exists(sha_file)
98*6dbdd20aSAndroid Build Coastguard Worker  file_exists = os.path.exists(fpath)
99*6dbdd20aSAndroid Build Coastguard Worker  actual_digest = None
100*6dbdd20aSAndroid Build Coastguard Worker  expected_digest = None
101*6dbdd20aSAndroid Build Coastguard Worker  if sha_exists:
102*6dbdd20aSAndroid Build Coastguard Worker    with open(sha_file, 'r') as f:
103*6dbdd20aSAndroid Build Coastguard Worker      expected_digest = f.readline().strip()
104*6dbdd20aSAndroid Build Coastguard Worker  if file_exists:
105*6dbdd20aSAndroid Build Coastguard Worker    actual_digest = hash_file(fpath)
106*6dbdd20aSAndroid Build Coastguard Worker  if sha_exists and not file_exists:
107*6dbdd20aSAndroid Build Coastguard Worker    status = FS_MISSING
108*6dbdd20aSAndroid Build Coastguard Worker  elif not sha_exists and file_exists:
109*6dbdd20aSAndroid Build Coastguard Worker    status = FS_NEW_FILE
110*6dbdd20aSAndroid Build Coastguard Worker  elif not sha_exists and not file_exists:
111*6dbdd20aSAndroid Build Coastguard Worker    raise Exception(fpath)
112*6dbdd20aSAndroid Build Coastguard Worker  elif expected_digest == actual_digest:
113*6dbdd20aSAndroid Build Coastguard Worker    status = FS_MATCH
114*6dbdd20aSAndroid Build Coastguard Worker  else:
115*6dbdd20aSAndroid Build Coastguard Worker    status = FS_MODIFIED
116*6dbdd20aSAndroid Build Coastguard Worker  return FileStat(fpath, status, actual_digest, expected_digest)
117*6dbdd20aSAndroid Build Coastguard Worker
118*6dbdd20aSAndroid Build Coastguard Worker
119*6dbdd20aSAndroid Build Coastguard Workerdef cmd_upload(dir):
120*6dbdd20aSAndroid Build Coastguard Worker  all_files = list_files(dir, scan_new_files=True)
121*6dbdd20aSAndroid Build Coastguard Worker  files_to_upload = []
122*6dbdd20aSAndroid Build Coastguard Worker  for fs in ThreadPool(args.jobs).imap_unordered(get_file_status, all_files):
123*6dbdd20aSAndroid Build Coastguard Worker    if fs.status in (FS_NEW_FILE, FS_MODIFIED):
124*6dbdd20aSAndroid Build Coastguard Worker      files_to_upload.append(fs)
125*6dbdd20aSAndroid Build Coastguard Worker  if len(files_to_upload) == 0:
126*6dbdd20aSAndroid Build Coastguard Worker    if not args.quiet:
127*6dbdd20aSAndroid Build Coastguard Worker      print('No modified or new files require uploading')
128*6dbdd20aSAndroid Build Coastguard Worker    return 0
129*6dbdd20aSAndroid Build Coastguard Worker  if args.dry_run:
130*6dbdd20aSAndroid Build Coastguard Worker    return 0
131*6dbdd20aSAndroid Build Coastguard Worker  if not args.quiet:
132*6dbdd20aSAndroid Build Coastguard Worker    print('\n'.join(relpath(f.path) for f in files_to_upload))
133*6dbdd20aSAndroid Build Coastguard Worker    print('')
134*6dbdd20aSAndroid Build Coastguard Worker    print('About to upload %d files' % len(files_to_upload))
135*6dbdd20aSAndroid Build Coastguard Worker    input('Press a key to continue or CTRL-C to abort')
136*6dbdd20aSAndroid Build Coastguard Worker
137*6dbdd20aSAndroid Build Coastguard Worker  def upload_one_file(fs):
138*6dbdd20aSAndroid Build Coastguard Worker    assert (fs.actual_digest is not None)
139*6dbdd20aSAndroid Build Coastguard Worker    dst_name = '%s/%s-%s' % (args.bucket, os.path.basename(
140*6dbdd20aSAndroid Build Coastguard Worker        fs.path), fs.actual_digest)
141*6dbdd20aSAndroid Build Coastguard Worker    cmd = ['gsutil', '-q', 'cp', '-n', '-a', 'public-read', fs.path, dst_name]
142*6dbdd20aSAndroid Build Coastguard Worker    logging.debug(' '.join(cmd))
143*6dbdd20aSAndroid Build Coastguard Worker    subprocess.check_call(cmd)
144*6dbdd20aSAndroid Build Coastguard Worker    with open(fs.path + SUFFIX + '.swp', 'w') as f:
145*6dbdd20aSAndroid Build Coastguard Worker      f.write(fs.actual_digest)
146*6dbdd20aSAndroid Build Coastguard Worker    os.replace(fs.path + SUFFIX + '.swp', fs.path + SUFFIX)
147*6dbdd20aSAndroid Build Coastguard Worker    return fs
148*6dbdd20aSAndroid Build Coastguard Worker
149*6dbdd20aSAndroid Build Coastguard Worker  map_concurrently(upload_one_file, files_to_upload)
150*6dbdd20aSAndroid Build Coastguard Worker  return 0
151*6dbdd20aSAndroid Build Coastguard Worker
152*6dbdd20aSAndroid Build Coastguard Worker
153*6dbdd20aSAndroid Build Coastguard Workerdef cmd_clean(dir):
154*6dbdd20aSAndroid Build Coastguard Worker  all_files = list_files(dir, scan_new_files=True)
155*6dbdd20aSAndroid Build Coastguard Worker  files_to_clean = []
156*6dbdd20aSAndroid Build Coastguard Worker  for fs in ThreadPool(args.jobs).imap_unordered(get_file_status, all_files):
157*6dbdd20aSAndroid Build Coastguard Worker    if fs.status in (FS_NEW_FILE, FS_MODIFIED):
158*6dbdd20aSAndroid Build Coastguard Worker      files_to_clean.append(fs.path)
159*6dbdd20aSAndroid Build Coastguard Worker  if len(files_to_clean) == 0:
160*6dbdd20aSAndroid Build Coastguard Worker    if not args.quiet:
161*6dbdd20aSAndroid Build Coastguard Worker      print('No modified or new files require cleaning')
162*6dbdd20aSAndroid Build Coastguard Worker    return 0
163*6dbdd20aSAndroid Build Coastguard Worker  if args.dry_run:
164*6dbdd20aSAndroid Build Coastguard Worker    return 0
165*6dbdd20aSAndroid Build Coastguard Worker  if not args.quiet:
166*6dbdd20aSAndroid Build Coastguard Worker    print('\n'.join(relpath(f) for f in files_to_clean))
167*6dbdd20aSAndroid Build Coastguard Worker    print('')
168*6dbdd20aSAndroid Build Coastguard Worker    print('About to remove %d files' % len(files_to_clean))
169*6dbdd20aSAndroid Build Coastguard Worker    input('Press a key to continue or CTRL-C to abort')
170*6dbdd20aSAndroid Build Coastguard Worker  list(map(os.remove, files_to_clean))
171*6dbdd20aSAndroid Build Coastguard Worker  return 0
172*6dbdd20aSAndroid Build Coastguard Worker
173*6dbdd20aSAndroid Build Coastguard Worker
174*6dbdd20aSAndroid Build Coastguard Workerdef cmd_download(dir, overwrite_locally_modified=False):
175*6dbdd20aSAndroid Build Coastguard Worker  files_to_download = []
176*6dbdd20aSAndroid Build Coastguard Worker  modified = []
177*6dbdd20aSAndroid Build Coastguard Worker  all_files = list_files(dir, scan_new_files=False)
178*6dbdd20aSAndroid Build Coastguard Worker  for fs in ThreadPool(args.jobs).imap_unordered(get_file_status, all_files):
179*6dbdd20aSAndroid Build Coastguard Worker    if fs.status == FS_MISSING:
180*6dbdd20aSAndroid Build Coastguard Worker      files_to_download.append(fs)
181*6dbdd20aSAndroid Build Coastguard Worker    elif fs.status == FS_MODIFIED:
182*6dbdd20aSAndroid Build Coastguard Worker      modified.append(fs)
183*6dbdd20aSAndroid Build Coastguard Worker
184*6dbdd20aSAndroid Build Coastguard Worker  if len(modified) > 0 and not overwrite_locally_modified:
185*6dbdd20aSAndroid Build Coastguard Worker    print('WARNING: The following files diverged locally and will NOT be ' +
186*6dbdd20aSAndroid Build Coastguard Worker          'overwritten if you continue')
187*6dbdd20aSAndroid Build Coastguard Worker    print('\n'.join(relpath(f.path) for f in modified))
188*6dbdd20aSAndroid Build Coastguard Worker    print('')
189*6dbdd20aSAndroid Build Coastguard Worker    print('Re run `download --overwrite` to overwrite locally modified files')
190*6dbdd20aSAndroid Build Coastguard Worker    print('or `upload` to sync them on the GCS bucket')
191*6dbdd20aSAndroid Build Coastguard Worker    print('')
192*6dbdd20aSAndroid Build Coastguard Worker    input('Press a key to continue or CTRL-C to abort')
193*6dbdd20aSAndroid Build Coastguard Worker  elif overwrite_locally_modified:
194*6dbdd20aSAndroid Build Coastguard Worker    files_to_download += modified
195*6dbdd20aSAndroid Build Coastguard Worker
196*6dbdd20aSAndroid Build Coastguard Worker  if len(files_to_download) == 0:
197*6dbdd20aSAndroid Build Coastguard Worker    if not args.quiet:
198*6dbdd20aSAndroid Build Coastguard Worker      print('Nothing to do, all files are synced')
199*6dbdd20aSAndroid Build Coastguard Worker    return 0
200*6dbdd20aSAndroid Build Coastguard Worker
201*6dbdd20aSAndroid Build Coastguard Worker  if not args.quiet:
202*6dbdd20aSAndroid Build Coastguard Worker    print('Downloading %d files in //%s' %
203*6dbdd20aSAndroid Build Coastguard Worker          (len(files_to_download), relpath(args.dir)))
204*6dbdd20aSAndroid Build Coastguard Worker  if args.dry_run:
205*6dbdd20aSAndroid Build Coastguard Worker    print('\n'.join(files_to_download))
206*6dbdd20aSAndroid Build Coastguard Worker    return
207*6dbdd20aSAndroid Build Coastguard Worker
208*6dbdd20aSAndroid Build Coastguard Worker  def download_one_file(fs):
209*6dbdd20aSAndroid Build Coastguard Worker    assert (fs.expected_digest is not None)
210*6dbdd20aSAndroid Build Coastguard Worker    uri = '%s/%s-%s' % (args.bucket, os.path.basename(
211*6dbdd20aSAndroid Build Coastguard Worker        fs.path), fs.expected_digest)
212*6dbdd20aSAndroid Build Coastguard Worker    uri = uri.replace('gs://', 'https://storage.googleapis.com/')
213*6dbdd20aSAndroid Build Coastguard Worker    logging.debug(uri)
214*6dbdd20aSAndroid Build Coastguard Worker    tmp_path = fs.path + '.swp'
215*6dbdd20aSAndroid Build Coastguard Worker    download(uri, tmp_path)
216*6dbdd20aSAndroid Build Coastguard Worker    digest = hash_file(tmp_path)
217*6dbdd20aSAndroid Build Coastguard Worker    if digest != fs.expected_digest:
218*6dbdd20aSAndroid Build Coastguard Worker      raise Exception('Mismatching digest for %s. expected=%s, actual=%s' %
219*6dbdd20aSAndroid Build Coastguard Worker                      (uri, fs.expected_digest, digest))
220*6dbdd20aSAndroid Build Coastguard Worker    os.replace(tmp_path, fs.path)
221*6dbdd20aSAndroid Build Coastguard Worker    return fs
222*6dbdd20aSAndroid Build Coastguard Worker
223*6dbdd20aSAndroid Build Coastguard Worker  map_concurrently(download_one_file, files_to_download)
224*6dbdd20aSAndroid Build Coastguard Worker  return 0
225*6dbdd20aSAndroid Build Coastguard Worker
226*6dbdd20aSAndroid Build Coastguard Worker
227*6dbdd20aSAndroid Build Coastguard Workerdef cmd_status(dir):
228*6dbdd20aSAndroid Build Coastguard Worker  files = list_files(dir, scan_new_files=True)
229*6dbdd20aSAndroid Build Coastguard Worker  file_by_status = defaultdict(list)
230*6dbdd20aSAndroid Build Coastguard Worker  num_files = 0
231*6dbdd20aSAndroid Build Coastguard Worker  num_out_of_sync = 0
232*6dbdd20aSAndroid Build Coastguard Worker  for fs in ThreadPool(args.jobs).imap_unordered(get_file_status, files):
233*6dbdd20aSAndroid Build Coastguard Worker    file_by_status[fs.status].append(relpath(fs.path))
234*6dbdd20aSAndroid Build Coastguard Worker    num_files += 1
235*6dbdd20aSAndroid Build Coastguard Worker  for status, rpaths in sorted(file_by_status.items()):
236*6dbdd20aSAndroid Build Coastguard Worker    if status == FS_NEW_FILE and args.ignore_new:
237*6dbdd20aSAndroid Build Coastguard Worker      continue
238*6dbdd20aSAndroid Build Coastguard Worker    if status != FS_MATCH:
239*6dbdd20aSAndroid Build Coastguard Worker      for rpath in rpaths:
240*6dbdd20aSAndroid Build Coastguard Worker        num_out_of_sync += 1
241*6dbdd20aSAndroid Build Coastguard Worker        if not args.quiet:
242*6dbdd20aSAndroid Build Coastguard Worker          print('%-15s: %s' % (status, rpath))
243*6dbdd20aSAndroid Build Coastguard Worker  if num_out_of_sync == 0:
244*6dbdd20aSAndroid Build Coastguard Worker    if not args.quiet:
245*6dbdd20aSAndroid Build Coastguard Worker      print('Scanned %d files in //%s, everything in sync.' %
246*6dbdd20aSAndroid Build Coastguard Worker            (num_files, relpath(dir)))
247*6dbdd20aSAndroid Build Coastguard Worker    return 0
248*6dbdd20aSAndroid Build Coastguard Worker  return 1
249*6dbdd20aSAndroid Build Coastguard Worker
250*6dbdd20aSAndroid Build Coastguard Worker
251*6dbdd20aSAndroid Build Coastguard Workerdef main():
252*6dbdd20aSAndroid Build Coastguard Worker  parser = argparse.ArgumentParser()
253*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument('--dir', default=os.path.join(ROOT_DIR, 'test/data'))
254*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument('--overwrite', action='store_true')
255*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument('--bucket', default=BUCKET)
256*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument('--jobs', '-j', default=10, type=int)
257*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument('--dry-run', '-n', action='store_true')
258*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument('--quiet', '-q', action='store_true')
259*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument('--verbose', '-v', action='store_true')
260*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument('--ignore-new', action='store_true')
261*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument('cmd', choices=['status', 'download', 'upload', 'clean'])
262*6dbdd20aSAndroid Build Coastguard Worker  global args
263*6dbdd20aSAndroid Build Coastguard Worker  args = parser.parse_args()
264*6dbdd20aSAndroid Build Coastguard Worker  logging.basicConfig(
265*6dbdd20aSAndroid Build Coastguard Worker      format='%(asctime)s %(levelname).1s %(message)s',
266*6dbdd20aSAndroid Build Coastguard Worker      level=logging.DEBUG if args.verbose else logging.INFO,
267*6dbdd20aSAndroid Build Coastguard Worker      datefmt=r'%H:%M:%S')
268*6dbdd20aSAndroid Build Coastguard Worker  if args.cmd == 'status':
269*6dbdd20aSAndroid Build Coastguard Worker    return cmd_status(args.dir)
270*6dbdd20aSAndroid Build Coastguard Worker  if args.cmd == 'download':
271*6dbdd20aSAndroid Build Coastguard Worker    return cmd_download(args.dir, overwrite_locally_modified=args.overwrite)
272*6dbdd20aSAndroid Build Coastguard Worker  if args.cmd == 'upload':
273*6dbdd20aSAndroid Build Coastguard Worker    return cmd_upload(args.dir)
274*6dbdd20aSAndroid Build Coastguard Worker  if args.cmd == 'clean':
275*6dbdd20aSAndroid Build Coastguard Worker    return cmd_clean(args.dir)
276*6dbdd20aSAndroid Build Coastguard Worker  print('Unknown command: %s' % args.cmd)
277*6dbdd20aSAndroid Build Coastguard Worker
278*6dbdd20aSAndroid Build Coastguard Worker
279*6dbdd20aSAndroid Build Coastguard Workerif __name__ == '__main__':
280*6dbdd20aSAndroid Build Coastguard Worker  sys.exit(main())
281