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.
6 """Windows can't run .sh files, so this is a Python implementation of
7 update.sh. This script should replace update.sh on all platforms eventually."""
17 # Do NOT CHANGE this if you don't know what you're doing -- see
18 # https://code.google.com/p/chromium/wiki/UpdatingClang
19 # Reverting problematic clang rolls is safe, though.
20 # Note: this revision is only used for Windows. Other platforms use update.sh.
21 LLVM_WIN_REVISION
= 'HEAD'
23 # ASan on Windows is useful enough to use it even while the clang/win is still
24 # in bringup. Use a pinned revision to make it slightly more stable.
25 if (re
.search(r
'\b(asan)=1', os
.environ
.get('GYP_DEFINES', '')) and
26 not 'LLVM_FORCE_HEAD_REVISION' in os
.environ
):
27 LLVM_WIN_REVISION
= '229860'
29 # Path constants. (All of these should be absolute paths.)
30 THIS_DIR
= os
.path
.abspath(os
.path
.dirname(__file__
))
31 CHROMIUM_DIR
= os
.path
.abspath(os
.path
.join(THIS_DIR
, '..', '..', '..'))
32 LLVM_DIR
= os
.path
.join(CHROMIUM_DIR
, 'third_party', 'llvm')
33 LLVM_BUILD_DIR
= os
.path
.join(CHROMIUM_DIR
, 'third_party', 'llvm-build',
35 COMPILER_RT_BUILD_DIR
= os
.path
.join(LLVM_BUILD_DIR
, '32bit-compiler-rt')
36 CLANG_DIR
= os
.path
.join(LLVM_DIR
, 'tools', 'clang')
37 LLD_DIR
= os
.path
.join(LLVM_DIR
, 'tools', 'lld')
38 COMPILER_RT_DIR
= os
.path
.join(LLVM_DIR
, 'projects', 'compiler-rt')
39 STAMP_FILE
= os
.path
.join(LLVM_BUILD_DIR
, 'cr_build_revision')
42 LLVM_REPO_URL
='https://llvm.org/svn/llvm-project'
43 if 'LLVM_REPO_URL' in os
.environ
:
44 LLVM_REPO_URL
= os
.environ
['LLVM_REPO_URL']
48 """Return the contents of the stamp file, or '' if it doesn't exist."""
50 with
open(STAMP_FILE
, 'r') as f
:
56 def WriteStampFile(s
):
57 """Write s to the stamp file."""
58 if not os
.path
.exists(LLVM_BUILD_DIR
):
59 os
.makedirs(LLVM_BUILD_DIR
)
60 with
open(STAMP_FILE
, 'w') as f
:
66 def ChmodAndRetry(func
, path
, _
):
67 # Subversion can leave read-only files around.
68 if not os
.access(path
, os
.W_OK
):
69 os
.chmod(path
, stat
.S_IWUSR
)
73 shutil
.rmtree(dir, onerror
=ChmodAndRetry
)
76 def ClobberChromiumBuildFiles():
77 """Clobber Chomium build files."""
78 print 'Clobbering Chromium build files...'
79 out_dir
= os
.path
.join(CHROMIUM_DIR
, 'out')
80 if os
.path
.isdir(out_dir
):
82 print 'Removed Chromium out dir: %s.' % (out_dir
)
85 def RunCommand(command
, fail_hard
=True):
86 """Run command and return success (True) or failure; or if fail_hard is
87 True, exit on failure."""
89 print 'Running %s' % (str(command
))
90 if subprocess
.call(command
, shell
=True) == 0:
98 def CopyFile(src
, dst
):
99 """Copy a file from src to dst."""
100 shutil
.copy(src
, dst
)
101 print "Copying %s to %s" % (src
, dst
)
104 def CopyDirectoryContents(src
, dst
, filename_filter
=None):
105 """Copy the files from directory src to dst
106 with an optional filename filter."""
107 if not os
.path
.exists(dst
):
109 for root
, _
, files
in os
.walk(src
):
111 if filename_filter
and not re
.match(filename_filter
, f
):
113 CopyFile(os
.path
.join(root
, f
), dst
)
116 def Checkout(name
, url
, dir):
117 """Checkout the SVN module at url into dir. Use name for the log message."""
118 print "Checking out %s r%s into '%s'" % (name
, LLVM_WIN_REVISION
, dir)
120 command
= ['svn', 'checkout', '--force', url
+ '@' + LLVM_WIN_REVISION
, dir]
121 if RunCommand(command
, fail_hard
=False):
124 if os
.path
.isdir(dir):
125 print "Removing %s." % (dir)
132 def AddCMakeToPath():
133 """Look for CMake and add it to PATH if it's not there already."""
135 # First check if cmake is already on PATH.
136 subprocess
.call(['cmake', '--version'])
139 if e
.errno
!= os
.errno
.ENOENT
:
142 cmake_locations
= ['C:\\Program Files (x86)\\CMake\\bin',
143 'C:\\Program Files (x86)\\CMake 2.8\\bin']
144 for d
in cmake_locations
:
146 os
.environ
['PATH'] = os
.environ
.get('PATH', '') + os
.pathsep
+ d
148 print 'Failed to find CMake!'
158 # Try using the toolchain in depot_tools.
159 # This sets environment variables used by SelectVisualStudioVersion below.
160 sys
.path
.append(os
.path
.join(CHROMIUM_DIR
, 'build'))
162 vs_toolchain
.SetEnvironmentAndGetRuntimeDllDirs()
164 # Use gyp to find the MSVS installation, either in depot_tools as per above,
165 # or a system-wide installation otherwise.
166 sys
.path
.append(os
.path
.join(CHROMIUM_DIR
, 'tools', 'gyp', 'pylib'))
167 import gyp
.MSVSVersion
168 vs_version
= gyp
.MSVSVersion
.SelectVisualStudioVersion('2013')
172 def SubversionCmakeArg():
173 # Since cmake's find_program can only find .exe and .com,
174 # svn.bat in depot_tools will be ignored.
175 default_pathext
= ('.com', '.exe', '.bat', '.cmd')
176 for path
in os
.environ
.get('PATH', '').split(os
.pathsep
):
177 for ext
in default_pathext
:
178 candidate
= os
.path
.join(path
, 'svn' + ext
)
179 if os
.path
.isfile(candidate
):
180 return '-DSubversion_SVN_EXECUTABLE=%s' % candidate
185 print 'Updating Clang to %s...' % (LLVM_WIN_REVISION
)
186 if LLVM_WIN_REVISION
!= 'HEAD' and ReadStampFile() == LLVM_WIN_REVISION
:
187 print 'Already up to date.'
191 ClobberChromiumBuildFiles()
193 # Reset the stamp file in case the build is unsuccessful.
196 Checkout('LLVM', LLVM_REPO_URL
+ '/llvm/trunk', LLVM_DIR
)
197 Checkout('Clang', LLVM_REPO_URL
+ '/cfe/trunk', CLANG_DIR
)
198 Checkout('LLD', LLVM_REPO_URL
+ '/lld/trunk', LLD_DIR
)
199 Checkout('compiler-rt', LLVM_REPO_URL
+ '/compiler-rt/trunk', COMPILER_RT_DIR
)
201 if not os
.path
.exists(LLVM_BUILD_DIR
):
202 os
.makedirs(LLVM_BUILD_DIR
)
203 os
.chdir(LLVM_BUILD_DIR
)
205 RunCommand(GetVSVersion().SetupScript('x64') +
206 ['&&', 'cmake', '-GNinja', '-DCMAKE_BUILD_TYPE=Release',
207 '-DLLVM_ENABLE_ASSERTIONS=ON', SubversionCmakeArg(), LLVM_DIR
])
208 RunCommand(GetVSVersion().SetupScript('x64') + ['&&', 'ninja', 'all'])
210 # Do an x86 build of compiler-rt to get the 32-bit ASan run-time.
211 # TODO(hans): Remove once the regular build above produces this.
212 if not os
.path
.exists(COMPILER_RT_BUILD_DIR
):
213 os
.makedirs(COMPILER_RT_BUILD_DIR
)
214 os
.chdir(COMPILER_RT_BUILD_DIR
)
215 RunCommand(GetVSVersion().SetupScript('x86') +
216 ['&&', 'cmake', '-GNinja', '-DCMAKE_BUILD_TYPE=Release',
217 '-DLLVM_ENABLE_ASSERTIONS=ON', LLVM_DIR
])
218 RunCommand(GetVSVersion().SetupScript('x86') + ['&&', 'ninja', 'compiler-rt'])
220 # TODO(hans): Make this (and the .gypi and .isolate files) version number
222 asan_rt_lib_src_dir
= os
.path
.join(COMPILER_RT_BUILD_DIR
, 'lib', 'clang',
223 VERSION
, 'lib', 'windows')
224 asan_rt_lib_dst_dir
= os
.path
.join(LLVM_BUILD_DIR
, 'lib', 'clang',
225 VERSION
, 'lib', 'windows')
226 CopyDirectoryContents(asan_rt_lib_src_dir
, asan_rt_lib_dst_dir
,
228 CopyDirectoryContents(asan_rt_lib_src_dir
, asan_rt_lib_dst_dir
,
231 CopyFile(os
.path
.join(asan_rt_lib_src_dir
, '..', '..', 'asan_blacklist.txt'),
232 os
.path
.join(asan_rt_lib_dst_dir
, '..', '..'))
234 # Make an extra copy of the sanitizer headers, to be put on the include path
235 # of the fallback compiler.
236 sanitizer_include_dir
= os
.path
.join(LLVM_BUILD_DIR
, 'lib', 'clang', VERSION
,
237 'include', 'sanitizer')
238 aux_sanitizer_include_dir
= os
.path
.join(LLVM_BUILD_DIR
, 'lib', 'clang',
239 VERSION
, 'include_sanitizer',
241 if not os
.path
.exists(aux_sanitizer_include_dir
):
242 os
.makedirs(aux_sanitizer_include_dir
)
243 for _
, _
, files
in os
.walk(sanitizer_include_dir
):
245 CopyFile(os
.path
.join(sanitizer_include_dir
, f
),
246 aux_sanitizer_include_dir
)
248 WriteStampFile(LLVM_WIN_REVISION
)
249 print 'Clang update was successful.'
254 if not sys
.platform
in ['win32', 'cygwin']:
255 # For non-Windows, fall back to update.sh.
256 # TODO(hans): Make update.py replace update.sh completely.
258 # This script is called by gclient. gclient opens its hooks subprocesses
259 # with (stdout=subprocess.PIPE, stderr=subprocess.STDOUT) and then does
260 # custom output processing that breaks printing '\r' characters for
261 # single-line updating status messages as printed by curl and wget.
262 # Work around this by setting stderr of the update.sh process to stdin (!):
263 # gclient doesn't redirect stdin, and while stdin itself is read-only, a
264 # dup()ed sys.stdin is writable, try
265 # fd2 = os.dup(sys.stdin.fileno()); os.write(fd2, 'hi')
266 # TODO: Fix gclient instead, http://crbug.com/95350
267 return subprocess
.call(
268 [os
.path
.join(os
.path
.dirname(__file__
), 'update.sh')] + sys
.argv
[1:],
269 stderr
=os
.fdopen(os
.dup(sys
.stdin
.fileno())))
271 if not re
.search(r
'\b(clang|asan)=1', os
.environ
.get('GYP_DEFINES', '')):
272 print 'Skipping Clang update (clang=1 was not set in GYP_DEFINES).'
275 if re
.search(r
'\b(make_clang_dir)=', os
.environ
.get('GYP_DEFINES', '')):
276 print 'Skipping Clang update (make_clang_dir= was set in GYP_DEFINES).'
282 if __name__
== '__main__':