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 parser
= argparse
.ArgumentParser(description
='build and package clang')
62 parser
.add_argument('--gcc-toolchain',
63 help="the prefix for the GCC version used for building. "
64 "For /opt/foo/bin/gcc, pass "
65 "'--gcc-toolchain '/opt/foo'")
67 args
= parser
.parse_args()
69 with
open('buildlog.txt', 'w') as log
:
70 Tee('Diff in llvm:\n', log
)
71 TeeCmd(['svn', 'stat', LLVM_DIR
], log
, fail_hard
=False)
72 TeeCmd(['svn', 'diff', LLVM_DIR
], log
, fail_hard
=False)
73 Tee('Diff in llvm/tools/clang:\n', log
)
74 TeeCmd(['svn', 'stat', os
.path
.join(LLVM_DIR
, 'tools', 'clang')],
76 TeeCmd(['svn', 'diff', os
.path
.join(LLVM_DIR
, 'tools', 'clang')],
78 # TODO(thakis): compiler-rt is in projects/compiler-rt on Windows but
79 # llvm/compiler-rt elsewhere. So this diff call is currently only right on
81 Tee('Diff in llvm/compiler-rt:\n', log
)
82 TeeCmd(['svn', 'stat', os
.path
.join(LLVM_DIR
, 'projects', 'compiler-rt')],
84 TeeCmd(['svn', 'diff', os
.path
.join(LLVM_DIR
, 'projects', 'compiler-rt')],
86 Tee('Diff in llvm/projects/libcxx:\n', log
)
87 TeeCmd(['svn', 'stat', os
.path
.join(LLVM_DIR
, 'projects', 'libcxx')],
89 TeeCmd(['svn', 'diff', os
.path
.join(LLVM_DIR
, 'projects', 'libcxx')],
91 Tee('Diff in llvm/projects/libcxxabi:\n', log
)
92 TeeCmd(['svn', 'stat', os
.path
.join(LLVM_DIR
, 'projects', 'libcxxabi')],
94 TeeCmd(['svn', 'diff', os
.path
.join(LLVM_DIR
, 'projects', 'libcxxabi')],
97 Tee('Starting build\n', log
)
100 shutil
.rmtree(LLVM_BOOTSTRAP_DIR
, ignore_errors
=True)
101 shutil
.rmtree(LLVM_BOOTSTRAP_INSTALL_DIR
, ignore_errors
=True)
102 shutil
.rmtree(LLVM_BUILD_DIR
, ignore_errors
=True)
104 build_cmd
= [sys
.executable
, os
.path
.join(THIS_DIR
, 'update.py'),
105 '--bootstrap', '--force-local-build', '--run-tests',
107 if args
.gcc_toolchain
is not None:
108 build_cmd
.extend(['--gcc-toolchain', args
.gcc_toolchain
])
109 TeeCmd(build_cmd
, log
)
111 stamp
= open(STAMP_FILE
).read().rstrip()
112 pdir
= 'clang-' + stamp
114 shutil
.rmtree(pdir
, ignore_errors
=True)
116 # Copy a whitelist of files to the directory we're going to tar up.
117 # This supports the same patterns that the fnmatch module understands.
118 exe_ext
= '.exe' if sys
.platform
== 'win32' else ''
119 want
= ['bin/llvm-symbolizer' + exe_ext
,
120 'lib/clang/*/asan_blacklist.txt',
121 'lib/clang/*/cfi_blacklist.txt',
122 # Copy built-in headers (lib/clang/3.x.y/include).
123 'lib/clang/*/include/*',
125 if sys
.platform
== 'win32':
126 want
.append('bin/clang-cl.exe')
127 want
.append('bin/lld-link.exe')
129 so_ext
= 'dylib' if sys
.platform
== 'darwin' else 'so'
130 want
.extend(['bin/clang',
131 'lib/libFindBadConstructs.' + so_ext
,
132 'lib/libBlinkGCPlugin.' + so_ext
,
134 if sys
.platform
== 'darwin':
135 want
.extend(['bin/libc++.1.dylib',
136 # Copy only the OSX (ASan and profile) and iossim (ASan)
138 'lib/clang/*/lib/darwin/*asan_osx*',
139 'lib/clang/*/lib/darwin/*asan_iossim*',
140 'lib/clang/*/lib/darwin/*profile_osx*',
142 elif sys
.platform
.startswith('linux'):
144 # lib/clang/*/lib/linux/libclang_rt.{[atm]san,san,ubsan,profile}-*.a ,
146 want
.extend(['lib/clang/*/lib/linux/*[atm]san*',
147 'lib/clang/*/lib/linux/*ubsan*',
148 'lib/clang/*/lib/linux/*libclang_rt.san*',
149 'lib/clang/*/lib/linux/*profile*',
150 'lib/clang/*/msan_blacklist.txt',
152 elif sys
.platform
== 'win32':
153 want
.extend(['lib/clang/*/lib/windows/clang_rt.asan*.dll',
154 'lib/clang/*/lib/windows/clang_rt.asan*.lib',
155 'lib/clang/*/include_sanitizer/*',
157 if args
.gcc_toolchain
is not None:
158 # Copy the stdlibc++.so.6 we linked Clang against so it can run.
159 want
.append('lib/libstdc++.so.6')
161 for root
, dirs
, files
in os
.walk(LLVM_RELEASE_DIR
):
162 # root: third_party/llvm-build/Release+Asserts/lib/..., rel_root: lib/...
163 rel_root
= root
[len(LLVM_RELEASE_DIR
)+1:]
164 rel_files
= [os
.path
.join(rel_root
, f
) for f
in files
]
165 wanted_files
= list(set(itertools
.chain
.from_iterable(
166 fnmatch
.filter(rel_files
, p
) for p
in want
)))
168 # Guaranteed to not yet exist at this point:
169 os
.makedirs(os
.path
.join(pdir
, rel_root
))
170 for f
in wanted_files
:
171 src
= os
.path
.join(LLVM_RELEASE_DIR
, f
)
172 dest
= os
.path
.join(pdir
, f
)
173 shutil
.copy(src
, dest
)
175 if sys
.platform
== 'darwin' and f
.endswith('.dylib'):
176 # Fix LC_ID_DYLIB for the ASan dynamic libraries to be relative to
178 # TODO(glider): this is transitional. We'll need to fix the dylib
179 # name either in our build system, or in Clang. See also
180 # http://crbug.com/344836.
181 subprocess
.call(['install_name_tool', '-id',
182 '@executable_path/' + os
.path
.basename(dest
), dest
])
183 subprocess
.call(['strip', '-x', dest
])
184 elif (sys
.platform
.startswith('linux') and
185 os
.path
.splitext(f
)[1] in ['.so', '.a']):
186 subprocess
.call(['strip', '-g', dest
])
189 if sys
.platform
!= 'win32':
190 os
.symlink('clang', os
.path
.join(pdir
, 'bin', 'clang++'))
191 os
.symlink('clang', os
.path
.join(pdir
, 'bin', 'clang-cl'))
192 if sys
.platform
== 'darwin':
193 os
.symlink('libc++.1.dylib', os
.path
.join(pdir
, 'bin', 'libc++.dylib'))
194 # Also copy libc++ headers.
195 shutil
.copytree(os
.path
.join(LLVM_BOOTSTRAP_INSTALL_DIR
, 'include', 'c++'),
196 os
.path
.join(pdir
, 'include', 'c++'))
198 # Copy buildlog over.
199 shutil
.copy('buildlog.txt', pdir
)
202 tar_entries
= ['bin', 'lib', 'buildlog.txt']
203 if sys
.platform
== 'darwin':
204 tar_entries
+= ['include']
205 with tarfile
.open(pdir
+ '.tgz', 'w:gz') as tar
:
206 for entry
in tar_entries
:
207 tar
.add(os
.path
.join(pdir
, entry
), arcname
=entry
, filter=PrintTarProgress
)
209 if sys
.platform
== 'darwin':
211 elif sys
.platform
== 'win32':
214 platform
= 'Linux_x64'
216 print 'To upload, run:'
217 print ('gsutil cp -a public-read %s.tgz '
218 'gs://chromium-browser-clang/%s/%s.tgz') % (pdir
, platform
, pdir
)
220 # Zip up gold plugin on Linux.
221 if sys
.platform
.startswith('linux'):
222 golddir
= 'llvmgold-' + stamp
223 shutil
.rmtree(golddir
, ignore_errors
=True)
224 os
.makedirs(os
.path
.join(golddir
, 'lib'))
225 shutil
.copy(os
.path
.join(LLVM_RELEASE_DIR
, 'lib', 'LLVMgold.so'),
226 os
.path
.join(golddir
, 'lib'))
227 with tarfile
.open(golddir
+ '.tgz', 'w:gz') as tar
:
228 tar
.add(os
.path
.join(golddir
, 'lib'), arcname
='lib',
229 filter=PrintTarProgress
)
230 print ('gsutil cp -a public-read %s.tgz '
231 'gs://chromium-browser-clang/%s/%s.tgz') % (golddir
, platform
,
234 # FIXME: Warn if the file already exists on the server.
237 if __name__
== '__main__':