1 # -*- Python -*- vim: set syntax=python tabstop=4 expandtab cc=80: 2 3 # Configuration file for the 'lit' test runner. 4 5 import errno 6 import os 7 import platform 8 import re 9 import shlex 10 import signal 11 import subprocess 12 import sys 13 import tempfile 14 import time 15 16 import lit.Test 17 import lit.formats 18 import lit.util 19 20 class 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. 170 config.name = 'libc++' 171 172 # suffixes: A list of file extensions to treat as test files. 173 config.suffixes = ['.cpp'] 174 175 # test_source_root: The root path where tests are located. 176 config.test_source_root = os.path.dirname(__file__) 177 178 # Gather various compiler parameters. 179 cxx_under_test = lit_config.params.get('cxx_under_test', None) 180 if 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,)) 189 if 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 193 libcxx_src_root = lit_config.params.get('libcxx_src_root', None) 194 if 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 199 libcxx_obj_root = lit_config.params.get('libcxx_obj_root', None) 200 if 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 205 cxx_has_stdcxx0x_flag_str = lit_config.params.get('cxx_has_stdcxx0x_flag', None) 206 if 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') 214 else: 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. 220 use_system_lib_str = lit_config.params.get('use_system_lib', None) 221 if 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') 228 else: 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 233 link_flags = [] 234 link_flags_str = lit_config.params.get('link_flags', None) 235 if 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,)) 259 if not link_flags_str is None: 260 link_flags += shlex.split(link_flags_str) 261 262 # Configure extra compiler flags. 263 include_paths = ['-I' + libcxx_src_root + '/include', 264 '-I' + libcxx_src_root + '/test/support'] 265 library_paths = ['-L' + libcxx_obj_root + '/lib'] 266 compile_flags = [] 267 if cxx_has_stdcxx0x_flag: 268 compile_flags += ['-std=c++0x'] 269 270 # Configure extra linker parameters. 271 exec_env = {} 272 if sys.platform == 'darwin': 273 if not use_system_lib: 274 exec_env['DYLD_LIBRARY_PATH'] = os.path.join(libcxx_obj_root, 'lib') 275 elif 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'] 280 else: 281 lit_config.fatal("unrecognized system") 282 283 config.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. 290 config.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. 292 if 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. 301 if 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