1#!/usr/bin/env python
2# Copyright 2014 The Chromium 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
6"""Scans build output directory for .isolated files, calculates their SHA1
7hashes, stores final list in JSON document and then removes *.isolated files
8found (to ensure no stale *.isolated stay around on the next build).
9
10Used to figure out what tests were build in isolated mode to trigger these
11tests to run on swarming.
12
13For more info see:
14https://sites.google.com/a/chromium.org/dev/developers/testing/isolated-testing
15"""
16
17import glob
18import hashlib
19import json
20import optparse
21import os
22import re
23import sys
24
25
26def hash_file(filepath):
27  """Calculates the hash of a file without reading it all in memory at once."""
28  digest = hashlib.sha1()
29  with open(filepath, 'rb') as f:
30    while True:
31      chunk = f.read(1024*1024)
32      if not chunk:
33        break
34      digest.update(chunk)
35  return digest.hexdigest()
36
37
38def main():
39  parser = optparse.OptionParser(
40      usage='%prog --build-dir <path> --output-json <path>',
41      description=sys.modules[__name__].__doc__)
42  parser.add_option(
43      '--build-dir',
44      help='Path to a directory to search for *.isolated files.')
45  parser.add_option(
46      '--output-json',
47      help='File to dump JSON results into.')
48
49  options, _ = parser.parse_args()
50  if not options.build_dir:
51    parser.error('--build-dir option is required')
52  if not options.output_json:
53    parser.error('--output-json option is required')
54
55  result = {}
56
57  # Get the file hash values and output the pair.
58  pattern = os.path.join(options.build_dir, '*.isolated')
59  for filepath in sorted(glob.glob(pattern)):
60    test_name = os.path.splitext(os.path.basename(filepath))[0]
61    if re.match(r'^.+?\.\d$', test_name):
62      # It's a split .isolated file, e.g. foo.0.isolated. Ignore these.
63      continue
64
65    # TODO(csharp): Remove deletion once the isolate tracked dependencies are
66    # inputs for the isolated files.
67    sha1_hash = hash_file(filepath)
68    os.remove(filepath)
69    result[test_name] = sha1_hash
70
71  with open(options.output_json, 'wb') as f:
72    json.dump(result, f)
73
74  return 0
75
76
77if __name__ == '__main__':
78  sys.exit(main())
79