Update run_tests.py to use Soong.

run_tests.py had bitrotted since it was last run (e.g. it used perl
which is now not allowed and the warning flags were out of date).
I changed it to use a different way of extracting the compile command
which is based on Soong instead of makefiles. This way is also
compatible with multiple build directories since it doesn't clobber
the source directory and doesn't require OUT_DIR == out.

This also changes run_tests.py to run the libcxxabi tests as well,
since they can be run using the same mechanism.

Bug: 120510768
Test: ./run_tests.py --bitness 32
Test: ./run_tests.py --bitness 64
Test: ./run_tests.py --bitness 64 --host
Change-Id: Id30129161f8519fa6c1bc106727326373ca9ab82
This commit is contained in:
Peter Collingbourne
2018-11-30 20:29:22 -08:00
committed by Dan Albert
parent 20fc590391
commit 26cd9b82f8
12 changed files with 158 additions and 168 deletions

View File

@@ -22,7 +22,6 @@ import logging
import os
import sys
THIS_DIR = os.path.dirname(os.path.realpath(__file__))
ANDROID_DIR = os.path.realpath(os.path.join(THIS_DIR, '../..'))
@@ -46,68 +45,101 @@ def check_call(cmd, *args, **kwargs):
return subprocess.check_call(cmd, *args, **kwargs)
def check_output(cmd, *args, **kwargs):
"""subprocess.check_output with logging."""
import subprocess
logger().info('check_output %s', ' '.join(cmd))
return subprocess.check_output(cmd, *args, **kwargs)
class ArgParser(argparse.ArgumentParser):
"""Parses command line arguments."""
def __init__(self):
super(ArgParser, self).__init__()
self.add_argument(
'--compiler', choices=('clang', 'gcc'), default='clang')
self.add_argument(
'--bitness', choices=(32, 64), type=int, default=32)
self.add_argument('--bitness', choices=(32, 64), type=int, default=32)
self.add_argument('--host', action='store_true')
def gen_test_config(bitness, compiler, host):
"""Generates the test configuration makefile for buildcmds."""
testconfig_mk_path = os.path.join(THIS_DIR, 'buildcmds/testconfig.mk')
with open(testconfig_mk_path, 'w') as test_config:
if compiler == 'clang':
print('LOCAL_CLANG := true', file=test_config)
elif compiler == 'gcc':
print('LOCAL_CLANG := false', file=test_config)
def extract_build_cmds(commands, exe_name):
"""Extracts build command information from `ninja -t commands` output.
if bitness == 32:
print('LOCAL_MULTILIB := 32', file=test_config)
elif bitness == 64:
print('LOCAL_MULTILIB := 64', file=test_config)
Args:
commands: String containing the output of `ninja -t commands` for the
libcxx_test_template.
exe_name: The basename of the built executable.
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)
Returns:
Tuple of (compiler, compiler_flags, linker_flags).
"""
cc = None
cflags = None
ldflags = None
template_name = 'external/libcxx/libcxx_test_template.cpp'
if host:
print('include $(BUILD_HOST_EXECUTABLE)', file=test_config)
else:
print('include $(BUILD_EXECUTABLE)', file=test_config)
for cmd in commands.splitlines():
cmd_args = cmd.split()
if cc is None and template_name in cmd_args:
for i, arg in enumerate(cmd_args):
if arg == '-o':
cmd_args[i + 1] = '%OUT%'
elif arg == template_name:
cmd_args[i] = '%SOURCE%'
# Drop dependency tracking args since they can cause file
# not found errors at test time.
if arg == '-MD':
cmd_args[i] = ''
if arg == '-MF':
cmd_args[i] = ''
cmd_args[i + 1] = ''
if cmd_args[0] == 'PWD=/proc/self/cwd':
cmd_args = cmd_args[1:]
if cmd_args[0].endswith('gomacc'):
cmd_args = cmd_args[1:]
cc = cmd_args[0]
cflags = cmd_args[1:]
if ldflags is None:
is_ld = False
for i, arg in enumerate(cmd_args):
# Here we assume that the rspfile contains the path to the
# object file and nothing else.
if arg.startswith('@'):
cmd_args[i] = '%SOURCE%'
if arg == '-o' and cmd_args[i + 1].endswith(exe_name):
cmd_args[i + 1] = '%OUT%'
is_ld = True
if is_ld:
ldflags = cmd_args[1:]
return cc, cflags, ldflags
def mmm(path):
"""Invokes the Android build command mmm."""
makefile = os.path.join(path, 'Android.mk')
main_mk = 'build/core/main.mk'
def get_build_cmds(bitness, host):
"""Use ninja -t commands to find the build commands for an executable."""
out_dir = os.getenv('OUT_DIR', os.path.join(ANDROID_DIR, 'out'))
product_out = os.getenv('ANDROID_PRODUCT_OUT')
env = dict(os.environ)
env['ONE_SHOT_MAKEFILE'] = makefile
env['LIBCXX_TESTING'] = 'true'
cmd = [
'make', '-j', '-C', ANDROID_DIR, '-f', main_mk,
'MODULES-IN-' + path.replace('/', '-'),
]
check_call(cmd, env=env)
if host:
rel_out_dir = os.path.relpath(
os.path.join(out_dir, 'soong/host/linux-x86/bin'), ANDROID_DIR)
target = os.path.join(rel_out_dir, 'libcxx_test_template64')
else:
exe_name = 'libcxx_test_template' + str(bitness)
rel_out_dir = os.path.relpath(product_out, ANDROID_DIR)
target = os.path.join(rel_out_dir, 'system/bin', exe_name)
# Generate $OUT_DIR/combined-$TARGET_PRODUCT.ninja and build the
# template target's dependencies.
check_call(['make', '-C', ANDROID_DIR, target])
def gen_build_cmds(bitness, compiler, host):
"""Generates the build commands file for the test runner."""
gen_test_config(bitness, compiler, host)
mmm('external/libcxx/buildcmds')
ninja_path = os.path.join(
out_dir, 'combined-' + os.getenv('TARGET_PRODUCT') + '.ninja')
commands = check_output([
os.path.join(ANDROID_DIR, 'prebuilts/build-tools/linux-x86/bin/ninja'),
'-C', ANDROID_DIR, '-f', ninja_path, '-t', 'commands', target
])
return extract_build_cmds(commands, os.path.basename(target))
def main():
@@ -116,20 +148,27 @@ def main():
args, lit_args = ArgParser().parse_known_args()
lit_path = os.path.join(ANDROID_DIR, 'external/llvm/utils/lit/lit.py')
gen_build_cmds(args.bitness, args.compiler, args.host)
cc, cflags, ldflags = get_build_cmds(args.bitness, args.host)
mode_str = 'host' if args.host else 'device'
android_mode_arg = '--param=android_mode=' + mode_str
cxx_under_test_arg = '--param=cxx_under_test=' + cc
cxx_template_arg = '--param=cxx_template=' + ' '.join(cflags)
link_template_arg = '--param=link_template=' + ' '.join(ldflags)
site_cfg_path = os.path.join(THIS_DIR, 'test/lit.site.cfg')
site_cfg_arg = '--param=libcxx_site_config=' + site_cfg_path
default_test_path = os.path.join(THIS_DIR, 'test')
libcxx_site_cfg_arg = '--param=libcxx_site_config=' + site_cfg_path
libcxxabi_site_cfg_arg = '--param=libcxxabi_site_config=' + site_cfg_path
default_test_paths = [
os.path.join(THIS_DIR, 'test'),
os.path.join(ANDROID_DIR, 'external/libcxxabi/test')
]
have_filter_args = False
for arg in lit_args:
# If the argument is a valid path with default_test_path, it is a test
# If the argument is a valid path with default_test_paths, it is a test
# filter.
real_path = os.path.realpath(arg)
if not real_path.startswith(default_test_path):
if not any(real_path.startswith(path) for path in default_test_paths):
continue
if not os.path.exists(real_path):
continue
@@ -137,10 +176,13 @@ def main():
have_filter_args = True
break # No need to keep scanning.
lit_args = ['-sv', android_mode_arg, site_cfg_arg] + lit_args
lit_args = [
'-sv', android_mode_arg, cxx_under_test_arg, cxx_template_arg,
link_template_arg, libcxx_site_cfg_arg, libcxxabi_site_cfg_arg
] + lit_args
cmd = ['python', lit_path] + lit_args
if not have_filter_args:
cmd.append(default_test_path)
cmd += default_test_paths
sys.exit(call(cmd))