1# -*- Python -*- vim: set syntax=python tabstop=4 expandtab cc=80: 2 3# Configuration file for the 'lit' test runner. 4 5import errno 6import os 7import platform 8import re 9import shlex 10import signal 11import subprocess 12import sys 13import tempfile 14import time 15 16import lit.Test 17import lit.formats 18import lit.util 19 20class LibcxxTestFormat(lit.formats.FileBasedTest): 21 """ 22 Custom test format handler for use with the test format use by libc++. 23 24 Tests fall into two categories: 25 FOO.pass.cpp - Executable test which should compile, run, and exit with 26 code 0. 27 FOO.fail.cpp - Negative test case which is expected to fail compilation. 28 """ 29 30 def __init__(self, cxx_under_test, cpp_flags, ld_flags, exec_env): 31 self.cxx_under_test = cxx_under_test 32 self.cpp_flags = list(cpp_flags) 33 self.ld_flags = list(ld_flags) 34 self.exec_env = dict(exec_env) 35 36 def execute_command(self, command, in_dir=None): 37 kwargs = { 38 'stdin' :subprocess.PIPE, 39 'stdout':subprocess.PIPE, 40 'stderr':subprocess.PIPE, 41 } 42 if in_dir: 43 kwargs['cwd'] = in_dir 44 p = subprocess.Popen(command, **kwargs) 45 out,err = p.communicate() 46 exitCode = p.wait() 47 48 # Detect Ctrl-C in subprocess. 49 if exitCode == -signal.SIGINT: 50 raise KeyboardInterrupt 51 52 return out, err, exitCode 53 54 def execute(self, test, lit_config): 55 while True: 56 try: 57 return self._execute(test, lit_config) 58 except OSError, oe: 59 if oe.errno != errno.ETXTBSY: 60 raise 61 time.sleep(0.1) 62 63 def _execute(self, test, lit_config): 64 # Extract test metadata from the test file. 65 requires = [] 66 with open(test.getSourcePath()) as f: 67 for ln in f: 68 if 'XFAIL:' in ln: 69 items = ln[ln.index('XFAIL:') + 6:].split(',') 70 test.xfails.extend([s.strip() for s in items]) 71 elif 'REQUIRES:' in ln: 72 items = ln[ln.index('REQUIRES:') + 9:].split(',') 73 requires.extend([s.strip() for s in items]) 74 elif not ln.startswith("//") and ln.strip(): 75 # Stop at the first non-empty line that is not a C++ 76 # comment. 77 break 78 79 # Check that we have the required features. 80 # 81 # FIXME: For now, this is cribbed from lit.TestRunner, to avoid 82 # introducing a dependency there. What we more ideally would like to do 83 # is lift the "requires" handling to be a core lit framework feature. 84 missing_required_features = [f for f in requires 85 if f not in test.config.available_features] 86 if missing_required_features: 87 return (lit.Test.UNSUPPORTED, 88 "Test requires the following features: %s" % ( 89 ', '.join(missing_required_features),)) 90 91 # Evaluate the test. 92 return self._evaluate_test(test, lit_config) 93 94 def _evaluate_test(self, test, lit_config): 95 name = test.path_in_suite[-1] 96 source_path = test.getSourcePath() 97 source_dir = os.path.dirname(source_path) 98 99 # Check what kind of test this is. 100 assert name.endswith('.pass.cpp') or name.endswith('.fail.cpp') 101 expected_compile_fail = name.endswith('.fail.cpp') 102 103 # If this is a compile (failure) test, build it and check for failure. 104 if expected_compile_fail: 105 cmd = [self.cxx_under_test, '-c', 106 '-o', '/dev/null', source_path] + self.cpp_flags 107 out, err, exitCode = self.execute_command(cmd) 108 if exitCode == 1: 109 return lit.Test.PASS, "" 110 else: 111 report = """Command: %s\n""" % ' '.join(["'%s'" % a 112 for a in cmd]) 113 report += """Exit Code: %d\n""" % exitCode 114 if out: 115 report += """Standard Output:\n--\n%s--""" % out 116 if err: 117 report += """Standard Error:\n--\n%s--""" % err 118 report += "\n\nExpected compilation to fail!" 119 return lit.Test.FAIL, report 120 else: 121 exec_file = tempfile.NamedTemporaryFile(suffix="exe", delete=False) 122 exec_path = exec_file.name 123 exec_file.close() 124 125 try: 126 compile_cmd = [self.cxx_under_test, '-o', exec_path, 127 source_path] + self.cpp_flags + self.ld_flags 128 cmd = compile_cmd 129 out, err, exitCode = self.execute_command(cmd) 130 if exitCode != 0: 131 report = """Command: %s\n""" % ' '.join(["'%s'" % a 132 for a in cmd]) 133 report += """Exit Code: %d\n""" % exitCode 134 if out: 135 report += """Standard Output:\n--\n%s--""" % out 136 if err: 137 report += """Standard Error:\n--\n%s--""" % err 138 report += "\n\nCompilation failed unexpectedly!" 139 return lit.Test.FAIL, report 140 141 cmd = [] 142 if self.exec_env: 143 cmd.append('env') 144 cmd.extend('%s=%s' % (name, value) 145 for name,value in self.exec_env.items()) 146 cmd.append(exec_path) 147 if lit_config.useValgrind: 148 cmd = lit_config.valgrindArgs + cmd 149 out, err, exitCode = self.execute_command(cmd, source_dir) 150 if exitCode != 0: 151 report = """Compiled With: %s\n""" % \ 152 ' '.join(["'%s'" % a for a in compile_cmd]) 153 report += """Command: %s\n""" % \ 154 ' '.join(["'%s'" % a for a in cmd]) 155 report += """Exit Code: %d\n""" % exitCode 156 if out: 157 report += """Standard Output:\n--\n%s--""" % out 158 if err: 159 report += """Standard Error:\n--\n%s--""" % err 160 report += "\n\nCompiled test failed unexpectedly!" 161 return lit.Test.FAIL, report 162 finally: 163 try: 164 os.remove(exec_path) 165 except: 166 pass 167 return lit.Test.PASS, "" 168 169# name: The name of this test suite. 170config.name = 'libc++' 171 172# suffixes: A list of file extensions to treat as test files. 173config.suffixes = ['.cpp'] 174 175# test_source_root: The root path where tests are located. 176config.test_source_root = os.path.dirname(__file__) 177 178# Gather various compiler parameters. 179cxx_under_test = lit_config.params.get('cxx_under_test', None) 180if cxx_under_test is None: 181 cxx_under_test = getattr(config, 'cxx_under_test', None) 182 183 # If no specific cxx_under_test was given, attempt to infer it as clang++. 184 if cxx_under_test is None: 185 clangxx = lit.util.which('clang++', config.environment['PATH']) 186 if clangxx is not None: 187 cxx_under_test = clangxx 188 lit_config.note("inferred cxx_under_test as: %r" % (cxx_under_test,)) 189if cxx_under_test is None: 190 lit_config.fatal('must specify user parameter cxx_under_test ' 191 '(e.g., --param=cxx_under_test=clang++)') 192 193libcxx_src_root = lit_config.params.get('libcxx_src_root', None) 194if libcxx_src_root is None: 195 libcxx_src_root = getattr(config, 'libcxx_src_root', None) 196 if libcxx_src_root is None: 197 libcxx_src_root = os.path.dirname(config.test_source_root) 198 199libcxx_obj_root = lit_config.params.get('libcxx_obj_root', None) 200if libcxx_obj_root is None: 201 libcxx_obj_root = getattr(config, 'libcxx_obj_root', None) 202 if libcxx_obj_root is None: 203 libcxx_obj_root = libcxx_src_root 204 205cxx_has_stdcxx0x_flag_str = lit_config.params.get('cxx_has_stdcxx0x_flag', None) 206if cxx_has_stdcxx0x_flag_str is not None: 207 if cxx_has_stdcxx0x_flag_str.lower() in ('1', 'true'): 208 cxx_has_stdcxx0x_flag = True 209 elif cxx_has_stdcxx0x_flag_str.lower() in ('', '0', 'false'): 210 cxx_has_stdcxx0x_flag = False 211 else: 212 lit_config.fatal( 213 'user parameter cxx_has_stdcxx0x_flag_str should be 0 or 1') 214else: 215 cxx_has_stdcxx0x_flag = getattr(config, 'cxx_has_stdcxx0x_flag', True) 216 217# This test suite supports testing against either the system library or the 218# locally built one; the former mode is useful for testing ABI compatibility 219# between the current headers and a shipping dynamic library. 220use_system_lib_str = lit_config.params.get('use_system_lib', None) 221if use_system_lib_str is not None: 222 if use_system_lib_str.lower() in ('1', 'true'): 223 use_system_lib = True 224 elif use_system_lib_str.lower() in ('', '0', 'false'): 225 use_system_lib = False 226 else: 227 lit_config.fatal('user parameter use_system_lib should be 0 or 1') 228else: 229 # Default to testing against the locally built libc++ library. 230 use_system_lib = False 231 lit_config.note("inferred use_system_lib as: %r" % (use_system_lib,)) 232 233link_flags = [] 234link_flags_str = lit_config.params.get('link_flags', None) 235if link_flags_str is None: 236 link_flags_str = getattr(config, 'link_flags', None) 237 if link_flags_str is None: 238 cxx_abi = getattr(config, 'cxx_abi', 'libcxxabi') 239 if cxx_abi == 'libstdc++': 240 link_flags += ['-lstdc++'] 241 elif cxx_abi == 'libsupc++': 242 link_flags += ['-lsupc++'] 243 elif cxx_abi == 'libcxxabi': 244 link_flags += ['-lc++abi'] 245 elif cxx_abi == 'none': 246 pass 247 else: 248 lit_config.fatal('C++ ABI setting %s unsupported for tests' % cxx_abi) 249 250 if sys.platform == 'darwin': 251 link_flags += ['-lSystem'] 252 elif sys.platform == 'linux2': 253 link_flags += [ '-lgcc_eh', '-lc', '-lm', '-lpthread', 254 '-lrt', '-lgcc_s'] 255 else: 256 lit_config.fatal("unrecognized system") 257 258 lit_config.note("inferred link_flags as: %r" % (link_flags,)) 259if not link_flags_str is None: 260 link_flags += shlex.split(link_flags_str) 261 262# Configure extra compiler flags. 263include_paths = ['-I' + libcxx_src_root + '/include', 264 '-I' + libcxx_src_root + '/test/support'] 265library_paths = ['-L' + libcxx_obj_root + '/lib'] 266compile_flags = [] 267if cxx_has_stdcxx0x_flag: 268 compile_flags += ['-std=c++0x'] 269 270# Configure extra linker parameters. 271exec_env = {} 272if sys.platform == 'darwin': 273 if not use_system_lib: 274 exec_env['DYLD_LIBRARY_PATH'] = os.path.join(libcxx_obj_root, 'lib') 275elif sys.platform == 'linux2': 276 if not use_system_lib: 277 link_flags += ['-Wl,-R', libcxx_obj_root + '/lib'] 278 compile_flags += ['-D__STDC_FORMAT_MACROS', '-D__STDC_LIMIT_MACROS', 279 '-D__STDC_CONSTANT_MACROS'] 280else: 281 lit_config.fatal("unrecognized system") 282 283config.test_format = LibcxxTestFormat( 284 cxx_under_test, 285 cpp_flags = ['-nostdinc++'] + compile_flags + include_paths, 286 ld_flags = ['-nodefaultlibs'] + library_paths + ['-lc++'] + link_flags, 287 exec_env = exec_env) 288 289# Get or infer the target triple. 290config.target_triple = lit_config.params.get('target_triple', None) 291# If no target triple was given, try to infer it from the compiler under test. 292if config.target_triple is None: 293 config.target_triple = lit.util.capture( 294 [cxx_under_test, '-dumpmachine']).strip() 295 lit_config.note("inferred target_triple as: %r" % (config.target_triple,)) 296 297# Write an "available feature" that combines the triple when use_system_lib is 298# enabled. This is so that we can easily write XFAIL markers for tests that are 299# known to fail with versions of libc++ as were shipped with a particular 300# triple. 301if use_system_lib: 302 # Drop sub-major version components from the triple, because the current 303 # XFAIL handling expects exact matches for feature checks. 304 sanitized_triple = re.sub(r"([^-]+)-([^-]+)-([^-.]+).*", r"\1-\2-\3", 305 config.target_triple) 306 config.available_features.add('with_system_lib=%s' % (sanitized_triple,)) 307