2 # Copyright (c) 2015 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 """This script will check out llvm and clang, and then package the results up
19 THIS_DIR
= os
.path
.dirname(__file__
)
20 THIRD_PARTY_DIR
= os
.path
.join(THIS_DIR
, '..', '..', '..', 'third_party')
21 LLVM_DIR
= os
.path
.join(THIRD_PARTY_DIR
, 'llvm')
22 LLVM_BOOTSTRAP_DIR
= os
.path
.join(THIRD_PARTY_DIR
, 'llvm-bootstrap')
23 LLVM_BOOTSTRAP_INSTALL_DIR
= os
.path
.join(THIRD_PARTY_DIR
,
24 'llvm-bootstrap-install')
25 LLVM_BUILD_DIR
= os
.path
.join(THIRD_PARTY_DIR
, 'llvm-build')
26 LLVM_RELEASE_DIR
= os
.path
.join(LLVM_BUILD_DIR
, 'Release+Asserts')
27 STAMP_FILE
= os
.path
.join(LLVM_BUILD_DIR
, 'cr_build_revision')
30 def Tee(output
, logfile
):
35 def TeeCmd(cmd
, logfile
, fail_hard
=True):
36 """Runs cmd and writes the output to both stdout and logfile."""
37 # Reading from PIPE can deadlock if one buffer is full but we wait on a
38 # different one. To work around this, pipe the subprocess's stderr to
39 # its stdout buffer and don't give it a stdin.
40 # shell=True is required in cmd.exe since depot_tools has an svn.bat, and
41 # bat files only work with shell=True set.
42 proc
= subprocess
.Popen(cmd
, bufsize
=1, shell
=sys
.platform
== 'win32',
43 stdin
=open(os
.devnull
), stdout
=subprocess
.PIPE
,
44 stderr
=subprocess
.STDOUT
)
45 for line
in iter(proc
.stdout
.readline
,''):
47 if proc
.poll() is not None:
49 exit_code
= proc
.wait()
50 if exit_code
!= 0 and fail_hard
:
55 def PrintTarProgress(tarinfo
):
56 print 'Adding', tarinfo
.name
61 if sys
.platform
== 'win32':
63 subprocess
.check_output(['grep', '--help'], shell
=True)
64 except subprocess
.CalledProcessError
:
65 print 'Add gnuwin32 to your PATH, then try again.'
68 parser
= argparse
.ArgumentParser(description
='build and package clang')
69 parser
.add_argument('--gcc-toolchain',
70 help="the prefix for the GCC version used for building. "
71 "For /opt/foo/bin/gcc, pass "
72 "'--gcc-toolchain '/opt/foo'")
74 args
= parser
.parse_args()
76 with
open('buildlog.txt', 'w') as log
:
77 Tee('Diff in llvm:\n', log
)
78 TeeCmd(['svn', 'stat', LLVM_DIR
], log
, fail_hard
=False)
79 TeeCmd(['svn', 'diff', LLVM_DIR
], log
, fail_hard
=False)
80 Tee('Diff in llvm/tools/clang:\n', log
)
81 TeeCmd(['svn', 'stat', os
.path
.join(LLVM_DIR
, 'tools', 'clang')],
83 TeeCmd(['svn', 'diff', os
.path
.join(LLVM_DIR
, 'tools', 'clang')],
85 # TODO(thakis): compiler-rt is in projects/compiler-rt on Windows but
86 # llvm/compiler-rt elsewhere. So this diff call is currently only right on
88 Tee('Diff in llvm/compiler-rt:\n', log
)
89 TeeCmd(['svn', 'stat', os
.path
.join(LLVM_DIR
, 'projects', 'compiler-rt')],
91 TeeCmd(['svn', 'diff', os
.path
.join(LLVM_DIR
, 'projects', 'compiler-rt')],
93 Tee('Diff in llvm/projects/libcxx:\n', log
)
94 TeeCmd(['svn', 'stat', os
.path
.join(LLVM_DIR
, 'projects', 'libcxx')],
96 TeeCmd(['svn', 'diff', os
.path
.join(LLVM_DIR
, 'projects', 'libcxx')],
98 Tee('Diff in llvm/projects/libcxxabi:\n', log
)
99 TeeCmd(['svn', 'stat', os
.path
.join(LLVM_DIR
, 'projects', 'libcxxabi')],
100 log
, fail_hard
=False)
101 TeeCmd(['svn', 'diff', os
.path
.join(LLVM_DIR
, 'projects', 'libcxxabi')],
102 log
, fail_hard
=False)
104 Tee('Starting build\n', log
)
106 # Do a clobber build.
107 shutil
.rmtree(LLVM_BOOTSTRAP_DIR
, ignore_errors
=True)
108 shutil
.rmtree(LLVM_BOOTSTRAP_INSTALL_DIR
, ignore_errors
=True)
109 shutil
.rmtree(LLVM_BUILD_DIR
, ignore_errors
=True)
111 build_cmd
= [sys
.executable
, os
.path
.join(THIS_DIR
, 'update.py'),
112 '--bootstrap', '--force-local-build', '--run-tests',
114 if args
.gcc_toolchain
is not None:
115 build_cmd
.extend(['--gcc-toolchain', args
.gcc_toolchain
])
116 TeeCmd(build_cmd
, log
)
118 stamp
= open(STAMP_FILE
).read().rstrip()
119 pdir
= 'clang-' + stamp
121 shutil
.rmtree(pdir
, ignore_errors
=True)
123 # Copy a whitelist of files to the directory we're going to tar up.
124 # This supports the same patterns that the fnmatch module understands.
125 exe_ext
= '.exe' if sys
.platform
== 'win32' else ''
126 want
= ['bin/llvm-symbolizer' + exe_ext
,
127 'lib/clang/*/asan_blacklist.txt',
128 'lib/clang/*/cfi_blacklist.txt',
129 # Copy built-in headers (lib/clang/3.x.y/include).
130 'lib/clang/*/include/*',
132 if sys
.platform
== 'win32':
133 want
.append('bin/clang-cl.exe')
134 want
.append('bin/lld-link.exe')
136 so_ext
= 'dylib' if sys
.platform
== 'darwin' else 'so'
137 want
.extend(['bin/clang',
138 'lib/libFindBadConstructs.' + so_ext
,
139 'lib/libBlinkGCPlugin.' + so_ext
,
141 if sys
.platform
== 'darwin':
142 want
.extend(['bin/libc++.1.dylib',
143 # Copy only the OSX (ASan and profile) and iossim (ASan)
145 'lib/clang/*/lib/darwin/*asan_osx*',
146 'lib/clang/*/lib/darwin/*asan_iossim*',
147 'lib/clang/*/lib/darwin/*profile_osx*',
149 elif sys
.platform
.startswith('linux'):
151 # lib/clang/*/lib/linux/libclang_rt.{[atm]san,san,ubsan,profile}-*.a ,
153 want
.extend(['lib/clang/*/lib/linux/*[atm]san*',
154 'lib/clang/*/lib/linux/*ubsan*',
155 'lib/clang/*/lib/linux/*libclang_rt.san*',
156 'lib/clang/*/lib/linux/*profile*',
157 'lib/clang/*/msan_blacklist.txt',
159 elif sys
.platform
== 'win32':
160 want
.extend(['lib/clang/*/lib/windows/clang_rt.asan*.dll',
161 'lib/clang/*/lib/windows/clang_rt.asan*.lib',
162 'lib/clang/*/include_sanitizer/*',
164 if args
.gcc_toolchain
is not None:
165 # Copy the stdlibc++.so.6 we linked Clang against so it can run.
166 want
.append('lib/libstdc++.so.6')
168 for root
, dirs
, files
in os
.walk(LLVM_RELEASE_DIR
):
169 # root: third_party/llvm-build/Release+Asserts/lib/..., rel_root: lib/...
170 rel_root
= root
[len(LLVM_RELEASE_DIR
)+1:]
171 rel_files
= [os
.path
.join(rel_root
, f
) for f
in files
]
172 wanted_files
= list(set(itertools
.chain
.from_iterable(
173 fnmatch
.filter(rel_files
, p
) for p
in want
)))
175 # Guaranteed to not yet exist at this point:
176 os
.makedirs(os
.path
.join(pdir
, rel_root
))
177 for f
in wanted_files
:
178 src
= os
.path
.join(LLVM_RELEASE_DIR
, f
)
179 dest
= os
.path
.join(pdir
, f
)
180 shutil
.copy(src
, dest
)
182 if sys
.platform
== 'darwin' and f
.endswith('.dylib'):
183 # Fix LC_ID_DYLIB for the ASan dynamic libraries to be relative to
185 # TODO(glider): this is transitional. We'll need to fix the dylib
186 # name either in our build system, or in Clang. See also
187 # http://crbug.com/344836.
188 subprocess
.call(['install_name_tool', '-id',
189 '@executable_path/' + os
.path
.basename(dest
), dest
])
190 subprocess
.call(['strip', '-x', dest
])
191 elif (sys
.platform
.startswith('linux') and
192 os
.path
.splitext(f
)[1] in ['.so', '.a']):
193 subprocess
.call(['strip', '-g', dest
])
196 if sys
.platform
!= 'win32':
197 os
.symlink('clang', os
.path
.join(pdir
, 'bin', 'clang++'))
198 os
.symlink('clang', os
.path
.join(pdir
, 'bin', 'clang-cl'))
199 if sys
.platform
== 'darwin':
200 os
.symlink('libc++.1.dylib', os
.path
.join(pdir
, 'bin', 'libc++.dylib'))
201 # Also copy libc++ headers.
202 shutil
.copytree(os
.path
.join(LLVM_BOOTSTRAP_INSTALL_DIR
, 'include', 'c++'),
203 os
.path
.join(pdir
, 'include', 'c++'))
205 # Copy buildlog over.
206 shutil
.copy('buildlog.txt', pdir
)
209 tar_entries
= ['bin', 'lib', 'buildlog.txt']
210 if sys
.platform
== 'darwin':
211 tar_entries
+= ['include']
212 with tarfile
.open(pdir
+ '.tgz', 'w:gz') as tar
:
213 for entry
in tar_entries
:
214 tar
.add(os
.path
.join(pdir
, entry
), arcname
=entry
, filter=PrintTarProgress
)
216 if sys
.platform
== 'darwin':
218 elif sys
.platform
== 'win32':
221 platform
= 'Linux_x64'
223 print 'To upload, run:'
224 print ('gsutil cp -a public-read %s.tgz '
225 'gs://chromium-browser-clang/%s/%s.tgz') % (pdir
, platform
, pdir
)
227 # Zip up gold plugin on Linux.
228 if sys
.platform
.startswith('linux'):
229 golddir
= 'llvmgold-' + stamp
230 shutil
.rmtree(golddir
, ignore_errors
=True)
231 os
.makedirs(os
.path
.join(golddir
, 'lib'))
232 shutil
.copy(os
.path
.join(LLVM_RELEASE_DIR
, 'lib', 'LLVMgold.so'),
233 os
.path
.join(golddir
, 'lib'))
234 with tarfile
.open(golddir
+ '.tgz', 'w:gz') as tar
:
235 tar
.add(os
.path
.join(golddir
, 'lib'), arcname
='lib',
236 filter=PrintTarProgress
)
237 print ('gsutil cp -a public-read %s.tgz '
238 'gs://chromium-browser-clang/%s/%s.tgz') % (golddir
, platform
,
241 # FIXME: Warn if the file already exists on the server.
244 if __name__
== '__main__':