1#!/usr/bin/env python 2# 3# Copyright (C) 2015 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17"""Runs the libc++ tests against the platform libc++.""" 18from __future__ import print_function 19 20import argparse 21import logging 22import os 23import sys 24 25 26THIS_DIR = os.path.dirname(os.path.realpath(__file__)) 27ANDROID_DIR = os.path.realpath(os.path.join(THIS_DIR, '../..')) 28 29 30def logger(): 31 """Returns the logger for the module.""" 32 return logging.getLogger(__name__) 33 34 35def call(cmd, *args, **kwargs): 36 """subprocess.call with logging.""" 37 import subprocess 38 logger().info('call %s', ' '.join(cmd)) 39 return subprocess.call(cmd, *args, **kwargs) 40 41 42def check_call(cmd, *args, **kwargs): 43 """subprocess.check_call with logging.""" 44 import subprocess 45 logger().info('check_call %s', ' '.join(cmd)) 46 return subprocess.check_call(cmd, *args, **kwargs) 47 48 49class ArgParser(argparse.ArgumentParser): 50 """Parses command line arguments.""" 51 def __init__(self): 52 super(ArgParser, self).__init__() 53 self.add_argument( 54 '--compiler', choices=('clang', 'gcc'), default='clang') 55 self.add_argument( 56 '--bitness', choices=(32, 64), type=int, default=32) 57 self.add_argument('--host', action='store_true') 58 59 60def gen_test_config(bitness, compiler, host): 61 """Generates the test configuration makefile for buildcmds.""" 62 testconfig_mk_path = os.path.join(THIS_DIR, 'buildcmds/testconfig.mk') 63 with open(testconfig_mk_path, 'w') as test_config: 64 if compiler == 'clang': 65 print('LOCAL_CLANG := true', file=test_config) 66 elif compiler == 'gcc': 67 print('LOCAL_CLANG := false', file=test_config) 68 69 if bitness == 32: 70 print('LOCAL_MULTILIB := 32', file=test_config) 71 elif bitness == 64: 72 print('LOCAL_MULTILIB := 64', file=test_config) 73 74 if compiler == 'clang': 75 print('LOCAL_CXX := $(LOCAL_PATH)/buildcmdscc $(CLANG_CXX)', 76 file=test_config) 77 else: 78 if host: 79 prefix = 'HOST_' 80 else: 81 prefix = 'TARGET_' 82 print('LOCAL_CXX := $(LOCAL_PATH)/buildcmdscc ' 83 '$($(LOCAL_2ND_ARCH_VAR_PREFIX){}CXX)'.format(prefix), 84 file=test_config) 85 86 if host: 87 print('include $(BUILD_HOST_EXECUTABLE)', file=test_config) 88 else: 89 print('include $(BUILD_EXECUTABLE)', file=test_config) 90 91 92def mmm(path): 93 """Invokes the Android build command mmm.""" 94 makefile = os.path.join(path, 'Android.mk') 95 main_mk = 'build/core/main.mk' 96 97 env = dict(os.environ) 98 env['ONE_SHOT_MAKEFILE'] = makefile 99 env['LIBCXX_TESTING'] = 'true' 100 cmd = [ 101 'make', '-j', '-C', ANDROID_DIR, '-f', main_mk, 102 'MODULES-IN-' + path.replace('/', '-'), 103 ] 104 check_call(cmd, env=env) 105 106 107def gen_build_cmds(bitness, compiler, host): 108 """Generates the build commands file for the test runner.""" 109 gen_test_config(bitness, compiler, host) 110 mmm('external/libcxx/buildcmds') 111 112 113def main(): 114 """Program entry point.""" 115 logging.basicConfig(level=logging.INFO) 116 117 args, lit_args = ArgParser().parse_known_args() 118 lit_path = os.path.join(ANDROID_DIR, 'external/llvm/utils/lit/lit.py') 119 gen_build_cmds(args.bitness, args.compiler, args.host) 120 121 mode_str = 'host' if args.host else 'device' 122 android_mode_arg = '--param=android_mode=' + mode_str 123 site_cfg_path = os.path.join(THIS_DIR, 'test/lit.site.cfg') 124 site_cfg_arg = '--param=libcxx_site_config=' + site_cfg_path 125 default_test_path = os.path.join(THIS_DIR, 'test') 126 127 have_filter_args = False 128 for arg in lit_args: 129 # If the argument is a valid path with default_test_path, it is a test 130 # filter. 131 real_path = os.path.realpath(arg) 132 if not real_path.startswith(default_test_path): 133 continue 134 if not os.path.exists(real_path): 135 continue 136 137 have_filter_args = True 138 break # No need to keep scanning. 139 140 lit_args = ['-sv', android_mode_arg, site_cfg_arg] + lit_args 141 cmd = ['python', lit_path] + lit_args 142 if not have_filter_args: 143 cmd.append(default_test_path) 144 sys.exit(call(cmd)) 145 146 147if __name__ == '__main__': 148 main() 149