OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Windows can't run .sh files, so this is a Python implementation of | 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.""" | 7 update.sh. This script should replace update.sh on all platforms eventually.""" |
8 | 8 |
9 import argparse | 9 import argparse |
10 import os | 10 import os |
11 import re | 11 import re |
12 import shutil | 12 import shutil |
13 import subprocess | 13 import subprocess |
14 import stat | 14 import stat |
15 import sys | 15 import sys |
16 import time | 16 import time |
17 | 17 |
18 # Do NOT CHANGE this if you don't know what you're doing -- see | 18 # Do NOT CHANGE this if you don't know what you're doing -- see |
19 # https://code.google.com/p/chromium/wiki/UpdatingClang | 19 # https://code.google.com/p/chromium/wiki/UpdatingClang |
20 # Reverting problematic clang rolls is safe, though. | 20 # Reverting problematic clang rolls is safe, though. |
21 # Note: this revision is only used for Windows. Other platforms use update.sh. | 21 # Note: this revision is only used for Windows. Other platforms use update.sh. |
22 LLVM_WIN_REVISION = 'HEAD' | 22 LLVM_WIN_REVISION = 'HEAD' |
23 | 23 |
24 # ASan on Windows is useful enough to use it even while the clang/win is still | 24 # ASan on Windows is useful enough to use it even while the clang/win is still |
25 # in bringup. Use a pinned revision to make it slightly more stable. | 25 # in bringup. Use a pinned revision to make it slightly more stable. |
26 if (re.search(r'\b(asan)=1', os.environ.get('GYP_DEFINES', '')) and | 26 use_head_revision = ('LLVM_FORCE_HEAD_REVISION' in os.environ or |
27 not 'LLVM_FORCE_HEAD_REVISION' in os.environ): | 27 not re.search(r'\b(asan)=1', os.environ.get('GYP_DEFINES', ''))) |
| 28 |
| 29 if not use_head_revision: |
28 LLVM_WIN_REVISION = '235968' | 30 LLVM_WIN_REVISION = '235968' |
29 | 31 |
30 # Path constants. (All of these should be absolute paths.) | 32 # Path constants. (All of these should be absolute paths.) |
31 THIS_DIR = os.path.abspath(os.path.dirname(__file__)) | 33 THIS_DIR = os.path.abspath(os.path.dirname(__file__)) |
32 CHROMIUM_DIR = os.path.abspath(os.path.join(THIS_DIR, '..', '..', '..')) | 34 CHROMIUM_DIR = os.path.abspath(os.path.join(THIS_DIR, '..', '..', '..')) |
33 LLVM_DIR = os.path.join(CHROMIUM_DIR, 'third_party', 'llvm') | 35 LLVM_DIR = os.path.join(CHROMIUM_DIR, 'third_party', 'llvm') |
34 CHROME_TOOLS_SHIM_DIR = os.path.join(LLVM_DIR, 'tools', 'chrometools') | 36 CHROME_TOOLS_SHIM_DIR = os.path.join(LLVM_DIR, 'tools', 'chrometools') |
35 LLVM_BUILD_DIR = os.path.join(CHROMIUM_DIR, 'third_party', 'llvm-build', | 37 LLVM_BUILD_DIR = os.path.join(CHROMIUM_DIR, 'third_party', 'llvm-build', |
36 'Release+Asserts') | 38 'Release+Asserts') |
37 COMPILER_RT_BUILD_DIR = os.path.join(LLVM_BUILD_DIR, '32bit-compiler-rt') | 39 COMPILER_RT_BUILD_DIR = os.path.join(LLVM_BUILD_DIR, '32bit-compiler-rt') |
(...skipping 18 matching lines...) Expand all Loading... |
56 | 58 |
57 | 59 |
58 def WriteStampFile(s): | 60 def WriteStampFile(s): |
59 """Write s to the stamp file.""" | 61 """Write s to the stamp file.""" |
60 if not os.path.exists(LLVM_BUILD_DIR): | 62 if not os.path.exists(LLVM_BUILD_DIR): |
61 os.makedirs(LLVM_BUILD_DIR) | 63 os.makedirs(LLVM_BUILD_DIR) |
62 with open(STAMP_FILE, 'w') as f: | 64 with open(STAMP_FILE, 'w') as f: |
63 f.write(s) | 65 f.write(s) |
64 | 66 |
65 | 67 |
| 68 def PrintRevision(): |
| 69 """Print the current Clang revision.""" |
| 70 # gyp runs update.py --print-revision even when clang isn't used. |
| 71 # It won't use the value, but we must not error. |
| 72 if not os.path.exists(LLVM_DIR): |
| 73 print "0" |
| 74 return |
| 75 |
| 76 # TODO(hans): This needs an update when we move to prebuilt Clang binaries. |
| 77 svn_info = subprocess.check_output(['svn', 'info', LLVM_DIR], shell=True) |
| 78 m = re.search(r'Revision: (\d+)', svn_info) |
| 79 assert m |
| 80 print m.group(1) |
| 81 |
| 82 |
66 def RmTree(dir): | 83 def RmTree(dir): |
67 """Delete dir.""" | 84 """Delete dir.""" |
68 def ChmodAndRetry(func, path, _): | 85 def ChmodAndRetry(func, path, _): |
69 # Subversion can leave read-only files around. | 86 # Subversion can leave read-only files around. |
70 if not os.access(path, os.W_OK): | 87 if not os.access(path, os.W_OK): |
71 os.chmod(path, stat.S_IWUSR) | 88 os.chmod(path, stat.S_IWUSR) |
72 return func(path) | 89 return func(path) |
73 raise | 90 raise |
74 | 91 |
75 shutil.rmtree(dir, onerror=ChmodAndRetry) | 92 shutil.rmtree(dir, onerror=ChmodAndRetry) |
76 | 93 |
77 | 94 |
78 def ClobberChromiumBuildFiles(): | |
79 """Clobber Chomium build files.""" | |
80 print 'Clobbering Chromium build files...' | |
81 out_dir = os.path.join(CHROMIUM_DIR, 'out') | |
82 if os.path.isdir(out_dir): | |
83 RmTree(out_dir) | |
84 print 'Removed Chromium out dir: %s.' % (out_dir) | |
85 | |
86 | |
87 def RunCommand(command, fail_hard=True): | 95 def RunCommand(command, fail_hard=True): |
88 """Run command and return success (True) or failure; or if fail_hard is | 96 """Run command and return success (True) or failure; or if fail_hard is |
89 True, exit on failure.""" | 97 True, exit on failure.""" |
90 | 98 |
91 print 'Running %s' % (str(command)) | 99 print 'Running %s' % (str(command)) |
92 if subprocess.call(command, shell=True) == 0: | 100 if subprocess.call(command, shell=True) == 0: |
93 return True | 101 return True |
94 print 'Failed.' | 102 print 'Failed.' |
95 if fail_hard: | 103 if fail_hard: |
96 sys.exit(1) | 104 sys.exit(1) |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
160 def AddCMakeToPath(): | 168 def AddCMakeToPath(): |
161 """Look for CMake and add it to PATH if it's not there already.""" | 169 """Look for CMake and add it to PATH if it's not there already.""" |
162 try: | 170 try: |
163 # First check if cmake is already on PATH. | 171 # First check if cmake is already on PATH. |
164 subprocess.call(['cmake', '--version']) | 172 subprocess.call(['cmake', '--version']) |
165 return | 173 return |
166 except OSError as e: | 174 except OSError as e: |
167 if e.errno != os.errno.ENOENT: | 175 if e.errno != os.errno.ENOENT: |
168 raise | 176 raise |
169 | 177 |
170 cmake_locations = ['C:\\Program Files (x86)\\CMake\\bin', | 178 cmake_dir = 'C:\\Program Files (x86)\\CMake\\bin' |
171 'C:\\Program Files (x86)\\CMake 2.8\\bin'] | 179 if os.path.isdir(cmake_dir): |
172 for d in cmake_locations: | 180 os.environ['PATH'] = os.environ.get('PATH', '') + os.pathsep + cmake_dir |
173 if os.path.isdir(d): | 181 return |
174 os.environ['PATH'] = os.environ.get('PATH', '') + os.pathsep + d | |
175 return | |
176 print 'Failed to find CMake!' | 182 print 'Failed to find CMake!' |
177 sys.exit(1) | 183 sys.exit(1) |
178 | 184 |
179 | 185 |
180 vs_version = None | 186 vs_version = None |
181 def GetVSVersion(): | 187 def GetVSVersion(): |
182 global vs_version | 188 global vs_version |
183 if vs_version: | 189 if vs_version: |
184 return vs_version | 190 return vs_version |
185 | 191 |
(...skipping 23 matching lines...) Expand all Loading... |
209 return '' | 215 return '' |
210 | 216 |
211 | 217 |
212 def UpdateClang(args): | 218 def UpdateClang(args): |
213 print 'Updating Clang to %s...' % (LLVM_WIN_REVISION) | 219 print 'Updating Clang to %s...' % (LLVM_WIN_REVISION) |
214 if LLVM_WIN_REVISION != 'HEAD' and ReadStampFile() == LLVM_WIN_REVISION: | 220 if LLVM_WIN_REVISION != 'HEAD' and ReadStampFile() == LLVM_WIN_REVISION: |
215 print 'Already up to date.' | 221 print 'Already up to date.' |
216 return 0 | 222 return 0 |
217 | 223 |
218 AddCMakeToPath() | 224 AddCMakeToPath() |
219 if args.clobber: | |
220 ClobberChromiumBuildFiles() | |
221 | |
222 # Reset the stamp file in case the build is unsuccessful. | 225 # Reset the stamp file in case the build is unsuccessful. |
223 WriteStampFile('') | 226 WriteStampFile('') |
224 | 227 |
225 DeleteChromeToolsShim(); | 228 DeleteChromeToolsShim(); |
226 Checkout('LLVM', LLVM_REPO_URL + '/llvm/trunk', LLVM_DIR) | 229 Checkout('LLVM', LLVM_REPO_URL + '/llvm/trunk', LLVM_DIR) |
227 Checkout('Clang', LLVM_REPO_URL + '/cfe/trunk', CLANG_DIR) | 230 Checkout('Clang', LLVM_REPO_URL + '/cfe/trunk', CLANG_DIR) |
228 Checkout('LLD', LLVM_REPO_URL + '/lld/trunk', LLD_DIR) | 231 Checkout('LLD', LLVM_REPO_URL + '/lld/trunk', LLD_DIR) |
229 Checkout('compiler-rt', LLVM_REPO_URL + '/compiler-rt/trunk', COMPILER_RT_DIR) | 232 Checkout('compiler-rt', LLVM_REPO_URL + '/compiler-rt/trunk', COMPILER_RT_DIR) |
230 CreateChromeToolsShim(); | 233 CreateChromeToolsShim(); |
231 | 234 |
232 if not os.path.exists(LLVM_BUILD_DIR): | 235 if not os.path.exists(LLVM_BUILD_DIR): |
233 os.makedirs(LLVM_BUILD_DIR) | 236 os.makedirs(LLVM_BUILD_DIR) |
234 os.chdir(LLVM_BUILD_DIR) | 237 os.chdir(LLVM_BUILD_DIR) |
235 | 238 |
| 239 # If building at head, define a macro that plugins can use for #ifdefing |
| 240 # out code that builds at head, but not at CLANG_REVISION or vice versa. |
| 241 cflags = cxxflags = '' |
| 242 |
| 243 # TODO(thakis): Set this only conditionally if use_head_revision once posix |
| 244 # and win clang are in sync. At the moment, the plugins only build at clang |
| 245 # head on posix, but they build at both head and the pinned win version :-/ |
| 246 cflags += ' -DLLVM_FORCE_HEAD_REVISION' |
| 247 cxxflags += ' -DLLVM_FORCE_HEAD_REVISION' |
| 248 |
236 cmake_args = ['-GNinja', '-DCMAKE_BUILD_TYPE=Release', | 249 cmake_args = ['-GNinja', '-DCMAKE_BUILD_TYPE=Release', |
237 '-DLLVM_ENABLE_ASSERTIONS=ON', SubversionCmakeArg(), | 250 '-DLLVM_ENABLE_ASSERTIONS=ON', SubversionCmakeArg(), |
| 251 '-DCMAKE_C_FLAGS=' + cflags, |
| 252 '-DCMAKE_CXX_FLAGS=' + cxxflags, |
238 '-DCHROMIUM_TOOLS_SRC=%s' % os.path.join( | 253 '-DCHROMIUM_TOOLS_SRC=%s' % os.path.join( |
239 CHROMIUM_DIR, 'tools', 'clang'), | 254 CHROMIUM_DIR, 'tools', 'clang'), |
240 '-DCHROMIUM_TOOLS=%s' % ';'.join(args.tools)] | 255 '-DCHROMIUM_TOOLS=%s' % ';'.join(args.tools)] |
241 | 256 |
242 RunCommand(GetVSVersion().SetupScript('x64') + | 257 RunCommand(GetVSVersion().SetupScript('x64') + |
243 ['&&', 'cmake'] + cmake_args + [LLVM_DIR]) | 258 ['&&', 'cmake'] + cmake_args + [LLVM_DIR]) |
244 RunCommand(GetVSVersion().SetupScript('x64') + ['&&', 'ninja', 'all']) | 259 RunCommand(GetVSVersion().SetupScript('x64') + ['&&', 'ninja', 'all']) |
245 | 260 |
246 # Do an x86 build of compiler-rt to get the 32-bit ASan run-time. | 261 # Do an x86 build of compiler-rt to get the 32-bit ASan run-time. |
247 # TODO(hans): Remove once the regular build above produces this. | 262 # TODO(hans): Remove once the regular build above produces this. |
(...skipping 25 matching lines...) Expand all Loading... |
273 aux_sanitizer_include_dir = os.path.join(LLVM_BUILD_DIR, 'lib', 'clang', | 288 aux_sanitizer_include_dir = os.path.join(LLVM_BUILD_DIR, 'lib', 'clang', |
274 VERSION, 'include_sanitizer', | 289 VERSION, 'include_sanitizer', |
275 'sanitizer') | 290 'sanitizer') |
276 if not os.path.exists(aux_sanitizer_include_dir): | 291 if not os.path.exists(aux_sanitizer_include_dir): |
277 os.makedirs(aux_sanitizer_include_dir) | 292 os.makedirs(aux_sanitizer_include_dir) |
278 for _, _, files in os.walk(sanitizer_include_dir): | 293 for _, _, files in os.walk(sanitizer_include_dir): |
279 for f in files: | 294 for f in files: |
280 CopyFile(os.path.join(sanitizer_include_dir, f), | 295 CopyFile(os.path.join(sanitizer_include_dir, f), |
281 aux_sanitizer_include_dir) | 296 aux_sanitizer_include_dir) |
282 | 297 |
| 298 if args.run_tests: |
| 299 os.chdir(LLVM_BUILD_DIR) |
| 300 RunCommand(GetVSVersion().SetupScript('x64') + |
| 301 ['&&', 'ninja', 'cr-check-all']) |
| 302 |
283 WriteStampFile(LLVM_WIN_REVISION) | 303 WriteStampFile(LLVM_WIN_REVISION) |
284 print 'Clang update was successful.' | 304 print 'Clang update was successful.' |
285 return 0 | 305 return 0 |
286 | 306 |
287 | 307 |
288 def main(): | 308 def main(): |
289 if not sys.platform in ['win32', 'cygwin']: | 309 if not sys.platform in ['win32', 'cygwin']: |
290 # For non-Windows, fall back to update.sh. | 310 # For non-Windows, fall back to update.sh. |
291 # TODO(hans): Make update.py replace update.sh completely. | 311 # TODO(hans): Make update.py replace update.sh completely. |
292 | 312 |
293 # This script is called by gclient. gclient opens its hooks subprocesses | 313 # This script is called by gclient. gclient opens its hooks subprocesses |
294 # with (stdout=subprocess.PIPE, stderr=subprocess.STDOUT) and then does | 314 # with (stdout=subprocess.PIPE, stderr=subprocess.STDOUT) and then does |
295 # custom output processing that breaks printing '\r' characters for | 315 # custom output processing that breaks printing '\r' characters for |
296 # single-line updating status messages as printed by curl and wget. | 316 # single-line updating status messages as printed by curl and wget. |
297 # Work around this by setting stderr of the update.sh process to stdin (!): | 317 # Work around this by setting stderr of the update.sh process to stdin (!): |
298 # gclient doesn't redirect stdin, and while stdin itself is read-only, a | 318 # gclient doesn't redirect stdin, and while stdin itself is read-only, a |
299 # dup()ed sys.stdin is writable, try | 319 # dup()ed sys.stdin is writable, try |
300 # fd2 = os.dup(sys.stdin.fileno()); os.write(fd2, 'hi') | 320 # fd2 = os.dup(sys.stdin.fileno()); os.write(fd2, 'hi') |
301 # TODO: Fix gclient instead, http://crbug.com/95350 | 321 # TODO: Fix gclient instead, http://crbug.com/95350 |
| 322 try: |
| 323 stderr = os.fdopen(os.dup(sys.stdin.fileno())) |
| 324 except: |
| 325 stderr = sys.stderr |
302 return subprocess.call( | 326 return subprocess.call( |
303 [os.path.join(os.path.dirname(__file__), 'update.sh')] + sys.argv[1:], | 327 [os.path.join(os.path.dirname(__file__), 'update.sh')] + sys.argv[1:], |
304 stderr=os.fdopen(os.dup(sys.stdin.fileno()))) | 328 stderr=stderr) |
| 329 |
| 330 parser = argparse.ArgumentParser(description='Build Clang.') |
| 331 parser.add_argument('--tools', nargs='*', |
| 332 default=['plugins', 'blink_gc_plugin']) |
| 333 # For now, this flag is only used for the non-Windows flow, but argparser gets |
| 334 # mad if it sees a flag it doesn't recognize. |
| 335 parser.add_argument('--if-needed', action='store_true') |
| 336 parser.add_argument('--print-revision', action='store_true') |
| 337 parser.add_argument('--run-tests', action='store_true') |
| 338 |
| 339 args = parser.parse_args() |
| 340 |
| 341 if args.print_revision: |
| 342 PrintRevision() |
| 343 return 0 |
305 | 344 |
306 if not re.search(r'\b(clang|asan)=1', os.environ.get('GYP_DEFINES', '')): | 345 if not re.search(r'\b(clang|asan)=1', os.environ.get('GYP_DEFINES', '')): |
307 print 'Skipping Clang update (clang=1 was not set in GYP_DEFINES).' | 346 print 'Skipping Clang update (clang=1 was not set in GYP_DEFINES).' |
308 return 0 | 347 return 0 |
309 | 348 |
310 if re.search(r'\b(make_clang_dir)=', os.environ.get('GYP_DEFINES', '')): | 349 if re.search(r'\b(make_clang_dir)=', os.environ.get('GYP_DEFINES', '')): |
311 print 'Skipping Clang update (make_clang_dir= was set in GYP_DEFINES).' | 350 print 'Skipping Clang update (make_clang_dir= was set in GYP_DEFINES).' |
312 return 0 | 351 return 0 |
313 | 352 |
314 parser = argparse.ArgumentParser(description='Build Clang.') | 353 return UpdateClang(args) |
315 parser.add_argument('--no-clobber', dest='clobber', action='store_false') | |
316 parser.add_argument('--tools', nargs='*', default=['plugins']) | |
317 # For now, this flag is only used for the non-Windows flow, but argparser gets | |
318 # mad if it sees a flag it doesn't recognize. | |
319 parser.add_argument('--if-needed', action='store_true') | |
320 return UpdateClang(parser.parse_args()) | |
321 | 354 |
322 | 355 |
323 if __name__ == '__main__': | 356 if __name__ == '__main__': |
324 sys.exit(main()) | 357 sys.exit(main()) |
OLD | NEW |