[llvmprojs] Rewrite llvm-projs in python
This is the first step in moving all our scripts to python. For now we keep the
current content of the repo, but we'll start adding a new directory hierarchy
for the python stuff:
* modules: should contain most of the python code, organized as a package that
can be imported by the scripts
* scripts: the scripts themselves, which should be parsing the command line
and calling stuff from 'modules' to do the actual work; can be
broken down into the same categories we had before (helpers, bisect
etc), or we could just have one big pile
* tests: should contain unittests (for the stuff in modules) and command line
interface tests (for the scripts)
The code is heavily using functionality from the tcwg-release-tools repo (which
probably needs to be renamed / reorganized), so you should have that in your
PYTHONPATH when trying to run any of the scripts. To run the tests, just invoke
check.sh.
One of the important changes is that we'll be using python's argparse module to
parse command line flags, which means we'll have to stick to a more traditional
interface for the scripts. In particular, we can't have short options like "+c"
anymore. This isn't much of a problem, because we will keep the bash scripts as
they are and just make them invoke a tool written in python (scripts/llvm.py) to
do the work. The tool will have subcommands for any functionality that we want,
for instance the new interface for adding/removing subprojects is:
llvm.py projects [-a subproject subproject ... subproject]
[-r subproject ... subproject]
The -a and -r options (followed by any number of subprojects) can be used to
selectively enable/disable things. You have to pass the full name of the
subproject (e.g. llvmprojs.py -a clang lld -r libcxx) for it to work. This is
invoked by the llvm-projs bash script, which has the old, more convenient
interface.
For now the bash scripts will live in the same directories as they used to, but
after the transition is complete we will want to move them to the scripts
directory.
Note that we're also eliding any dashes in the names of the scripts, in keeping
with Python best practices for module names (i.e. using valid Python identifiers
as names).
Change-Id: I9ec08632dbb17464673240d6f6881a90f45d5371
diff --git a/scripts/llvm.py b/scripts/llvm.py
new file mode 100644
index 0000000..762f9e7
--- /dev/null
+++ b/scripts/llvm.py
@@ -0,0 +1,106 @@
+"""This is the main tool for handling llvm builds, bisects etc."""
+
+import os
+from sys import exit
+
+from modules.llvm import LLVMSubproject, LLVMSourceConfig
+from linaropy.git.clone import Clone
+from linaropy.proj import Proj
+
+from argparse import Action, ArgumentParser, RawTextHelpFormatter
+
+
+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)
+
+# Figure out the path to the LLVM repos
+if "LLVM_ROOT" not in os.environ:
+ die("Please, define $LLVM_ROOT to point to the root\n"
+ "path where the worktree setup should be performed")
+llvm_repos_root = os.path.join(os.environ["LLVM_ROOT"], "repos")
+
+# Figure out the path to the current LLVM tree
+if "LLVM_SRC" not in os.environ:
+ die("Please, define $LLVM_SRC to point to the current LLVM\n"
+ "worktree directory, or run llvm-env to set it for you")
+llvm_worktree_root = os.environ["LLVM_SRC"]
+
+
+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()
+ 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)
+
+##########################################################################
+# Command line parsing #
+##########################################################################
+
+# If we decide we want shorthands for the subprojects, we can append to this
+# list
+valid_subprojects = LLVMSubproject.get_all_subprojects().keys()
+
+options = ArgumentParser(formatter_class=RawTextHelpFormatter)
+subcommands = options.add_subparsers()
+
+# Subcommand for adding / removing subprojects
+projs = subcommands.add_parser("projects", help="Add/remove LLVM subprojects")
+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="Link 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="Unlink given subprojects.")
+
+args = options.parse_args()
+args.run_command(args)