brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.7 KiB · 8af7d11 Raw
133 lines · plain
1#!/usr/bin/env python32 3import argparse4import os5import subprocess6import sys7import tempfile8 9# Unofficial list of directories required to build libc++. This is a best guess that should work10# when checking out the monorepo at most commits, but it's technically not guaranteed to work11# (especially for much older commits).12LIBCXX_REQUIRED_DIRECTORIES = [13    'libcxx',14    'libcxxabi',15    'llvm/cmake',16    'llvm/utils/llvm-lit',17    'llvm/utils/lit',18    'runtimes',19    'cmake',20    'third-party/benchmark',21    'libc'22]23 24def directory_path(string):25    if os.path.isdir(string):26        return string27    else:28        raise NotADirectoryError(string)29 30def resolve_commit(git_repo, commit):31    """32    Resolve the full commit SHA from any tree-ish.33    """34    return subprocess.check_output(['git', '-C', git_repo, 'rev-parse', commit], text=True).strip()35 36def checkout_subdirectories(git_repo, commit, paths, destination):37    """38    Produce a copy of the specified Git-tracked files/directories at the given commit.39    The resulting files and directories at placed at the given location.40    """41    with tempfile.TemporaryDirectory() as tmp:42        tmpfile = os.path.join(tmp, 'archive.tar.gz')43        git_archive = ['git', '-C', git_repo, 'archive', '--format', 'tar.gz', '--output', tmpfile, commit, '--'] + list(paths)44        subprocess.check_call(git_archive)45        os.makedirs(destination, exist_ok=True)46        subprocess.check_call(['tar', '-x', '-z', '-f', tmpfile, '-C', destination])47 48def build_libcxx(src_dir, build_dir, install_dir, cmake_options):49    """50    Build and install libc++ using the provided source, build and installation directories.51    """52    configure = ['cmake', '-S', os.path.join(src_dir, 'runtimes'), '-B', build_dir, '-G', 'Ninja']53    configure += ['-D', 'LLVM_ENABLE_RUNTIMES=libcxx;libcxxabi']54    configure += ['-D', f'CMAKE_INSTALL_PREFIX={install_dir}']55    configure += ['-D', 'LIBCXXABI_USE_LLVM_UNWINDER=OFF']56    configure += list(cmake_options)57    subprocess.check_call(configure)58 59    build = ['cmake', '--build', build_dir, '--target', 'install']60    subprocess.check_call(build)61 62def exists_in_commit(git_repo, commit, path):63    """64    Return whether the given path (file or directory) existed at the given commit.65    """66    cmd = ['git', '-C', git_repo, 'show', f'{commit}:{path}']67    result = subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)68    return result == 069 70def main(argv):71    parser = argparse.ArgumentParser(72        prog='build-at-commit',73        description='Attempt to build libc++ at the specified commit. '74                    'This script checks out libc++ at the given commit and does a best effort attempt '75                    'to build it and install it to the specified location. This can be useful when '76                    'performing bisections or historical analyses of libc++ behavior, performance, etc. '77                    'This may not work for some commits, for example commits where the library is broken '78                    'or much older commits when the build process was different from today\'s build process.')79    parser.add_argument('--commit', type=str, required=True,80        help='Commit to checkout and build.')81    parser.add_argument('--install-dir', type=str, required=True,82        help='Path to install the library at. This is equivalent to the `CMAKE_INSTALL_PREFIX` '83             'used when building.')84    parser.add_argument('cmake_options', nargs=argparse.REMAINDER,85        help='Optional arguments passed to CMake when configuring the build. Should be provided last and '86             'separated from other arguments with a `--`.')87    parser.add_argument('--git-repo', type=directory_path, default=os.getcwd(),88        help='Optional path to the Git repository to use. By default, the current working directory is used.')89    parser.add_argument('--tmp-src-dir', type=str, required=False,90        help='Optional path to use for the ephemeral source checkout used to perform the build. '91             'By default, a temporary directory is used and it is cleaned up after the build. '92             'If a custom directory is specified, it is not cleaned up automatically.')93    parser.add_argument('--tmp-build-dir', type=str, required=False,94        help='Optional path to use for the ephemeral build directory used during the build.'95             'By default, a temporary directory is used and it is cleaned up after the build. '96             'If a custom directory is specified, it is not cleaned up automatically.')97    args = parser.parse_args(argv)98 99    # Gather CMake options100    cmake_options = []101    if args.cmake_options is not None:102        if args.cmake_options[0] != '--':103            raise ArgumentError('For clarity, CMake options must be separated from other options by --')104        cmake_options = args.cmake_options[1:]105 106    # Figure out which directories to check out at the given commit. We avoid checking107    # out the whole monorepo as an optimization.108    sha = resolve_commit(args.git_repo, args.commit)109    checkout_dirs = [d for d in LIBCXX_REQUIRED_DIRECTORIES if exists_in_commit(args.git_repo, sha, d)]110 111    tempdirs = []112    if args.tmp_src_dir is not None:113        src_dir = args.tmp_src_dir114    else:115        tempdirs.append(tempfile.TemporaryDirectory())116        src_dir = tempdirs[-1].name117 118    if args.tmp_build_dir is not None:119        build_dir = args.tmp_build_dir120    else:121        tempdirs.append(tempfile.TemporaryDirectory())122        build_dir = tempdirs[-1].name123 124    try:125        checkout_subdirectories(args.git_repo, sha, checkout_dirs, src_dir)126        build_libcxx(src_dir, build_dir, args.install_dir, cmake_options)127    finally:128        for d in tempdirs:129            d.cleanup()130 131if __name__ == '__main__':132    main(sys.argv[1:])133