Exemplo n.º 1
0
def CheckoutLLVM(commit, dir):
  """Checkout the LLVM monorepo at a certain git commit in dir. Any local
  modifications in dir will be lost."""
  print("Checking out LLVM monorepo %s into '%s'" % (commit, dir))

  git_dir = os.path.join(dir, '.git')
  fetch_cmd = ['git', '--git-dir', git_dir, 'fetch']
  checkout_cmd = ['git', 'checkout', commit]

  # Do a somewhat shallow clone to save on bandwidth for GitHub and for us.
  # The depth was whosen to be deep enough to contain the version we're
  # building, and shallow enough to save significantly on bandwidth compared to
  # a full clone.
  clone_cmd = ['git', 'clone', '--depth', '10000',
               'https://github.com/llvm/llvm-project/', dir]

  # Try updating the current repo.
  if RunCommand(fetch_cmd, fail_hard=False):
    os.chdir(dir)
    if RunCommand(checkout_cmd, fail_hard=False):
      return

  # Otherwise, do a fresh clone.
  if os.path.isdir(dir):
    print("Removing %s." % dir)
    RmTree(dir)
  if RunCommand(clone_cmd, fail_hard=False):
    os.chdir(dir)
    if RunCommand(checkout_cmd, fail_hard=False):
      return

  print('CheckoutLLVM failed.')
  sys.exit(1)
Exemplo n.º 2
0
def CheckoutLLVM(commit, dir):
    """Checkout the LLVM monorepo at a certain git commit in dir. Any local
  modifications in dir will be lost."""

    print('Checking out LLVM monorepo %s into %s' % (commit, dir))

    # Try updating the current repo if it exists and has no local diff.
    if os.path.isdir(dir):
        os.chdir(dir)
        # git diff-index --quiet returns success when there is no diff.
        # Also check that the first commit is reachable.
        if (RunCommand(['git', 'diff-index', '--quiet', 'HEAD'],
                       fail_hard=False)
                and RunCommand(['git', 'fetch'], fail_hard=False)
                and RunCommand(['git', 'checkout', commit], fail_hard=False)
                and RunCommand(['git', 'show', FIRST_LLVM_COMMIT],
                               fail_hard=False)):
            return

        # If we can't use the current repo, delete it.
        os.chdir(CHROMIUM_DIR)  # Can't remove dir if we're in it.
        print('Removing %s.' % dir)
        RmTree(dir)

    clone_cmd = ['git', 'clone', 'https://github.com/llvm/llvm-project/', dir]

    if RunCommand(clone_cmd, fail_hard=False):
        os.chdir(dir)
        if RunCommand(['git', 'checkout', commit], fail_hard=False):
            return

    print('CheckoutLLVM failed.')
    sys.exit(1)
Exemplo n.º 3
0
def AddZlibToPath():
  """Download and build zlib, and add to PATH."""
  zlib_dir = os.path.join(LLVM_BUILD_TOOLS_DIR, 'zlib-1.2.11')
  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, msvc_arch='x64')
  RunCommand(
      ['lib.exe'] + [f + '.obj'
                     for f in zlib_files] + ['/nologo', '/out:zlib.lib'],
      msvc_arch='x64')
  # Remove the test directory so it isn't found when trying to find
  # test.exe.
  shutil.rmtree('test')

  os.environ['PATH'] = zlib_dir + os.pathsep + os.environ.get('PATH', '')
  return zlib_dir
Exemplo n.º 4
0
def CheckoutLLVM(commit, dir):
  """Checkout the LLVM monorepo at a certain git commit in dir. Any local
  modifications in dir will be lost."""

  print('Checking out LLVM monorepo %s into %s' % (commit, dir))

  # Try updating the current repo if it exists and has no local diff.
  if os.path.isdir(dir):
    os.chdir(dir)
    # git diff-index --quiet returns success when there is no diff.
    if (RunCommand(['git', 'diff-index', '--quiet', 'HEAD'], fail_hard=False)
        and RunCommand(['git', 'fetch'], fail_hard=False)
        and RunCommand(['git', 'checkout', commit], fail_hard=False)):
      return

    # If we can't use the current repo, delete it.
    os.chdir(CHROMIUM_DIR)  # Can't remove dir if we're in it.
    print('Removing %s.' % dir)
    RmTree(dir)

  # Do a somewhat shallow clone to save on bandwidth for GitHub and for us.
  # The depth was chosen to be deep enough to contain the version we're
  # building, and shallow enough to save significantly on bandwidth compared to
  # a full clone.
  clone_cmd = ['git', 'clone', '--depth', '10000',
               'https://github.com/llvm/llvm-project/', dir]

  if RunCommand(clone_cmd, fail_hard=False):
    os.chdir(dir)
    if RunCommand(['git', 'checkout', commit], fail_hard=False):
      return

  print('CheckoutLLVM failed.')
  sys.exit(1)
Exemplo n.º 5
0
def RmCmakeCache(dir):
    """Delete CMake cache related files from dir."""
    for dirpath, dirs, files in os.walk(dir):
        if 'CMakeCache.txt' in files:
            os.remove(os.path.join(dirpath, 'CMakeCache.txt'))
        if 'CMakeFiles' in dirs:
            RmTree(os.path.join(dirpath, 'CMakeFiles'))
Exemplo n.º 6
0
def CheckoutRepos(args):
    if args.skip_checkout:
        return

    Checkout('LLVM', LLVM_REPO_URL + '/llvm/trunk', LLVM_DIR)
    Checkout('Clang', LLVM_REPO_URL + '/cfe/trunk', CLANG_DIR)
    if True:
        Checkout('LLD', LLVM_REPO_URL + '/lld/trunk', LLD_DIR)
    elif os.path.exists(LLD_DIR):
        # In case someone sends a tryjob that temporary adds lld to the checkout,
        # make sure it's not around on future builds.
        RmTree(LLD_DIR)
    Checkout('compiler-rt', LLVM_REPO_URL + '/compiler-rt/trunk',
             COMPILER_RT_DIR)
    if sys.platform == 'darwin':
        # clang needs a libc++ checkout, else -stdlib=libc++ won't find includes
        # (i.e. this is needed for bootstrap builds).
        Checkout('libcxx', LLVM_REPO_URL + '/libcxx/trunk', LIBCXX_DIR)
        # We used to check out libcxxabi on OS X; we no longer need that.
        if os.path.exists(LIBCXXABI_DIR):
            RmTree(LIBCXXABI_DIR)
Exemplo n.º 7
0
def Checkout(name, url, dir):
    """Checkout the SVN module at url into dir. Use name for the log message."""
    print("Checking out %s r%s into '%s'" % (name, CLANG_REVISION, dir))

    command = ['svn', 'checkout', '--force', url + '@' + CLANG_REVISION, dir]
    if RunCommand(command, fail_hard=False):
        return

    if os.path.isdir(dir):
        print("Removing %s." % dir)
        RmTree(dir)

    print("Retrying.")
    RunCommand(command)
Exemplo n.º 8
0
def DownloadRPMalloc():
  """Download rpmalloc."""
  rpmalloc_dir = os.path.join(LLVM_BUILD_TOOLS_DIR, 'rpmalloc')
  if os.path.exists(rpmalloc_dir):
    RmTree(rpmalloc_dir)

  # Using rpmalloc bc1923f rather than the latest release (1.4.1) because
  # it contains the fix for https://github.com/mjansson/rpmalloc/pull/186
  # which would cause lld to deadlock.
  # The zip file was created and uploaded as follows:
  # $ mkdir rpmalloc
  # $ curl -L https://github.com/mjansson/rpmalloc/archive/bc1923f436539327707b08ef9751a7a87bdd9d2f.tar.gz \
  #     | tar -C rpmalloc --strip-components=1 -xzf -
  # $ GZIP=-9 tar vzcf rpmalloc-bc1923f.tgz rpmalloc
  # $ gsutil.py cp -n -a public-read rpmalloc-bc1923f.tgz \
  #     gs://chromium-browser-clang/tools/
  zip_name = 'rpmalloc-bc1923f.tgz'
  DownloadAndUnpack(CDS_URL + '/tools/' + zip_name, LLVM_BUILD_TOOLS_DIR)
  rpmalloc_dir = rpmalloc_dir.replace('\\', '/')
  return rpmalloc_dir
