"""This script is used to build clang binaries. It is used by package.py to
create the prebuilt binaries downloaded by update.py and used by developers.
The expectation is that update.py downloads prebuilt binaries for everyone, and
nobody should run this script as part of normal development.
DEAR MAC USER: YOU NEED XCODE INSTALLED TO BUILD LLVM/CLANG WITH THIS SCRIPT.
The Xcode command line tools that are installed as part of the Chromium
development setup process are not sufficient. CMake will fail to configure, as
the non-system Clang we use will not find any standard library headers. To use
this build script on Mac:
1. Download Xcode. (Visit http://go/xcode for googlers.)
2. Install to /Applications
3. sudo xcode-select --switch /Applications/Xcode.app
"""
import argparse
import atexit
import glob
import io
import json
import multiprocessing
import os
import shlex
import platform
import re
import shutil
import subprocess
import sys
import tempfile
import time
import urllib
from update import (CDS_URL, CHROMIUM_DIR, CLANG_REVISION, LLVM_BUILD_DIR,
FORCE_HEAD_REVISION_FILENAME, FORCE_HEAD_REVISION_FILE,
PACKAGE_VERSION, RELEASE_VERSION, STAMP_FILENAME,
STAMP_FILE, THIS_DIR, DownloadUrl, DownloadAndUnpack,
DownloadAndUnpackPackage, EnsureDirExists, GetDefaultHostOs,
ReadStampFile, RmTree, WriteStampFile)
THIRD_PARTY_DIR = os.path.join(CHROMIUM_DIR, 'third_party')
LLVM_DIR = os.path.join(THIRD_PARTY_DIR, 'llvm')
COMPILER_RT_DIR = os.path.join(LLVM_DIR, 'compiler-rt')
LLVM_GIT_URL = ('https://chromium.googlesource.com/external/' +
'github.com/llvm/llvm-project')
LLVM_BOOTSTRAP_DIR = os.path.join(THIRD_PARTY_DIR, 'llvm-bootstrap')
LLVM_BOOTSTRAP_INSTALL_DIR = os.path.join(THIRD_PARTY_DIR,
'llvm-bootstrap-install')
LLVM_INSTRUMENTED_DIR = os.path.join(THIRD_PARTY_DIR, 'llvm-instrumented')
LLVM_PROFDATA_FILE = os.path.join(LLVM_INSTRUMENTED_DIR, 'profdata.prof')
LLVM_BUILD_TOOLS_DIR = os.path.abspath(
os.path.join(LLVM_DIR, '..', 'llvm-build-tools'))
ANDROID_NDK_DIR = os.path.join(CHROMIUM_DIR, 'third_party',
'android_toolchain', 'ndk')
ANDROID_NDK_TOOLCHAIN_RELATIVE_DIR = os.path.join('toolchains', 'llvm',
'prebuilt', 'linux-x86_64')
ANDROID_NDK_TOOLCHAIN_DIR = os.path.join(ANDROID_NDK_DIR,
ANDROID_NDK_TOOLCHAIN_RELATIVE_DIR)
FUCHSIA_SDK_DIR = os.path.join(CHROMIUM_DIR, 'third_party', 'fuchsia-sdk',
'sdk')
PINNED_CLANG_DIR = os.path.join(LLVM_BUILD_TOOLS_DIR, 'pinned-clang')
BUG_REPORT_URL = ('https://crbug.com in the Tools>LLVM component,'
' run tools/clang/scripts/process_crashreports.py'
' (only if inside Google) to upload crash related files,')
LIBXML2_VERSION = 'libxml2-v2.9.12'
ZSTD_VERSION = 'zstd-1.5.5'
win_sdk_dir = None
def GetWinSDKDir():
"""Get the location of the current SDK."""
global win_sdk_dir
if win_sdk_dir:
return win_sdk_dir
environ_bak = dict(os.environ)
sys.path.append(os.path.join(CHROMIUM_DIR, 'build'))
import vs_toolchain
win_sdk_dir = vs_toolchain.SetEnvironmentAndGetSDKDir()
msvs_version = vs_toolchain.GetVisualStudioVersion()
if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1'))):
dia_path = os.path.join(win_sdk_dir, '..', 'DIA SDK', 'bin', 'amd64')
else:
if 'GYP_MSVS_OVERRIDE_PATH' not in os.environ:
vs_path = vs_toolchain.DetectVisualStudioPath()
else:
vs_path = os.environ['GYP_MSVS_OVERRIDE_PATH']
dia_path = os.path.join(vs_path, 'DIA SDK', 'bin', 'amd64')
os.environ.clear()
os.environ.update(environ_bak)
return win_sdk_dir
def RunCommand(command, setenv=False, env=None, fail_hard=True):
"""Run command and return success (True) or failure; or if fail_hard is
True, exit on failure. If setenv is True, runs the command in a
shell with the msvc tools for x64 architecture."""
if setenv and sys.platform == 'win32':
command = [os.path.join(CHROMIUM_DIR, 'tools', 'win', 'setenv.bat'), '&&'
] + command
if sys.platform != 'win32':
command = ' '.join([shlex.quote(c) for c in command])
print('Running', command)
if subprocess.call(command, env=env, shell=True) == 0:
return True
print('Failed.')
if fail_hard:
sys.exit(1)
return False
def CopyFile(src, dst):
"""Copy a file from src to dst."""
print("Copying %s to %s" % (src, dst))
shutil.copy(src, dst)
def CopyDirectoryContents(src, dst):
"""Copy the files from directory src to dst."""
dst = os.path.realpath(dst)
EnsureDirExists(dst)
for f in os.listdir(src):
CopyFile(os.path.join(src, f), dst)
def CheckoutGitRepo(name, git_url, commit, dir):
"""Checkout the git repo at a certain git commit in dir. Any local
modifications in dir will be lost."""
print(f'Checking out {name} {commit} into {dir}')
if os.path.isdir(dir):
os.chdir(dir)
if (RunCommand(['git', 'restore', '.'], fail_hard=False) and RunCommand(
['git', 'diff-index', '--exit-code', 'HEAD'], fail_hard=False)
and RunCommand(['git', 'fetch'], fail_hard=False)
and RunCommand(['git', 'checkout', commit], fail_hard=False)
and RunCommand(['git', 'clean', '-f'], fail_hard=False)):
return
os.chdir(CHROMIUM_DIR)
print('Removing %s.' % dir)
RmTree(dir)
clone_cmd = ['git', 'clone', git_url, dir]
if RunCommand(clone_cmd, fail_hard=False):
os.chdir(dir)
if RunCommand(['git', 'checkout', commit], fail_hard=False):
return
print('CheckoutGitRepo failed.')
sys.exit(1)
MODIFICATION_DATES = {
'GIT_AUTHOR_DATE': '2099-01-01 10:10:10',
'GIT_COMMITTER_DATE': '2099-01-01 10:10:10'
}
def IsGitAncestorToHead(git_repository, commit):
"""Returns if commit is an ancestor of HEAD."""
return RunCommand([
'git', '-C', git_repository, 'merge-base', '--is-ancestor', commit, 'HEAD'
],
fail_hard=False)
def GitCherryPick(git_repository,
commit,
git_remote=None,
git_remote_name='github'):
print(f'Cherry-picking {commit} in {git_repository} from {git_remote}')
git_cmd = ['git', '-C', git_repository]
if git_remote is not None:
RunCommand(git_cmd + ['remote', 'add', git_remote_name, git_remote],
fail_hard=False)
RunCommand(git_cmd +
['fetch', '--recurse-submodules=no', git_remote_name, commit])
if IsGitAncestorToHead(git_repository, commit):
print('Commit already an ancestor; skipping.')
return
env = os.environ.copy()
env.update(MODIFICATION_DATES)
RunCommand([
'git', '-C', git_repository, 'cherry-pick', '--keep-redundant-commits',
commit
],
env=env)
def GitRevert(git_repository, commit):
print(f'Reverting {commit} in {git_repository}')
if not IsGitAncestorToHead(git_repository, commit):
print('Commit not an ancestor; skipping.')
return
env = os.environ.copy()
env.update(MODIFICATION_DATES)
RunCommand(['git', '-C', git_repository, 'revert', '--no-edit', commit],
env=env)
def GetLatestLLVMCommit():
"""Get the latest commit hash in the LLVM monorepo."""
main = json.loads(
urllib.request.urlopen('https://chromium.googlesource.com/external/' +
'github.com/llvm/llvm-project/' +
'+/refs/heads/main?format=JSON').read().decode(
"utf-8").replace(")]}'", ""))
return main['commit']
def GetCommitDescription(commit):
"""Get the output of `git describe`.
Needs to be called from inside the git repository dir."""
git_exe = 'git.bat' if sys.platform.startswith('win') else 'git'
return subprocess.check_output([
git_exe, 'describe', '--long', '--abbrev=8', '--match=*llvmorg-*-init',
commit
], universal_newlines=True).rstrip()
def AddCMakeToPath():
"""Download CMake and add it to PATH."""
if sys.platform == 'win32':
zip_name = 'cmake-3.26.4-windows-x86_64.zip'
dir_name = ['cmake-3.26.4-windows-x86_64', 'bin']
elif sys.platform == 'darwin':
zip_name = 'cmake-3.26.4-macos-universal.tar.gz'
dir_name = ['cmake-3.26.4-macos-universal', 'CMake.app', 'Contents', 'bin']
else:
zip_name = 'cmake-3.26.4-linux-x86_64.tar.gz'
dir_name = ['cmake-3.26.4-linux-x86_64', 'bin']
cmake_dir = os.path.join(LLVM_BUILD_TOOLS_DIR, *dir_name)
if not os.path.exists(cmake_dir):
DownloadAndUnpack(CDS_URL + '/tools/' + zip_name, LLVM_BUILD_TOOLS_DIR)
os.environ['PATH'] = cmake_dir + os.pathsep + os.environ.get('PATH', '')
def AddGitForWindowsToPath():
"""Download Git for Windows and add it to PATH.
Git for Windows provides command line utilities (not Git) for tests."""
assert sys.platform == 'win32'
git_dir = os.path.join(LLVM_BUILD_TOOLS_DIR, 'git-for-windows')
version = '2.47.0'
stamp_file = os.path.join(git_dir, 'stamp')
if ReadStampFile(stamp_file) == version:
print('Git for Windows already up to date.')
else:
archive_name = 'PortableGit-%s-64-bit.zip' % version
DownloadAndUnpack(CDS_URL + '/tools/' + archive_name, git_dir)
WriteStampFile(version, stamp_file)
os.environ['PATH'] = os.path.join(
git_dir, 'usr', 'bin') + os.pathsep + os.environ.get('PATH', '')
def AddZlibToPath(dry_run = False):
"""Download and build zlib, and add to PATH."""
zlib_dir = os.path.join(LLVM_BUILD_TOOLS_DIR, 'zlib-1.2.11')
if dry_run:
return zlib_dir
if os.path.exists(zlib_dir):
RmTree(zlib_dir)
zip_name = 'zlib-1.2.11.tar.gz'
DownloadAndUnpack(CDS_URL + '/tools/' + zip_name, LLVM_BUILD_TOOLS_DIR)
os.chdir(zlib_dir)
zlib_files = [
'adler32', 'compress', 'crc32', 'deflate', 'gzclose', 'gzlib', 'gzread',
'gzwrite', 'inflate', 'infback', 'inftrees', 'inffast', 'trees',
'uncompr', 'zutil'
]
cl_flags = [
'/nologo', '/O2', '/DZLIB_DLL', '/c', '/D_CRT_SECURE_NO_DEPRECATE',
'/D_CRT_NONSTDC_NO_DEPRECATE'
]
RunCommand(['cl.exe'] + [f + '.c' for f in zlib_files] + cl_flags,
setenv=True)
RunCommand(['lib.exe'] + [f + '.obj'
for f in zlib_files] + ['/nologo', '/out:zlib.lib'],
setenv=True)
shutil.rmtree('test')
os.environ['PATH'] = zlib_dir + os.pathsep + os.environ.get('PATH', '')
return zlib_dir
class LibXmlDirs:
def __init__(self):
self.unzip_dir = LLVM_BUILD_TOOLS_DIR
self.src_dir = os.path.join(self.unzip_dir, LIBXML2_VERSION)
self.build_dir = os.path.join(self.src_dir, 'build')
self.install_dir = os.path.join(self.build_dir, 'install')
self.include_dir = os.path.join(self.install_dir, 'include', 'libxml2')
self.lib_dir = os.path.join(self.install_dir, 'lib')
def GetLibXml2Dirs():
"""Gets the set of directories where LibXml2 is located.
Includes the diractories where the source is unpacked, where it is built,
and installed."""
return LibXmlDirs()
def BuildLibXml2():
"""Download and build libxml2"""
dirs = GetLibXml2Dirs()
if os.path.exists(dirs.src_dir):
RmTree(dirs.src_dir)
zip_name = LIBXML2_VERSION + '.tar.gz'
DownloadAndUnpack(CDS_URL + '/tools/' + zip_name, dirs.unzip_dir)
os.mkdir(dirs.build_dir)
os.chdir(dirs.build_dir)
RunCommand(
[
'cmake',
'-GNinja',
'-DCMAKE_BUILD_TYPE=Release',
'-DCMAKE_INSTALL_PREFIX=install',
'-DCMAKE_INSTALL_LIBDIR=lib',
'-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded',
'-DBUILD_SHARED_LIBS=OFF',
'-DLIBXML2_WITH_C14N=OFF',
'-DLIBXML2_WITH_CATALOG=OFF',
'-DLIBXML2_WITH_DEBUG=OFF',
'-DLIBXML2_WITH_DOCB=OFF',
'-DLIBXML2_WITH_FTP=OFF',
'-DLIBXML2_WITH_HTML=OFF',
'-DLIBXML2_WITH_HTTP=OFF',
'-DLIBXML2_WITH_ICONV=OFF',
'-DLIBXML2_WITH_ICU=OFF',
'-DLIBXML2_WITH_ISO8859X=OFF',
'-DLIBXML2_WITH_LEGACY=OFF',
'-DLIBXML2_WITH_LZMA=OFF',
'-DLIBXML2_WITH_MEM_DEBUG=OFF',
'-DLIBXML2_WITH_MODULES=OFF',
'-DLIBXML2_WITH_OUTPUT=ON',
'-DLIBXML2_WITH_PATTERN=OFF',
'-DLIBXML2_WITH_PROGRAMS=OFF',
'-DLIBXML2_WITH_PUSH=OFF',
'-DLIBXML2_WITH_PYTHON=OFF',
'-DLIBXML2_WITH_READER=OFF',
'-DLIBXML2_WITH_REGEXPS=OFF',
'-DLIBXML2_WITH_RUN_DEBUG=OFF',
'-DLIBXML2_WITH_SAX1=OFF',
'-DLIBXML2_WITH_SCHEMAS=OFF',
'-DLIBXML2_WITH_SCHEMATRON=OFF',
'-DLIBXML2_WITH_TESTS=OFF',
'-DLIBXML2_WITH_THREADS=ON',
'-DLIBXML2_WITH_THREAD_ALLOC=OFF',
'-DLIBXML2_WITH_TREE=ON',
'-DLIBXML2_WITH_VALID=OFF',
'-DLIBXML2_WITH_WRITER=OFF',
'-DLIBXML2_WITH_XINCLUDE=OFF',
'-DLIBXML2_WITH_XPATH=OFF',
'-DLIBXML2_WITH_XPTR=OFF',
'-DLIBXML2_WITH_ZLIB=OFF',
'..',
],
setenv=True)
RunCommand(['ninja', 'install'], setenv=True)
if sys.platform == 'win32':
libxml2_lib = os.path.join(dirs.lib_dir, 'libxml2s.lib')
else:
libxml2_lib = os.path.join(dirs.lib_dir, 'libxml2.a')
extra_cmake_flags = [
'-DLLVM_ENABLE_LIBXML2=FORCE_ON',
'-DLIBXML2_INCLUDE_DIR=' + dirs.include_dir.replace('\\', '/'),
'-DLIBXML2_LIBRARIES=' + libxml2_lib.replace('\\', '/'),
'-DLIBXML2_LIBRARY=' + libxml2_lib.replace('\\', '/'),
'-DCLANG_ENABLE_LIBXML2=NO',
]
extra_cflags = ['-DLIBXML_STATIC']
return extra_cmake_flags, extra_cflags
class ZStdDirs:
"""
The set of directories where zstd is located.
Includes the diractories where the source is unpacked, where it is built,
and installed.
"""
def __init__(self):
self.unzip_dir = LLVM_BUILD_TOOLS_DIR
self.src_dir = os.path.join(self.unzip_dir, ZSTD_VERSION)
self.build_dir = os.path.join(self.src_dir, 'cmake_build')
self.install_dir = os.path.join(self.build_dir, 'install')
self.include_dir = os.path.join(self.install_dir, 'include')
self.lib_dir = os.path.join(self.install_dir, 'lib')
def BuildZStd():
"""Download and build zstd lib"""
dirs = ZStdDirs()
if os.path.exists(dirs.src_dir):
RmTree(dirs.src_dir)
zip_name = ZSTD_VERSION + '.tar.gz'
DownloadAndUnpack(CDS_URL + '/tools/' + zip_name, dirs.unzip_dir)
os.mkdir(dirs.build_dir)
os.chdir(dirs.build_dir)
RunCommand(
[
'cmake',
'-GNinja',
'-DCMAKE_BUILD_TYPE=Release',
'-DCMAKE_INSTALL_PREFIX=install',
'-DCMAKE_INSTALL_LIBDIR=lib',
'-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded',
'-DZSTD_BUILD_SHARED=OFF',
'../build/cmake',
],
setenv=True)
RunCommand(['ninja', 'install'], setenv=True)
if sys.platform == 'win32':
zstd_lib = os.path.join(dirs.lib_dir, 'zstd_static.lib')
else:
zstd_lib = os.path.join(dirs.lib_dir, 'libzstd.a')
extra_cmake_flags = [
'-DLLVM_ENABLE_ZSTD=ON',
'-DLLVM_USE_STATIC_ZSTD=ON',
'-Dzstd_INCLUDE_DIR=' + dirs.include_dir.replace('\\', '/'),
'-Dzstd_LIBRARY=' + zstd_lib.replace('\\', '/'),
]
extra_cflags = []
return extra_cmake_flags, extra_cflags
def DownloadPinnedClang():
PINNED_CLANG_VERSION = 'llvmorg-21-init-5118-g52cd27e6-4'
DownloadAndUnpackPackage('clang', PINNED_CLANG_DIR, GetDefaultHostOs(),
PINNED_CLANG_VERSION)
def VerifyVersionOfBuiltClangMatchesVERSION():
"""Checks that `clang --version` outputs RELEASE_VERSION. If this
fails, update.RELEASE_VERSION is out-of-date and needs to be updated (possibly
in an `if args.llvm_force_head_revision:` block inupdate. main() first)."""
clang = os.path.join(LLVM_BUILD_DIR, 'bin', 'clang')
if sys.platform == 'win32':
clang += '-cl.exe'
version_out = subprocess.check_output([clang, '--version'],
universal_newlines=True)
version_out = re.match(r'clang version ([0-9]+)', version_out).group(1)
if version_out != RELEASE_VERSION:
print(('unexpected clang version %s (not %s), '
'update RELEASE_VERSION in update.py')
% (version_out, RELEASE_VERSION))
sys.exit(1)
def VerifyZlibSupport():
"""Check that clang was built with zlib support enabled."""
clang = os.path.join(LLVM_BUILD_DIR, 'bin', 'clang')
test_file = '/dev/null'
if sys.platform == 'win32':
clang += '.exe'
test_file = 'nul'
print('Checking for zlib support')
clang_out = subprocess.check_output([
clang, '-target', 'x86_64-unknown-linux-gnu', '-gz', '-c', '-###', '-x',
'c', test_file
],
stderr=subprocess.STDOUT,
universal_newlines=True)
if (re.search(r'--compress-debug-sections', clang_out)):
print('OK')
else:
print(('Failed to detect zlib support!\n\n(driver output: %s)') % clang_out)
sys.exit(1)
def VerifyZStdSupport():
"""Check that lld was built with zstd support enabled."""
lld = os.path.join(LLVM_BUILD_DIR, 'bin')
if sys.platform == 'win32':
lld = os.path.join(lld, 'lld-link.exe')
elif sys.platform == 'linux':
lld = os.path.join(lld, 'ld.lld')
else:
print('zstd support check cannot be performed on the unsupported ' \
'platform ' + sys.platform)
return
print('Checking for zstd support')
lld_out = subprocess.run([lld, '--compress-debug-sections=zstd'],
check=False,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True).stdout
if '--compress-debug-sections: zstd is not available' in lld_out:
print(('Failed to detect zlib support!\n\n(driver output: %s)') % lld_out)
sys.exit(1)
else:
print('OK')
def DownloadDebianSysroot(platform_name, skip_download=False):
toolchain_bucket = 'https://commondatastorage.googleapis.com/chrome-linux-sysroot/'
hashes = {
'amd64': 'dec7a3a0fc5b83b909cba1b6d119077e0429a138eadef6bf5a0f2e03b1904631',
'i386': 'b53933120bb08ffc38140a817e3f0f99782254a6bf9622271574fa004e8783a4',
'arm': 'fe81e7114b97440262bce004caf02c1514732e2fa7f99693b2836932ad1c4626',
'arm64': '308e23faba3174bd01accfe358467b8a40fad4db4c49ef629da30219f65a275f',
'riscv64': '6c924a8f88bb4731f3c2334c6ae5b5da47d5ca196ff571a91071f104dbacecad',
}
releases = {
'riscv64': 'trixie',
}
release = releases.get(platform_name, 'bullseye')
toolchain_name = f'debian_{release}_{platform_name}_sysroot'
output = os.path.join(LLVM_BUILD_TOOLS_DIR, toolchain_name)
stamp_file = os.path.join(output, 'stamp')
version = hashes[platform_name]
if ReadStampFile(stamp_file) == version:
print(f'Sysroot for {platform_name} already up to date')
else:
U = toolchain_bucket + version
if not skip_download:
DownloadAndUnpack(U, output)
WriteStampFile(version, stamp_file)
return output
def compiler_rt_cmake_flags(*, sanitizers, profile):
args = [
'COMPILER_RT_BUILD_CRT=ON',
'COMPILER_RT_BUILD_LIBFUZZER=OFF',
'COMPILER_RT_BUILD_CTX_PROFILE=OFF',
'COMPILER_RT_BUILD_MEMPROF=OFF',
'COMPILER_RT_BUILD_ORC=OFF',
'COMPILER_RT_BUILD_PROFILE=' + ('ON' if profile else 'OFF'),
'COMPILER_RT_BUILD_SANITIZERS=' + ('ON' if sanitizers else 'OFF'),
'COMPILER_RT_BUILD_XRAY=OFF',
'COMPILER_RT_SANITIZERS_TO_BUILD=asan;dfsan;msan;hwasan;tsan;cfi',
'COMPILER_RT_DEFAULT_TARGET_ONLY=ON',
]
return args
def gn_arg(v):
if v == 'True':
return True
if v == 'False':
return False
raise argparse.ArgumentTypeError('Expected one of %r or %r' % (
'True', 'False'))
class Timer:
class Region:
def __init__(self, phase, parent):
self.parent = parent
self.phase = phase
def __enter__(self):
self.start = time.time()
def __exit__(self, *args):
elapsed = time.time() - self.start
self.parent.times.append((self.phase, elapsed))
def __init__(self):
self.times = []
def time(self, phase):
return Timer.Region(phase, self)
def dump(self):
if not self.times:
return
longest_phase = max(len(phase) for (phase, elapsed) in self.times)
longest_elapsed = max(len(str(int(elapsed))) for (phase, elapsed) in self.times)
print('-- timers --')
for (phase, elapsed) in self.times:
print('{}: {:{}.1f}'.format(phase.rjust(longest_phase), elapsed, longest_elapsed + 2))
def main():
timer = Timer()
atexit.register(Timer.dump, timer)
parser = argparse.ArgumentParser(description='Build Clang.')
parser.add_argument('--bootstrap',
action='store_true',
help='first build clang with CC, then with itself.')
parser.add_argument('--disable-asserts', action='store_true',
help='build with asserts disabled')
parser.add_argument('--host-cc',
help='build with host C compiler, requires --host-cxx as '
'well')
parser.add_argument('--host-cxx',
help='build with host C++ compiler, requires --host-cc '
'as well')
parser.add_argument('--pgo', action='store_true', help='build with PGO')
parser.add_argument('--thinlto',
action='store_true',
help='build with ThinLTO')
parser.add_argument('--bolt', action='store_true', help='build with BOLT')
parser.add_argument('--llvm-force-head-revision', action='store_true',
help='build the latest revision')
parser.add_argument('--run-tests', action='store_true',
help='run tests after building')
parser.add_argument('--skip-build', action='store_true',
help='do not build anything')
parser.add_argument('--skip-checkout', action='store_true',
help='do not create or update any checkouts')
parser.add_argument('--build-dir',
help='Override build directory')
parser.add_argument('--install-dir',
help='override the install directory for the final '
'compiler. If not specified, no install happens for '
'the compiler.')
parser.add_argument('--no-tools',
action='store_true',
help='don\'t build any chromium tools or '
'clang-extra-tools. Overrides --extra-tools.')
parser.add_argument('--extra-tools', nargs='*', default=[],
help='select additional chrome tools to build')
parser.add_argument('--no-runtimes',
action='store_true',
help='don\'t build compiler-rt, sanitizer and profile '
'runtimes. This is incompatible with --pgo. On Mac, '
'compiler-rt is always built regardless.')
parser.add_argument('--use-system-cmake',
action='store_true',
help='use the cmake from PATH instead of downloading '
'and using prebuilt cmake binaries')
parser.add_argument('--tf-path',
help='path to python tensorflow pip package. '
'Used for embedding an MLGO model')
parser.add_argument(
'--with-ml-inliner-model',
help='path to MLGO inliner model to embed. Setting to '
'\'default\', will download an official model which was '
'trained for Chrome on Android',
default='default' if sys.platform.startswith('linux') else '')
parser.add_argument('--with-android', type=gn_arg, nargs='?', const=True,
help='build the Android ASan runtime (linux only)',
default=sys.platform.startswith('linux'))
parser.add_argument('--pic',
action='store_true',
help='Uses PIC when building LLVM')
parser.add_argument('--with-fuchsia',
type=gn_arg,
nargs='?',
const=True,
help='build the Fuchsia runtimes (linux only)',
default=sys.platform.startswith('linux'))
parser.add_argument('--without-android', action='store_false',
help='don\'t build Android ASan runtime (linux only)',
dest='with_android')
parser.add_argument('--without-fuchsia', action='store_false',
help='don\'t build Fuchsia clang_rt runtime (linux/mac)',
dest='with_fuchsia',
default=sys.platform in ('linux2', 'darwin'))
parser.add_argument('--with-ccache',
action='store_true',
help='Use ccache to build the stage 1 compiler')
parser.add_argument('--without-zstd',
dest='with_zstd',
action='store_false',
help='Disable zstd in the build')
parser.add_argument('--preserve-gcs-signature',
action='store_true',
help='By default, this script removes gcs hash files '
'so that third_party/llvm-build is clobbered on the next'
'run of gclient sync. This disables that, so that the'
'directory will be preserved when syncing. Useful for'
'local development.')
args = parser.parse_args()
global CLANG_REVISION, PACKAGE_VERSION, LLVM_BUILD_DIR, STAMP_FILE, FORCE_HEAD_REVISION_FILE
if (args.pgo or args.thinlto) and not args.bootstrap:
print('--pgo/--thinlto requires --bootstrap')
return 1
if args.with_android and not os.path.exists(ANDROID_NDK_DIR):
print('Android NDK not found at ' + ANDROID_NDK_DIR)
print('The Android NDK is needed to build a Clang whose -fsanitize=address')
print('works on Android. See ')
print('https://www.chromium.org/developers/how-tos/android-build-instructions')
print('for how to install the NDK, or pass --without-android.')
return 1
if args.no_runtimes and args.pgo:
print('--pgo requires runtimes, can\'t use --no-runtimes')
return 1
if args.with_fuchsia and not os.path.exists(FUCHSIA_SDK_DIR):
print('Fuchsia SDK not found at ' + FUCHSIA_SDK_DIR)
print('The Fuchsia SDK is needed to build libclang_rt for Fuchsia.')
print('Install the Fuchsia SDK by adding fuchsia to the ')
print('target_os section in your .gclient and running hooks, ')
print('or pass --without-fuchsia.')
print(
'https://chromium.googlesource.com/chromium/src/+/main/docs/fuchsia/build_instructions.md'
)
print('for general Fuchsia build instructions.')
return 1
if args.with_ml_inliner_model and not sys.platform.startswith('linux'):
print('--with-ml-inliner-model only supports linux hosts')
return 1
major, _, _, _, _ = sys.version_info
if major == 3:
sys.stdout = io.TextIOWrapper(open(sys.stdout.fileno(), 'wb', 0),
write_through=True)
else:
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
if args.build_dir:
LLVM_BUILD_DIR = args.build_dir
STAMP_FILE = os.path.normpath(os.path.join(LLVM_BUILD_DIR, STAMP_FILENAME))
FORCE_HEAD_REVISION_FILE = os.path.normpath(
os.path.join(LLVM_BUILD_DIR, "..", FORCE_HEAD_REVISION_FILENAME))
if args.llvm_force_head_revision:
checkout_revision = GetLatestLLVMCommit()
else:
checkout_revision = CLANG_REVISION
if not args.skip_checkout:
with timer.time('checkout llvm'):
CheckoutGitRepo('LLVM monorepo', LLVM_GIT_URL, checkout_revision, LLVM_DIR)
GitCherryPick(LLVM_DIR, '10e97641f53a6eba5ad9430dc25f1ad6e5e8abed')
if args.llvm_force_head_revision:
CLANG_REVISION = GetCommitDescription(checkout_revision)
PACKAGE_VERSION = '%s-0' % CLANG_REVISION
print('Locally building clang %s...' % PACKAGE_VERSION)
WriteStampFile('',
STAMP_FILE,
preserve_hash_files=args.preserve_gcs_signature)
WriteStampFile('',
FORCE_HEAD_REVISION_FILE,
preserve_hash_files=args.preserve_gcs_signature)
if not args.use_system_cmake:
AddCMakeToPath()
ninja_dir = os.path.join(THIRD_PARTY_DIR, 'ninja')
os.environ['PATH'] = ninja_dir + os.pathsep + os.environ.get('PATH', '')
if sys.platform.startswith('linux'):
with timer.time('get sysroots'):
sysroot_amd64 = DownloadDebianSysroot('amd64', args.skip_checkout)
sysroot_i386 = DownloadDebianSysroot('i386', args.skip_checkout)
sysroot_arm = DownloadDebianSysroot('arm', args.skip_checkout)
sysroot_arm64 = DownloadDebianSysroot('arm64', args.skip_checkout)
sysroot_riscv64 = DownloadDebianSysroot('riscv64', args.skip_checkout)
if args.skip_build:
return 0
cc, cxx, lld = None, None, None
cflags = []
cxxflags = []
ldflags = []
targets = 'AArch64;ARM;LoongArch;Mips;PowerPC;RISCV;SystemZ;WebAssembly;X86'
projects = 'clang;lld'
if not args.no_tools:
projects += ';clang-tools-extra'
if args.bolt:
projects += ';bolt'
runtimes = ''
if not args.no_runtimes or sys.platform == 'darwin':
runtimes = 'compiler-rt'
pic_default = sys.platform == 'win32'
pic_mode = 'ON' if args.pic or pic_default else 'OFF'
base_cmake_args = [
'-GNinja',
'-DCMAKE_BUILD_TYPE=Release',
'-DLLVM_ENABLE_ASSERTIONS=%s' % ('OFF' if args.disable_asserts else 'ON'),
f'-DLLVM_ENABLE_PROJECTS={projects}',
f'-DLLVM_ENABLE_RUNTIMES={runtimes}',
f'-DLLVM_TARGETS_TO_BUILD={targets}',
f'-DLLVM_ENABLE_PIC={pic_mode}',
'-DLLVM_ENABLE_TERMINFO=OFF',
'-DLLVM_ENABLE_Z3_SOLVER=OFF',
'-DCLANG_PLUGIN_SUPPORT=OFF',
'-DCLANG_ENABLE_STATIC_ANALYZER=OFF',
'-DCLANG_ENABLE_ARCMT=OFF',
'-DBUG_REPORT_URL=' + BUG_REPORT_URL,
'-DLLVM_ENABLE_DIA_SDK=OFF',
'-DLLVM_ENABLE_LLD=ON',
'-DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF',
'-DLLVM_ENABLE_CURL=OFF',
'-DLIBCLANG_BUILD_STATIC=ON',
'-DLLVM_INSTALL_UTILS=ON',
'-DLLVM_ENABLE_ZSTD=%s' % ('ON' if args.with_zstd else 'OFF'),
]
if sys.platform == 'darwin':
isysroot = subprocess.check_output(['xcrun', '--show-sdk-path'],
universal_newlines=True).rstrip()
base_cmake_args += ['-DLLVM_ENABLE_UNWIND_TABLES=OFF']
ccache_cmake_args = []
if args.with_ccache:
ccache_cmake_args.append('-DCMAKE_C_COMPILER_LAUNCHER=ccache')
ccache_cmake_args.append('-DCMAKE_CXX_COMPILER_LAUNCHER=ccache')
if args.host_cc or args.host_cxx:
assert args.host_cc and args.host_cxx, \
"--host-cc and --host-cxx need to be used together"
cc = args.host_cc
cxx = args.host_cxx
else:
with timer.time('get pinned clang'):
DownloadPinnedClang()
if sys.platform == 'win32':
cc = os.path.join(PINNED_CLANG_DIR, 'bin', 'clang-cl.exe')
cxx = os.path.join(PINNED_CLANG_DIR, 'bin', 'clang-cl.exe')
lld = os.path.join(PINNED_CLANG_DIR, 'bin', 'lld-link.exe')
cc = cc.replace('\\', '/')
cxx = cxx.replace('\\', '/')
lld = lld.replace('\\', '/')
else:
cc = os.path.join(PINNED_CLANG_DIR, 'bin', 'clang')
cxx = os.path.join(PINNED_CLANG_DIR, 'bin', 'clang++')
if sys.platform.startswith('linux'):
base_cmake_args += [ '-DLLVM_STATIC_LINK_CXX_STDLIB=ON' ]
if sys.platform.startswith('linux'):
if platform.machine() == 'aarch64':
base_cmake_args.append('-DCMAKE_SYSROOT=' + sysroot_arm64)
else:
base_cmake_args.append('-DCMAKE_SYSROOT=' + sysroot_amd64)
if sys.platform == 'win32':
AddGitForWindowsToPath()
base_cmake_args.append('-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded')
zlib_dir = AddZlibToPath()
cflags.append('-I' + zlib_dir)
cxxflags.append('-I' + zlib_dir)
ldflags.append('-LIBPATH:' + zlib_dir)
base_cmake_args.append('-DLLVM_ENABLE_RPMALLOC=ON')
base_cmake_args.append('-DLLVM_WINSYSROOT="%s"' %
os.path.dirname(os.path.dirname(GetWinSDKDir())))
with timer.time('libxml2 build'):
libxml_cmake_args, libxml_cflags = BuildLibXml2()
base_cmake_args += libxml_cmake_args
cflags += libxml_cflags
cxxflags += libxml_cflags
if args.with_zstd:
with timer.time('zstd build'):
zstd_cmake_args, zstd_cflags = BuildZStd()
base_cmake_args += zstd_cmake_args
cflags += zstd_cflags
cxxflags += zstd_cflags
lit_excludes = []
if sys.platform.startswith('linux'):
lit_excludes += [
'^MemorySanitizer-.* f?stat(at)?(64)?.cpp$',
'^.*Sanitizer-.*sunrpc.*cpp$',
'^.*Sanitizer.*mallinfo2.cpp$',
'^BOLT.*runtime/instrumentation-indirect-2.c$',
]
elif sys.platform == 'darwin':
lit_excludes += [
'^.*instrprof-darwin-exports.c$',
]
if platform.machine() == 'arm64':
lit_excludes += [
'^.*AddressSanitizer-arm64-darwin.*$',
'^.*SanitizerCommon-lsan-arm64-Darwin.*$',
'^.*SanitizerCommon-ubsan-arm64-Darwin.*Posix/dedup_token_length_test.cpp$',
]
elif sys.platform == 'win32':
lit_excludes += [
'^.*Profile-x86_64.*ContinuousSyncMode/online-merging-windows.c$',
]
test_env = os.environ.copy()
test_env['FILECHECK_OPTS'] = '--dump-input-filter=all'
test_env['LIT_OPTS'] = '--show-flakypass'
if lit_excludes:
test_env['LIT_FILTER_OUT'] = '|'.join(lit_excludes)
if args.bootstrap:
print('Building bootstrap compiler')
if os.path.exists(LLVM_BOOTSTRAP_DIR):
RmTree(LLVM_BOOTSTRAP_DIR)
EnsureDirExists(LLVM_BOOTSTRAP_DIR)
os.chdir(LLVM_BOOTSTRAP_DIR)
runtimes = []
if args.pgo or sys.platform == 'darwin':
runtimes.append('compiler-rt')
bootstrap_targets = 'X86'
if sys.platform == 'darwin':
bootstrap_targets += ';ARM;AArch64'
bootstrap_args = base_cmake_args + ccache_cmake_args + [
'-DLLVM_TARGETS_TO_BUILD=' + bootstrap_targets,
'-DLLVM_ENABLE_PROJECTS=clang;lld',
'-DLLVM_ENABLE_RUNTIMES=' + ';'.join(runtimes),
'-DCMAKE_INSTALL_PREFIX=' + LLVM_BOOTSTRAP_INSTALL_DIR,
'-DCMAKE_C_FLAGS=' + ' '.join(cflags),
'-DCMAKE_CXX_FLAGS=' + ' '.join(cxxflags),
'-DCMAKE_EXE_LINKER_FLAGS=' + ' '.join(ldflags),
'-DCMAKE_SHARED_LINKER_FLAGS=' + ' '.join(ldflags),
'-DCMAKE_MODULE_LINKER_FLAGS=' + ' '.join(ldflags),
'-DLLVM_ENABLE_ASSERTIONS=ON',
]
bootstrap_args.extend([
'-D' + f
for f in compiler_rt_cmake_flags(sanitizers=False, profile=args.pgo)
])
if sys.platform == 'darwin':
bootstrap_args.extend([
'-DCOMPILER_RT_ENABLE_IOS=OFF',
'-DCOMPILER_RT_ENABLE_WATCHOS=OFF',
'-DCOMPILER_RT_ENABLE_TVOS=OFF',
])
if platform.machine() == 'arm64':
bootstrap_args.extend(['-DDARWIN_osx_ARCHS=arm64'])
else:
bootstrap_args.extend(['-DDARWIN_osx_ARCHS=x86_64'])
if cc is not None: bootstrap_args.append('-DCMAKE_C_COMPILER=' + cc)
if cxx is not None: bootstrap_args.append('-DCMAKE_CXX_COMPILER=' + cxx)
if lld is not None: bootstrap_args.append('-DCMAKE_LINKER=' + lld)
with timer.time('bootstrap cmake'):
RunCommand(['cmake'] + bootstrap_args + [os.path.join(LLVM_DIR, 'llvm')],
setenv=True)
with timer.time('bootstrap build'):
RunCommand(['ninja'], setenv=True)
if args.run_tests and not (platform.machine() == 'x86_64'
and sys.platform == 'darwin'):
with timer.time('bootstrap check-all'):
RunCommand(['ninja', 'check-all'], env=test_env, setenv=True)
with timer.time('bootstrap install'):
RunCommand(['ninja', 'install'], setenv=True)
if sys.platform == 'win32':
cc = os.path.join(LLVM_BOOTSTRAP_INSTALL_DIR, 'bin', 'clang-cl.exe')
cxx = os.path.join(LLVM_BOOTSTRAP_INSTALL_DIR, 'bin', 'clang-cl.exe')
lld = os.path.join(LLVM_BOOTSTRAP_INSTALL_DIR, 'bin', 'lld-link.exe')
cc = cc.replace('\\', '/')
cxx = cxx.replace('\\', '/')
lld = lld.replace('\\', '/')
else:
cc = os.path.join(LLVM_BOOTSTRAP_INSTALL_DIR, 'bin', 'clang')
cxx = os.path.join(LLVM_BOOTSTRAP_INSTALL_DIR, 'bin', 'clang++')
print('Bootstrap compiler installed.')
if args.pgo:
print('Building instrumented compiler')
if os.path.exists(LLVM_INSTRUMENTED_DIR):
RmTree(LLVM_INSTRUMENTED_DIR)
EnsureDirExists(LLVM_INSTRUMENTED_DIR)
os.chdir(LLVM_INSTRUMENTED_DIR)
cflags += ['-Wno-backend-plugin']
cxxflags += ['-Wno-backend-plugin']
instrument_args = base_cmake_args + [
'-DLLVM_ENABLE_PROJECTS=clang',
'-DCMAKE_C_FLAGS=' + ' '.join(cflags),
'-DCMAKE_CXX_FLAGS=' + ' '.join(cxxflags),
'-DCMAKE_EXE_LINKER_FLAGS=' + ' '.join(ldflags),
'-DCMAKE_SHARED_LINKER_FLAGS=' + ' '.join(ldflags),
'-DCMAKE_MODULE_LINKER_FLAGS=' + ' '.join(ldflags),
'-DLLVM_BUILD_INSTRUMENTED=IR',
]
if cc is not None: instrument_args.append('-DCMAKE_C_COMPILER=' + cc)
if cxx is not None: instrument_args.append('-DCMAKE_CXX_COMPILER=' + cxx)
if lld is not None: instrument_args.append('-DCMAKE_LINKER=' + lld)
with timer.time('pgo cmake'):
RunCommand(['cmake'] + instrument_args + [os.path.join(LLVM_DIR, 'llvm')],
setenv=True)
with timer.time('pgo build'):
RunCommand(['ninja', 'clang'], setenv=True)
print('Instrumented compiler built.')
with timer.time('pgo training'):
training_source = 'pgo_training-3.ii'
with open(training_source, 'wb') as f:
DownloadUrl(CDS_URL + '/' + training_source, f)
train_cmd = [os.path.join(LLVM_INSTRUMENTED_DIR, 'bin', 'clang++'),
'-target', 'x86_64-unknown-unknown', '-O2', '-g', '-std=c++20',
'-fno-exceptions', '-fno-rtti', '-w', '-c', training_source]
if sys.platform == 'darwin':
train_cmd.extend(['-isysroot', isysroot])
RunCommand(train_cmd, setenv=True)
profdata = os.path.join(LLVM_BOOTSTRAP_INSTALL_DIR, 'bin', 'llvm-profdata')
RunCommand(
[profdata, 'merge', '-output=' + LLVM_PROFDATA_FILE] +
glob.glob(os.path.join(LLVM_INSTRUMENTED_DIR, 'profiles', '*.profraw')),
setenv=True)
print('Profile generated.')
deployment_target = '10.12'
if args.llvm_force_head_revision:
cflags += ['-DLLVM_FORCE_HEAD_REVISION']
cxxflags += ['-DLLVM_FORCE_HEAD_REVISION']
if sys.platform == 'win32':
cflags += ['/Zi', '/GS-']
cxxflags += ['/Zi', '/GS-']
ldflags += ['/DEBUG', '/OPT:REF', '/OPT:ICF']
deployment_env = None
if deployment_target:
deployment_env = os.environ.copy()
deployment_env['MACOSX_DEPLOYMENT_TARGET'] = deployment_target
print('Building final compiler.')
if args.bolt:
ldflags += ['-Wl,--emit-relocs', '-Wl,-znow']
chrome_tools = []
if not args.no_tools:
default_tools = [
'plugins', 'blink_gc_plugin', 'raw_ptr_plugin', 'translation_unit'
]
chrome_tools = list(set(default_tools + args.extra_tools))
if cc is not None: base_cmake_args.append('-DCMAKE_C_COMPILER=' + cc)
if cxx is not None: base_cmake_args.append('-DCMAKE_CXX_COMPILER=' + cxx)
if lld is not None: base_cmake_args.append('-DCMAKE_LINKER=' + lld)
final_install_dir = args.install_dir if args.install_dir else LLVM_BUILD_DIR
cmake_args = base_cmake_args + [
'-DCMAKE_C_FLAGS=' + ' '.join(cflags),
'-DCMAKE_CXX_FLAGS=' + ' '.join(cxxflags),
'-DCMAKE_EXE_LINKER_FLAGS=' + ' '.join(ldflags),
'-DCMAKE_SHARED_LINKER_FLAGS=' + ' '.join(ldflags),
'-DCMAKE_MODULE_LINKER_FLAGS=' + ' '.join(ldflags),
'-DCMAKE_INSTALL_PREFIX=' + final_install_dir,
]
if not args.no_tools:
cmake_args.extend([
'-DLLVM_EXTERNAL_PROJECTS=chrometools',
'-DLLVM_EXTERNAL_CHROMETOOLS_SOURCE_DIR=' +
os.path.join(CHROMIUM_DIR, 'tools', 'clang'),
'-DCHROMIUM_TOOLS=%s' % ';'.join(chrome_tools)
])
if args.pgo:
cmake_args.append('-DLLVM_PROFDATA_FILE=' + LLVM_PROFDATA_FILE)
if args.thinlto:
cmake_args.append('-DLLVM_ENABLE_LTO=Thin')
if sys.platform == 'win32':
cmake_args.append('-DLLVM_ENABLE_ZLIB=FORCE_ON')
if sys.platform == 'darwin':
if platform.machine() == 'arm64':
cmake_args.append('-DLLVM_DEFAULT_TARGET_TRIPLE=arm64-apple-darwin')
else:
cmake_args.append('-DLLVM_DEFAULT_TARGET_TRIPLE=x86_64-apple-darwin')
elif sys.platform.startswith('linux'):
if platform.machine() == 'aarch64':
cmake_args.append(
'-DLLVM_DEFAULT_TARGET_TRIPLE=aarch64-unknown-linux-gnu')
elif platform.machine() == 'riscv64':
cmake_args.append(
'-DLLVM_DEFAULT_TARGET_TRIPLE=riscv64-unknown-linux-gnu')
elif platform.machine() == 'loongarch64':
cmake_args.append(
'-DLLVM_DEFAULT_TARGET_TRIPLE=loongarch64-unknown-linux-gnu')
else:
cmake_args.append('-DLLVM_DEFAULT_TARGET_TRIPLE=x86_64-unknown-linux-gnu')
cmake_args.append('-DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=ON')
elif sys.platform == 'win32':
cmake_args.append('-DLLVM_DEFAULT_TARGET_TRIPLE=x86_64-pc-windows-msvc')
runtimes_triples_args = {}
if sys.platform.startswith('linux'):
runtimes_triples_args['i386-unknown-linux-gnu'] = {
"args": [
'CMAKE_SYSROOT=%s' % sysroot_i386,
'LLVM_INCLUDE_TESTS=OFF',
],
"profile":
True,
"sanitizers":
True,
}
runtimes_triples_args['x86_64-unknown-linux-gnu'] = {
"args": [
'CMAKE_SYSROOT=%s' % sysroot_amd64,
],
"profile": True,
"sanitizers": True,
}
runtimes_triples_args['armv7-unknown-linux-gnueabihf'] = {
"args": [
'CMAKE_SYSROOT=%s' % sysroot_arm,
'LLVM_INCLUDE_TESTS=OFF',
],
"profile":
True,
"sanitizers":
True,
}
runtimes_triples_args['aarch64-unknown-linux-gnu'] = {
"args": [
'CMAKE_SYSROOT=%s' % sysroot_arm64,
'LLVM_INCLUDE_TESTS=OFF',
],
"profile":
True,
"sanitizers":
True,
}
runtimes_triples_args['riscv64-unknown-linux-gnu'] = {
"args": [
'CMAKE_SYSROOT=%s' % sysroot_riscv64,
'LLVM_INCLUDE_TESTS=OFF',
],
"profile":
True,
"sanitizers":
True,
}
elif sys.platform == 'win32':
sysroot = os.path.dirname(os.path.dirname(GetWinSDKDir()))
runtimes_triples_args['i386-pc-windows-msvc'] = {
"args": [
'LLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF',
'LLVM_WINSYSROOT="%s"' % sysroot,
],
"profile":
True,
"sanitizers":
False,
}
runtimes_triples_args['x86_64-pc-windows-msvc'] = {
"args": [
'LLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF',
'LLVM_WINSYSROOT="%s"' % sysroot,
],
"profile":
True,
"sanitizers":
True,
}
runtimes_triples_args['aarch64-pc-windows-msvc'] = {
"args": [
'LLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF',
'LLVM_WINSYSROOT="%s"' % sysroot,
'LLVM_INCLUDE_TESTS=OFF',
],
"profile":
True,
"sanitizers":
False,
}
elif sys.platform == 'darwin':
runtimes_triples_args['default'] = {
"args": [
'SANITIZER_MIN_OSX_VERSION=' + deployment_target,
'COMPILER_RT_ENABLE_MACCATALYST=ON',
'COMPILER_RT_ENABLE_IOS=ON',
'COMPILER_RT_ENABLE_WATCHOS=ON',
'COMPILER_RT_ENABLE_TVOS=ON',
'COMPILER_RT_ENABLE_XROS=ON',
'DARWIN_ios_ARCHS=arm64',
'DARWIN_iossim_ARCHS=arm64;x86_64',
'DARWIN_osx_ARCHS=arm64;x86_64',
'DARWIN_tvos_BUILTIN_ARCHS=arm64',
'DARWIN_tvossim_BUILTIN_ARCHS=arm64;x86_64',
'DARWIN_watchos_BUILTIN_ARCHS=arm64',
'DARWIN_watchossim_BUILTIN_ARCHS=arm64;x86_64',
],
"sanitizers":
True,
"profile":
True
}
if args.with_android:
for target_arch in ['aarch64', 'arm', 'i686', 'riscv64', 'x86_64']:
toolchain_dir = ANDROID_NDK_TOOLCHAIN_DIR
target_triple = target_arch
if target_arch == 'arm':
target_triple = 'armv7'
api_level = '21'
if target_arch == 'riscv64':
api_level = '35'
target_triple += '-linux-android' + api_level
android_cflags = [
'--sysroot=%s/sysroot' % toolchain_dir,
'--unwindlib=none',
]
if target_arch == 'aarch64':
android_cflags += ['-mbranch-protection=standard']
android_args = [
'LLVM_ENABLE_RUNTIMES=compiler-rt',
'CMAKE_BUILD_TYPE=RelWithDebInfo',
'CMAKE_C_FLAGS=' + ' '.join(android_cflags),
'CMAKE_CXX_FLAGS=' + ' '.join(android_cflags),
'CMAKE_ASM_FLAGS=' + ' '.join(android_cflags),
'COMPILER_RT_USE_BUILTINS_LIBRARY=ON',
'SANITIZER_CXX_ABI=libcxxabi',
'CMAKE_SHARED_LINKER_FLAGS=-Wl,-u__cxa_demangle',
'ANDROID=1',
'LLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF',
'LLVM_INCLUDE_TESTS=OFF',
'ANDROID_NATIVE_API_LEVEL=' + api_level,
]
runtimes_triples_args[target_triple] = {
"args": android_args,
"sanitizers": True,
"profile": True
}
if args.with_fuchsia:
for target_arch in ['aarch64', 'x86_64']:
fuchsia_arch_name = {'aarch64': 'arm64', 'x86_64': 'x64'}[target_arch]
toolchain_dir = os.path.join(
FUCHSIA_SDK_DIR, 'arch', fuchsia_arch_name, 'sysroot')
target_triple = target_arch + '-unknown-fuchsia'
build_profile = target_arch == 'x86_64'
build_sanitizers = build_profile and sys.platform != 'darwin'
fuchsia_args = [
'LLVM_ENABLE_RUNTIMES=compiler-rt',
'CMAKE_SYSTEM_NAME=Fuchsia',
'CMAKE_SYSROOT=%s' % toolchain_dir,
'LLVM_ENABLE_PER_TARGET_RUNTIME_DIR=ON',
]
if build_sanitizers:
fuchsia_args.append('SANITIZER_NO_UNDEFINED_SYMBOLS=OFF')
runtimes_triples_args[target_triple] = {
"args": fuchsia_args,
"sanitizers": build_sanitizers,
"profile": build_profile
}
if args.with_ml_inliner_model:
if args.with_ml_inliner_model == 'default':
model_path = ('https://commondatastorage.googleapis.com/'
'chromium-browser-clang/tools/mlgo_model3.tgz')
else:
model_path = args.with_ml_inliner_model
if not args.tf_path:
with timer.time('get tensorflow'):
tf_path = subprocess.check_output(
['vpython3', os.path.join(THIS_DIR, 'get_tensorflow.py')],
universal_newlines=True).rstrip()
else:
tf_path = args.tf_path
print('Embedding MLGO inliner model at %s using Tensorflow at %s' %
(model_path, tf_path))
cmake_args += [
'-DLLVM_INLINER_MODEL_PATH=%s' % model_path,
'-DTENSORFLOW_AOT_PATH=%s' % tf_path,
'-DLLVM_RAEVICT_MODEL_PATH=none'
]
all_triples = ''
for triple in sorted(runtimes_triples_args.keys()):
all_triples += triple + ';'
for arg in runtimes_triples_args[triple]["args"]:
assert not arg.startswith('-')
if triple == 'default':
cmake_args.append('-D' + arg)
else:
cmake_args.append('-DRUNTIMES_' + triple + '_' + arg)
cmake_args.append('-DBUILTINS_' + triple + '_' + arg)
if not args.no_runtimes:
profile = runtimes_triples_args[triple]["profile"],
sanitizers = runtimes_triples_args[triple]["sanitizers"]
else:
profile = False
sanitizers = False
for arg in compiler_rt_cmake_flags(profile=profile, sanitizers=sanitizers):
if triple == 'default':
cmake_args.append('-D' + arg)
else:
cmake_args.append('-DRUNTIMES_' + triple + '_' + arg)
cmake_args.append('-DLLVM_BUILTIN_TARGETS=' + all_triples)
cmake_args.append('-DLLVM_RUNTIME_TARGETS=' + all_triples)
if not args.bootstrap:
cmake_args.extend(ccache_cmake_args)
if os.path.exists(LLVM_BUILD_DIR):
RmTree(LLVM_BUILD_DIR)
EnsureDirExists(LLVM_BUILD_DIR)
os.chdir(LLVM_BUILD_DIR)
with timer.time('cmake'):
RunCommand(['cmake'] + cmake_args + [os.path.join(LLVM_DIR, 'llvm')],
setenv=True,
env=deployment_env)
with timer.time('build'):
RunCommand(['ninja'], setenv=True)
if chrome_tools:
with timer.time('cr-install'):
RunCommand(['ninja', 'cr-install'], setenv=True)
if args.bolt:
print('Performing BOLT post-link optimizations.')
bolt_profiles_dir = os.path.join(LLVM_BUILD_DIR, 'bolt-profiles')
os.mkdir(bolt_profiles_dir)
with timer.time('bolt instrument'):
RunCommand([
'bin/llvm-bolt', 'bin/clang', '-o', 'bin/clang-bolt.inst',
'-instrument', '--instrumentation-file-append-pid',
'--instrumentation-file=' +
os.path.join(bolt_profiles_dir, 'prof.fdata')
])
RunCommand([
'ln', '-s',
os.path.join(LLVM_BUILD_DIR, 'bin', 'clang-bolt.inst'),
os.path.join(LLVM_BUILD_DIR, 'bin', 'clang++-bolt.inst')
])
os.mkdir('bolt-training')
os.chdir('bolt-training')
bolt_train_cmake_args = base_cmake_args + [
'-DLLVM_TARGETS_TO_BUILD=X86',
'-DLLVM_ENABLE_PROJECTS=clang',
'-DCMAKE_C_FLAGS=' + ' '.join(cflags),
'-DCMAKE_CXX_FLAGS=' + ' '.join(cxxflags),
'-DCMAKE_EXE_LINKER_FLAGS=' + ' '.join(ldflags),
'-DCMAKE_SHARED_LINKER_FLAGS=' + ' '.join(ldflags),
'-DCMAKE_MODULE_LINKER_FLAGS=' + ' '.join(ldflags),
'-DCMAKE_C_COMPILER=' +
os.path.join(LLVM_BUILD_DIR, 'bin/clang-bolt.inst'),
'-DCMAKE_CXX_COMPILER=' +
os.path.join(LLVM_BUILD_DIR, 'bin/clang++-bolt.inst'),
'-DCMAKE_ASM_COMPILER=' +
os.path.join(LLVM_BUILD_DIR, 'bin/clang-bolt.inst'),
'-DCMAKE_ASM_COMPILER_ID=Clang',
]
with timer.time('bolt training cmake'):
RunCommand(['cmake'] + bolt_train_cmake_args +
[os.path.join(LLVM_DIR, 'llvm')])
with timer.time('bolt training benchmark'):
RunCommand([
'ninja', 'tools/clang/lib/Sema/CMakeFiles/obj.clangSema.dir/Sema.cpp.o'
])
os.chdir(LLVM_BUILD_DIR)
with timer.time('bolt optimize'):
RunCommand([
sys.executable,
os.path.join(LLVM_DIR, 'clang', 'utils', 'perf-training',
'perf-helper.py'), 'merge-fdata', 'bin/merge-fdata',
'merged.fdata', bolt_profiles_dir
])
RunCommand([
'bin/llvm-bolt', 'bin/clang', '-o', 'bin/clang-bolt.opt', '-data',
'merged.fdata', '-reorder-blocks=ext-tsp', '-reorder-functions=hfsort+',
'-split-functions', '-split-all-cold', '-split-eh', '-dyno-stats',
'-icf=1', '-use-gnu-stack', '-use-old-text'
])
RunCommand(['touch', '-r', 'bin/clang', 'bin/clang-bolt.opt'])
RunCommand(['mv', 'bin/clang-bolt.opt', 'bin/clang'])
VerifyVersionOfBuiltClangMatchesVERSION()
VerifyZlibSupport()
if args.with_zstd:
VerifyZStdSupport()
if (chrome_tools and (args.run_tests or args.llvm_force_head_revision)):
with timer.time('cr-check-all'):
RunCommand(['ninja', '-C', LLVM_BUILD_DIR, 'cr-check-all'], setenv=True)
if args.run_tests:
with timer.time('check-all'):
RunCommand(['ninja', '-C', LLVM_BUILD_DIR, 'check-all'],
env=test_env,
setenv=True)
if args.install_dir:
with timer.time('install'):
RunCommand(['ninja', 'install'], setenv=True)
WriteStampFile(PACKAGE_VERSION,
STAMP_FILE,
preserve_hash_files=args.preserve_gcs_signature)
WriteStampFile(PACKAGE_VERSION,
FORCE_HEAD_REVISION_FILE,
preserve_hash_files=args.preserve_gcs_signature)
print('Clang build was successful.')
return 0
if __name__ == '__main__':
sys.exit(main())