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 clangxx = lit.util.which('clang++', config.environment['PATH']) 185 if clangxx is not None: 186 cxx_under_test = clangxx 187 lit_config.note("inferred cxx_under_test as: %r" % (cxx_under_test,)) 188if cxx_under_test is None: 189 lit_config.fatal('must specify user parameter cxx_under_test ' 190 '(e.g., --param=cxx_under_test=clang++)') 191 192libcxx_src_root = lit_config.params.get('libcxx_src_root', None) 193if libcxx_src_root is None: 194 libcxx_src_root = getattr(config, 'libcxx_src_root', None) 195 if libcxx_src_root is None: 196 libcxx_src_root = os.path.dirname(config.test_source_root) 197 198libcxx_obj_root = lit_config.params.get('libcxx_obj_root', None) 199if libcxx_obj_root is None: 200 libcxx_obj_root = getattr(config, 'libcxx_obj_root', None) 201 if libcxx_obj_root is None: 202 libcxx_obj_root = libcxx_src_root 203 204cxx_has_stdcxx0x_flag_str = lit_config.params.get('cxx_has_stdcxx0x_flag', None) 205if cxx_has_stdcxx0x_flag_str is not None: 206 if cxx_has_stdcxx0x_flag_str.lower() in ('1', 'true'): 207 cxx_has_stdcxx0x_flag = True 208 elif cxx_has_stdcxx0x_flag_str.lower() in ('', '0', 'false'): 209 cxx_has_stdcxx0x_flag = False 210 else: 211 lit_config.fatal( 212 'user parameter cxx_has_stdcxx0x_flag_str should be 0 or 1') 213else: 214 cxx_has_stdcxx0x_flag = getattr(config, 'cxx_has_stdcxx0x_flag', True) 215 216# This test suite supports testing against either the system library or the 217# locally built one; the former mode is useful for testing ABI compatibility 218# between the current headers and a shipping dynamic library. 219use_system_lib_str = lit_config.params.get('use_system_lib', None) 220if use_system_lib_str is not None: 221 if use_system_lib_str.lower() in ('1', 'true'): 222 use_system_lib = True 223 elif use_system_lib_str.lower() in ('', '0', 'false'): 224 use_system_lib = False 225 else: 226 lit_config.fatal('user parameter use_system_lib should be 0 or 1') 227else: 228 # Default to testing against the locally built libc++ library. 229 use_system_lib = False 230 lit_config.note("inferred use_system_lib as: %r" % (use_system_lib,)) 231 232link_flags = [] 233link_flags_str = lit_config.params.get('link_flags', None) 234if link_flags_str is None: 235 link_flags_str = getattr(config, 'link_flags', None) 236 if link_flags_str is None: 237 cxx_abi = getattr(config, 'cxx_abi', 'libcxxabi') 238 if cxx_abi == 'libstdc++': 239 link_flags += ['-lstdc++'] 240 elif cxx_abi == 'libsupc++': 241 link_flags += ['-lsupc++'] 242 elif cxx_abi == 'libcxxabi': 243 link_flags += ['-lc++abi'] 244 elif cxx_abi == 'none': 245 pass 246 else: 247 lit_config.fatal('C++ ABI setting %s unsupported for tests' % cxx_abi) 248 249 if sys.platform == 'darwin': 250 link_flags += ['-lSystem'] 251 elif sys.platform == 'linux2': 252 link_flags += [ '-lgcc_eh', '-lc', '-lm', '-lpthread', 253 '-lrt', '-lgcc_s'] 254 else: 255 lit_config.fatal("unrecognized system") 256 257 lit_config.note("inferred link_flags as: %r" % (link_flags,)) 258if not link_flags_str is None: 259 link_flags += shlex.split(link_flags_str) 260 261# Configure extra compiler flags. 262include_paths = ['-I' + libcxx_src_root + '/include', 263 '-I' + libcxx_src_root + '/test/support'] 264library_paths = ['-L' + libcxx_obj_root + '/lib'] 265compile_flags = [] 266if cxx_has_stdcxx0x_flag: 267 compile_flags += ['-std=c++0x'] 268 269# Configure extra linker parameters. 270exec_env = {} 271if sys.platform == 'darwin': 272 if not use_system_lib: 273 exec_env['DYLD_LIBRARY_PATH'] = os.path.join(libcxx_obj_root, 'lib') 274elif sys.platform == 'linux2': 275 if not use_system_lib: 276 link_flags += ['-Wl,-R', libcxx_obj_root + '/lib'] 277 compile_flags += ['-D__STDC_FORMAT_MACROS', '-D__STDC_LIMIT_MACROS', 278 '-D__STDC_CONSTANT_MACROS'] 279else: 280 lit_config.fatal("unrecognized system") 281 282config.test_format = LibcxxTestFormat( 283 cxx_under_test, 284 cpp_flags = ['-nostdinc++'] + compile_flags + include_paths, 285 ld_flags = ['-nodefaultlibs'] + library_paths + ['-lc++'] + link_flags, 286 exec_env = exec_env) 287 288# Get or infer the target triple. 289config.target_triple = lit_config.params.get('target_triple', None) 290# If no target triple was given, try to infer it from the compiler under test. 291if config.target_triple is None: 292 config.target_triple = lit.util.capture( 293 [cxx_under_test, '-dumpmachine']).strip() 294 lit_config.note("inferred target_triple as: %r" % (config.target_triple,)) 295 296# Write an "available feature" that combines the triple when use_system_lib is 297# enabled. This is so that we can easily write XFAIL markers for tests that are 298# known to fail with versions of libc++ as were shipped with a particular 299# triple. 300if use_system_lib: 301 # Drop sub-major version components from the triple, because the current 302 # XFAIL handling expects exact matches for feature checks. 303 sanitized_triple = re.sub(r"([^-]+)-([^-]+)-([^-.]+).*", r"\1-\2-\3", 304 config.target_triple) 305 config.available_features.add('with_system_lib=%s' % (sanitized_triple,)) 306