• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2# Copyright (c) 2012 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"""Windows can't run .sh files, so this is a Python implementation of
7update.sh. This script should replace update.sh on all platforms eventually."""
8
9import os
10import re
11import shutil
12import subprocess
13import sys
14
15# Do NOT CHANGE this if you don't know what you're doing -- see
16# https://code.google.com/p/chromium/wiki/UpdatingClang
17# Reverting problematic clang rolls is safe, though.
18# Note: this revision is only used for Windows. Other platforms use update.sh.
19LLVM_WIN_REVISION = 'HEAD'
20
21# ASan on Windows is useful enough to use it even while the clang/win is still
22# in bringup. Use a pinned revision to make it slightly more stable.
23if (re.search(r'\b(asan)=1', os.environ.get('GYP_DEFINES', '')) and
24    not 'LLVM_FORCE_HEAD_REVISION' in os.environ):
25  LLVM_WIN_REVISION = '210586'
26
27# Path constants. (All of these should be absolute paths.)
28THIS_DIR = os.path.abspath(os.path.dirname(__file__))
29CHROMIUM_DIR = os.path.abspath(os.path.join(THIS_DIR, '..', '..', '..'))
30LLVM_DIR = os.path.join(CHROMIUM_DIR, 'third_party', 'llvm')
31LLVM_BUILD_DIR = os.path.join(CHROMIUM_DIR, 'third_party', 'llvm-build',
32                              'Release+Asserts')
33COMPILER_RT_BUILD_DIR = os.path.join(LLVM_BUILD_DIR, '32bit-compiler-rt')
34CLANG_DIR = os.path.join(LLVM_DIR, 'tools', 'clang')
35COMPILER_RT_DIR = os.path.join(LLVM_DIR, 'projects', 'compiler-rt')
36STAMP_FILE = os.path.join(LLVM_BUILD_DIR, 'cr_build_revision')
37
38LLVM_REPO_URL='https://llvm.org/svn/llvm-project'
39if 'LLVM_REPO_URL' in os.environ:
40  LLVM_REPO_URL = os.environ['LLVM_REPO_URL']
41
42
43def ReadStampFile():
44  """Return the contents of the stamp file, or '' if it doesn't exist."""
45  try:
46    with open(STAMP_FILE, 'r') as f:
47      return f.read();
48  except IOError:
49    return ''
50
51
52def WriteStampFile(s):
53  """Write s to the stamp file."""
54  if not os.path.exists(LLVM_BUILD_DIR):
55    os.makedirs(LLVM_BUILD_DIR)
56  with open(STAMP_FILE, 'w') as f:
57    f.write(s)
58
59
60def DeleteFiles(dir, pattern):
61  """Delete all files in dir matching pattern."""
62  n = 0
63  regex = re.compile(r'^' + pattern + r'$')
64  for root, _, files in os.walk(dir):
65    for f in files:
66      if regex.match(f):
67        os.remove(os.path.join(root, f))
68        n += 1
69  return n
70
71
72def ClobberChromiumBuildFiles():
73  """Clobber Chomium build files."""
74  print 'Clobbering Chromium build files...'
75  out_dir = os.path.join(CHROMIUM_DIR, 'out')
76  if os.path.isdir(out_dir):
77    shutil.rmtree(out_dir)
78    print 'Removed Chromium out dir: %s.' % (out_dir)
79
80
81def RunCommand(command, tries=1):
82  """Run a command, possibly with multiple retries."""
83  for i in range(0, tries):
84    print 'Running %s (try #%d)' % (str(command), i + 1)
85    if subprocess.call(command, shell=True) == 0:
86      return
87    print 'Failed.'
88  sys.exit(1)
89
90def CopyFile(src, dst):
91  """Copy a file from src to dst."""
92  shutil.copy(src, dst)
93  print "Copying %s to %s" % (src, dst)
94
95def Checkout(name, url, dir):
96  """Checkout the SVN module at url into dir. Use name for the log message."""
97  print "Checking out %s r%s into '%s'" % (name, LLVM_WIN_REVISION, dir)
98  RunCommand(['svn', 'checkout', '--force',
99              url + '@' + LLVM_WIN_REVISION, dir], tries=2)
100
101
102vs_version = None
103def GetVSVersion():
104  global vs_version
105  if not vs_version:
106    # TODO(hans): Find a less hacky way to find the MSVS installation.
107    sys.path.append(os.path.join(CHROMIUM_DIR, 'tools', 'gyp', 'pylib'))
108    import gyp.MSVSVersion
109    # We request VS 2013 because Clang won't build with 2010, and 2013 will be
110    # the default for Chromium soon anyway.
111    vs_version = gyp.MSVSVersion.SelectVisualStudioVersion('2013')
112  return vs_version
113
114
115def UpdateClang():
116  print 'Updating Clang to %s...' % (LLVM_WIN_REVISION)
117  if LLVM_WIN_REVISION != 'HEAD' and ReadStampFile() == LLVM_WIN_REVISION:
118    print 'Already up to date.'
119    return 0
120
121  ClobberChromiumBuildFiles()
122
123  # Reset the stamp file in case the build is unsuccessful.
124  WriteStampFile('')
125
126  Checkout('LLVM', LLVM_REPO_URL + '/llvm/trunk', LLVM_DIR)
127  Checkout('Clang', LLVM_REPO_URL + '/cfe/trunk', CLANG_DIR)
128  Checkout('compiler-rt', LLVM_REPO_URL + '/compiler-rt/trunk', COMPILER_RT_DIR)
129
130  if not os.path.exists(LLVM_BUILD_DIR):
131    os.makedirs(LLVM_BUILD_DIR)
132  os.chdir(LLVM_BUILD_DIR)
133
134  if not re.search(r'cmake', os.environ['PATH'], flags=re.IGNORECASE):
135    # If CMake is not on the path, try looking in a standard location.
136    os.environ['PATH'] += os.pathsep + 'C:\\Program Files (x86)\\CMake 2.8\\bin'
137
138  RunCommand(GetVSVersion().SetupScript('x64') +
139             ['&&', 'cmake', '-GNinja', '-DCMAKE_BUILD_TYPE=Release',
140              '-DLLVM_ENABLE_ASSERTIONS=ON', LLVM_DIR])
141  RunCommand(GetVSVersion().SetupScript('x64') + ['&&', 'ninja', 'all'])
142
143  # Do an x86 build of compiler-rt to get the 32-bit ASan run-time.
144  # TODO(hans): Remove once the regular build above produces this.
145  if not os.path.exists(COMPILER_RT_BUILD_DIR):
146    os.makedirs(COMPILER_RT_BUILD_DIR)
147  os.chdir(COMPILER_RT_BUILD_DIR)
148  RunCommand(GetVSVersion().SetupScript('x86') +
149             ['&&', 'cmake', '-GNinja', '-DCMAKE_BUILD_TYPE=Release',
150              '-DLLVM_ENABLE_ASSERTIONS=ON', LLVM_DIR])
151  RunCommand(GetVSVersion().SetupScript('x86') + ['&&', 'ninja', 'compiler-rt'])
152
153  # TODO(hans): Make this (and the .gypi file) version number independent.
154  asan_rt_lib_src_dir = os.path.join(COMPILER_RT_BUILD_DIR, 'lib', 'clang',
155                                     '3.5.0', 'lib', 'windows')
156  asan_rt_lib_dst_dir = os.path.join(LLVM_BUILD_DIR, 'lib', 'clang',
157                                     '3.5.0', 'lib', 'windows')
158
159  if not os.path.exists(asan_rt_lib_dst_dir):
160    os.makedirs(asan_rt_lib_dst_dir)
161  for root, _, files in os.walk(asan_rt_lib_src_dir):
162    for f in files:
163      if re.match(r'^.*-i386\.lib$', f):
164        CopyFile(os.path.join(root, f), asan_rt_lib_dst_dir)
165
166  CopyFile(os.path.join(asan_rt_lib_src_dir, '..', '..', 'asan_blacklist.txt'),
167           os.path.join(asan_rt_lib_dst_dir, '..', '..'))
168
169  # Make an extra copy of the sanitizer headers, to be put on the include path
170  # of the fallback compiler.
171  sanitizer_include_dir = os.path.join(LLVM_BUILD_DIR, 'lib', 'clang', '3.5.0',
172                                       'include', 'sanitizer')
173  aux_sanitizer_include_dir = os.path.join(LLVM_BUILD_DIR, 'lib', 'clang',
174                                           '3.5.0', 'include_sanitizer',
175                                           'sanitizer')
176  if not os.path.exists(aux_sanitizer_include_dir):
177    os.makedirs(aux_sanitizer_include_dir)
178  for _, _, files in os.walk(sanitizer_include_dir):
179    for f in files:
180      CopyFile(os.path.join(sanitizer_include_dir, f),
181               aux_sanitizer_include_dir)
182
183  WriteStampFile(LLVM_WIN_REVISION)
184  print 'Clang update was successful.'
185  return 0
186
187
188def main():
189  if not sys.platform in ['win32', 'cygwin']:
190    # For non-Windows, fall back to update.sh.
191    # TODO(hans): Make update.py replace update.sh completely.
192
193    # This script is called by gclient. gclient opens its hooks subprocesses
194    # with (stdout=subprocess.PIPE, stderr=subprocess.STDOUT) and then does
195    # custom output processing that breaks printing '\r' characters for
196    # single-line updating status messages as printed by curl and wget.
197    # Work around this by setting stderr of the update.sh process to stdin (!):
198    # gclient doesn't redirect stdin, and while stdin itself is read-only, a
199    # dup()ed sys.stdin is writable, try
200    #   fd2 = os.dup(sys.stdin.fileno()); os.write(fd2, 'hi')
201    # TODO: Fix gclient instead, http://crbug.com/95350
202    return subprocess.call(
203        [os.path.join(os.path.dirname(__file__), 'update.sh')] +  sys.argv[1:],
204        stderr=os.fdopen(os.dup(sys.stdin.fileno())))
205
206  if not re.search(r'\b(clang|asan)=1', os.environ.get('GYP_DEFINES', '')):
207    print 'Skipping Clang update (clang=1 was not set in GYP_DEFINES).'
208    return 0
209
210  if re.search(r'\b(make_clang_dir)=', os.environ.get('GYP_DEFINES', '')):
211    print 'Skipping Clang update (make_clang_dir= was set in GYP_DEFINES).'
212    return 0
213
214  return UpdateClang()
215
216
217if __name__ == '__main__':
218  sys.exit(main())
219