Exemplo n.º 9
0
def main():
    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('--gcc-toolchain',
                        help='what gcc toolchain to use for '
                        'building; --gcc-toolchain=/opt/foo picks '
                        '/opt/foo/bin/gcc')
    parser.add_argument('--lto-lld',
                        action='store_true',
                        help='build lld with LTO (only applies on Linux)')
    parser.add_argument('--pgo', action='store_true', help='build with PGO')
    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('--extra-tools',
                        nargs='*',
                        default=[],
                        help='select additional chrome tools to build')
    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('--with-android',
                        type=gn_arg,
                        nargs='?',
                        const=True,
                        help='build the Android ASan runtime (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'))
    args = parser.parse_args()

    if args.lto_lld and not args.bootstrap:
        print('--lto-lld requires --bootstrap')
        return 1
    if args.lto_lld and not sys.platform.startswith('linux'):
        # TODO(hans): Use it on Windows too.
        print('--lto-lld is only effective on Linux. Ignoring the option.')
        args.lto_lld = False
    if args.pgo and not args.bootstrap:
        print('--pgo 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.llvm_force_head_revision:
        # Don't build fuchsia runtime on ToT bots at all.
        args.with_fuchsia = False

    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/+/master/docs/fuchsia_build_instructions.md'
        )
        print('for general Fuchsia build instructions.')
        return 1

    # Don't buffer stdout, so that print statements are immediately flushed.
    sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

    # The gnuwin package also includes curl, which is needed to interact with the
    # github API below.
    # TODO(crbug.com/965937): Use urllib once our Python is recent enough, and
    # move this down to where we fetch other build tools.
    AddGnuWinToPath()

    # TODO(crbug.com/929645): Remove once we build on host systems with a modern
    # enough GCC to build Clang.
    MaybeDownloadHostGcc(args)

    global CLANG_REVISION, PACKAGE_VERSION
    if args.llvm_force_head_revision:
        CLANG_REVISION = GetLatestLLVMCommit()

    if not args.skip_checkout:
        CheckoutLLVM(CLANG_REVISION, LLVM_DIR)

    if args.llvm_force_head_revision:
        PACKAGE_VERSION = 'n%s-%s-0' % (GetCommitCount(CLANG_REVISION),
                                        CLANG_REVISION[:8])

    print('Locally building clang %s...' % PACKAGE_VERSION)
    WriteStampFile('', STAMP_FILE)
    WriteStampFile('', FORCE_HEAD_REVISION_FILE)

    AddCMakeToPath(args)
    DeleteChromeToolsShim()

    if args.skip_build:
        return 0

    # The variable "lld" is only used on Windows because only there does setting
    # CMAKE_LINKER have an effect: On Windows, the linker is called directly,
    # while elsewhere it's called through the compiler driver, and we pass
    # -fuse-ld=lld there to make the compiler driver call the linker (by setting
    # LLVM_ENABLE_LLD).
    cc, cxx, lld = None, None, None

    if args.gcc_toolchain:
        # Use the specified gcc installation for building.
        cc = os.path.join(args.gcc_toolchain, 'bin', 'gcc')
        cxx = os.path.join(args.gcc_toolchain, 'bin', 'g++')
        if not os.access(cc, os.X_OK):
            print('Invalid --gcc-toolchain: ' + args.gcc_toolchain)
            return 1

    cflags = []
    cxxflags = []
    ldflags = []

    targets = 'AArch64;ARM;Mips;PowerPC;SystemZ;WebAssembly;X86'

    projects = 'clang;compiler-rt;lld;chrometools'

    if sys.platform == 'darwin':
        # clang needs libc++, else -stdlib=libc++ won't find includes
        # (this is needed for bootstrap builds and for building the fuchsia runtime)
        projects += ';libcxx'

    base_cmake_args = [
        '-GNinja',
        '-DCMAKE_BUILD_TYPE=Release',
        '-DLLVM_ENABLE_ASSERTIONS=%s' %
        ('OFF' if args.disable_asserts else 'ON'),
        '-DLLVM_ENABLE_PROJECTS=' + projects,
        '-DLLVM_TARGETS_TO_BUILD=' + targets,
        '-DLLVM_ENABLE_PIC=OFF',
        '-DLLVM_ENABLE_UNWIND_TABLES=OFF',
        '-DLLVM_ENABLE_TERMINFO=OFF',
        '-DCLANG_PLUGIN_SUPPORT=OFF',
        '-DCLANG_ENABLE_STATIC_ANALYZER=OFF',
        '-DCLANG_ENABLE_ARCMT=OFF',
        '-DBUG_REPORT_URL=' + BUG_REPORT_URL,
        # See PR41956: Don't link libcxx into libfuzzer.
        '-DCOMPILER_RT_USE_LIBCXX=NO',
        # Don't run Go bindings tests; PGO makes them confused.
        '-DLLVM_INCLUDE_GO_TESTS=OFF',
    ]

    if args.gcc_toolchain:
        # Don't use the custom gcc toolchain when building compiler-rt tests; those
        # tests are built with the just-built Clang, and target both i386 and x86_64
        # for example, so should ust the system's libstdc++.
        base_cmake_args.append(
            '-DCOMPILER_RT_TEST_COMPILER_CFLAGS=--gcc-toolchain=')

    if sys.platform == 'win32':
        base_cmake_args.append('-DLLVM_USE_CRT_RELEASE=MT')

    if sys.platform == 'darwin':
        # Use the system libc++abi.
        # TODO(hans): use https://reviews.llvm.org/D62060 instead
        base_cmake_args.append('-DLIBCXX_CXX_ABI=libcxxabi')
        base_cmake_args.append('-DLIBCXX_CXX_ABI_SYSTEM=1')

    if sys.platform != 'win32':
        # libxml2 is required by the Win manifest merging tool used in cross-builds.
        base_cmake_args.append('-DLLVM_ENABLE_LIBXML2=FORCE_ON')

    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)

        projects = 'clang'
        if args.pgo:
            # Need libclang_rt.profile
            projects += ';compiler-rt'
        if sys.platform != 'darwin' or args.lto_lld:
            projects += ';lld'
        if sys.platform == 'darwin':
            # Need libc++ and compiler-rt for the bootstrap compiler on mac.
            projects += ';libcxx;compiler-rt'

        bootstrap_targets = 'X86'
        if sys.platform == 'darwin':
            # Need ARM and AArch64 for building the ios clang_rt.
            bootstrap_targets += ';ARM;AArch64'
        bootstrap_args = base_cmake_args + [
            '-DLLVM_TARGETS_TO_BUILD=' + bootstrap_targets,
            '-DLLVM_ENABLE_PROJECTS=' + projects,
            '-DCMAKE_INSTALL_PREFIX=' + LLVM_BOOTSTRAP_INSTALL_DIR,
            '-DCMAKE_C_FLAGS=' + ' '.join(cflags),
            '-DCMAKE_CXX_FLAGS=' + ' '.join(cxxflags),
            # Ignore args.disable_asserts for the bootstrap compiler.
            '-DLLVM_ENABLE_ASSERTIONS=ON',
        ]
        if sys.platform == 'darwin':
            # On macOS, the bootstrap toolchain needs to have compiler-rt because
            # dsymutil's link needs libclang_rt.osx.a. Only the x86_64 osx
            # libraries are needed though, and only libclang_rt (i.e.
            # COMPILER_RT_BUILD_BUILTINS).
            bootstrap_args.extend([
                '-DDARWIN_osx_ARCHS=x86_64',
                '-DCOMPILER_RT_BUILD_BUILTINS=ON',
                '-DCOMPILER_RT_BUILD_CRT=OFF',
                '-DCOMPILER_RT_BUILD_LIBFUZZER=OFF',
                '-DCOMPILER_RT_BUILD_SANITIZERS=OFF',
                '-DCOMPILER_RT_BUILD_XRAY=OFF',
                '-DCOMPILER_RT_ENABLE_IOS=OFF',
                '-DCOMPILER_RT_ENABLE_WATCHOS=OFF',
                '-DCOMPILER_RT_ENABLE_TVOS=OFF',
            ])
        elif args.pgo:
            # PGO needs libclang_rt.profile but none of the other compiler-rt stuff.
            bootstrap_args.extend([
                '-DCOMPILER_RT_BUILD_BUILTINS=OFF',
                '-DCOMPILER_RT_BUILD_CRT=OFF',
                '-DCOMPILER_RT_BUILD_LIBFUZZER=OFF',
                '-DCOMPILER_RT_BUILD_PROFILE=ON',
                '-DCOMPILER_RT_BUILD_SANITIZERS=OFF',
                '-DCOMPILER_RT_BUILD_XRAY=OFF',
            ])

        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)
        RunCommand(['cmake'] + bootstrap_args +
                   [os.path.join(LLVM_DIR, 'llvm')],
                   msvc_arch='x64')
        CopyLibstdcpp(args, LLVM_BOOTSTRAP_DIR)
        CopyLibstdcpp(args, LLVM_BOOTSTRAP_INSTALL_DIR)
        RunCommand(['ninja'], msvc_arch='x64')
        if args.run_tests:
            test_targets = ['check-all']
            if sys.platform == 'darwin':
                # TODO(crbug.com/731375): Run check-all on Darwin too.
                test_targets = ['check-llvm', 'check-clang', 'check-builtins']
            if sys.platform == 'win32':
                CopyDiaDllTo(os.path.join(LLVM_BOOTSTRAP_DIR, 'bin'))
            RunCommand(['ninja'] + test_targets, msvc_arch='x64')
        RunCommand(['ninja', 'install'], msvc_arch='x64')

        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')
            # CMake has a hard time with backslashes in compiler paths:
            # https://stackoverflow.com/questions/13050827
            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++')
        if sys.platform.startswith('linux'):
            base_cmake_args.append('-DLLVM_ENABLE_LLD=ON')

        if args.gcc_toolchain:
            # Tell the bootstrap compiler where to find the standard library headers
            # and shared object files.
            cflags.append('--gcc-toolchain=' + args.gcc_toolchain)
            cxxflags.append('--gcc-toolchain=' + args.gcc_toolchain)

        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)

        projects = 'clang'
        if sys.platform == 'darwin':
            projects += ';libcxx;compiler-rt'

        instrument_args = base_cmake_args + [
            '-DLLVM_ENABLE_THREADS=OFF',
            '-DLLVM_ENABLE_PROJECTS=' + projects,
            '-DCMAKE_C_FLAGS=' + ' '.join(cflags),
            '-DCMAKE_CXX_FLAGS=' + ' '.join(cxxflags),
            # Build with instrumentation.
            '-DLLVM_BUILD_INSTRUMENTED=IR',
        ]
        # Build with the bootstrap compiler.
        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)

        RunCommand(['cmake'] + instrument_args +
                   [os.path.join(LLVM_DIR, 'llvm')],
                   msvc_arch='x64')
        CopyLibstdcpp(args, LLVM_INSTRUMENTED_DIR)
        RunCommand(['ninja'], msvc_arch='x64')
        print('Instrumented compiler built.')

        # Train by building some C++ code.
        #
        # pgo_training-1.ii is a preprocessed (on Linux) version of
        # src/third_party/blink/renderer/core/layout/layout_object.cc, selected
        # because it's a large translation unit in Blink, which is normally the
        # slowest part of Chromium to compile. Using this, we get ~20% shorter
        # build times for Linux, Android, and Mac, which is also what we got when
        # training by actually building a target in Chromium. (For comparison, a
        # C++-y "Hello World" program only resulted in 14% faster builds.)
        # See https://crbug.com/966403#c16 for all numbers.
        #
        # TODO(hans): Enhance the training, perhaps by including preprocessed code
        # from more platforms, and by doing some linking so that lld can benefit
        # from PGO as well. Perhaps the training could be done asynchronously by
        # dedicated buildbots that upload profiles to the cloud.
        training_source = 'pgo_training-1.ii'
        with open(training_source, 'w') 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++14',
            '-fno-exceptions', '-fno-rtti', '-w', '-c', training_source
        ]
        if sys.platform == 'darwin':
            train_cmd.extend([
                '-stdlib=libc++', '-isysroot',
                subprocess.check_output(['xcrun', '--show-sdk-path']).rstrip()
            ])
        RunCommand(train_cmd, msvc_arch='x64')

        # Merge profiles.
        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')),
            msvc_arch='x64')
        print('Profile generated.')

    compiler_rt_args = [
        '-DCOMPILER_RT_BUILD_CRT=OFF',
        '-DCOMPILER_RT_BUILD_LIBFUZZER=OFF',
        '-DCOMPILER_RT_BUILD_PROFILE=ON',
        '-DCOMPILER_RT_BUILD_SANITIZERS=ON',
        '-DCOMPILER_RT_BUILD_XRAY=OFF',
    ]
    if sys.platform == 'darwin':
        compiler_rt_args.extend([
            '-DCOMPILER_RT_BUILD_BUILTINS=ON',
            '-DCOMPILER_RT_ENABLE_IOS=ON',
            '-DCOMPILER_RT_ENABLE_WATCHOS=OFF',
            '-DCOMPILER_RT_ENABLE_TVOS=OFF',
            # armv7 is A5 and earlier, armv7s is A6+ (2012 and later, before 64-bit
            # iPhones). armv7k is Apple Watch, which we don't need.
            '-DDARWIN_ios_ARCHS=armv7;armv7s;arm64',
            '-DDARWIN_iossim_ARCHS=i386;x86_64',
            # We don't need 32-bit intel support for macOS, we only ship 64-bit.
            '-DDARWIN_osx_ARCHS=x86_64',
        ])
    else:
        compiler_rt_args.append('-DCOMPILER_RT_BUILD_BUILTINS=OFF')

    # LLVM uses C++11 starting in llvm 3.5. On Linux, this means libstdc++4.7+ is
    # needed, on OS X it requires libc++. clang only automatically links to libc++
    # when targeting OS X 10.9+, so add stdlib=libc++ explicitly so clang can run
    # on OS X versions as old as 10.7.
    deployment_target = ''

    if sys.platform == 'darwin' and args.bootstrap:
        # When building on 10.9, /usr/include usually doesn't exist, and while
        # Xcode's clang automatically sets a sysroot, self-built clangs don't.
        cflags = [
            '-isysroot',
            subprocess.check_output(['xcrun', '--show-sdk-path']).rstrip()
        ]
        cxxflags = ['-stdlib=libc++'] + cflags
        ldflags += ['-stdlib=libc++']
        deployment_target = '10.7'

    # If building at head, define a macro that plugins can use for #ifdefing
    # out code that builds at head, but not at CLANG_REVISION or vice versa.
    if args.llvm_force_head_revision:
        cflags += ['-DLLVM_FORCE_HEAD_REVISION']
        cxxflags += ['-DLLVM_FORCE_HEAD_REVISION']

    # Build PDBs for archival on Windows.  Don't use RelWithDebInfo since it
    # has different optimization defaults than Release.
    # Also disable stack cookies (/GS-) for performance.
    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

    # Build lld and code coverage tools. This is done separately from the rest of
    # the build because these tools require threading support.
    print('Building thread-enabled tools.')
    tools_with_threading = ['dsymutil', 'lld', 'llvm-cov', 'llvm-profdata']
    print('Building the following tools with threading support: %s' %
          str(tools_with_threading))

    if os.path.exists(THREADS_ENABLED_BUILD_DIR):
        RmTree(THREADS_ENABLED_BUILD_DIR)
    EnsureDirExists(THREADS_ENABLED_BUILD_DIR)
    os.chdir(THREADS_ENABLED_BUILD_DIR)

    threads_enabled_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)
    ]
    if cc is not None:
        threads_enabled_cmake_args.append('-DCMAKE_C_COMPILER=' + cc)
    if cxx is not None:
        threads_enabled_cmake_args.append('-DCMAKE_CXX_COMPILER=' + cxx)
    if lld is not None:
        threads_enabled_cmake_args.append('-DCMAKE_LINKER=' + lld)

    if args.lto_lld:
        # Build lld with LTO. That speeds up the linker by ~10%.
        # We only use LTO for Linux now.
        #
        # The linker expects all archive members to have symbol tables, so the
        # archiver needs to be able to create symbol tables for bitcode files.
        # GNU ar and ranlib don't understand bitcode files, but llvm-ar and
        # llvm-ranlib do, so use them.
        ar = os.path.join(LLVM_BOOTSTRAP_INSTALL_DIR, 'bin', 'llvm-ar')
        ranlib = os.path.join(LLVM_BOOTSTRAP_INSTALL_DIR, 'bin', 'llvm-ranlib')
        threads_enabled_cmake_args += [
            '-DCMAKE_AR=' + ar,
            '-DCMAKE_RANLIB=' + ranlib,
            '-DLLVM_ENABLE_LTO=thin',
        ]

    RunCommand(['cmake'] + threads_enabled_cmake_args +
               [os.path.join(LLVM_DIR, 'llvm')],
               msvc_arch='x64',
               env=deployment_env)
    CopyLibstdcpp(args, THREADS_ENABLED_BUILD_DIR)
    RunCommand(['ninja'] + tools_with_threading, msvc_arch='x64')

    print('Building final compiler.')

    default_tools = ['plugins', 'blink_gc_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)
    cmake_args = base_cmake_args + compiler_rt_args + [
        '-DLLVM_ENABLE_THREADS=OFF', '-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=' + LLVM_BUILD_DIR,
        '-DCHROMIUM_TOOLS_SRC=%s' %
        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 sys.platform == 'darwin':
        cmake_args += [
            '-DCOMPILER_RT_ENABLE_IOS=ON', '-DSANITIZER_MIN_OSX_VERSION=10.7'
        ]

    # TODO(crbug.com/962988): Use -DLLVM_EXTERNAL_PROJECTS instead.
    CreateChromeToolsShim()

    if os.path.exists(LLVM_BUILD_DIR):
        RmTree(LLVM_BUILD_DIR)
    EnsureDirExists(LLVM_BUILD_DIR)
    os.chdir(LLVM_BUILD_DIR)
    RunCommand(['cmake'] + cmake_args + [os.path.join(LLVM_DIR, 'llvm')],
               msvc_arch='x64',
               env=deployment_env)
    CopyLibstdcpp(args, LLVM_BUILD_DIR)
    RunCommand(['ninja'], msvc_arch='x64')

    # Copy in the threaded versions of lld and other tools.
    if sys.platform == 'win32':
        CopyFile(
            os.path.join(THREADS_ENABLED_BUILD_DIR, 'bin', 'lld-link.exe'),
            os.path.join(LLVM_BUILD_DIR, 'bin'))
        CopyFile(os.path.join(THREADS_ENABLED_BUILD_DIR, 'bin', 'lld.pdb'),
                 os.path.join(LLVM_BUILD_DIR, 'bin'))
    else:
        for tool in tools_with_threading:
            CopyFile(os.path.join(THREADS_ENABLED_BUILD_DIR, 'bin', tool),
                     os.path.join(LLVM_BUILD_DIR, 'bin'))

    if chrome_tools:
        # If any Chromium tools were built, install those now.
        RunCommand(['ninja', 'cr-install'], msvc_arch='x64')

    VerifyVersionOfBuiltClangMatchesVERSION()

    if sys.platform == 'win32':
        platform = 'windows'
    elif sys.platform == 'darwin':
        platform = 'darwin'
    else:
        assert sys.platform.startswith('linux')
        platform = 'linux'
    rt_lib_dst_dir = os.path.join(LLVM_BUILD_DIR, 'lib', 'clang',
                                  RELEASE_VERSION, 'lib', platform)

    # Do an out-of-tree build of compiler-rt for 32-bit Win clang_rt.profile.lib.
    if sys.platform == 'win32':
        if os.path.isdir(COMPILER_RT_BUILD_DIR):
            RmTree(COMPILER_RT_BUILD_DIR)
        os.makedirs(COMPILER_RT_BUILD_DIR)
        os.chdir(COMPILER_RT_BUILD_DIR)
        if args.bootstrap:
            # The bootstrap compiler produces 64-bit binaries by default.
            cflags += ['-m32']
            cxxflags += ['-m32']
        compiler_rt_args = base_cmake_args + [
            '-DLLVM_ENABLE_THREADS=OFF',
            '-DCMAKE_C_FLAGS=' + ' '.join(cflags),
            '-DCMAKE_CXX_FLAGS=' + ' '.join(cxxflags),
            '-DCOMPILER_RT_BUILD_BUILTINS=OFF',
            '-DCOMPILER_RT_BUILD_CRT=OFF',
            '-DCOMPILER_RT_BUILD_LIBFUZZER=OFF',
            '-DCOMPILER_RT_BUILD_PROFILE=ON',
            '-DCOMPILER_RT_BUILD_SANITIZERS=OFF',
            '-DCOMPILER_RT_BUILD_XRAY=OFF',
        ]
        RunCommand(['cmake'] + compiler_rt_args +
                   [os.path.join(LLVM_DIR, 'llvm')],
                   msvc_arch='x86',
                   env=deployment_env)
        RunCommand(['ninja', 'compiler-rt'], msvc_arch='x86')

        # Copy select output to the main tree.
        rt_lib_src_dir = os.path.join(COMPILER_RT_BUILD_DIR, 'lib', 'clang',
                                      RELEASE_VERSION, 'lib', platform)
        # Static and dynamic libraries:
        CopyDirectoryContents(rt_lib_src_dir, rt_lib_dst_dir)

    if args.with_android:
        make_toolchain = os.path.join(ANDROID_NDK_DIR, 'build', 'tools',
                                      'make_standalone_toolchain.py')
        # TODO(thakis): Now that the NDK uses clang, try to build all archs in
        # one LLVM build instead of making 3 different toolchains and building
        # 3 times.
        for target_arch in ['aarch64', 'arm', 'i686']:
            # Make standalone Android toolchain for target_arch.
            toolchain_dir = os.path.join(LLVM_BUILD_DIR,
                                         'android-toolchain-' + target_arch)
            api_level = '21' if target_arch == 'aarch64' else '19'
            RunCommand([
                make_toolchain, '--api=' + api_level, '--force',
                '--install-dir=%s' % toolchain_dir, '--stl=libc++',
                '--arch=' + {
                    'aarch64': 'arm64',
                    'arm': 'arm',
                    'i686': 'x86',
                }[target_arch]
            ])

            # Build compiler-rt runtimes needed for Android in a separate build tree.
            build_dir = os.path.join(LLVM_BUILD_DIR, 'android-' + target_arch)
            if not os.path.exists(build_dir):
                os.mkdir(os.path.join(build_dir))
            os.chdir(build_dir)
            target_triple = target_arch
            if target_arch == 'arm':
                target_triple = 'armv7'
            target_triple += '-linux-android' + api_level
            cflags = [
                '--target=%s' % target_triple,
                '--sysroot=%s/sysroot' % toolchain_dir,
                '-B%s' % toolchain_dir
            ]
            android_args = base_cmake_args + [
                '-DLLVM_ENABLE_THREADS=OFF', '-DCMAKE_C_COMPILER=' +
                os.path.join(LLVM_BUILD_DIR, 'bin/clang'),
                '-DCMAKE_CXX_COMPILER=' + os.path.join(
                    LLVM_BUILD_DIR, 'bin/clang++'), '-DLLVM_CONFIG_PATH=' +
                os.path.join(LLVM_BUILD_DIR, 'bin/llvm-config'),
                '-DCMAKE_C_FLAGS=' + ' '.join(cflags), '-DCMAKE_CXX_FLAGS=' +
                ' '.join(cflags), '-DCMAKE_ASM_FLAGS=' + ' '.join(cflags),
                '-DCOMPILER_RT_BUILD_BUILTINS=OFF',
                '-DCOMPILER_RT_BUILD_CRT=OFF',
                '-DCOMPILER_RT_BUILD_LIBFUZZER=OFF',
                '-DCOMPILER_RT_BUILD_PROFILE=ON',
                '-DCOMPILER_RT_BUILD_SANITIZERS=ON',
                '-DCOMPILER_RT_BUILD_XRAY=OFF',
                '-DSANITIZER_CXX_ABI=libcxxabi',
                '-DCMAKE_SHARED_LINKER_FLAGS=-Wl,-u__cxa_demangle',
                '-DANDROID=1'
            ]
            RunCommand(['cmake'] + android_args + [COMPILER_RT_DIR])

            # We use ASan i686 build for fuzzing.
            libs_want = ['lib/linux/libclang_rt.asan-{0}-android.so']
            if target_arch in ['aarch64', 'arm']:
                libs_want += [
                    'lib/linux/libclang_rt.ubsan_standalone-{0}-android.so',
                    'lib/linux/libclang_rt.profile-{0}-android.a',
                ]
            if target_arch == 'aarch64':
                libs_want += ['lib/linux/libclang_rt.hwasan-{0}-android.so']
            libs_want = [lib.format(target_arch) for lib in libs_want]
            RunCommand(['ninja'] + libs_want)

            # And copy them into the main build tree.
            for p in libs_want:
                shutil.copy(p, rt_lib_dst_dir)

    if args.with_fuchsia:
        # Fuchsia links against libclang_rt.builtins-<arch>.a instead of libgcc.a.
        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')
            # Build clang_rt runtime for Fuchsia in a separate build tree.
            build_dir = os.path.join(LLVM_BUILD_DIR, 'fuchsia-' + target_arch)
            if not os.path.exists(build_dir):
                os.mkdir(os.path.join(build_dir))
            os.chdir(build_dir)
            target_spec = target_arch + '-fuchsia'
            # TODO(thakis): Might have to pass -B here once sysroot contains
            # binaries (e.g. gas for arm64?)
            fuchsia_args = base_cmake_args + [
                '-DLLVM_ENABLE_THREADS=OFF',
                '-DCMAKE_C_COMPILER=' +
                os.path.join(LLVM_BUILD_DIR, 'bin/clang'),
                '-DCMAKE_CXX_COMPILER=' +
                os.path.join(LLVM_BUILD_DIR, 'bin/clang++'),
                '-DCMAKE_LINKER=' + os.path.join(LLVM_BUILD_DIR, 'bin/clang'),
                '-DCMAKE_AR=' + os.path.join(LLVM_BUILD_DIR, 'bin/llvm-ar'),
                '-DLLVM_CONFIG_PATH=' +
                os.path.join(LLVM_BUILD_DIR, 'bin/llvm-config'),
                '-DCMAKE_SYSTEM_NAME=Fuchsia',
                '-DCMAKE_C_COMPILER_TARGET=%s-fuchsia' % target_arch,
                '-DCMAKE_ASM_COMPILER_TARGET=%s-fuchsia' % target_arch,
                '-DCOMPILER_RT_BUILD_BUILTINS=ON',
                '-DCOMPILER_RT_BUILD_CRT=OFF',
                '-DCOMPILER_RT_BUILD_LIBFUZZER=OFF',
                '-DCOMPILER_RT_BUILD_PROFILE=OFF',
                '-DCOMPILER_RT_BUILD_SANITIZERS=OFF',
                '-DCOMPILER_RT_BUILD_XRAY=OFF',
                '-DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON',
                '-DCMAKE_SYSROOT=%s' % toolchain_dir,
                # TODO(thakis|scottmg): Use PER_TARGET_RUNTIME_DIR for all platforms.
                # https://crbug.com/882485.
                '-DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=ON',

                # These are necessary because otherwise CMake tries to build an
                # executable to test to see if the compiler is working, but in doing so,
                # it links against the builtins.a that we're about to build.
                '-DCMAKE_C_COMPILER_WORKS=ON',
                '-DCMAKE_ASM_COMPILER_WORKS=ON',
            ]
            RunCommand(['cmake'] + fuchsia_args +
                       [os.path.join(COMPILER_RT_DIR, 'lib', 'builtins')])
            builtins_a = 'libclang_rt.builtins.a'
            RunCommand(['ninja', builtins_a])

            # And copy it into the main build tree.
            fuchsia_lib_dst_dir = os.path.join(LLVM_BUILD_DIR, 'lib', 'clang',
                                               RELEASE_VERSION, 'lib',
                                               target_spec)
            if not os.path.exists(fuchsia_lib_dst_dir):
                os.makedirs(fuchsia_lib_dst_dir)
            CopyFile(os.path.join(build_dir, 'lib', target_spec, builtins_a),
                     fuchsia_lib_dst_dir)

    # Run tests.
    if args.run_tests or args.llvm_force_head_revision:
        RunCommand(['ninja', '-C', LLVM_BUILD_DIR, 'cr-check-all'],
                   msvc_arch='x64')

    if args.run_tests:
        if sys.platform == 'win32':
            CopyDiaDllTo(os.path.join(LLVM_BUILD_DIR, 'bin'))
        test_targets = ['check-all']
        if sys.platform == 'darwin':
            # TODO(thakis): Run check-all on Darwin too, https://crbug.com/959361
            test_targets = ['check-llvm', 'check-clang', 'check-lld']
        RunCommand(['ninja', '-C', LLVM_BUILD_DIR] + test_targets,
                   msvc_arch='x64')

    if sys.platform == 'darwin':
        for dylib in glob.glob(os.path.join(rt_lib_dst_dir, '*.dylib')):
            # Fix LC_ID_DYLIB for the ASan dynamic libraries to be relative to
            # @executable_path.
            # Has to happen after running tests.
            # TODO(glider): this is transitional. We'll need to fix the dylib
            # name either in our build system, or in Clang. See also
            # http://crbug.com/344836.
            subprocess.call([
                'install_name_tool', '-id',
                '@executable_path/' + os.path.basename(dylib), dylib
            ])

    WriteStampFile(PACKAGE_VERSION, STAMP_FILE)
    WriteStampFile(PACKAGE_VERSION, FORCE_HEAD_REVISION_FILE)
    print('Clang build was successful.')
    return 0
Exemplo n.º 10
0
def main():
  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('--gcc-toolchain', help='what gcc toolchain to use for '
                      'building; --gcc-toolchain=/opt/foo picks '
                      '/opt/foo/bin/gcc')
  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('--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('--extra-tools', nargs='*', default=[],
                      help='select additional chrome tools to build')
  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('--with-android', type=gn_arg, nargs='?', const=True,
                      help='build the Android ASan runtime (linux only)',
                      default=sys.platform.startswith('linux'))
  parser.add_argument('--with-fuchsia',
                      type=gn_arg,
                      nargs='?',
                      const=True,
                      help='build the Fuchsia runtimes (linux and mac only)',
                      default=sys.platform.startswith('linux')
                      or sys.platform.startswith('darwin'))
  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'))
  args = parser.parse_args()

  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.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/+/master/docs/fuchsia/build_instructions.md'
    )
    print('for general Fuchsia build instructions.')
    return 1


  # Don't buffer stdout, so that print statements are immediately flushed.
  # LLVM tests print output without newlines, so with buffering they won't be
  # immediately printed.
  major, _, _, _, _ = sys.version_info
  if major == 3:
    # Python3 only allows unbuffered output for binary streams. This
    # workaround comes from https://stackoverflow.com/a/181654/4052492.
    sys.stdout = io.TextIOWrapper(open(sys.stdout.fileno(), 'wb', 0),
                                  write_through=True)
  else:
    sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

  # The gnuwin package also includes curl, which is needed to interact with the
  # github API below.
  # TODO(crbug.com/1067752): Use urllib once certificates are fixed, and
  # move this down to where we fetch other build tools.
  AddGnuWinToPath()

  # TODO(crbug.com/929645): Remove once we build on host systems with a modern
  # enough GCC to build Clang.
  MaybeDownloadHostGcc(args)

  if sys.platform == 'darwin':
    isysroot = subprocess.check_output(['xcrun', '--show-sdk-path']).rstrip()

  global CLANG_REVISION, PACKAGE_VERSION, LLVM_BUILD_DIR

  if args.build_dir:
    LLVM_BUILD_DIR = args.build_dir

  if args.llvm_force_head_revision:
    checkout_revision = GetLatestLLVMCommit()
  else:
    checkout_revision = CLANG_REVISION

  if not args.skip_checkout:
    CheckoutLLVM(checkout_revision, LLVM_DIR)

  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)
  WriteStampFile('', FORCE_HEAD_REVISION_FILE)

  AddCMakeToPath(args)
  DeleteChromeToolsShim()


  if args.skip_build:
    return 0

  # The variable "lld" is only used on Windows because only there does setting
  # CMAKE_LINKER have an effect: On Windows, the linker is called directly,
  # while elsewhere it's called through the compiler driver, and we pass
  # -fuse-ld=lld there to make the compiler driver call the linker (by setting
  # LLVM_ENABLE_LLD).
  cc, cxx, lld = None, None, None

  cflags = []
  cxxflags = []
  ldflags = []

  targets = 'AArch64;ARM;Mips;PowerPC;SystemZ;WebAssembly;X86'

  projects = 'clang;compiler-rt;lld;chrometools;clang-tools-extra'

  if sys.platform == 'darwin':
    # clang needs libc++, else -stdlib=libc++ won't find includes
    # (this is needed for bootstrap builds and for building the fuchsia runtime)
    projects += ';libcxx'

  base_cmake_args = [
      '-GNinja',
      '-DCMAKE_BUILD_TYPE=Release',
      '-DLLVM_ENABLE_ASSERTIONS=%s' % ('OFF' if args.disable_asserts else 'ON'),
      '-DLLVM_ENABLE_PROJECTS=' + projects,
      '-DLLVM_TARGETS_TO_BUILD=' + targets,
      '-DLLVM_ENABLE_PIC=OFF',
      '-DLLVM_ENABLE_UNWIND_TABLES=OFF',
      '-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,
      # Don't run Go bindings tests; PGO makes them confused.
      '-DLLVM_INCLUDE_GO_TESTS=OFF',
      # TODO(crbug.com/1113475): Update binutils.
      '-DENABLE_X86_RELAX_RELOCATIONS=NO',
      # See crbug.com/1126219: Use native symbolizer instead of DIA
      '-DLLVM_ENABLE_DIA_SDK=OFF',
  ]

  if args.gcc_toolchain:
    # Use the specified gcc installation for building.
    cc = os.path.join(args.gcc_toolchain, 'bin', 'gcc')
    cxx = os.path.join(args.gcc_toolchain, 'bin', 'g++')
    if not os.access(cc, os.X_OK):
      print('Invalid --gcc-toolchain: ' + args.gcc_toolchain)
      return 1
    base_cmake_args += [
        '-DLLVM_LOCAL_RPATH=' + os.path.join(args.gcc_toolchain, 'lib64')
    ]

  if sys.platform == 'darwin':
    # For libc++, we only want the headers.
    base_cmake_args.extend([
        '-DLIBCXX_ENABLE_SHARED=OFF',
        '-DLIBCXX_ENABLE_STATIC=OFF',
        '-DLIBCXX_INCLUDE_TESTS=OFF',
        '-DLIBCXX_ENABLE_EXPERIMENTAL_LIBRARY=OFF',
    ])

  if args.gcc_toolchain:
    # Force compiler-rt tests to use our gcc toolchain (including libstdc++.so)
    # because the one on the host may be too old.
    base_cmake_args.append(
        '-DCOMPILER_RT_TEST_COMPILER_CFLAGS=--gcc-toolchain=' +
        args.gcc_toolchain + ' -Wl,-rpath,' +
        os.path.join(args.gcc_toolchain, 'lib64') + ' -Wl,-rpath,' +
        os.path.join(args.gcc_toolchain, 'lib32'))

  if sys.platform == 'win32':
    base_cmake_args.append('-DLLVM_USE_CRT_RELEASE=MT')

    # Require zlib compression.
    zlib_dir = AddZlibToPath()
    cflags.append('-I' + zlib_dir)
    cxxflags.append('-I' + zlib_dir)
    ldflags.append('-LIBPATH:' + zlib_dir)

    # Use rpmalloc. For faster ThinLTO linking.
    rpmalloc_dir = DownloadRPMalloc()
    base_cmake_args.append('-DLLVM_INTEGRATED_CRT_ALLOC=' + rpmalloc_dir)

  if sys.platform != 'win32':
    # libxml2 is required by the Win manifest merging tool used in cross-builds.
    base_cmake_args.append('-DLLVM_ENABLE_LIBXML2=FORCE_ON')

  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)

    projects = 'clang'
    if args.pgo:
      # Need libclang_rt.profile
      projects += ';compiler-rt'
    if sys.platform != 'darwin':
      projects += ';lld'
    if sys.platform == 'darwin':
      # Need libc++ and compiler-rt for the bootstrap compiler on mac.
      projects += ';libcxx;compiler-rt'

    bootstrap_targets = 'X86'
    if sys.platform == 'darwin':
      # Need ARM and AArch64 for building the ios clang_rt.
      bootstrap_targets += ';ARM;AArch64'
    bootstrap_args = base_cmake_args + [
        '-DLLVM_TARGETS_TO_BUILD=' + bootstrap_targets,
        '-DLLVM_ENABLE_PROJECTS=' + projects,
        '-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),
        # Ignore args.disable_asserts for the bootstrap compiler.
        '-DLLVM_ENABLE_ASSERTIONS=ON',
    ]
    if sys.platform == 'darwin':
      # On macOS, the bootstrap toolchain needs to have compiler-rt because
      # dsymutil's link needs libclang_rt.osx.a. Only the x86_64 osx
      # libraries are needed though, and only libclang_rt (i.e.
      # COMPILER_RT_BUILD_BUILTINS).
      bootstrap_args.extend([
          '-DDARWIN_osx_ARCHS=x86_64',
          '-DCOMPILER_RT_BUILD_BUILTINS=ON',
          '-DCOMPILER_RT_BUILD_CRT=OFF',
          '-DCOMPILER_RT_BUILD_LIBFUZZER=OFF',
          '-DCOMPILER_RT_BUILD_MEMPROF=OFF',
          '-DCOMPILER_RT_BUILD_SANITIZERS=OFF',
          '-DCOMPILER_RT_BUILD_XRAY=OFF',
          '-DCOMPILER_RT_ENABLE_IOS=OFF',
          '-DCOMPILER_RT_ENABLE_WATCHOS=OFF',
          '-DCOMPILER_RT_ENABLE_TVOS=OFF',
          ])
    elif args.pgo:
      # PGO needs libclang_rt.profile but none of the other compiler-rt stuff.
      bootstrap_args.extend([
          '-DCOMPILER_RT_BUILD_BUILTINS=OFF',
          '-DCOMPILER_RT_BUILD_CRT=OFF',
          '-DCOMPILER_RT_BUILD_LIBFUZZER=OFF',
          '-DCOMPILER_RT_BUILD_MEMPROF=OFF',
          '-DCOMPILER_RT_BUILD_PROFILE=ON',
          '-DCOMPILER_RT_BUILD_SANITIZERS=OFF',
          '-DCOMPILER_RT_BUILD_XRAY=OFF',
          ])

    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)
    RunCommand(['cmake'] + bootstrap_args + [os.path.join(LLVM_DIR, 'llvm')],
               msvc_arch='x64')
    CopyLibstdcpp(args, LLVM_BOOTSTRAP_DIR)
    CopyLibstdcpp(args, LLVM_BOOTSTRAP_INSTALL_DIR)
    RunCommand(['ninja'], msvc_arch='x64')
    if args.run_tests:
      RunCommand(['ninja', 'check-all'], msvc_arch='x64')
    RunCommand(['ninja', 'install'], msvc_arch='x64')

    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')
      # CMake has a hard time with backslashes in compiler paths:
      # https://stackoverflow.com/questions/13050827
      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++')
    if sys.platform.startswith('linux'):
      base_cmake_args.append('-DLLVM_ENABLE_LLD=ON')

    if args.gcc_toolchain:
      # Tell the bootstrap compiler where to find the standard library headers
      # and shared object files.
      cflags.append('--gcc-toolchain=' + args.gcc_toolchain)
      cxxflags.append('--gcc-toolchain=' + args.gcc_toolchain)

    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)

    projects = 'clang'
    if sys.platform == 'darwin':
      projects += ';libcxx;compiler-rt'

    instrument_args = base_cmake_args + [
        '-DLLVM_ENABLE_PROJECTS=' + projects,
        '-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),
        # Build with instrumentation.
        '-DLLVM_BUILD_INSTRUMENTED=IR',
    ]
    # Build with the bootstrap compiler.
    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)

    RunCommand(['cmake'] + instrument_args + [os.path.join(LLVM_DIR, 'llvm')],
               msvc_arch='x64')
    CopyLibstdcpp(args, LLVM_INSTRUMENTED_DIR)
    RunCommand(['ninja'], msvc_arch='x64')
    print('Instrumented compiler built.')

    # Train by building some C++ code.
    #
    # pgo_training-1.ii is a preprocessed (on Linux) version of
    # src/third_party/blink/renderer/core/layout/layout_object.cc, selected
    # because it's a large translation unit in Blink, which is normally the
    # slowest part of Chromium to compile. Using this, we get ~20% shorter
    # build times for Linux, Android, and Mac, which is also what we got when
    # training by actually building a target in Chromium. (For comparison, a
    # C++-y "Hello World" program only resulted in 14% faster builds.)
    # See https://crbug.com/966403#c16 for all numbers.
    #
    # Although the training currently only exercises Clang, it does involve LLVM
    # internals, and so LLD also benefits when used for ThinLTO links.
    #
    # NOTE: Tidy uses binaries built with this profile, but doesn't seem to
    # gain much from it. If tidy's execution time becomes a concern, it might
    # be good to investigate that.
    #
    # TODO(hans): Enhance the training, perhaps by including preprocessed code
    # from more platforms, and by doing some linking so that lld can benefit
    # from PGO as well. Perhaps the training could be done asynchronously by
    # dedicated buildbots that upload profiles to the cloud.
    training_source = 'pgo_training-1.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++14',
                 '-fno-exceptions', '-fno-rtti', '-w', '-c', training_source]
    if sys.platform == 'darwin':
      train_cmd.extend(['-stdlib=libc++', '-isysroot', isysroot])
    RunCommand(train_cmd, msvc_arch='x64')

    # Merge profiles.
    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')), msvc_arch='x64')
    print('Profile generated.')

  compiler_rt_args = [
    '-DCOMPILER_RT_BUILD_CRT=OFF',
    '-DCOMPILER_RT_BUILD_LIBFUZZER=OFF',
    '-DCOMPILER_RT_BUILD_MEMPROF=OFF',
    '-DCOMPILER_RT_BUILD_PROFILE=ON',
    '-DCOMPILER_RT_BUILD_SANITIZERS=ON',
    '-DCOMPILER_RT_BUILD_XRAY=OFF',
  ]
  if sys.platform == 'darwin':
    compiler_rt_args.extend([
        '-DCOMPILER_RT_BUILD_BUILTINS=ON',
        '-DCOMPILER_RT_ENABLE_IOS=ON',
        '-DCOMPILER_RT_ENABLE_WATCHOS=OFF',
        '-DCOMPILER_RT_ENABLE_TVOS=OFF',
        # armv7 is A5 and earlier, armv7s is A6+ (2012 and later, before 64-bit
        # iPhones). armv7k is Apple Watch, which we don't need.
        '-DDARWIN_ios_ARCHS=armv7;armv7s;arm64',
        '-DDARWIN_iossim_ARCHS=i386;x86_64;arm64',
    ])
    if args.bootstrap:
      # mac/arm64 needs MacOSX11.0.sdk. System Xcode (+ SDK) may be something
      # else, so use the hermetic Xcode.
      # Options:
      # - temporarily set system Xcode to Xcode 12 beta while running this
      #   script, (cf build/swarming_xcode_install.py, but it looks unused)
      # - use Xcode 12 beta for everything on tot bots, only need to fuzz with
      #   scripts/slave/recipes/chromium_upload_clang.py then (but now the
      #   chrome/ios build will use the 11.0 SDK too and we'd be on the hook for
      #   keeping it green -- if it's currently green, who knows)
      # - pass flags to cmake to try to coax it into using Xcode 12 beta for the
      #   LLVM build without it being system Xcode.
      #
      # The last option seems best, so let's go with that. We need to pass
      # -isysroot to the SDK and -B to the /usr/bin so that the new ld64 is
      # used.
      # The compiler-rt build overrides -isysroot flags set via cflags, and we
      # only need to use the 11 SDK for the compiler-rt build. So set only
      # DARWIN_macosx_CACHED_SYSROOT to the 11 SDK and use the regular SDK
      # for the rest of the build. (The new ld is used for all links.)
      sys.path.insert(1, os.path.join(CHROMIUM_DIR, 'build'))
      import mac_toolchain
      LLVM_XCODE = os.path.join(THIRD_PARTY_DIR, 'llvm-xcode')
      mac_toolchain.InstallXcodeBinaries(LLVM_XCODE)
      isysroot_11 = os.path.join(LLVM_XCODE, 'Contents', 'Developer',
                                 'Platforms', 'MacOSX.platform', 'Developer',
                                 'SDKs', 'MacOSX11.1.sdk')
      xcode_bin = os.path.join(LLVM_XCODE, 'Contents', 'Developer',
                               'Toolchains', 'XcodeDefault.xctoolchain', 'usr',
                               'bin')
      # Include an arm64 slice for libclang_rt.osx.a. This requires using
      # MacOSX11.x.sdk (via -isysroot, via DARWIN_macosx_CACHED_SYSROOT) and
      # the new ld, via -B
      compiler_rt_args.extend([
          # We don't need 32-bit intel support for macOS, we only ship 64-bit.
          '-DDARWIN_osx_ARCHS=arm64;x86_64',
          '-DDARWIN_macosx_CACHED_SYSROOT=' + isysroot_11,
      ])
      ldflags += ['-B', xcode_bin]
    else:
      compiler_rt_args.extend(['-DDARWIN_osx_ARCHS=x86_64'])
  else:
    compiler_rt_args.append('-DCOMPILER_RT_BUILD_BUILTINS=OFF')

  # LLVM uses C++11 starting in llvm 3.5. On Linux, this means libstdc++4.7+ is
  # needed, on OS X it requires libc++. clang only automatically links to libc++
  # when targeting OS X 10.9+, so add stdlib=libc++ explicitly so clang can run
  # on OS X versions as old as 10.7.
  deployment_target = ''

  if sys.platform == 'darwin' and args.bootstrap:
    # When building on 10.9, /usr/include usually doesn't exist, and while
    # Xcode's clang automatically sets a sysroot, self-built clangs don't.
    cflags = ['-isysroot', isysroot]
    cxxflags = ['-stdlib=libc++'] + cflags
    ldflags += ['-stdlib=libc++']
    deployment_target = '10.7'

  # If building at head, define a macro that plugins can use for #ifdefing
  # out code that builds at head, but not at CLANG_REVISION or vice versa.
  if args.llvm_force_head_revision:
    cflags += ['-DLLVM_FORCE_HEAD_REVISION']
    cxxflags += ['-DLLVM_FORCE_HEAD_REVISION']

  # Build PDBs for archival on Windows.  Don't use RelWithDebInfo since it
  # has different optimization defaults than Release.
  # Also disable stack cookies (/GS-) for performance.
  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.')

  default_tools = ['plugins', 'blink_gc_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)
  cmake_args = base_cmake_args + compiler_rt_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=' + LLVM_BUILD_DIR,
      '-DCHROMIUM_TOOLS_SRC=%s' % 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':
    cmake_args += ['-DCOMPILER_RT_ENABLE_IOS=ON',
                   '-DSANITIZER_MIN_OSX_VERSION=10.7']

  # TODO(crbug.com/962988): Use -DLLVM_EXTERNAL_PROJECTS instead.
  CreateChromeToolsShim()

  if os.path.exists(LLVM_BUILD_DIR):
    RmTree(LLVM_BUILD_DIR)
  EnsureDirExists(LLVM_BUILD_DIR)
  os.chdir(LLVM_BUILD_DIR)
  RunCommand(['cmake'] + cmake_args + [os.path.join(LLVM_DIR, 'llvm')],
             msvc_arch='x64', env=deployment_env)
  CopyLibstdcpp(args, LLVM_BUILD_DIR)
  RunCommand(['ninja'], msvc_arch='x64')

  if chrome_tools:
    # If any Chromium tools were built, install those now.
    RunCommand(['ninja', 'cr-install'], msvc_arch='x64')

  VerifyVersionOfBuiltClangMatchesVERSION()
  VerifyZlibSupport()

  if sys.platform == 'win32':
    platform = 'windows'
  elif sys.platform == 'darwin':
    platform = 'darwin'
  else:
    assert sys.platform.startswith('linux')
    platform = 'linux'
  rt_lib_dst_dir = os.path.join(LLVM_BUILD_DIR, 'lib', 'clang',
                                RELEASE_VERSION, 'lib', platform)

  # Do an out-of-tree build of compiler-rt for 32-bit Win clang_rt.profile.lib.
  if sys.platform == 'win32':
    compiler_rt_build_dir = os.path.join(LLVM_BUILD_DIR, 'compiler-rt')
    if os.path.isdir(compiler_rt_build_dir):
      RmTree(compiler_rt_build_dir)
    os.makedirs(compiler_rt_build_dir)
    os.chdir(compiler_rt_build_dir)
    if args.bootstrap:
      # The bootstrap compiler produces 64-bit binaries by default.
      cflags += ['-m32']
      cxxflags += ['-m32']

    compiler_rt_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),
        '-DCOMPILER_RT_BUILD_BUILTINS=OFF',
        '-DCOMPILER_RT_BUILD_CRT=OFF',
        '-DCOMPILER_RT_BUILD_LIBFUZZER=OFF',
        '-DCOMPILER_RT_BUILD_MEMPROF=OFF',
        '-DCOMPILER_RT_BUILD_PROFILE=ON',
        '-DCOMPILER_RT_BUILD_SANITIZERS=OFF',
        '-DCOMPILER_RT_BUILD_XRAY=OFF',
    ]
    RunCommand(['cmake'] + compiler_rt_args +
               [os.path.join(LLVM_DIR, 'llvm')],
               msvc_arch='x86', env=deployment_env)
    RunCommand(['ninja', 'compiler-rt'], msvc_arch='x86')

    # Copy select output to the main tree.
    rt_lib_src_dir = os.path.join(compiler_rt_build_dir, 'lib', 'clang',
                                  RELEASE_VERSION, 'lib', platform)
    # Static and dynamic libraries:
    CopyDirectoryContents(rt_lib_src_dir, rt_lib_dst_dir)

  if args.with_android:
    # TODO(thakis): Now that the NDK uses clang, try to build all archs in
    # one LLVM build instead of building 3 times.
    toolchain_dir = ANDROID_NDK_DIR + '/toolchains/llvm/prebuilt/linux-x86_64'
    for target_arch in ['aarch64', 'arm', 'i686']:
      # Build compiler-rt runtimes needed for Android in a separate build tree.
      build_dir = os.path.join(LLVM_BUILD_DIR, 'android-' + target_arch)
      if not os.path.exists(build_dir):
        os.mkdir(os.path.join(build_dir))
      os.chdir(build_dir)
      target_triple = target_arch
      if target_arch == 'arm':
        target_triple = 'armv7'
      api_level = '21' if target_arch == 'aarch64' else '19'
      target_triple += '-linux-android' + api_level
      cflags = [
          '--target=' + target_triple,
          '--sysroot=%s/sysroot' % toolchain_dir,
          '--gcc-toolchain=' + toolchain_dir,
          # android_ndk/toolchains/llvm/prebuilt/linux-x86_64/aarch64-linux-android/bin/ld
          # depends on a newer version of libxml2.so than what's available on
          # the bots. To make things work, use our just-built lld as linker.
          '-fuse-ld=lld',
          # Clang defaults to compiler-rt when targeting android after
          # a478b0a199f4. Stick with libgcc for now. (crbug.com/1184398).
          '--rtlib=libgcc',
      ]
      android_args = base_cmake_args + [
        '-DCMAKE_C_COMPILER=' + os.path.join(LLVM_BUILD_DIR, 'bin/clang'),
        '-DCMAKE_CXX_COMPILER=' + os.path.join(LLVM_BUILD_DIR, 'bin/clang++'),
        '-DLLVM_CONFIG_PATH=' + os.path.join(LLVM_BUILD_DIR, 'bin/llvm-config'),
        '-DCMAKE_C_FLAGS=' + ' '.join(cflags),
        '-DCMAKE_CXX_FLAGS=' + ' '.join(cflags),
        '-DCMAKE_ASM_FLAGS=' + ' '.join(cflags),
        '-DCOMPILER_RT_BUILD_BUILTINS=OFF',
        '-DCOMPILER_RT_BUILD_CRT=OFF',
        '-DCOMPILER_RT_BUILD_LIBFUZZER=OFF',
        '-DCOMPILER_RT_BUILD_MEMPROF=OFF',
        '-DCOMPILER_RT_BUILD_PROFILE=ON',
        '-DCOMPILER_RT_BUILD_SANITIZERS=ON',
        '-DCOMPILER_RT_BUILD_XRAY=OFF',
        '-DSANITIZER_CXX_ABI=libcxxabi',
        '-DCMAKE_SHARED_LINKER_FLAGS=-Wl,-u__cxa_demangle',
        '-DANDROID=1']
      RunCommand(['cmake'] + android_args + [COMPILER_RT_DIR])

      # We use ASan i686 build for fuzzing.
      libs_want = ['lib/linux/libclang_rt.asan-{0}-android.so']
      if target_arch in ['aarch64', 'arm']:
        libs_want += [
            'lib/linux/libclang_rt.ubsan_standalone-{0}-android.so',
            'lib/linux/libclang_rt.profile-{0}-android.a',
        ]
      if target_arch == 'aarch64':
        libs_want += ['lib/linux/libclang_rt.hwasan-{0}-android.so']
      libs_want = [lib.format(target_arch) for lib in libs_want]
      RunCommand(['ninja'] + libs_want)

      # And copy them into the main build tree.
      for p in libs_want:
        shutil.copy(p, rt_lib_dst_dir)

  if args.with_fuchsia:
    # Fuchsia links against libclang_rt.builtins-<arch>.a instead of libgcc.a.
    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')
      # Build clang_rt runtime for Fuchsia in a separate build tree.
      build_dir = os.path.join(LLVM_BUILD_DIR, 'fuchsia-' + target_arch)
      if not os.path.exists(build_dir):
        os.mkdir(os.path.join(build_dir))
      os.chdir(build_dir)
      target_spec = target_arch + '-fuchsia'
      # TODO(thakis): Might have to pass -B here once sysroot contains
      # binaries (e.g. gas for arm64?)
      fuchsia_args = base_cmake_args + [
        '-DCMAKE_C_COMPILER=' + os.path.join(LLVM_BUILD_DIR, 'bin/clang'),
        '-DCMAKE_CXX_COMPILER=' + os.path.join(LLVM_BUILD_DIR, 'bin/clang++'),
        '-DCMAKE_LINKER=' + os.path.join(LLVM_BUILD_DIR, 'bin/clang'),
        '-DCMAKE_AR=' + os.path.join(LLVM_BUILD_DIR, 'bin/llvm-ar'),
        '-DLLVM_CONFIG_PATH=' + os.path.join(LLVM_BUILD_DIR, 'bin/llvm-config'),
        '-DCMAKE_SYSTEM_NAME=Fuchsia',
        '-DCMAKE_C_COMPILER_TARGET=%s-fuchsia' % target_arch,
        '-DCMAKE_ASM_COMPILER_TARGET=%s-fuchsia' % target_arch,
        '-DCOMPILER_RT_BUILD_BUILTINS=ON',
        '-DCOMPILER_RT_BUILD_CRT=OFF',
        '-DCOMPILER_RT_BUILD_LIBFUZZER=OFF',
        '-DCOMPILER_RT_BUILD_MEMPROF=OFF',
        '-DCOMPILER_RT_BUILD_PROFILE=OFF',
        '-DCOMPILER_RT_BUILD_SANITIZERS=OFF',
        '-DCOMPILER_RT_BUILD_XRAY=OFF',
        '-DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON',
        '-DCMAKE_SYSROOT=%s' % toolchain_dir,
        # TODO(thakis|scottmg): Use PER_TARGET_RUNTIME_DIR for all platforms.
        # https://crbug.com/882485.
        '-DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=ON',

        # These are necessary because otherwise CMake tries to build an
        # executable to test to see if the compiler is working, but in doing so,
        # it links against the builtins.a that we're about to build.
        '-DCMAKE_C_COMPILER_WORKS=ON',
        '-DCMAKE_ASM_COMPILER_WORKS=ON',
        ]
      RunCommand(['cmake'] +
                 fuchsia_args +
                 [os.path.join(COMPILER_RT_DIR, 'lib', 'builtins')])
      builtins_a = 'libclang_rt.builtins.a'
      RunCommand(['ninja', builtins_a])

      # And copy it into the main build tree.
      fuchsia_lib_dst_dir = os.path.join(LLVM_BUILD_DIR, 'lib', 'clang',
                                         RELEASE_VERSION, 'lib', target_spec)
      if not os.path.exists(fuchsia_lib_dst_dir):
        os.makedirs(fuchsia_lib_dst_dir)
      CopyFile(os.path.join(build_dir, 'lib', target_spec, builtins_a),
               fuchsia_lib_dst_dir)

      # Build the Fuchsia profile runtime.
      if target_arch == 'x86_64':
        fuchsia_args.extend([
            '-DCOMPILER_RT_BUILD_BUILTINS=OFF',
            '-DCOMPILER_RT_BUILD_PROFILE=ON',
            '-DCMAKE_CXX_COMPILER_TARGET=%s-fuchsia' % target_arch,
            '-DCMAKE_CXX_COMPILER_WORKS=ON',
        ])
        profile_build_dir = os.path.join(LLVM_BUILD_DIR,
                                         'fuchsia-profile-' + target_arch)
        if not os.path.exists(profile_build_dir):
          os.mkdir(os.path.join(profile_build_dir))
        os.chdir(profile_build_dir)
        RunCommand(['cmake'] +
                   fuchsia_args +
                   [COMPILER_RT_DIR])
        profile_a = 'libclang_rt.profile.a'
        RunCommand(['ninja', profile_a])
        CopyFile(os.path.join(profile_build_dir, 'lib', target_spec, profile_a),
                              fuchsia_lib_dst_dir)

  # Run tests.
  if args.run_tests or args.llvm_force_head_revision:
    RunCommand(['ninja', '-C', LLVM_BUILD_DIR, 'cr-check-all'], msvc_arch='x64')

  if args.run_tests:
    test_targets = [ 'check-all' ]
    if sys.platform == 'darwin':
      # TODO(thakis): Run check-all on Darwin too, https://crbug.com/959361
      test_targets = [ 'check-llvm', 'check-clang', 'check-lld' ]
    RunCommand(['ninja', '-C', LLVM_BUILD_DIR] + test_targets, msvc_arch='x64')

  if sys.platform == 'darwin':
    for dylib in glob.glob(os.path.join(rt_lib_dst_dir, '*.dylib')):
      # Fix LC_ID_DYLIB for the ASan dynamic libraries to be relative to
      # @executable_path.
      # Has to happen after running tests.
      # TODO(glider): this is transitional. We'll need to fix the dylib
      # name either in our build system, or in Clang. See also
      # http://crbug.com/344836.
      subprocess.call(['install_name_tool', '-id',
                       '@executable_path/' + os.path.basename(dylib), dylib])

  WriteStampFile(PACKAGE_VERSION, STAMP_FILE)
  WriteStampFile(PACKAGE_VERSION, FORCE_HEAD_REVISION_FILE)
  print('Clang build was successful.')
  return 0
