Add llvm.py configure

Add a subcommand that runs CMake in a given build directory, with a
custom generator and custom CMake definitions. Also update llvm-build to
use it instead of calling CMake directly, and add calls to it in
llvm-projs as well to make sure we update the build directories whenever
we enable or disable a project.

One known issue with the current code is that the output of the CMake
command is not printed out live, but rather after the command has
finished execution. This is going to be more important for llvm.py build
than for llvm.py configure, so we can fix it in a future commit.

Change-Id: I263b2d47c2083a1778608253bbd437149375c539
diff --git a/modules/llvm.py b/modules/llvm.py
index 8d549fd..2625038 100644
--- a/modules/llvm.py
+++ b/modules/llvm.py
@@ -1,6 +1,8 @@
 import os
 import re
 
+from functools import partial
+
 from linaropy.git.worktree import Worktree
 
 
@@ -98,6 +100,10 @@
         self.llvmSourceTree = Worktree(proj, sourcePath)
         self.subprojs = subprojs
 
+    def get_path(self):
+        """Get the path corresponding to this source config."""
+        return self.llvmSourceTree.repodir
+
     def get_enabled_subprojects(self):
         """Get a list of the subprojects enabled in this configuration."""
         enabled = []
@@ -263,6 +269,63 @@
         worktree.clean(False)
 
 
+class LLVMBuildConfig(object):
+    """Class for managing an LLVM build directory.
+
+    It should know how to configure a build directory (with CMake) and how to
+    run a build command. The directory must already exist, but it may be empty.
+    """
+
+    def __init__(self, sourceConfig, buildDir=None):
+        """Create an LLVMBuildConfig."""
+        self.sourceConfig = sourceConfig
+        self.buildDir = buildDir
+
+    def cmake(self, commandConsumer, cmakeFlags, generator):
+        """
+        Generate the CMake command needed for configuring the build directory
+        with the given flags and generator, and pass it to the 'commandConsumer'.
+
+        The command will always explicitly enable or disable the build of
+        specific subprojects to mirror the source config. This is important
+        because projects can always be added or removed from the source config,
+        and CMake doesn't by default pick up the new situation (so we might end
+        up trying to build subprojects that were removed, or not build
+        subprojects that were added).
+
+        The 'commandConsumer' should have a 'consume' method taking two
+        parameters: the command to be consumed (in the form of a list) and the
+        directory where the command should be run. Any exceptions that may be
+        raised by that method should be handled by the calling code.
+        """
+        cmakeSubprojFlags = self.__get_subproj_flags()
+        command = ["cmake", "-G", generator] + cmakeSubprojFlags + \
+            cmakeFlags + [self.sourceConfig.get_path()]
+        commandConsumer.consume(command, self.buildDir)
+
+    def __get_subproj_flags(self):
+        """
+        Get the CMake flags needed to explicitly enable or disable the build of
+        each specific subproject, depending on whether it is enabled or disabled
+        in the source config.
+        """
+        def append_cmake_var(cmakeVars, subproj, enabled):
+            if subproj.cmake_var is None:
+                return
+
+            if enabled:
+                status = "ON"
+            else:
+                status = "OFF"
+            cmakeVars.append("-D{}={}".format(subproj.cmake_var, status))
+
+        cmakeSubprojFlags = []
+        self.sourceConfig.for_each_subproj(partial(append_cmake_var,
+                                                   cmakeSubprojFlags))
+
+        return cmakeSubprojFlags
+
+
 # FIXME: repo.pushToBranch doesn't work, because it doesn't handle remote
 # branches properly. Furthermore, there's no support for getting the remote URL,
 # so we need to resort to raw git commands. We may also consider moving the
diff --git a/modules/utils.py b/modules/utils.py
new file mode 100644
index 0000000..9e6f29a
--- /dev/null
+++ b/modules/utils.py
@@ -0,0 +1,29 @@
+from subprocess import CalledProcessError
+from subprocess import check_output
+from subprocess import STDOUT
+
+
+class CommandPrinter(object):
+    """Command consumer that just prints the commands that it receives."""
+
+    def consume(self, command, directory):
+        print("{}$ {}".format(directory, ' '.join(command)))
+
+
+class CommandRunner(object):
+    """Command consumer that runs the commands that it receives."""
+
+    def consume(self, command, directory):
+        """
+        Run the given command in the given directory and print the stdout and
+        stderr. If an exception is thrown while running the command, it will be
+        rethrown as a RuntimeError.
+        """
+        # FIXME: This prints the results after the command has finished running.
+        # For long-running commands (e.g. an LLVM build) we'll want live
+        # output.
+        try:
+            print(str(check_output(command, stderr=STDOUT, cwd=directory), 'utf-8'))
+        except CalledProcessError as exc:
+            raise RuntimeError(
+                "Error while running command\n{}".format(str(exc.output, 'utf-8'))) from exc