"""This is the main tool for handling llvm builds, bisects etc.""" import os from sys import argv from sys import exit from modules.llvm import LLVMBuildConfig from modules.llvm import LLVMSubproject from modules.llvm import LLVMSourceConfig from modules.utils import CommandPrinter from modules.utils import CommandRunner from modules.utils import get_remote_branch from modules.utils import push_branch from linaropy.cd import cd from linaropy.git.clone import Clone from linaropy.proj import Proj from argparse import Action, ArgumentParser, RawTextHelpFormatter from functools import partial def die(message, config_to_dump=None): """Print an error message and exit.""" print(message) if config_to_dump is not None: dump_config(config_to_dump) exit(1) def get_worktree_root(env): """Get the path to the LLVM worktree corresponding to env.""" return os.path.join(env, "llvm") def dump_config(config): """Dump the list of projects that are enabled in the given config.""" print("Projects linked:") enabled = config.get_enabled_subprojects() if not enabled: print("none") else: for subproj in sorted(enabled): print(" + {}".format(subproj)) def projects(args): """Add/remove subprojects based on the values in args.""" proj = Proj() llvm_worktree_root = get_worktree_root(args.env) llvm_repos_root = args.repos config = LLVMSourceConfig(proj, llvm_worktree_root) if not args.add and not args.remove: # Nothing to change, just print the current configuration dump_config(config) exit(0) to_add = {} if args.add: for subproj in args.add: repo = Clone(proj, os.path.join(llvm_repos_root, subproj)) to_add[subproj] = repo try: config.update(to_add, args.remove) except (EnvironmentError, ValueError) as exc: die("Failed to update subprojects because:\n{}".format(str(exc))) dump_config(config) def push_current_branch(args): """Push current branch to origin.""" proj = Proj() llvm_worktree_root = get_worktree_root(args.env) config = LLVMSourceConfig(proj, llvm_worktree_root) llvm_worktree = Clone(proj, llvm_worktree_root) local_branch = llvm_worktree.getbranch() try: remote_branch = get_remote_branch(llvm_worktree, local_branch) config.for_each_enabled(partial(push_branch, proj, local_branch, remote_branch)) print("Pushed to {}".format(remote_branch)) except (EnvironmentError, RuntimeError) as exc: die("Failed to push branch because: " + str(exc) + str(exc.__cause__)) def configure_build(args): """Configure a given build directory.""" proj = Proj() llvm_worktree_root = get_worktree_root(args.env) sourceConfig = LLVMSourceConfig(proj, llvm_worktree_root) buildConfig = LLVMBuildConfig(sourceConfig, args.build) if args.defs: args.defs = ["-D{}".format(v) for v in args.defs] if args.dry: consumer = CommandPrinter() else: if not os.path.exists(args.build): os.makedirs(args.build) consumer = CommandRunner() try: buildConfig.cmake(consumer, args.defs, args.generator) except RuntimeError as exc: die("Failed to configure {} because:\n{}".format(args.build, str(exc))) ########################################################################## # Command line parsing # ########################################################################## # If we decide we want shorthands for the subprojects, we can append to this # list valid_subprojects = list(LLVMSubproject.get_all_subprojects().keys()) options = ArgumentParser(formatter_class=RawTextHelpFormatter) options.add_argument( '--env', required=True, help="Path to the environment to update.") subcommands = options.add_subparsers(dest="subcommand") # Subcommand for adding / removing subprojects projs = subcommands.add_parser( "projects", help="Add/remove LLVM subprojects.\n" "Adding a subproject will create a worktree for it " "somewhere in the LLVM source tree, on the same git " "branch as LLVM itself.\n" "Removing a subproject will remove the worktree, but " "not the underlying git branch.") projs.set_defaults(run_command=projects) # TODO: Overwriting previous values is not necessarily what users expect (so for # instance --add S1 S2 --remove S3 --add S4 would lead to adding only S4). We # can do better by using action='append', which would create a list (of lists? # or of lists and scalars?) that we can flatten to obtain all the values passed # by the user. projs.add_argument( '-a', '--add', nargs='+', choices=valid_subprojects, metavar='subproject', help="Enable given subprojects. Valid values are:\n\t{}\n".format( "\n\t".join(valid_subprojects))) projs.add_argument( '-r', '--remove', nargs='+', choices=valid_subprojects, metavar='subproject', help="Disable given subprojects.") projs.add_argument( '--repos', help="Path to the directory containing the repositories for all LLVM " "subprojects.") # Subcommand for pushing the current branch to origin push = subcommands.add_parser( "push", help="Push current branch to origin linaro-local//, " "for all enabled subprojects.") push.set_defaults(run_command=push_current_branch) # Subcommand for configuring a build directory configure = subcommands.add_parser( 'configure', help="Run CMake in the given build directory.") configure.add_argument( '--build-dir', dest='build', required=True, help="Path to the build directory. It will be created if it does not exist") configure.add_argument( '--cmake-generator', dest='generator', default='Ninja', help="CMake generator to use (default is Ninja).") configure.add_argument( '--cmake-def', dest='defs', metavar='VAR=VALUE', default=[], action='append', # We add the -D in front of the variable ourselves because the argument # parsing gets confused otherwise (and quoting doesn't help). help="Additional CMake definitions, e.g. CMAKE_BUILD_TYPE=Release." "May be passed several times. The -D is added automatically.") configure.add_argument( '-n', '--dry-run', dest='dry', action='store_true', default=False, help="Print the CMake command instead of executing it.") configure.set_defaults(run_command=configure_build) args = options.parse_args() if args.subcommand == "projects" and args.add and not args.repos: projs.error( "When adding a subproject you must also pass the --repos argument") args.run_command(args)