Index: test/lit.cfg =================================================================== --- test/lit.cfg +++ test/lit.cfg @@ -92,6 +92,31 @@ # Evaluate the test. return self._evaluate_test(test, lit_config) + def _build(self, exec_path, source_path, compile_only=False): + cmd = [self.cxx_under_test, '-o', exec_path, + source_path] + self.cpp_flags + if compile_only: + cmd += ['-c'] + else: + cmd += self.ld_flags + out, err, exitCode = self.execute_command(cmd) + return cmd, out, err, exitCode + + def _clean(self, exec_path): + os.remove(exec_path) + + def _run(self, exec_path, lit_config, in_dir=None): + cmd = [] + if self.exec_env: + cmd.append('env') + cmd.extend('%s=%s' % (name, value) + for name, value in self.exec_env.items()) + cmd.append(exec_path) + if lit_config.useValgrind: + cmd = lit_config.valgrindArgs + cmd + out, err, exitCode = self.execute_command(cmd, in_dir) + return cmd, out, err, exitCode + def _evaluate_test(self, test, lit_config): name = test.path_in_suite[-1] source_path = test.getSourcePath() @@ -103,9 +128,8 @@ # If this is a compile (failure) test, build it and check for failure. if expected_compile_fail: - cmd = [self.cxx_under_test, '-c', - '-o', '/dev/null', source_path] + self.cpp_flags - out, err, exitCode = self.execute_command(cmd) + cmd, out, err, exitCode = self._build('/dev/null', source_path, + compile_only=True) if exitCode == 1: return lit.Test.PASS, "" else: @@ -124,10 +148,8 @@ exec_file.close() try: - compile_cmd = [self.cxx_under_test, '-o', exec_path, - source_path] + self.cpp_flags + self.ld_flags - cmd = compile_cmd - out, err, exitCode = self.execute_command(cmd) + cmd, out, err, exitCode = self._build(exec_path, source_path) + compile_cmd = cmd if exitCode != 0: report = """Command: %s\n""" % ' '.join(["'%s'" % a for a in cmd]) @@ -139,15 +161,8 @@ report += "\n\nCompilation failed unexpectedly!" return lit.Test.FAIL, report - cmd = [] - if self.exec_env: - cmd.append('env') - cmd.extend('%s=%s' % (name, value) - for name,value in self.exec_env.items()) - cmd.append(exec_path) - if lit_config.useValgrind: - cmd = lit_config.valgrindArgs + cmd - out, err, exitCode = self.execute_command(cmd, source_dir) + cmd, out, err, exitCode = self._run(exec_path, lit_config, + source_dir) if exitCode != 0: report = """Compiled With: %s\n""" % \ ' '.join(["'%s'" % a for a in compile_cmd]) @@ -162,11 +177,113 @@ return lit.Test.FAIL, report finally: try: - os.remove(exec_path) + self._clean(exec_path) except: pass return lit.Test.PASS, "" + +class AdbError(RuntimeError): + def __init__(cmd, out, err, exit_code): + self.cmd = cmd + self.out = out + self.err = err + self.exit_code = exit_code + + +class AndroidLibcxxTestFormat(LibcxxTestFormat): + def __init__(self, cxx_under_test, libcxx_src_root, libcxx_obj_root, + cpp_flags, ld_flags, crtbegin, crtend, device_dir, timeout): + self.cxx_under_test = cxx_under_test + self.libcxx_src_root = libcxx_src_root + self.libcxx_obj_root = libcxx_obj_root + self.cpp_flags = cpp_flags + self.ld_flags = ld_flags + self.crtbegin = crtbegin + self.crtend = crtend + self.device_dir = device_dir + self.timeout = timeout + + def _working_directory(self, file_name): + return os.path.join(self.device_dir, file_name) + + def _wd_path(self, test_name, file_name): + return os.path.join(self._working_directory(test_name), file_name) + + def _adb_mkdir(self, path): + cmd = ['adb', 'shell', 'mkdir', path] + out, err, exit_code = self.execute_command(cmd) + if exit_code != 0: + raise AdbError(cmd, out, err, exit_code) + + def _adb_push(self, src, dst): + cmd = ['adb', 'push', src, dst] + out, err, exit_code = self.execute_command(cmd) + if exit_code != 0: + raise AdbError(cmd, out, err, exit_code) + + def _build(self, exec_path, source_path, compile_only=False): + cmd = [self.cxx_under_test, '-o', exec_path] + self.cpp_flags + if compile_only: + cmd += ['-c', source_path] + else: + cmd += [self.crtbegin, source_path] + self.ld_flags + [self.crtend] + try: + out, err, exit_code = self.execute_command(cmd) + if exit_code != 0: + return cmd, out, err, exit_code + exec_file = os.path.basename(exec_path) + + self._adb_mkdir(self._working_directory(exec_file)) + self._adb_push(exec_path, self._wd_path(exec_file, exec_file)) + + # Push any .dat files in the same directory as the source to the + # working directory. + src_dir = os.path.dirname(source_path) + data_files = [f for f in os.listdir(src_dir) if f.endswith('.dat')] + for data_file in data_files: + df_path = os.path.join(src_dir, data_file) + df_dev_path = self._wd_path(exec_file, data_file) + self._adb_push(df_path, df_dev_path) + return cmd, out, err, exit_code + except AdbError as ex: + return ex.cmd, ex.out, ex.err, ex.exit_code + except: + return cmd, out, err, exit_code + + def _clean(self, exec_path): + exec_file = os.path.basename(exec_path) + cmd = ['adb', 'shell', 'rm', '-rf', self._working_directory(exec_file)] + self.execute_command(cmd) + os.remove(exec_path) + + def _run(self, exec_path, lit_config, in_dir=None): + exec_file = os.path.basename(exec_path) + shell_cmd = 'cd {} && {}; echo $?'.format( + self._working_directory(exec_file), + self._wd_path(exec_file, exec_file)) + cmd = ['timeout', self.timeout, 'adb', 'shell', shell_cmd] + + # Tests will commonly fail with ETXTBSY. Possibly related to this bug: + # https://code.google.com/p/android/issues/detail?id=65857. Work around + # it by just waiting a second and then retrying. + for _ in range(10): + out, err, exit_code = self.execute_command(cmd) + if exit_code == 0: + if 'Text file busy' in out: + time.sleep(1) + else: + out = out.strip().split('\r\n') + status_line = out[-1:][0] + out = '\n'.join(out[:-1]) + exit_code = int(status_line) + break + else: + err += '\nTimed out after {} seconds'.format(self.timeout) + break + return cmd, out, err, exit_code + + # name: The name of this test suite. config.name = 'libc++' @@ -176,197 +293,229 @@ # test_source_root: The root path where tests are located. config.test_source_root = os.path.dirname(__file__) -# Figure out which of the required locales we support -locales = { - 'Darwin': { - 'en_US.UTF-8': 'en_US.UTF-8', - 'cs_CZ.ISO8859-2': 'cs_CZ.ISO8859-2', - 'fr_FR.UTF-8': 'fr_FR.UTF-8', - 'fr_CA.ISO8859-1': 'cs_CZ.ISO8859-1', - 'ru_RU.UTF-8': 'ru_RU.UTF-8', - 'zh_CN.UTF-8': 'zh_CN.UTF-8', - }, - 'FreeBSD' : { - 'en_US.UTF-8': 'en_US.UTF-8', - 'cs_CZ.ISO8859-2': 'cs_CZ.ISO8859-2', - 'fr_FR.UTF-8': 'fr_FR.UTF-8', - 'fr_CA.ISO8859-1': 'fr_CA.ISO8859-1', - 'ru_RU.UTF-8': 'ru_RU.UTF-8', - 'zh_CN.UTF-8': 'zh_CN.UTF-8', - }, - 'Linux': { - 'en_US.UTF-8': 'en_US.UTF-8', - 'cs_CZ.ISO8859-2': 'cs_CZ.ISO-8859-2', - 'fr_FR.UTF-8': 'fr_FR.UTF-8', - 'fr_CA.ISO8859-1': 'fr_CA.ISO-8859-1', - 'ru_RU.UTF-8': 'ru_RU.UTF-8', - 'zh_CN.UTF-8': 'zh_CN.UTF-8', - }, - 'Windows': { - 'en_US.UTF-8': 'English_United States.1252', - 'cs_CZ.ISO8859-2': 'Czech_Czech Republic.1250', - 'fr_FR.UTF-8': 'French_France.1252', - 'fr_CA.ISO8859-1': 'French_Canada.1252', - 'ru_RU.UTF-8': 'Russian_Russia.1251', - 'zh_CN.UTF-8': 'Chinese_China.936', - }, -} - -for feature, loc in locales[platform.system()].items(): - try: - locale.setlocale(locale.LC_ALL, loc) - config.available_features.add('locale.{}'.format(feature)) - except: - lit_config.warning('The locale {} is not supported by your platform. ' - 'Some tests will be unsupported.'.format(loc)) - -# Gather various compiler parameters. -cxx_under_test = lit_config.params.get('cxx_under_test', None) -if cxx_under_test is None: - cxx_under_test = getattr(config, 'cxx_under_test', None) - - # If no specific cxx_under_test was given, attempt to infer it as clang++. +if getattr(config, 'android', False): + android_root = getattr(config, 'android_root', None) + if not android_root: + lit_config.fatal('config.android_root must be set') + cxx_under_test = lit_config.params.get('cxx_under_test', None) + if cxx_under_test is None: + cxx_under_test = getattr(config, 'cxx_under_test', None) + if cxx_under_test is None: + lit_config.fatal('config.cxx_under_test must be set') + libcxx_src_root = lit_config.params.get('libcxx_src_root', None) + if libcxx_src_root is None: + libcxx_src_root = getattr(config, 'libcxx_src_root', None) + if libcxx_src_root is None: + libcxx_src_root = os.path.dirname(config.test_source_root) + libcxx_obj_root = lit_config.params.get('libcxx_obj_root', None) + if libcxx_obj_root is None: + libcxx_obj_root = getattr(config, 'libcxx_obj_root', None) + if libcxx_obj_root is None: + libcxx_obj_root = libcxx_src_root + config.test_format = AndroidLibcxxTestFormat( + cxx_under_test, + libcxx_src_root, + libcxx_obj_root, + config.cppflags, + config.ldflags, + config.crtbegin, + config.crtend, + getattr(config, 'device_dir', '/data/local/tmp/'), + getattr(config, 'timeout', '60')) +else: + # Figure out which of the required locales we support + locales = { + 'Darwin': { + 'en_US.UTF-8': 'en_US.UTF-8', + 'cs_CZ.ISO8859-2': 'cs_CZ.ISO8859-2', + 'fr_FR.UTF-8': 'fr_FR.UTF-8', + 'fr_CA.ISO8859-1': 'cs_CZ.ISO8859-1', + 'ru_RU.UTF-8': 'ru_RU.UTF-8', + 'zh_CN.UTF-8': 'zh_CN.UTF-8', + }, + 'FreeBSD' : { + 'en_US.UTF-8': 'en_US.UTF-8', + 'cs_CZ.ISO8859-2': 'cs_CZ.ISO8859-2', + 'fr_FR.UTF-8': 'fr_FR.UTF-8', + 'fr_CA.ISO8859-1': 'fr_CA.ISO8859-1', + 'ru_RU.UTF-8': 'ru_RU.UTF-8', + 'zh_CN.UTF-8': 'zh_CN.UTF-8', + }, + 'Linux': { + 'en_US.UTF-8': 'en_US.UTF-8', + 'cs_CZ.ISO8859-2': 'cs_CZ.ISO-8859-2', + 'fr_FR.UTF-8': 'fr_FR.UTF-8', + 'fr_CA.ISO8859-1': 'fr_CA.ISO-8859-1', + 'ru_RU.UTF-8': 'ru_RU.UTF-8', + 'zh_CN.UTF-8': 'zh_CN.UTF-8', + }, + 'Windows': { + 'en_US.UTF-8': 'English_United States.1252', + 'cs_CZ.ISO8859-2': 'Czech_Czech Republic.1250', + 'fr_FR.UTF-8': 'French_France.1252', + 'fr_CA.ISO8859-1': 'French_Canada.1252', + 'ru_RU.UTF-8': 'Russian_Russia.1251', + 'zh_CN.UTF-8': 'Chinese_China.936', + }, + } + + for feature, loc in locales[platform.system()].items(): + try: + locale.setlocale(locale.LC_ALL, loc) + config.available_features.add('locale.{}'.format(feature)) + except: + lit_config.warning('The locale {} is not supported by your ' + 'platform. Some tests will be ' + 'unsupported.'.format(loc)) + + # Gather various compiler parameters. + cxx_under_test = lit_config.params.get('cxx_under_test', None) if cxx_under_test is None: - clangxx = lit.util.which('clang++', config.environment['PATH']) - if clangxx is not None: - cxx_under_test = clangxx - lit_config.note("inferred cxx_under_test as: %r" % (cxx_under_test,)) -if cxx_under_test is None: - lit_config.fatal('must specify user parameter cxx_under_test ' - '(e.g., --param=cxx_under_test=clang++)') - -libcxx_src_root = lit_config.params.get('libcxx_src_root', None) -if libcxx_src_root is None: - libcxx_src_root = getattr(config, 'libcxx_src_root', None) + cxx_under_test = getattr(config, 'cxx_under_test', None) + + # If no specific cxx_under_test was given, attempt to infer it as + # clang++. + if cxx_under_test is None: + clangxx = lit.util.which('clang++', config.environment['PATH']) + if clangxx is not None: + cxx_under_test = clangxx + lit_config.note("inferred cxx_under_test as: %r" % (cxx_under_test,)) + if cxx_under_test is None: + lit_config.fatal('must specify user parameter cxx_under_test ' + '(e.g., --param=cxx_under_test=clang++)') + + libcxx_src_root = lit_config.params.get('libcxx_src_root', None) if libcxx_src_root is None: - libcxx_src_root = os.path.dirname(config.test_source_root) + libcxx_src_root = getattr(config, 'libcxx_src_root', None) + if libcxx_src_root is None: + libcxx_src_root = os.path.dirname(config.test_source_root) -libcxx_obj_root = lit_config.params.get('libcxx_obj_root', None) -if libcxx_obj_root is None: - libcxx_obj_root = getattr(config, 'libcxx_obj_root', None) + libcxx_obj_root = lit_config.params.get('libcxx_obj_root', None) if libcxx_obj_root is None: - libcxx_obj_root = libcxx_src_root - -# This test suite supports testing against either the system library or the -# locally built one; the former mode is useful for testing ABI compatibility -# between the current headers and a shipping dynamic library. -use_system_lib_str = lit_config.params.get('use_system_lib', None) -if use_system_lib_str is not None: - if use_system_lib_str.lower() in ('1', 'true'): - use_system_lib = True - elif use_system_lib_str.lower() in ('', '0', 'false'): - use_system_lib = False + libcxx_obj_root = getattr(config, 'libcxx_obj_root', None) + if libcxx_obj_root is None: + libcxx_obj_root = libcxx_src_root + + # This test suite supports testing against either the system library or the + # locally built one; the former mode is useful for testing ABI compatibility + # between the current headers and a shipping dynamic library. + use_system_lib_str = lit_config.params.get('use_system_lib', None) + if use_system_lib_str is not None: + if use_system_lib_str.lower() in ('1', 'true'): + use_system_lib = True + elif use_system_lib_str.lower() in ('', '0', 'false'): + use_system_lib = False + else: + lit_config.fatal('user parameter use_system_lib should be 0 or 1') else: - lit_config.fatal('user parameter use_system_lib should be 0 or 1') -else: - # Default to testing against the locally built libc++ library. - use_system_lib = False - lit_config.note("inferred use_system_lib as: %r" % (use_system_lib,)) - -link_flags = [] -link_flags_str = lit_config.params.get('link_flags', None) -if link_flags_str is None: - link_flags_str = getattr(config, 'link_flags', None) - if link_flags_str is None: - cxx_abi = getattr(config, 'cxx_abi', 'libcxxabi') - if cxx_abi == 'libstdc++': - link_flags += ['-lstdc++'] - elif cxx_abi == 'libsupc++': - link_flags += ['-lsupc++'] - elif cxx_abi == 'libcxxabi': - link_flags += ['-lc++abi'] - elif cxx_abi == 'libcxxrt': - link_flags += ['-lcxxrt'] - elif cxx_abi == 'none': - pass - else: - lit_config.fatal('C++ ABI setting %s unsupported for tests' % cxx_abi) - - if sys.platform == 'darwin': - link_flags += ['-lSystem'] - elif sys.platform == 'linux2': - link_flags += [ '-lgcc_eh', '-lc', '-lm', '-lpthread', - '-lrt', '-lgcc_s'] - elif sys.platform.startswith('freebsd'): - link_flags += ['-lc', '-lm', '-pthread', '-lgcc_s'] - else: - lit_config.fatal("unrecognized system") + # Default to testing against the locally built libc++ library. + use_system_lib = False + lit_config.note("inferred use_system_lib as: %r" % (use_system_lib,)) - lit_config.note("inferred link_flags as: %r" % (link_flags,)) -if not link_flags_str is None: - link_flags += shlex.split(link_flags_str) - -# Configure extra compiler flags. -include_paths = ['-I' + libcxx_src_root + '/include', - '-I' + libcxx_src_root + '/test/support'] -library_paths = ['-L' + libcxx_obj_root + '/lib'] -compile_flags = [] - -# Try and get the std version from the command line. Fall back to default given -# in lit.site.cfg is not present. If default is not present then force c++11. -std = lit_config.params.get('std', None) -if std is None: - std = getattr(config, 'std', None) + link_flags = [] + link_flags_str = lit_config.params.get('link_flags', None) + if link_flags_str is None: + link_flags_str = getattr(config, 'link_flags', None) + if link_flags_str is None: + cxx_abi = getattr(config, 'cxx_abi', 'libcxxabi') + if cxx_abi == 'libstdc++': + link_flags += ['-lstdc++'] + elif cxx_abi == 'libsupc++': + link_flags += ['-lsupc++'] + elif cxx_abi == 'libcxxabi': + link_flags += ['-lc++abi'] + elif cxx_abi == 'libcxxrt': + link_flags += ['-lcxxrt'] + elif cxx_abi == 'none': + pass + else: + lit_config.fatal('C++ ABI setting %s unsupported for tests' % cxx_abi) + + if sys.platform == 'darwin': + link_flags += ['-lSystem'] + elif sys.platform == 'linux2': + link_flags += [ '-lgcc_eh', '-lc', '-lm', '-lpthread', + '-lrt', '-lgcc_s'] + elif sys.platform.startswith('freebsd'): + link_flags += ['-lc', '-lm', '-pthread', '-lgcc_s'] + else: + lit_config.fatal("unrecognized system") + + lit_config.note("inferred link_flags as: %r" % (link_flags,)) + if not link_flags_str is None: + link_flags += shlex.split(link_flags_str) + + # Configure extra compiler flags. + include_paths = ['-I' + libcxx_src_root + '/include', + '-I' + libcxx_src_root + '/test/support'] + library_paths = ['-L' + libcxx_obj_root + '/lib'] + compile_flags = [] + + # Try and get the std version from the command line. Fall back to default given + # in lit.site.cfg is not present. If default is not present then force c++11. + std = lit_config.params.get('std', None) if std is None: - std = 'c++11' - lit_config.note('using default std: \'-std=c++11\'') -else: - lit_config.note('using user specified std: \'-std={}\''.format(std)) -compile_flags += ['-std={}'.format(std)] - -built_w_san = getattr(config, 'llvm_use_sanitizer') -if built_w_san and built_w_san.strip(): - built_w_san = built_w_san.strip() - compile_flags += ['-fno-omit-frame-pointer'] - if built_w_san == 'Address': - compile_flags += ['-fsanitize=address'] - config.available_features.add('asan') - elif built_w_san == 'Memory' or built_w_san == 'MemoryWithOrigins': - compile_flags += ['-fsanitize=memory'] - if built_w_san == 'MemoryWithOrigins': - compile_flags += ['-fsanitize-memory-track-origins'] - config.available_features.add('msan') + std = getattr(config, 'std', None) + if std is None: + std = 'c++11' + lit_config.note('using default std: \'-std=c++11\'') else: - lit_config.fatal( - 'unsupported value for libcxx_use_sanitizer: {}'.format(built_w_san)) - -# Configure extra linker parameters. -exec_env = {} -if sys.platform == 'darwin': - if not use_system_lib: - exec_env['DYLD_LIBRARY_PATH'] = os.path.join(libcxx_obj_root, 'lib') -elif sys.platform == 'linux2': - if not use_system_lib: - link_flags += ['-Wl,-R', libcxx_obj_root + '/lib'] - compile_flags += ['-D__STDC_FORMAT_MACROS', '-D__STDC_LIMIT_MACROS', - '-D__STDC_CONSTANT_MACROS'] -elif sys.platform.startswith('freebsd'): - if not use_system_lib: - link_flags += ['-Wl,-R', libcxx_obj_root + '/lib'] -else: - lit_config.fatal("unrecognized system") - -config.test_format = LibcxxTestFormat( - cxx_under_test, - cpp_flags = ['-nostdinc++'] + compile_flags + include_paths, - ld_flags = ['-nodefaultlibs'] + library_paths + ['-lc++'] + link_flags, - exec_env = exec_env) - -# Get or infer the target triple. -config.target_triple = lit_config.params.get('target_triple', None) -# If no target triple was given, try to infer it from the compiler under test. -if config.target_triple is None: - config.target_triple = lit.util.capture( - [cxx_under_test, '-dumpmachine']).strip() - lit_config.note("inferred target_triple as: %r" % (config.target_triple,)) - -# Write an "available feature" that combines the triple when use_system_lib is -# enabled. This is so that we can easily write XFAIL markers for tests that are -# known to fail with versions of libc++ as were shipped with a particular -# triple. -if use_system_lib: - # Drop sub-major version components from the triple, because the current - # XFAIL handling expects exact matches for feature checks. - sanitized_triple = re.sub(r"([^-]+)-([^-]+)-([^-.]+).*", r"\1-\2-\3", - config.target_triple) - config.available_features.add('with_system_lib=%s' % (sanitized_triple,)) + lit_config.note('using user specified std: \'-std={}\''.format(std)) + compile_flags += ['-std={}'.format(std)] + + built_w_san = getattr(config, 'llvm_use_sanitizer') + if built_w_san and built_w_san.strip(): + built_w_san = built_w_san.strip() + compile_flags += ['-fno-omit-frame-pointer'] + if built_w_san == 'Address': + compile_flags += ['-fsanitize=address'] + config.available_features.add('asan') + elif built_w_san == 'Memory' or built_w_san == 'MemoryWithOrigins': + compile_flags += ['-fsanitize=memory'] + if built_w_san == 'MemoryWithOrigins': + compile_flags += ['-fsanitize-memory-track-origins'] + config.available_features.add('msan') + else: + lit_config.fatal( + 'unsupported value for libcxx_use_sanitizer: {}'.format(built_w_san)) + + # Configure extra linker parameters. + exec_env = {} + if sys.platform == 'darwin': + if not use_system_lib: + exec_env['DYLD_LIBRARY_PATH'] = os.path.join(libcxx_obj_root, 'lib') + elif sys.platform == 'linux2': + if not use_system_lib: + link_flags += ['-Wl,-R', libcxx_obj_root + '/lib'] + compile_flags += ['-D__STDC_FORMAT_MACROS', '-D__STDC_LIMIT_MACROS', + '-D__STDC_CONSTANT_MACROS'] + elif sys.platform.startswith('freebsd'): + if not use_system_lib: + link_flags += ['-Wl,-R', libcxx_obj_root + '/lib'] + else: + lit_config.fatal("unrecognized system") + + config.test_format = LibcxxTestFormat( + cxx_under_test, + cpp_flags = ['-nostdinc++'] + compile_flags + include_paths, + ld_flags = ['-nodefaultlibs'] + library_paths + ['-lc++'] + link_flags, + exec_env = exec_env) + + # Get or infer the target triple. + config.target_triple = lit_config.params.get('target_triple', None) + # If no target triple was given, try to infer it from the compiler under test. + if config.target_triple is None: + config.target_triple = lit.util.capture( + [cxx_under_test, '-dumpmachine']).strip() + lit_config.note("inferred target_triple as: %r" % (config.target_triple,)) + + # Write an "available feature" that combines the triple when use_system_lib is + # enabled. This is so that we can easily write XFAIL markers for tests that are + # known to fail with versions of libc++ as were shipped with a particular + # triple. + if use_system_lib: + # Drop sub-major version components from the triple, because the current + # XFAIL handling expects exact matches for feature checks. + sanitized_triple = re.sub(r"([^-]+)-([^-]+)-([^-.]+).*", r"\1-\2-\3", + config.target_triple) + config.available_features.add('with_system_lib=%s' % (sanitized_triple,))