Exemplo n.º 11
0
def main():
    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('--gcc-toolchain', help='(no longer used)')
    parser.add_argument('--lto-lld',
                        action='store_true',
                        help='build lld with LTO')
    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('--extra-tools',
                        nargs='*',
                        default=[],
                        help='select additional chrome tools to build')
    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('--with-android',
                        type=gn_arg,
                        nargs='?',
                        const=True,
                        help='build the Android ASan runtime (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'))
    args = parser.parse_args()

    if args.lto_lld and not args.bootstrap:
        print('--lto-lld requires --bootstrap')
        return 1
    if args.lto_lld and not sys.platform.startswith('linux'):
        print('--lto-lld is only effective on Linux. Ignoring the option.')
        args.lto_lld = False
    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.llvm_force_head_revision:
        # Don't build fuchsia runtime on ToT bots at all.
        args.with_fuchsia = False

    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/+/master/docs/fuchsia_build_instructions.md'
        )
        print('for general Fuchsia build instructions.')
        return 1

    # DEVELOPER_DIR needs to be set when Xcode isn't in a standard location
    # and xcode-select wasn't run.  This is needed for running clang and ld
    # for the build done by this script, but it's also needed for running
    # macOS system svn, so this needs to happen before calling functions using
    # svn.
    SetMacXcodePath()

    AddSvnToPathOnWin()

    global CLANG_REVISION, PACKAGE_VERSION
    if args.llvm_force_head_revision:
        CLANG_REVISION = GetSvnRevision(LLVM_REPO_URL)
        PACKAGE_VERSION = CLANG_REVISION + '-0'

    # Don't buffer stdout, so that print statements are immediately flushed.
    sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

    print('Locally building clang %s...' % PACKAGE_VERSION)
    WriteStampFile('', STAMP_FILE)
    WriteStampFile('', FORCE_HEAD_REVISION_FILE)

    AddCMakeToPath(args)
    AddGnuWinToPath()

    DeleteChromeToolsShim()

    CheckoutRepos(args)

    if args.skip_build:
        return

    cc, cxx = None, None

    cflags = []
    cxxflags = []
    ldflags = []

    targets = 'AArch64;ARM;Mips;PowerPC;SystemZ;WebAssembly;X86'
    base_cmake_args = [
        '-GNinja',
        '-DCMAKE_BUILD_TYPE=Release',
        '-DLLVM_ENABLE_ASSERTIONS=%s' %
        ('OFF' if args.disable_asserts else 'ON'),
        '-DLLVM_ENABLE_PIC=OFF',
        '-DLLVM_ENABLE_TERMINFO=OFF',
        '-DLLVM_TARGETS_TO_BUILD=' + targets,
        # Statically link MSVCRT to avoid DLL dependencies.
        '-DLLVM_USE_CRT_RELEASE=MT',
        '-DCLANG_PLUGIN_SUPPORT=OFF',
        '-DCLANG_ENABLE_STATIC_ANALYZER=OFF',
        '-DCLANG_ENABLE_ARCMT=OFF',
        # TODO(crbug.com/929645): Use newer toolchain to host.
        '-DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=ON',
        '-DBUG_REPORT_URL=' + BUG_REPORT_URL,
    ]

    if sys.platform != 'win32':
        # libxml2 is required by the Win manifest merging tool used in cross-builds.
        base_cmake_args.append('-DLLVM_ENABLE_LIBXML2=FORCE_ON')

    if args.bootstrap:
        print('Building bootstrap compiler')
        EnsureDirExists(LLVM_BOOTSTRAP_DIR)
        os.chdir(LLVM_BOOTSTRAP_DIR)
        bootstrap_args = base_cmake_args + [
            '-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64',
            '-DCMAKE_INSTALL_PREFIX=' + LLVM_BOOTSTRAP_INSTALL_DIR,
            '-DCMAKE_C_FLAGS=' + ' '.join(cflags),
            '-DCMAKE_CXX_FLAGS=' + ' '.join(cxxflags),
            # Ignore args.disable_asserts for the bootstrap compiler.
            '-DLLVM_ENABLE_ASSERTIONS=ON',
        ]
        if cc is not None: bootstrap_args.append('-DCMAKE_C_COMPILER=' + cc)
        if cxx is not None:
            bootstrap_args.append('-DCMAKE_CXX_COMPILER=' + cxx)
        RmCmakeCache('.')
        RunCommand(['cmake'] + bootstrap_args + [LLVM_DIR], msvc_arch='x64')
        RunCommand(['ninja'], msvc_arch='x64')
        if args.run_tests:
            if sys.platform == 'win32':
                CopyDiaDllTo(os.path.join(LLVM_BOOTSTRAP_DIR, 'bin'))
            RunCommand(['ninja', 'check-all'], msvc_arch='x64')
        RunCommand(['ninja', 'install'], msvc_arch='x64')

        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')
            # CMake has a hard time with backslashes in compiler paths:
            # https://stackoverflow.com/questions/13050827
            cc = cc.replace('\\', '/')
            cxx = cxx.replace('\\', '/')
        else:
            cc = os.path.join(LLVM_BOOTSTRAP_INSTALL_DIR, 'bin', 'clang')
            cxx = os.path.join(LLVM_BOOTSTRAP_INSTALL_DIR, 'bin', 'clang++')

        print('Building final compiler')

    # LLVM uses C++11 starting in llvm 3.5. On Linux, this means libstdc++4.7+ is
    # needed, on OS X it requires libc++. clang only automatically links to libc++
    # when targeting OS X 10.9+, so add stdlib=libc++ explicitly so clang can run
    # on OS X versions as old as 10.7.
    deployment_target = ''

    if sys.platform == 'darwin' and args.bootstrap:
        # When building on 10.9, /usr/include usually doesn't exist, and while
        # Xcode's clang automatically sets a sysroot, self-built clangs don't.
        cflags = [
            '-isysroot',
            subprocess.check_output(['xcrun', '--show-sdk-path']).rstrip()
        ]
        cxxflags = ['-stdlib=libc++'] + cflags
        ldflags += ['-stdlib=libc++']
        deployment_target = '10.7'
        # Running libc++ tests takes a long time. Since it was only needed for
        # the install step above, don't build it as part of the main build.
        # This makes running package.py over 10% faster (30 min instead of 34 min)
        RmTree(LIBCXX_DIR)

    # If building at head, define a macro that plugins can use for #ifdefing
    # out code that builds at head, but not at CLANG_REVISION or vice versa.
    if args.llvm_force_head_revision:
        cflags += ['-DLLVM_FORCE_HEAD_REVISION']
        cxxflags += ['-DLLVM_FORCE_HEAD_REVISION']

    # Build PDBs for archival on Windows.  Don't use RelWithDebInfo since it
    # has different optimization defaults than Release.
    # Also disable stack cookies (/GS-) for performance.
    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

    # Build lld and code coverage tools. This is done separately from the rest of
    # the build because these tools require threading support.
    tools_with_threading = ['dsymutil', 'lld', 'llvm-cov', 'llvm-profdata']
    print('Building the following tools with threading support: %s' %
          str(tools_with_threading))

    if os.path.exists(THREADS_ENABLED_BUILD_DIR):
        RmTree(THREADS_ENABLED_BUILD_DIR)
    EnsureDirExists(THREADS_ENABLED_BUILD_DIR)
    os.chdir(THREADS_ENABLED_BUILD_DIR)

    threads_enabled_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)
    ]
    if cc is not None:
        threads_enabled_cmake_args.append('-DCMAKE_C_COMPILER=' + cc)
    if cxx is not None:
        threads_enabled_cmake_args.append('-DCMAKE_CXX_COMPILER=' + cxx)

    if args.lto_lld:
        # Build lld with LTO. That speeds up the linker by ~10%.
        # We only use LTO for Linux now.
        #
        # The linker expects all archive members to have symbol tables, so the
        # archiver needs to be able to create symbol tables for bitcode files.
        # GNU ar and ranlib don't understand bitcode files, but llvm-ar and
        # llvm-ranlib do, so use them.
        ar = os.path.join(LLVM_BOOTSTRAP_INSTALL_DIR, 'bin', 'llvm-ar')
        ranlib = os.path.join(LLVM_BOOTSTRAP_INSTALL_DIR, 'bin', 'llvm-ranlib')
        threads_enabled_cmake_args += [
            '-DCMAKE_AR=' + ar, '-DCMAKE_RANLIB=' + ranlib,
            '-DLLVM_ENABLE_LTO=thin', '-DLLVM_USE_LINKER=lld'
        ]

    RmCmakeCache('.')
    RunCommand(['cmake'] + threads_enabled_cmake_args + [LLVM_DIR],
               msvc_arch='x64',
               env=deployment_env)
    RunCommand(['ninja'] + tools_with_threading, msvc_arch='x64')

    # Build clang and other tools.
    CreateChromeToolsShim()

    cmake_args = []
    # TODO(thakis): Unconditionally append this to base_cmake_args instead once
    # compiler-rt can build with clang-cl on Windows (http://llvm.org/PR23698)
    cc_args = base_cmake_args if sys.platform != 'win32' else cmake_args
    if cc is not None: cc_args.append('-DCMAKE_C_COMPILER=' + cc)
    if cxx is not None: cc_args.append('-DCMAKE_CXX_COMPILER=' + cxx)
    default_tools = ['plugins', 'blink_gc_plugin', 'translation_unit']
    chrome_tools = list(set(default_tools + args.extra_tools))
    cmake_args += base_cmake_args + [
        '-DLLVM_ENABLE_THREADS=OFF', '-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=' + LLVM_BUILD_DIR,
        '-DCHROMIUM_TOOLS_SRC=%s' %
        os.path.join(CHROMIUM_DIR, 'tools', 'clang'),
        '-DCHROMIUM_TOOLS=%s' % ';'.join(chrome_tools)
    ]

    EnsureDirExists(LLVM_BUILD_DIR)
    os.chdir(LLVM_BUILD_DIR)
    RmCmakeCache('.')
    RunCommand(['cmake'] + cmake_args + [LLVM_DIR],
               msvc_arch='x64',
               env=deployment_env)
    RunCommand(['ninja'], msvc_arch='x64')

    # Copy in the threaded versions of lld and other tools.
    if sys.platform == 'win32':
        CopyFile(
            os.path.join(THREADS_ENABLED_BUILD_DIR, 'bin', 'lld-link.exe'),
            os.path.join(LLVM_BUILD_DIR, 'bin'))
        CopyFile(os.path.join(THREADS_ENABLED_BUILD_DIR, 'bin', 'lld.pdb'),
                 os.path.join(LLVM_BUILD_DIR, 'bin'))
    else:
        for tool in tools_with_threading:
            CopyFile(os.path.join(THREADS_ENABLED_BUILD_DIR, 'bin', tool),
                     os.path.join(LLVM_BUILD_DIR, 'bin'))

    if chrome_tools:
        # If any Chromium tools were built, install those now.
        RunCommand(['ninja', 'cr-install'], msvc_arch='x64')

    VerifyVersionOfBuiltClangMatchesVERSION()

    # Do an out-of-tree build of compiler-rt.
    # On Windows, this is used to get the 32-bit ASan run-time.
    # TODO(hans): Remove once the regular build above produces this.
    # On Mac and Linux, this is used to get the regular 64-bit run-time.
    # Do a clobbered build due to cmake changes.
    if os.path.isdir(COMPILER_RT_BUILD_DIR):
        RmTree(COMPILER_RT_BUILD_DIR)
    os.makedirs(COMPILER_RT_BUILD_DIR)
    os.chdir(COMPILER_RT_BUILD_DIR)
    # TODO(thakis): Add this once compiler-rt can build with clang-cl (see
    # above).
    #if args.bootstrap and sys.platform == 'win32':
    # The bootstrap compiler produces 64-bit binaries by default.
    #cflags += ['-m32']
    #cxxflags += ['-m32']
    compiler_rt_args = base_cmake_args + [
        '-DLLVM_ENABLE_THREADS=OFF', '-DCMAKE_C_FLAGS=' + ' '.join(cflags),
        '-DCMAKE_CXX_FLAGS=' + ' '.join(cxxflags)
    ]
    if sys.platform == 'darwin':
        compiler_rt_args += ['-DCOMPILER_RT_ENABLE_IOS=ON']
    if sys.platform != 'win32':
        compiler_rt_args += [
            '-DLLVM_CONFIG_PATH=' +
            os.path.join(LLVM_BUILD_DIR, 'bin', 'llvm-config'),
            '-DSANITIZER_MIN_OSX_VERSION="10.7"'
        ]
    # compiler-rt is part of the llvm checkout on Windows but a stand-alone
    # directory elsewhere, see the TODO above COMPILER_RT_DIR.
    RmCmakeCache('.')
    RunCommand(['cmake'] + compiler_rt_args +
               [LLVM_DIR if sys.platform == 'win32' else COMPILER_RT_DIR],
               msvc_arch='x86',
               env=deployment_env)
    RunCommand(['ninja', 'compiler-rt'], msvc_arch='x86')
    if sys.platform != 'win32':
        RunCommand(['ninja', 'fuzzer'])

    # Copy select output to the main tree.
    # TODO(hans): Make this (and the .gypi and .isolate files) version number
    # independent.
    if sys.platform == 'win32':
        platform = 'windows'
    elif sys.platform == 'darwin':
        platform = 'darwin'
    else:
        assert sys.platform.startswith('linux')
        platform = 'linux'
    rt_lib_src_dir = os.path.join(COMPILER_RT_BUILD_DIR, 'lib', platform)
    if sys.platform == 'win32':
        # TODO(thakis): This too is due to compiler-rt being part of the checkout
        # on Windows, see TODO above COMPILER_RT_DIR.
        rt_lib_src_dir = os.path.join(COMPILER_RT_BUILD_DIR, 'lib', 'clang',
                                      RELEASE_VERSION, 'lib', platform)
    rt_lib_dst_dir = os.path.join(LLVM_BUILD_DIR, 'lib', 'clang',
                                  RELEASE_VERSION, 'lib', platform)
    # Blacklists:
    CopyDirectoryContents(os.path.join(rt_lib_src_dir, '..', '..', 'share'),
                          os.path.join(rt_lib_dst_dir, '..', '..', 'share'))
    # Headers:
    if sys.platform != 'win32':
        CopyDirectoryContents(
            os.path.join(COMPILER_RT_BUILD_DIR, 'include/sanitizer'),
            os.path.join(LLVM_BUILD_DIR, 'lib/clang', RELEASE_VERSION,
                         'include/sanitizer'))
    # Static and dynamic libraries:
    CopyDirectoryContents(rt_lib_src_dir, rt_lib_dst_dir)
    if sys.platform == 'darwin':
        for dylib in glob.glob(os.path.join(rt_lib_dst_dir, '*.dylib')):
            # Fix LC_ID_DYLIB for the ASan dynamic libraries to be relative to
            # @executable_path.
            # TODO(glider): this is transitional. We'll need to fix the dylib
            # name either in our build system, or in Clang. See also
            # http://crbug.com/344836.
            subprocess.call([
                'install_name_tool', '-id',
                '@executable_path/' + os.path.basename(dylib), dylib
            ])

    if args.with_android:
        make_toolchain = os.path.join(ANDROID_NDK_DIR, 'build', 'tools',
                                      'make_standalone_toolchain.py')
        for target_arch in ['aarch64', 'arm', 'i686']:
            # Make standalone Android toolchain for target_arch.
            toolchain_dir = os.path.join(LLVM_BUILD_DIR,
                                         'android-toolchain-' + target_arch)
            api_level = '21' if target_arch == 'aarch64' else '19'
            RunCommand([
                make_toolchain, '--api=' + api_level, '--force',
                '--install-dir=%s' % toolchain_dir, '--stl=libc++',
                '--arch=' + {
                    'aarch64': 'arm64',
                    'arm': 'arm',
                    'i686': 'x86',
                }[target_arch]
            ])

            # NDK r16 "helpfully" installs libc++ as libstdc++ "so the compiler will
            # pick it up by default". Only these days, the compiler tries to find
            # libc++ instead. See https://crbug.com/902270.
            shutil.copy(
                os.path.join(toolchain_dir, 'sysroot/usr/lib/libstdc++.a'),
                os.path.join(toolchain_dir, 'sysroot/usr/lib/libc++.a'))
            shutil.copy(
                os.path.join(toolchain_dir, 'sysroot/usr/lib/libstdc++.so'),
                os.path.join(toolchain_dir, 'sysroot/usr/lib/libc++.so'))

            # Build compiler-rt runtimes needed for Android in a separate build tree.
            build_dir = os.path.join(LLVM_BUILD_DIR, 'android-' + target_arch)
            if not os.path.exists(build_dir):
                os.mkdir(os.path.join(build_dir))
            os.chdir(build_dir)
            target_triple = target_arch
            if target_arch == 'arm':
                target_triple = 'armv7'
            target_triple += '-linux-android' + api_level
            cflags = [
                '--target=%s' % target_triple,
                '--sysroot=%s/sysroot' % toolchain_dir,
                '-B%s' % toolchain_dir
            ]
            android_args = base_cmake_args + [
                '-DLLVM_ENABLE_THREADS=OFF', '-DCMAKE_C_COMPILER=' +
                os.path.join(LLVM_BUILD_DIR, 'bin/clang'),
                '-DCMAKE_CXX_COMPILER=' + os.path.join(
                    LLVM_BUILD_DIR, 'bin/clang++'), '-DLLVM_CONFIG_PATH=' +
                os.path.join(LLVM_BUILD_DIR, 'bin/llvm-config'),
                '-DCMAKE_C_FLAGS=' + ' '.join(cflags), '-DCMAKE_CXX_FLAGS=' +
                ' '.join(cflags), '-DCMAKE_ASM_FLAGS=' + ' '.join(cflags),
                '-DSANITIZER_CXX_ABI=libcxxabi',
                '-DCMAKE_SHARED_LINKER_FLAGS=-Wl,-u__cxa_demangle',
                '-DANDROID=1'
            ]
            RmCmakeCache('.')
            RunCommand(['cmake'] + android_args + [COMPILER_RT_DIR])

            # We use ASan i686 build for fuzzing.
            libs_want = ['lib/linux/libclang_rt.asan-{0}-android.so']
            if target_arch in ['aarch64', 'arm']:
                libs_want += [
                    'lib/linux/libclang_rt.ubsan_standalone-{0}-android.so',
                    'lib/linux/libclang_rt.profile-{0}-android.a',
                ]
            if target_arch == 'aarch64':
                libs_want += ['lib/linux/libclang_rt.hwasan-{0}-android.so']
            libs_want = [lib.format(target_arch) for lib in libs_want]
            RunCommand(['ninja'] + libs_want)

            # And copy them into the main build tree.
            for p in libs_want:
                shutil.copy(p, rt_lib_dst_dir)

    if args.with_fuchsia:
        # Fuchsia links against libclang_rt.builtins-<arch>.a instead of libgcc.a.
        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')
            # Build clang_rt runtime for Fuchsia in a separate build tree.
            build_dir = os.path.join(LLVM_BUILD_DIR, 'fuchsia-' + target_arch)
            if not os.path.exists(build_dir):
                os.mkdir(os.path.join(build_dir))
            os.chdir(build_dir)
            target_spec = target_arch + '-fuchsia'
            # TODO(thakis): Might have to pass -B here once sysroot contains
            # binaries (e.g. gas for arm64?)
            fuchsia_args = base_cmake_args + [
                '-DLLVM_ENABLE_THREADS=OFF',
                '-DCMAKE_C_COMPILER=' +
                os.path.join(LLVM_BUILD_DIR, 'bin/clang'),
                '-DCMAKE_CXX_COMPILER=' +
                os.path.join(LLVM_BUILD_DIR, 'bin/clang++'),
                '-DCMAKE_LINKER=' + os.path.join(LLVM_BUILD_DIR, 'bin/clang'),
                '-DCMAKE_AR=' + os.path.join(LLVM_BUILD_DIR, 'bin/llvm-ar'),
                '-DLLVM_CONFIG_PATH=' +
                os.path.join(LLVM_BUILD_DIR, 'bin/llvm-config'),
                '-DCMAKE_SYSTEM_NAME=Fuchsia',
                '-DCMAKE_C_COMPILER_TARGET=%s-fuchsia' % target_arch,
                '-DCMAKE_ASM_COMPILER_TARGET=%s-fuchsia' % target_arch,
                '-DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON',
                '-DCMAKE_SYSROOT=%s' % toolchain_dir,
                # TODO(thakis|scottmg): Use PER_TARGET_RUNTIME_DIR for all platforms.
                # https://crbug.com/882485.
                '-DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=ON',

                # These are necessary because otherwise CMake tries to build an
                # executable to test to see if the compiler is working, but in doing so,
                # it links against the builtins.a that we're about to build.
                '-DCMAKE_C_COMPILER_WORKS=ON',
                '-DCMAKE_ASM_COMPILER_WORKS=ON',
            ]
            RmCmakeCache('.')
            RunCommand(['cmake'] + fuchsia_args +
                       [os.path.join(COMPILER_RT_DIR, 'lib', 'builtins')])
            builtins_a = 'libclang_rt.builtins.a'
            RunCommand(['ninja', builtins_a])

            # And copy it into the main build tree.
            fuchsia_lib_dst_dir = os.path.join(LLVM_BUILD_DIR, 'lib', 'clang',
                                               RELEASE_VERSION, target_spec,
                                               'lib')
            if not os.path.exists(fuchsia_lib_dst_dir):
                os.makedirs(fuchsia_lib_dst_dir)
            CopyFile(os.path.join(build_dir, target_spec, 'lib', builtins_a),
                     fuchsia_lib_dst_dir)

    # Run tests.
    if args.run_tests or args.llvm_force_head_revision:
        os.chdir(LLVM_BUILD_DIR)
        RunCommand(['ninja', 'cr-check-all'], msvc_arch='x64')
    if args.run_tests:
        if sys.platform == 'win32':
            CopyDiaDllTo(os.path.join(LLVM_BUILD_DIR, 'bin'))
        os.chdir(LLVM_BUILD_DIR)
        RunCommand(['ninja', 'check-all'], msvc_arch='x64')

    WriteStampFile(PACKAGE_VERSION, STAMP_FILE)
    WriteStampFile(PACKAGE_VERSION, FORCE_HEAD_REVISION_FILE)

    print('Clang build was successful.')
    return 0