diff options
Diffstat (limited to 'lldb/scripts/Python/remote-build.py')
| -rwxr-xr-x | lldb/scripts/Python/remote-build.py | 300 | 
1 files changed, 300 insertions, 0 deletions
diff --git a/lldb/scripts/Python/remote-build.py b/lldb/scripts/Python/remote-build.py new file mode 100755 index 0000000..72986a0 --- /dev/null +++ b/lldb/scripts/Python/remote-build.py @@ -0,0 +1,300 @@ +#!/usr/bin/python + +from __future__ import print_function + +import argparse +import getpass +import os +import os.path +import re +import select +import sys +import subprocess + +_COMMON_SYNC_OPTS = "-avzh --delete" +_COMMON_EXCLUDE_OPTS = "--exclude=DerivedData --exclude=.svn --exclude=.git --exclude=llvm-build/Release+Asserts" + +def normalize_configuration(config_text): +    if not config_text: +        return "debug" + +    config_lower = config_text.lower() +    if config_lower in ["debug", "release"]: +        return config_lower +    else: +        raise Exception("unknown configuration specified: %s" % config_text) + +def parse_args(): +    DEFAULT_REMOTE_ROOT_DIR = "/mnt/ssd/work/macosx.sync" +    DEFAULT_REMOTE_HOSTNAME = "tfiala2.mtv.corp.google.com" +    OPTIONS_FILENAME = ".remote-build.conf" +    DEFAULT_SSH_PORT = "22" + +    parser = argparse.ArgumentParser(fromfile_prefix_chars='@') + +    parser.add_argument( +        "--configuration", "-c", +        help="specify configuration (Debug, Release)", +        default=normalize_configuration(os.environ.get('CONFIGURATION', 'Debug'))) +    parser.add_argument( +        "--debug", "-d", +        action="store_true", +        help="help debug the remote-build script by adding extra logging") +    parser.add_argument( +        "--local-lldb-dir", "-l", metavar="DIR", +        help="specify local lldb directory (Xcode layout assumed for llvm/clang)", +        default=os.getcwd()) +    parser.add_argument( +        "--port", "-p", +        help="specify the port ssh should use to connect to the remote side", +        default=DEFAULT_SSH_PORT) +    parser.add_argument( +        "--remote-address", "-r", metavar="REMOTE-ADDR", +        help="specify the dns name or ip address of the remote linux system", +        default=DEFAULT_REMOTE_HOSTNAME) +    parser.add_argument( +        "--remote-dir", metavar="DIR", +        help="specify the root of the linux source/build dir", +        default=DEFAULT_REMOTE_ROOT_DIR) +    parser.add_argument( +        "--user", "-u", help="specify the user name for the remote system", +        default=getpass.getuser()) +    parser.add_argument( +        "--xcode-action", "-x", help="$(ACTION) from Xcode", nargs='?', default=None) + +    command_line_args = sys.argv[1:] +    if os.path.exists(OPTIONS_FILENAME): +        # Prepend the file so that command line args override the file contents. +        command_line_args.insert(0, "@%s" % OPTIONS_FILENAME) + +    return parser.parse_args(command_line_args) + + +def maybe_create_remote_root_dir(args): +    commandline = [ +        "ssh", +        "-p", args.port, +        "%s@%s" % (args.user, args.remote_address), +        "mkdir", +        "-p", +        args.remote_dir] +    print("create remote root dir command:\n{}".format(commandline)) +    return subprocess.call(commandline) + + +def init_with_args(args): +    # Expand any user directory specs in local-side source dir (on MacOSX). +    args.local_lldb_dir = os.path.expanduser(args.local_lldb_dir) + +    # Append the configuration type to the remote build dir. +    args.configuration = normalize_configuration(args.configuration) +    args.remote_build_dir = os.path.join( +        args.remote_dir, +        "build-%s" % args.configuration) + +    # We assume the local lldb directory is really named 'lldb'. +    # This is because on the remote end, the local lldb root dir +    # is copied over underneath llvm/tools and will be named there +    # whatever it is named locally.  The remote build will assume +    # is is called lldb. +    if os.path.basename(args.local_lldb_dir) != 'lldb': +        raise Exception( +            "local lldb root needs to be called 'lldb' but was {} instead" +            .format(os.path.basename(args.local_lldb_dir))) + +    args.lldb_dir_relative_regex = re.compile("%s/llvm/tools/lldb/" % args.remote_dir) +    args.llvm_dir_relative_regex = re.compile("%s/" % args.remote_dir) + +    print("Xcode action:", args.xcode_action) + +    # Ensure the remote directory exists. +    result = maybe_create_remote_root_dir(args) +    if result == 0: +        print("using remote root dir: %s" % args.remote_dir) +    else: +        print("remote root dir doesn't exist and could not be created, " +              + "error code:", result) +        return False + +    return True + +def sync_llvm(args): +    commandline = ["rsync"] +    commandline.extend(_COMMON_SYNC_OPTS.split()) +    commandline.extend(_COMMON_EXCLUDE_OPTS.split()) +    commandline.append("--exclude=/llvm/tools/lldb") +    commandline.extend(["-e", "ssh -p {}".format(args.port)]) +    commandline.extend([ +        "%s/llvm" % args.local_lldb_dir, +        "%s@%s:%s" % (args.user, args.remote_address, args.remote_dir)]) +    if args.debug: +        print("going to execute llvm sync: {}".format(commandline)) +    return subprocess.call(commandline) + + +def sync_lldb(args): +    commandline = ["rsync"] +    commandline.extend(_COMMON_SYNC_OPTS.split()) +    commandline.extend(_COMMON_EXCLUDE_OPTS.split()) +    commandline.append("--exclude=/lldb/llvm") +    commandline.extend(["-e", "ssh -p {}".format(args.port)]) +    commandline.extend([ +        args.local_lldb_dir, +        "%s@%s:%s/llvm/tools" % (args.user, args.remote_address, args.remote_dir)]) +    if args.debug: +        print("going to execute lldb sync: {}".format(commandline)) +    return subprocess.call(commandline) + + +def build_cmake_command(args): +    # args.remote_build_dir +    # args.configuration in ('release', 'debug') + +    if args.configuration == 'debug-optimized': +        build_type_name = "RelWithDebInfo" +    elif args.configuration == 'release': +        build_type_name = "Release" +    else: +        build_type_name = "Debug" + +    ld_flags = "\"-lstdc++ -lm\"" + +    install_dir = os.path.join( +        args.remote_build_dir, "..", "install-{}".format(args.configuration)) + +    command_line = [ +        "cmake", +        "-GNinja", +        "-DCMAKE_CXX_COMPILER=clang", +        "-DCMAKE_C_COMPILER=clang", +        # "-DCMAKE_CXX_FLAGS=%s" % cxx_flags, +        "-DCMAKE_SHARED_LINKER_FLAGS=%s" % ld_flags, +        "-DCMAKE_EXE_LINKER_FLAGS=%s" % ld_flags, +        "-DCMAKE_INSTALL_PREFIX:PATH=%s" % install_dir, +        "-DCMAKE_BUILD_TYPE=%s" % build_type_name, +        "-Wno-dev", +        os.path.join("..", "llvm") +        ] + +    return command_line + + +def maybe_configure(args): +    commandline = [ +        "ssh", +        "-p", args.port, +        "%s@%s" % (args.user, args.remote_address), +        "cd", args.remote_dir, "&&", +        "mkdir", "-p", args.remote_build_dir, "&&", +        "cd", args.remote_build_dir, "&&" +        ] +    commandline.extend(build_cmake_command(args)) + +    if args.debug: +        print("configure command: {}".format(commandline)) + +    return subprocess.call(commandline) + + +def filter_build_line(args, line): +    lldb_relative_line = args.lldb_dir_relative_regex.sub('', line) +    if len(lldb_relative_line) != len(line): +        # We substituted - return the modified line +        return lldb_relative_line + +    # No match on lldb path (longer on linux than llvm path).  Try +    # the llvm path match. +    return args.llvm_dir_relative_regex.sub('', line) + + +def run_remote_build_command(args, build_command_list): +    commandline = [ +        "ssh", +        "-p", args.port, +        "%s@%s" % (args.user, args.remote_address), +        "cd", args.remote_build_dir, "&&"] +    commandline.extend(build_command_list) + +    if args.debug: +        print("running remote build command: {}".format(commandline)) + +    proc = subprocess.Popen( +        commandline, +        stdout=subprocess.PIPE, +        stderr=subprocess.PIPE) + +    # Filter stdout/stderr output for file path mapping. +    # We do this to enable Xcode to see filenames relative to the +    # MacOSX-side directory structure. +    while True: +        reads = [proc.stdout.fileno(), proc.stderr.fileno()] +        select_result = select.select(reads, [], []) + +        for fd in select_result[0]: +            if fd == proc.stdout.fileno(): +                line = proc.stdout.readline() +                display_line = filter_build_line(args, line.rstrip()) +                if display_line and len(display_line) > 0: +                    print(display_line) +            elif fd == proc.stderr.fileno(): +                line = proc.stderr.readline() +                display_line = filter_build_line(args, line.rstrip()) +                if display_line and len(display_line) > 0: +                    print(display_line, file=sys.stderr) + +        proc_retval = proc.poll() +        if proc_retval != None: +            # Process stopped.  Drain output before finishing up. + +            # Drain stdout. +            while True: +                line = proc.stdout.readline() +                if line: +                    display_line = filter_build_line(args, line.rstrip()) +                    if display_line and len(display_line) > 0: +                        print(display_line) +                else: +                    break + +            # Drain stderr. +            while True: +                line = proc.stderr.readline() +                if line: +                    display_line = filter_build_line(args, line.rstrip()) +                    if display_line and len(display_line) > 0: +                        print(display_line, file=sys.stderr) +                else: +                    break + +            return proc_retval + + +def build(args): +    return run_remote_build_command(args, ["time", "ninja"]) + + +def clean(args): +    return run_remote_build_command(args, ["ninja", "clean"]) + + +if __name__ == "__main__": +    # Handle arg parsing. +    args = parse_args() + +    # Initialize the system. +    if not init_with_args(args): +        exit(1) + +    # Sync over llvm and clang source. +    sync_llvm(args) + +    # Sync over lldb source. +    sync_lldb(args) + +    # Configure the remote build if it's not already. +    maybe_configure(args) + +    if args.xcode_action == 'clean': +        exit(clean(args)) +    else: +        exit(build(args))  | 
