• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2013 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import ctypes.wintypes
6import hashlib
7import json
8import os
9import subprocess
10import sys
11
12
13BASEDIR = os.path.dirname(os.path.abspath(__file__))
14
15
16GetFileAttributes = ctypes.windll.kernel32.GetFileAttributesW
17GetFileAttributes.argtypes = (ctypes.wintypes.LPWSTR,)
18GetFileAttributes.restype = ctypes.wintypes.DWORD
19FILE_ATTRIBUTE_HIDDEN = 0x2
20FILE_ATTRIBUTE_SYSTEM = 0x4
21
22
23def IsHidden(file_path):
24  """Returns whether the given |file_path| has the 'system' or 'hidden'
25  attribute set."""
26  p = GetFileAttributes(file_path)
27  assert p != 0xffffffff
28  return bool(p & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM))
29
30
31def GetFileList(root):
32  """Gets a normalized list of files under |root|."""
33  assert not os.path.isabs(root)
34  assert os.path.normpath(root) == root
35  file_list = []
36  for base, _, files in os.walk(root):
37    paths = [os.path.join(base, f) for f in files]
38    file_list.extend(x.lower() for x in paths if not IsHidden(x))
39  return sorted(file_list)
40
41
42def MakeTimestampsFileName(root):
43  return os.path.join(root, '..', '.timestamps')
44
45
46def CalculateHash(root):
47  """Calculates the sha1 of the paths to all files in the given |root| and the
48  contents of those files, and returns as a hex string."""
49  file_list = GetFileList(root)
50
51  # Check whether we previously saved timestamps in $root/../.timestamps. If
52  # we didn't, or they don't match, then do the full calculation, otherwise
53  # return the saved value.
54  timestamps_file = MakeTimestampsFileName(root)
55  timestamps_data = {'files': [], 'sha1': ''}
56  if os.path.exists(timestamps_file):
57    with open(timestamps_file, 'rb') as f:
58      try:
59        timestamps_data = json.load(f)
60      except ValueError:
61        # json couldn't be loaded, empty data will force a re-hash.
62        pass
63
64  matches = len(file_list) == len(timestamps_data['files'])
65  if matches:
66    for disk, cached in zip(file_list, timestamps_data['files']):
67      if disk != cached[0] or os.stat(disk).st_mtime != cached[1]:
68        matches = False
69        break
70  if matches:
71    return timestamps_data['sha1']
72
73  digest = hashlib.sha1()
74  for path in file_list:
75    digest.update(path)
76    with open(path, 'rb') as f:
77      digest.update(f.read())
78  return digest.hexdigest()
79
80
81def SaveTimestampsAndHash(root, sha1):
82  """Save timestamps and the final hash to be able to early-out more quickly
83  next time."""
84  file_list = GetFileList(root)
85  timestamps_data = {
86    'files': [[f, os.stat(f).st_mtime] for f in file_list],
87    'sha1': sha1,
88  }
89  with open(MakeTimestampsFileName(root), 'wb') as f:
90    json.dump(timestamps_data, f)
91
92
93def main():
94  if sys.platform not in ('win32', 'cygwin'):
95    return 0
96
97  if len(sys.argv) != 1:
98    print >> sys.stderr, 'Unexpected arguments.'
99    return 1
100
101  # Move to same location as .gclient. This is a no-op when run via gclient.
102  os.chdir(os.path.normpath(os.path.join(BASEDIR, '..\\..\\..\\..')))
103  toolchain_dir = 'src\\third_party\\win_toolchain'
104  target_dir = os.path.join(toolchain_dir, 'files')
105
106  sha1path = os.path.join(toolchain_dir, 'toolchain.sha1')
107  desired_hash = ''
108  if os.path.isfile(sha1path):
109    with open(sha1path, 'rb') as f:
110      desired_hash = f.read().strip()
111
112  # If the current hash doesn't match what we want in the file, nuke and pave.
113  # Typically this script is only run when the .sha1 one file is updated, but
114  # directly calling "gclient runhooks" will also run it, so we cache
115  # based on timestamps to make that case fast.
116  current_hash = CalculateHash(target_dir)
117  if current_hash != desired_hash:
118    print 'Windows toolchain out of date or doesn\'t exist, updating...'
119    if os.path.isdir(target_dir):
120      subprocess.check_call('rmdir /s/q "%s"' % target_dir, shell=True)
121    subprocess.check_call([
122        sys.executable,
123        'src\\tools\\win\\toolchain\\toolchain2013.py',
124        '--targetdir', target_dir])
125    current_hash = CalculateHash(target_dir)
126    if current_hash != desired_hash:
127      print >> sys.stderr, (
128          'Got wrong hash after pulling a new toolchain. '
129          'Wanted \'%s\', got \'%s\'.' % (
130              desired_hash, current_hash))
131      return 1
132    SaveTimestampsAndHash(target_dir, current_hash)
133
134  return 0
135
136
137if __name__ == '__main__':
138  sys.exit(main())
139