Esempio n. 1
0
def CopyLibstdcpp(args, build_dir):
    if not args.gcc_toolchain:
        return
    # Find libstdc++.so.6
    libstdcpp = subprocess.check_output([
        os.path.join(args.gcc_toolchain, 'bin', 'g++'),
        '-print-file-name=libstdc++.so.6'
    ]).rstrip()

    # Copy libstdc++.so.6 into the build dir so that the built binaries can find
    # it. Binaries get their rpath set to $origin/../lib/. For clang, lld,
    # etc. that live in the bin/ directory, this means they expect to find the .so
    # in their neighbouring lib/ dir. For other tools however, this doesn't work
    # since those exeuctables are spread out into different directories.
    # TODO(hans): Unit tests don't get rpath set at all, unittests/ copying
    # below doesn't help at the moment.
    for d in [
            'lib', 'test/tools/llvm-isel-fuzzer/lib',
            'test/tools/llvm-opt-fuzzer/lib', 'unittests/CodeGen/lib',
            'unittests/DebugInfo/lib', 'unittests/ExecutionEngine/lib',
            'unittests/Support/lib', 'unittests/Target/lib',
            'unittests/Transforms/lib', 'unittests/lib', 'unittests/tools/lib',
            'unittests/tools/llvm-exegesis/lib'
    ]:
        EnsureDirExists(os.path.join(build_dir, d))
        CopyFile(libstdcpp, os.path.join(build_dir, d))
Esempio n. 2
0
def CopyDirectoryContents(src, dst):
    """Copy the files from directory src to dst."""
    dst = os.path.realpath(dst)  # realpath() in case dst ends in /..
    EnsureDirExists(dst)
    for f in os.listdir(src):
        CopyFile(os.path.join(src, f), dst)
Esempio n. 3
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
Esempio n. 4
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