Make it easier to run test variants.

This adds all the permutations for tests as make targets of the form
test-libcxx-(host|target)-(clang|gcc)-(32|64).

This also changes the host tests to use the Android build system (like
I had done with https://android-review.googlesource.com/#/c/111924/).
This probably should have been a separate patch, but I got carried
away (and wanted to make sure this new approach would work for both),
and now they're non-trivial to split.

Change-Id: Ie99caf6c3ff21c833408f99d37299d966ee7bc94
diff --git a/Android.mk b/Android.mk
index 0a10de1..9a1d4d0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -126,15 +126,56 @@
 include $(BUILD_HOST_SHARED_LIBRARY)
 
 LIT := $(ANDROID_BUILD_TOP)/external/llvm/utils/lit/lit.py
-test-libcxx-host: libc++
-	LIT=$(LIT) LIT_MODE=host make -f $(ANDROID_BUILD_TOP)/external/libcxx/test.mk
-test-libcxx-target: libc++
-	LIT=$(LIT) LIT_MODE=device make -f $(ANDROID_BUILD_TOP)/external/libcxx/test.mk
+LIBCXX_CONFIGTESTS := $(ANDROID_BUILD_TOP)/external/libcxx/buildcmds/configtests.py
+LIBCXX_TEST_MK := $(ANDROID_BUILD_TOP)/external/libcxx/test.mk
+
+test-libcxx-target: test-libcxx-target-clang
+test-libcxx-host: test-libcxx-host-clang
+
+test-libcxx-target-clang: libc++
+	python $(LIBCXX_CONFIGTESTS) --compiler=clang
+	LIT=$(LIT) LIT_MODE=device make -f $(LIBCXX_TEST_MK)
+test-libcxx-target-gcc: libc++
+	python $(LIBCXX_CONFIGTESTS) --compiler=gcc
+	LIT=$(LIT) LIT_MODE=device make -f $(LIBCXX_TEST_MK)
+test-libcxx-target-clang-32: libc++
+	python $(LIBCXX_CONFIGTESTS) --bitness=32 --compiler=clang
+	LIT=$(LIT) LIT_MODE=device make -f $(LIBCXX_TEST_MK)
+test-libcxx-target-gcc-32: libc++
+	python $(LIBCXX_CONFIGTESTS) --bitness=32 --compiler=gcc
+	LIT=$(LIT) LIT_MODE=device make -f $(LIBCXX_TEST_MK)
+test-libcxx-target-clang-64: libc++
+	python $(LIBCXX_CONFIGTESTS) --bitness=64 --compiler=clang
+	LIT=$(LIT) LIT_MODE=device make -f $(LIBCXX_TEST_MK)
+test-libcxx-target-gcc-64: libc++
+	python $(LIBCXX_CONFIGTESTS) --bitness=64 --compiler=gcc
+	LIT=$(LIT) LIT_MODE=device make -f $(LIBCXX_TEST_MK)
+
+test-libcxx-host-clang: libc++
+	python $(LIBCXX_CONFIGTESTS) --compiler=clang --host
+	LIT=$(LIT) LIT_MODE=host make -f $(LIBCXX_TEST_MK)
+test-libcxx-host-gcc: libc++
+	python $(LIBCXX_CONFIGTESTS) --compiler=gcc --host
+	LIT=$(LIT) LIT_MODE=host make -f $(LIBCXX_TEST_MK)
+test-libcxx-host-clang-32: libc++
+	python $(LIBCXX_CONFIGTESTS) --bitness=32 --compiler=clang --host
+	LIT=$(LIT) LIT_MODE=host make -f $(LIBCXX_TEST_MK)
+test-libcxx-host-gcc-32: libc++
+	python $(LIBCXX_CONFIGTESTS) --bitness=32 --compiler=gcc --host
+	LIT=$(LIT) LIT_MODE=host make -f $(LIBCXX_TEST_MK)
+test-libcxx-host-clang-64: libc++
+	python $(LIBCXX_CONFIGTESTS) --bitness=64 --compiler=clang --host
+	LIT=$(LIT) LIT_MODE=host make -f $(LIBCXX_TEST_MK)
+test-libcxx-host-gcc-64: libc++
+	python $(LIBCXX_CONFIGTESTS) --bitness=64 --compiler=gcc --host
+	LIT=$(LIT) LIT_MODE=host make -f $(LIBCXX_TEST_MK)
 
 # Don't want to just make test-libcxx-(host|target) dependencies of this because
 # the two families can't be run concurrently.
 test-libcxx: libc++
+	python buildcmds/configtests.py --host
 	LIT=$(LIT) LIT_MODE=host make -f $(ANDROID_BUILD_TOP)/external/libcxx/test.mk
+	python buildcmds/configtests.py
 	LIT=$(LIT) LIT_MODE=device make -f $(ANDROID_BUILD_TOP)/external/libcxx/test.mk
 
 endif  # TARGET_BUILD_APPS
diff --git a/buildcmds/.gitignore b/buildcmds/.gitignore
new file mode 100644
index 0000000..a77d525
--- /dev/null
+++ b/buildcmds/.gitignore
@@ -0,0 +1,4 @@
+cxx_under_test
+cxx.cmds
+link.cmds
+testconfig.mk
diff --git a/buildcmds/Android.mk b/buildcmds/Android.mk
index 601c257..7849244 100644
--- a/buildcmds/Android.mk
+++ b/buildcmds/Android.mk
@@ -21,7 +21,6 @@
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := libc++_build_commands_$(ANDROID_DEVICE)
-LOCAL_CXX := $(LOCAL_PATH)/buildcmdscc $(CLANG_CXX)
 LOCAL_SRC_FILES := dummy.cpp
 LOCAL_CXX_STL := libc++
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/../test/support
@@ -37,7 +36,6 @@
 LOCAL_CPPFLAGS += -O0
 
 LOCAL_RTTI_FLAG := -frtti
-LOCAL_CLANG := true
-include $(BUILD_EXECUTABLE)
+include $(LOCAL_PATH)/testconfig.mk
 
 endif
diff --git a/buildcmds/configtests.py b/buildcmds/configtests.py
new file mode 100644
index 0000000..9a8b1e3
--- /dev/null
+++ b/buildcmds/configtests.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+from __future__ import print_function
+import getopt
+import sys
+
+
+def main():
+    try:
+        opts, _ = getopt.getopt(sys.argv[1:], '', [
+            'bitness=', 'compiler=', 'host'])
+    except getopt.GetoptError as err:
+        sys.exit(err)
+
+    bitness = None
+    compiler = 'clang'
+    host = False
+    for opt, val in opts:
+        if opt == '--bitness':
+            bitness = int(val)
+            if bitness not in (32, 64):
+                sys.exit('Invalid bitness: {}'.format(bitness))
+        elif opt == '--compiler':
+            if val not in ('clang', 'gcc'):
+                sys.exit('Unknown compiler: {}'.format(val))
+            compiler = val
+        elif opt == '--host':
+            host = True
+        else:
+            raise NotImplementedError('unhandled option: {}'.format(opt))
+
+    with open('external/libcxx/buildcmds/testconfig.mk', 'w') as test_config:
+        if compiler == 'clang':
+            print('LOCAL_CLANG := true', file=test_config)
+        elif compiler == 'gcc':
+            print('LOCAL_CLANG := false', file=test_config)
+
+        if bitness == 32:
+            print('LOCAL_MULTILIB := 32', file=test_config)
+        elif bitness == 64:
+            print('LOCAL_MULTILIB := 64', file=test_config)
+
+        if compiler == 'clang':
+            print('LOCAL_CXX := $(LOCAL_PATH)/buildcmdscc $(CLANG_CXX)',
+                  file=test_config)
+        else:
+            if host:
+                prefix = 'HOST_'
+            else:
+                prefix = 'TARGET_'
+            print('LOCAL_CXX := $(LOCAL_PATH)/buildcmdscc '
+                  '$($(LOCAL_2ND_ARCH_VAR_PREFIX){}CXX)'.format(prefix),
+                  file=test_config)
+
+        if host:
+            print('include $(BUILD_HOST_EXECUTABLE)', file=test_config)
+        else:
+            print('include $(BUILD_EXECUTABLE)', file=test_config)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/test.mk b/test.mk
index 31e8ed2..0837d11 100644
--- a/test.mk
+++ b/test.mk
@@ -1,6 +1,4 @@
 .NOTPARALLEL:
 default:
-	cp $(ANDROID_BUILD_TOP)/external/libcxx/test/lit.$(LIT_MODE).cfg \
-	   $(ANDROID_BUILD_TOP)/external/libcxx/test/lit.site.cfg
-	-python $(LIT) -sv $(ANDROID_BUILD_TOP)/external/libcxx/test
-	rm $(ANDROID_BUILD_TOP)/external/libcxx/test/lit.site.cfg
+	-python $(LIT) -sv --param android_mode=$(LIT_MODE) \
+        $(ANDROID_BUILD_TOP)/external/libcxx/test
diff --git a/test/lit.cfg b/test/lit.cfg
index 33f1818..1fc7cee 100644
--- a/test/lit.cfg
+++ b/test/lit.cfg
@@ -204,6 +204,52 @@
         return lit.Test.PASS, ""
 
 
+class AndroidHostLibcxxTestFormat(LibcxxTestFormat):
+    def __init__(self, cxx_under_test, libcxx_src_root, libcxx_obj_root,
+                 cxx_template, link_template, timeout):
+        self.cxx_under_test = cxx_under_test
+        self.libcxx_src_root = libcxx_src_root
+        self.libcxx_obj_root = libcxx_obj_root
+        self.cxx_template = cxx_template
+        self.link_template = link_template
+        self.timeout = timeout
+        self.use_verify_for_fail = False
+
+    def _compile(self, output_path, source_path, use_verify=False):
+        if use_verify:
+            raise NotImplementedError(
+                'AndroidConfiguration does not support use_verify mode.')
+        cxx_args = self.cxx_template.replace('%OUT%', output_path)
+        cxx_args = cxx_args.replace('%SOURCE%', source_path)
+        cmd = [self.cxx_under_test] + shlex.split(cxx_args)
+        out, err, exit_code = lit.util.executeCommand(cmd)
+        return cmd, out, err, exit_code
+
+    def _link(self, exec_path, object_path):
+        link_args = self.link_template.replace('%OUT%', exec_path)
+        link_args = link_args.replace('%SOURCE%', object_path)
+        cmd = [self.cxx_under_test] + shlex.split(link_args)
+        out, err, exit_code = lit.util.executeCommand(cmd)
+        return cmd, out, err, exit_code
+
+    def _run(self, exec_path, lit_config, in_dir=None):
+        cmd = [exec_path]
+        # We need to use LD_LIBRARY_PATH because the build system's rpath is
+        # relative, which won't work since we're running from /tmp. We can
+        # either scan `cxx_under_test`/`link_template` to determine whether
+        # we're 32-bit or 64-bit, scan testconfig.mk, or just add both
+        # directories and let the linker sort it out. I'm choosing the lazy
+        # option.
+        outdir = os.getenv('ANDROID_HOST_OUT')
+        libpath = os.pathsep.join([
+            os.path.join(outdir, 'lib'),
+            os.path.join(outdir, 'lib64'),
+        ])
+        out, err, rc = lit.util.executeCommand(
+            cmd, cwd=in_dir, env={'LD_LIBRARY_PATH': libpath})
+        return self._make_report(cmd, out, err, rc)
+
+
 class AdbError(RuntimeError):
     def __init__(self, cmd, out, err, exit_code):
         self.cmd = cmd
@@ -212,17 +258,18 @@
         self.exit_code = exit_code
 
 
-class AndroidLibcxxTestFormat(LibcxxTestFormat):
+class AndroidLibcxxTestFormat(AndroidHostLibcxxTestFormat):
     def __init__(self, cxx_under_test, libcxx_src_root, libcxx_obj_root,
                  cxx_template, link_template, device_dir, timeout):
-        self.cxx_under_test = cxx_under_test
-        self.libcxx_src_root = libcxx_src_root
-        self.libcxx_obj_root = libcxx_obj_root
-        self.cxx_template = cxx_template
-        self.link_template = link_template
+        AndroidHostLibcxxTestFormat.__init__(
+            self,
+            cxx_under_test,
+            libcxx_src_root,
+            libcxx_obj_root,
+            cxx_template,
+            link_template,
+            timeout)
         self.device_dir = device_dir
-        self.timeout = timeout
-        self.use_verify_for_fail = False
 
     def _working_directory(self, file_name):
         return os.path.join(self.device_dir, file_name)
@@ -242,23 +289,6 @@
         if exit_code != 0:
             raise AdbError(cmd, out, err, exit_code)
 
-    def _compile(self, output_path, source_path, use_verify=False):
-        if use_verify:
-            raise NotImplementedError(
-                'AndroidConfiguration does not support use_verify mode.')
-        cxx_args = self.cxx_template.replace('%OUT%', output_path)
-        cxx_args = cxx_args.replace('%SOURCE%', source_path)
-        cmd = [self.cxx_under_test] + shlex.split(cxx_args)
-        out, err, exit_code = lit.util.executeCommand(cmd)
-        return cmd, out, err, exit_code
-
-    def _link(self, exec_path, object_path):
-        link_args = self.link_template.replace('%OUT%', exec_path)
-        link_args = link_args.replace('%SOURCE%', object_path)
-        cmd = [self.cxx_under_test] + shlex.split(link_args)
-        out, err, exit_code = lit.util.executeCommand(cmd)
-        return cmd, out, err, exit_code
-
     def _build(self, exec_path, source_path, compile_only=False,
                use_verify=False):
         cmd, report, rc = LibcxxTestFormat._build(
@@ -793,23 +823,56 @@
         self.link_template = open(link_template_file).read().strip()
 
     def configure_triple(self):
-        match = re.search(r'-target\s+(\S+)', self.cxx_template)
-        if not match:
+        if 'clang' in self.cxx_under_test:
+            triple = self.configure_clang_triple()
+        else:
+            triple = self.configure_gcc_triple()
+
+        if not triple:
             raise RuntimeError('Could not determine target triple.')
-        self.config.target_triple = match.group(1)
+        self.config.target_triple = triple
+
+    def configure_clang_triple(self):
+        match = re.search(r'-target\s+(\S+)', self.cxx_template)
+        if match:
+            return match.group(1)
+        return None
+
+    def configure_gcc_triple(self):
+        proc = subprocess.Popen([self.cxx_under_test, '-v'],
+                                stderr=subprocess.PIPE)
+        _, stderr = proc.communicate()
+        for line in stderr.split('\n'):
+            print 'Checking {}'.format(line)
+            match = re.search(r'^Target: (.+)$', line)
+            if match:
+                return match.group(1)
+        return None
 
     def configure_features(self):
         self.config.available_features.add('long_tests')
 
     def get_test_format(self):
-        return AndroidLibcxxTestFormat(
-            self.cxx_under_test,
-            self.src_root,
-            self.obj_root,
-            self.cxx_template,
-            self.link_template,
-            getattr(self.config, 'device_dir', '/data/local/tmp/'),
-            getattr(self.config, 'timeout', '60'))
+        mode = self.lit_config.params.get('android_mode', 'device')
+        if mode == 'device':
+            return AndroidLibcxxTestFormat(
+                self.cxx_under_test,
+                self.src_root,
+                self.obj_root,
+                self.cxx_template,
+                self.link_template,
+                getattr(self.config, 'device_dir', '/data/local/tmp/'),
+                getattr(self.config, 'timeout', '60'))
+        elif mode == 'host':
+            return AndroidHostLibcxxTestFormat(
+                self.cxx_under_test,
+                self.src_root,
+                self.obj_root,
+                self.cxx_template,
+                self.link_template,
+                getattr(self.config, 'timeout', '60'))
+        else:
+            raise RuntimeError('Invalid android_mode: {}'.format(mode))
 
 
 # name: The name of this test suite.
diff --git a/test/lit.host.cfg b/test/lit.host.cfg
deleted file mode 100644
index e2d9813..0000000
--- a/test/lit.host.cfg
+++ /dev/null
@@ -1,15 +0,0 @@
-import os
-
-android_root = os.getenv('ANDROID_BUILD_TOP')
-config.cxx_under_test = os.path.join(
-    android_root, 'prebuilts/clang/linux-x86/host/3.5/bin/clang++')
-config.cxx_has_stdcxx0x_flag = True
-config.libcxx_src_root = os.path.join(android_root, 'external/libcxx')
-config.libcxx_obj_root = os.getenv('ANDROID_HOST_OUT')
-config.python_executable = "/usr/bin/python"
-config.enable_shared = True
-config.cxx_abi = "none"
-
-# Let the main config do the real work.
-lit_config.load_config(
-    config, os.path.join(config.libcxx_src_root, 'test/lit.cfg'))
diff --git a/test/lit.device.cfg b/test/lit.site.cfg
similarity index 100%
rename from test/lit.device.cfg
rename to test/lit.site.cfg