Example #1
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
Example #2
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