[libcxx] Allow use of ShTest in libc++ tests along with other changes.

Summary:
This patch allows the use of LIT's ShTest format in the libc++ test suite. ShTests have the suffix '.sh.cpp'. It also introduces a series of other changes. These changes are:

- More functionality including parsing test metadata has been moved into LIT.
- LibcxxTestFormat now supports multi-part suffixes.
- the `CXXCompiler` functionality has been used to shrink the size of LibcxxTestFormat. 
- The recursive loading of the site config has been turned into `libcxx.test.config.loadSiteConfig` so it can be used with libc++abi.
- Temporary files are now created in the build directory of libc++. This follows how it is down in ShTest.
- `not.py` was added as a utility executable that mirrors the functionality of LLVM's `not` executable. 
- The first ShTest test was added under test/libcxx/double_include.sh.cpp


Reviewers: jroelofs, danalbert

Reviewed By: danalbert

Subscribers: cfe-commits

Differential Revision: http://reviews.llvm.org/D7073

llvm-svn: 226844
This commit is contained in:
Eric Fiselier 2015-01-22 18:05:58 +00:00
parent d4255baede
commit 74e82fa4f3
12 changed files with 542 additions and 221 deletions

View File

@ -1,13 +1,15 @@
import os
import lit.util
import libcxx.util
class CXXCompiler(object):
def __init__(self, path, flags=[], compile_flags=[], link_flags=[], use_ccache=False):
def __init__(self, path, flags=None, compile_flags=None, link_flags=None,
use_ccache=False):
self.path = path
self.flags = list(flags)
self.compile_flags = list(compile_flags)
self.link_flags = list(link_flags)
self.flags = list(flags or [])
self.compile_flags = list(compile_flags or [])
self.link_flags = list(link_flags or [])
self.use_ccache = use_ccache
self.type = None
self.version = None
@ -36,66 +38,85 @@ class CXXCompiler(object):
self.type = compiler_type
self.version = (major_ver, minor_ver, patchlevel)
def _basicCmd(self, infiles, out, is_link=False):
def _basicCmd(self, source_files, out, is_link=False):
cmd = []
if self.use_ccache and not is_link:
cmd += ['ccache']
cmd += [self.path]
if out is not None:
cmd += ['-o', out]
if isinstance(infiles, list):
cmd += infiles
elif isinstance(infiles, str):
cmd += [infiles]
if isinstance(source_files, list):
cmd += source_files
elif isinstance(source_files, str):
cmd += [source_files]
else:
raise TypeError('infiles must be a string or list')
raise TypeError('source_files must be a string or list')
return cmd
def preprocessCmd(self, infiles, out=None, flags=[]):
cmd = self._basicCmd(infiles, out) + ['-x', 'c++', '-E']
def preprocessCmd(self, source_files, out=None, flags=[]):
cmd = self._basicCmd(source_files, out) + ['-x', 'c++', '-E']
cmd += self.flags + self.compile_flags + flags
return cmd
def compileCmd(self, infiles, out=None, flags=[]):
cmd = self._basicCmd(infiles, out) + ['-x', 'c++', '-c']
def compileCmd(self, source_files, out=None, flags=[]):
cmd = self._basicCmd(source_files, out) + ['-x', 'c++', '-c']
cmd += self.flags + self.compile_flags + flags
return cmd
def linkCmd(self, infiles, out=None, flags=[]):
cmd = self._basicCmd(infiles, out, is_link=True)
def linkCmd(self, source_files, out=None, flags=[]):
cmd = self._basicCmd(source_files, out, is_link=True)
cmd += self.flags + self.link_flags + flags
return cmd
def compileLinkCmd(self, infiles, out=None, flags=[]):
cmd = self._basicCmd(infiles, out, is_link=True) + ['-x', 'c++']
def compileLinkCmd(self, source_files, out=None, flags=[]):
cmd = self._basicCmd(source_files, out, is_link=True) + ['-x', 'c++']
cmd += self.flags + self.compile_flags + self.link_flags + flags
return cmd
def preprocess(self, infiles, out=None, flags=[], env=None, cwd=None):
cmd = self.preprocessCmd(infiles, out, flags)
def preprocess(self, source_files, out=None, flags=[], env=None, cwd=None):
cmd = self.preprocessCmd(source_files, out, flags)
out, err, rc = lit.util.executeCommand(cmd, env=env, cwd=cwd)
return cmd, out, err, rc
def compile(self, infiles, out=None, flags=[], env=None, cwd=None):
cmd = self.compileCmd(infiles, out, flags)
def compile(self, source_files, out=None, flags=[], env=None, cwd=None):
cmd = self.compileCmd(source_files, out, flags)
out, err, rc = lit.util.executeCommand(cmd, env=env, cwd=cwd)
return cmd, out, err, rc
def link(self, infiles, out=None, flags=[], env=None, cwd=None):
cmd = self.linkCmd(infiles, out, flags)
def link(self, source_files, out=None, flags=[], env=None, cwd=None):
cmd = self.linkCmd(source_files, out, flags)
out, err, rc = lit.util.executeCommand(cmd, env=env, cwd=cwd)
return cmd, out, err, rc
def compileLink(self, infiles, out=None, flags=[], env=None, cwd=None):
cmd = self.compileLinkCmd(infiles, out, flags)
def compileLink(self, source_files, out=None, flags=[], env=None,
cwd=None):
cmd = self.compileLinkCmd(source_files, out, flags)
out, err, rc = lit.util.executeCommand(cmd, env=env, cwd=cwd)
return cmd, out, err, rc
def dumpMacros(self, infiles=None, flags=[], env=None, cwd=None):
if infiles is None:
infiles = '/dev/null'
def compileLinkTwoSteps(self, source_file, out=None, object_file=None,
flags=[], env=None, cwd=None):
if not isinstance(source_file, str):
raise TypeError('This function only accepts a single input file')
if object_file is None:
# Create, use and delete a temporary object file if none is given.
with_fn = lambda: libcxx.util.guardedTempFilename(suffix='.o')
else:
# Otherwise wrap the filename in a context manager function.
with_fn = lambda: libcxx.util.nullContext(object_file)
with with_fn() as object_file:
cmd, output, err, rc = self.compile(source_file, object_file,
flags=flags, env=env, cwd=cwd)
if rc != 0:
return cmd, output, err, rc
return self.link(object_file, out=out, flags=flags, env=env,
cwd=cwd)
def dumpMacros(self, source_files=None, flags=[], env=None, cwd=None):
if source_files is None:
source_files = os.devnull
flags = ['-dM'] + flags
cmd, out, err, rc = self.preprocess(infiles, flags=flags, env=env,
cmd, out, err, rc = self.preprocess(source_files, flags=flags, env=env,
cwd=cwd)
if rc != 0:
return None

View File

@ -0,0 +1,111 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Test that we can include each header in two TU's and link them together.
// RUN: %cxx -c %s -o %t.first.o %flags %compile_flags
// RUN: %cxx -c %s -o %t.second.o -DWITH_MAIN %flags %compile_flags
// RUN: %cxx -o %t.exe %t.first.o %t.second.o %flags %link_flags
// RUN: %run
#include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <ccomplex>
#include <cctype>
#include <cerrno>
#include <cfenv>
#include <cfloat>
#include <chrono>
#include <cinttypes>
#include <ciso646>
#include <climits>
#include <clocale>
#include <cmath>
#include <codecvt>
#include <complex>
#include <complex.h>
#include <condition_variable>
#include <csetjmp>
#include <csignal>
#include <cstdarg>
#include <cstdbool>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctgmath>
#include <ctime>
#include <cwchar>
#include <cwctype>
#include <deque>
#include <exception>
#include <experimental/dynarray>
#include <experimental/optional>
#include <experimental/string_view>
#include <experimental/type_traits>
#include <experimental/utility>
#include <ext/hash_map>
#include <ext/hash_set>
#include <forward_list>
#include <fstream>
#include <functional>
#include <initializer_list>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <iterator>
#include <limits>
#include <list>
#include <locale>
#include <map>
#include <memory>
#include <new>
#include <numeric>
#include <ostream>
#include <queue>
#include <random>
#include <ratio>
#include <regex>
#include <scoped_allocator>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <strstream>
#include <system_error>
#include <tgmath.h>
#include <tuple>
#include <typeindex>
#include <typeinfo>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <valarray>
#include <vector>
#ifndef _LIBCPP_HAS_NO_THREADS
#include <atomic>
#include <future>
#include <mutex>
#include <shared_mutex>
#include <thread>
#endif
#if defined(WITH_MAIN)
int main() {}
#endif

View File

@ -0,0 +1,17 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// RUN: %build
// RUN: not %run
int main()
{
return 1;
}

View File

@ -0,0 +1,11 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#error This test should not compile.

View File

@ -0,0 +1,13 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
int main()
{
}

View File

@ -0,0 +1,16 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// RUN: %build
// RUN: %run
int main()
{
}

View File

@ -1,6 +1,7 @@
import locale
import os
import platform
import pkgutil
import re
import shlex
import sys
@ -12,6 +13,34 @@ from libcxx.test.format import LibcxxTestFormat
from libcxx.compiler import CXXCompiler
def loadSiteConfig(lit_config, config, param_name, env_name):
# We haven't loaded the site specific configuration (the user is
# probably trying to run on a test file directly, and either the site
# configuration hasn't been created by the build system, or we are in an
# out-of-tree build situation).
site_cfg = lit_config.params.get(param_name,
os.environ.get(env_name))
if not site_cfg:
lit_config.warning('No site specific configuration file found!'
' Running the tests in the default configuration.')
elif not os.path.isfile(site_cfg):
lit_config.fatal(
"Specified site configuration file does not exist: '%s'" %
site_cfg)
else:
lit_config.note('using site specific configuration at %s' % site_cfg)
ld_fn = lit_config.load_config
# Null out the load_config function so that lit.site.cfg doesn't
# recursively load a config even if it tries.
# TODO: This is one hell of a hack. Fix it.
def prevent_reload_fn(*args, **kwargs):
pass
lit_config.load_config = prevent_reload_fn
ld_fn(config, site_cfg)
lit_config.load_config = ld_fn
class Configuration(object):
# pylint: disable=redefined-outer-name
def __init__(self, lit_config, config):
@ -26,6 +55,7 @@ class Configuration(object):
self.use_system_cxx_lib = False
self.use_clang_verify = False
self.long_tests = None
self.execute_external = False
if platform.system() not in ('Darwin', 'FreeBSD', 'Linux'):
self.lit_config.fatal("unrecognized system")
@ -57,18 +87,21 @@ class Configuration(object):
self.configure_cxx_library_root()
self.configure_use_system_cxx_lib()
self.configure_use_clang_verify()
self.configure_execute_external()
self.configure_ccache()
self.configure_env()
self.configure_compile_flags()
self.configure_link_flags()
self.configure_sanitizer()
self.configure_substitutions()
self.configure_features()
def print_config_info(self):
# Print the final compile and link flags.
self.lit_config.note('Using compiler: %s' % self.cxx.path)
self.lit_config.note('Using flags: %s' % self.cxx.flags)
self.lit_config.note('Using compile flags: %s' % self.cxx.compile_flags)
self.lit_config.note('Using compile flags: %s'
% self.cxx.compile_flags)
self.lit_config.note('Using link flags: %s' % self.cxx.link_flags)
# Print as list to prevent "set([...])" from being printed.
self.lit_config.note('Using available_features: %s' %
@ -79,6 +112,7 @@ class Configuration(object):
return LibcxxTestFormat(
self.cxx,
self.use_clang_verify,
self.execute_external,
exec_env=self.env)
def configure_cxx(self):
@ -106,7 +140,6 @@ class Configuration(object):
self.config.available_features.add('%s-%s.%s' % (
cxx_type, maj_v, min_v))
def configure_src_root(self):
self.libcxx_src_root = self.get_lit_conf(
'libcxx_src_root', os.path.dirname(self.config.test_source_root))
@ -140,6 +173,22 @@ class Configuration(object):
self.lit_config.note(
"inferred use_clang_verify as: %r" % self.use_clang_verify)
def configure_execute_external(self):
# Choose between lit's internal shell pipeline runner and a real shell.
# If LIT_USE_INTERNAL_SHELL is in the environment, we use that as the
# default value. Otherwise we default to internal on Windows and
# external elsewhere, as bash on Windows is usually very slow.
use_lit_shell_default = os.environ.get('LIT_USE_INTERNAL_SHELL')
if use_lit_shell_default is not None:
use_lit_shell_default = use_lit_shell_default != '0'
else:
use_lit_shell_default = sys.platform == 'win32'
# Check for the command line parameter using the default value if it is
# not present.
use_lit_shell = self.get_lit_bool('use_lit_shell',
use_lit_shell_default)
self.execute_external = not use_lit_shell
def configure_ccache(self):
use_ccache = self.get_lit_bool('use_ccache', False)
if use_ccache:
@ -233,12 +282,12 @@ class Configuration(object):
self.config.available_features.add('long_tests')
def configure_compile_flags(self):
no_default_flags = self.get_lit_bool('no_default_flags', False)
if not no_default_flags:
self.configure_default_compile_flags()
# Configure extra flags
compile_flags_str = self.get_lit_conf('compile_flags', '')
self.cxx.compile_flags += shlex.split(compile_flags_str)
no_default_flags = self.get_lit_bool('no_default_flags', False)
if not no_default_flags:
self.configure_default_compile_flags()
# Configure extra flags
compile_flags_str = self.get_lit_conf('compile_flags', '')
self.cxx.compile_flags += shlex.split(compile_flags_str)
def configure_default_compile_flags(self):
# Try and get the std version from the command line. Fall back to
@ -283,9 +332,10 @@ class Configuration(object):
self.cxx.flags += ['-target', self.config.target_triple]
def configure_compile_flags_header_includes(self):
self.cxx.compile_flags += ['-I' + self.libcxx_src_root + '/test/support']
libcxx_headers = self.get_lit_conf('libcxx_headers',
self.libcxx_src_root + '/include')
self.cxx.compile_flags += [
'-I' + os.path.join(self.libcxx_src_root, 'test/support')]
libcxx_headers = self.get_lit_conf(
'libcxx_headers', os.path.join(self.libcxx_src_root, 'include'))
if not os.path.isdir(libcxx_headers):
self.lit_config.fatal("libcxx_headers='%s' is not a directory."
% libcxx_headers)
@ -316,16 +366,16 @@ class Configuration(object):
def configure_link_flags(self):
no_default_flags = self.get_lit_bool('no_default_flags', False)
if not no_default_flags:
self.cxx.link_flags += ['-nodefaultlibs']
self.cxx.link_flags += ['-nodefaultlibs']
# Configure library path
self.configure_link_flags_cxx_library_path()
self.configure_link_flags_abi_library_path()
# Configure library path
self.configure_link_flags_cxx_library_path()
self.configure_link_flags_abi_library_path()
# Configure libraries
self.configure_link_flags_cxx_library()
self.configure_link_flags_abi_library()
self.configure_extra_library_flags()
# Configure libraries
self.configure_link_flags_cxx_library()
self.configure_link_flags_abi_library()
self.configure_extra_library_flags()
link_flags_str = self.get_lit_conf('link_flags', '')
self.cxx.link_flags += shlex.split(link_flags_str)
@ -425,7 +475,8 @@ class Configuration(object):
elif san == 'Memory' or san == 'MemoryWithOrigins':
self.cxx.flags += ['-fsanitize=memory']
if san == 'MemoryWithOrigins':
self.cxx.compile_flags += ['-fsanitize-memory-track-origins']
self.cxx.compile_flags += [
'-fsanitize-memory-track-origins']
if llvm_symbolizer is not None:
self.env['MSAN_SYMBOLIZER_PATH'] = llvm_symbolizer
self.config.available_features.add('msan')
@ -442,6 +493,45 @@ class Configuration(object):
self.lit_config.fatal('unsupported value for '
'use_sanitizer: {0}'.format(san))
def configure_substitutions(self):
sub = self.config.substitutions
# Configure compiler substitions
sub.append(('%cxx', self.cxx.path))
# Configure flags substitutions
flags_str = ' '.join(self.cxx.flags)
compile_flags_str = ' '.join(self.cxx.compile_flags)
link_flags_str = ' '.join(self.cxx.link_flags)
all_flags = '%s %s %s' % (flags_str, compile_flags_str, link_flags_str)
sub.append(('%flags', flags_str))
sub.append(('%compile_flags', compile_flags_str))
sub.append(('%link_flags', link_flags_str))
sub.append(('%all_flags', all_flags))
# Add compile and link shortcuts
compile_str = (self.cxx.path + ' -o %t.o %s -c ' + flags_str
+ compile_flags_str)
link_str = (self.cxx.path + ' -o %t.exe %t.o ' + flags_str
+ link_flags_str)
assert type(link_str) is str
build_str = self.cxx.path + ' -o %t.exe %s ' + all_flags
sub.append(('%compile', compile_str))
sub.append(('%link', link_str))
sub.append(('%build', build_str))
# Configure exec prefix substitutions.
exec_env_str = 'env ' if len(self.env) != 0 else ''
for k, v in self.env.items():
exec_env_str += ' %s=%s' % (k, v)
# Configure run env substitution.
exec_str = ''
if self.lit_config.useValgrind:
exec_str = ' '.join(self.lit_config.valgrindArgs) + exec_env_str
sub.append(('%exec', exec_str))
# Configure run shortcut
sub.append(('%run', exec_str + ' %t.exe'))
# Configure not program substitions
not_py = os.path.join(self.libcxx_src_root, 'utils', 'not', 'not.py')
not_str = '%s %s' % (sys.executable, not_py)
sub.append(('not', not_str))
def configure_triple(self):
# Get or infer the target triple.
self.config.target_triple = self.get_lit_conf('target_triple')

View File

@ -1,12 +1,15 @@
import errno
import os
import tempfile
import time
import lit.formats # pylint: disable=import-error
import lit.Test # pylint: disable=import-error
import lit.TestRunner # pylint: disable=import-error
import lit.util # pylint: disable=import-error
import libcxx.util
class LibcxxTestFormat(lit.formats.FileBasedTest):
class LibcxxTestFormat(object):
"""
Custom test format handler for use with the test format use by libc++.
@ -14,13 +17,31 @@ class LibcxxTestFormat(lit.formats.FileBasedTest):
FOO.pass.cpp - Executable test which should compile, run, and exit with
code 0.
FOO.fail.cpp - Negative test case which is expected to fail compilation.
FOO.sh.cpp - A test that uses LIT's ShTest format.
"""
def __init__(self, cxx, use_verify_for_fail, exec_env):
def __init__(self, cxx, use_verify_for_fail, execute_external, exec_env):
self.cxx = cxx
self.use_verify_for_fail = use_verify_for_fail
self.execute_external = execute_external
self.exec_env = dict(exec_env)
# TODO: Move this into lit's FileBasedTest
def getTestsInDirectory(self, testSuite, path_in_suite,
litConfig, localConfig):
source_path = testSuite.getSourcePath(path_in_suite)
for filename in os.listdir(source_path):
# Ignore dot files and excluded tests.
if filename.startswith('.') or filename in localConfig.excludes:
continue
filepath = os.path.join(source_path, filename)
if not os.path.isdir(filepath):
if any([filename.endswith(ext)
for ext in localConfig.suffixes]):
yield lit.Test.Test(testSuite, path_in_suite + (filename,),
localConfig)
def execute(self, test, lit_config):
while True:
try:
@ -31,154 +52,94 @@ class LibcxxTestFormat(lit.formats.FileBasedTest):
time.sleep(0.1)
def _execute(self, test, lit_config):
# Extract test metadata from the test file.
requires = []
unsupported = []
use_verify = False
with open(test.getSourcePath()) as f:
for ln in f:
if 'XFAIL:' in ln:
items = ln[ln.index('XFAIL:') + 6:].split(',')
test.xfails.extend([s.strip() for s in items])
elif 'REQUIRES:' in ln:
items = ln[ln.index('REQUIRES:') + 9:].split(',')
requires.extend([s.strip() for s in items])
elif 'UNSUPPORTED:' in ln:
items = ln[ln.index('UNSUPPORTED:') + 12:].split(',')
unsupported.extend([s.strip() for s in items])
elif 'USE_VERIFY' in ln and self.use_verify_for_fail:
use_verify = True
elif not ln.strip().startswith("//") and ln.strip():
# Stop at the first non-empty line that is not a C++
# comment.
break
name = test.path_in_suite[-1]
is_sh_test = name.endswith('.sh.cpp')
is_pass_test = name.endswith('.pass.cpp')
is_fail_test = name.endswith('.fail.cpp')
# Check that we have the required features.
#
# FIXME: For now, this is cribbed from lit.TestRunner, to avoid
# introducing a dependency there. What we more ideally would like to do
# is lift the "requires" handling to be a core lit framework feature.
missing_required_features = [
f for f in requires
if f not in test.config.available_features
]
if missing_required_features:
return (lit.Test.UNSUPPORTED,
"Test requires the following features: %s" % (
', '.join(missing_required_features),))
res = lit.TestRunner.parseIntegratedTestScript(
test, require_script=is_sh_test)
# Check if a result for the test was returned. If so return that
# result.
if isinstance(res, lit.Test.Result):
return res
if lit_config.noExecute:
return lit.Test.Result(lit.Test.PASS)
# res is not an instance of lit.test.Result. Expand res into its parts.
script, tmpBase, execDir = res
# Check that we don't have run lines on tests that don't support them.
if not is_sh_test and len(script) != 0:
lit_config.fatal('Unsupported RUN line found in test %s' % name)
unsupported_features = [f for f in unsupported
if f in test.config.available_features]
if unsupported_features:
return (lit.Test.UNSUPPORTED,
"Test is unsupported with the following features: %s" % (
', '.join(unsupported_features),))
# Evaluate the test.
return self._evaluate_test(test, use_verify, lit_config)
def _make_report(self, cmd, out, err, rc): # pylint: disable=no-self-use
report = "Command: %s\n" % cmd
report += "Exit Code: %d\n" % rc
if out:
report += "Standard Output:\n--\n%s--\n" % out
if err:
report += "Standard Error:\n--\n%s--\n" % err
report += '\n'
return cmd, report, rc
def _compile(self, output_path, source_path, use_verify=False):
extra_flags = []
if use_verify:
extra_flags += ['-Xclang', '-verify']
return self.cxx.compile(source_path, out=output_path, flags=extra_flags)
def _link(self, exec_path, object_path):
return self.cxx.link(object_path, out=exec_path)
def _compile_and_link(self, exec_path, source_path):
object_file = tempfile.NamedTemporaryFile(suffix=".o", delete=False)
object_path = object_file.name
object_file.close()
try:
cmd, out, err, rc = self.cxx.compile(source_path, out=object_path)
if rc != 0:
return cmd, out, err, rc
return self.cxx.link(object_path, out=exec_path)
finally:
try:
os.remove(object_path)
except OSError:
pass
def _build(self, exec_path, source_path, compile_only=False,
use_verify=False):
if compile_only:
cmd, out, err, rc = self._compile(exec_path, source_path,
use_verify)
# Dispatch the test based on its suffix.
if is_sh_test:
return lit.TestRunner._runShTest(test, lit_config,
self.execute_external, script,
tmpBase, execDir)
elif is_fail_test:
return self._evaluate_fail_test(test)
elif is_pass_test:
return self._evaluate_pass_test(test, tmpBase, execDir, lit_config)
else:
assert not use_verify
cmd, out, err, rc = self._compile_and_link(exec_path, source_path)
return self._make_report(cmd, out, err, rc)
# No other test type is supported
assert False
def _clean(self, exec_path): # pylint: disable=no-self-use
try:
os.remove(exec_path)
except OSError:
pass
libcxx.util.cleanFile(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, rc = lit.util.executeCommand(cmd, cwd=in_dir)
return self._make_report(cmd, out, err, rc)
def _evaluate_test(self, test, use_verify, lit_config):
name = test.path_in_suite[-1]
def _evaluate_pass_test(self, test, tmpBase, execDir, lit_config):
source_path = test.getSourcePath()
source_dir = os.path.dirname(source_path)
exec_path = tmpBase + '.exe'
object_path = tmpBase + '.o'
# Create the output directory if it does not already exist.
lit.util.mkdir_p(os.path.dirname(tmpBase))
try:
# Compile the test
cmd, out, err, rc = self.cxx.compileLinkTwoSteps(
source_path, out=exec_path, object_file=object_path,
cwd=execDir)
compile_cmd = cmd
if rc != 0:
report = libcxx.util.makeReport(cmd, out, err, rc)
report += "Compilation failed unexpectedly!"
return lit.Test.FAIL, report
# Run the test
cmd = []
if self.exec_env:
cmd += ['env']
cmd += ['%s=%s' % (k, v) for k, v in self.exec_env.items()]
if lit_config.useValgrind:
cmd = lit_config.valgrindArgs + cmd
cmd += [exec_path]
out, err, rc = lit.util.executeCommand(
cmd, cwd=os.path.dirname(source_path))
if rc != 0:
report = libcxx.util.makeReport(cmd, out, err, rc)
report = "Compiled With: %s\n%s" % (compile_cmd, report)
report += "Compiled test failed unexpectedly!"
return lit.Test.FAIL, report
return lit.Test.PASS, ''
finally:
# Note that cleanup of exec_file happens in `_clean()`. If you
# override this, cleanup is your reponsibility.
libcxx.util.cleanFile(object_path)
self._clean(exec_path)
# Check what kind of test this is.
assert name.endswith('.pass.cpp') or name.endswith('.fail.cpp')
expected_compile_fail = name.endswith('.fail.cpp')
# If this is a compile (failure) test, build it and check for failure.
if expected_compile_fail:
cmd, report, rc = self._build('/dev/null', source_path,
compile_only=True,
use_verify=use_verify)
expected_rc = 0 if use_verify else 1
if rc == expected_rc:
return lit.Test.PASS, ""
else:
return (lit.Test.FAIL,
report + 'Expected compilation to fail!\n')
def _evaluate_fail_test(self, test):
source_path = test.getSourcePath()
# TODO: Move the checking of USE_VERIFY into
# lit.TestRunner.parseIntegratedTestScript by adding support for custom
# tags.
with open(source_path, 'r') as f:
contents = f.read()
use_verify = 'USE_VERIFY' in contents and self.use_verify_for_fail
extra_flags = ['-Xclang', '-verify'] if use_verify else []
cmd, out, err, rc = self.cxx.compile(source_path, out=os.devnull,
flags=extra_flags)
expected_rc = 0 if use_verify else 1
if rc == expected_rc:
return lit.Test.PASS, ''
else:
exec_file = tempfile.NamedTemporaryFile(suffix="exe", delete=False)
exec_path = exec_file.name
exec_file.close()
try:
cmd, report, rc = self._build(exec_path, source_path)
compile_cmd = cmd
if rc != 0:
report += "Compilation failed unexpectedly!"
return lit.Test.FAIL, report
cmd, report, rc = self._run(exec_path, lit_config,
source_dir)
if rc != 0:
report = "Compiled With: %s\n%s" % (compile_cmd, report)
report += "Compiled test failed unexpectedly!"
return lit.Test.FAIL, report
finally:
# Note that cleanup of exec_file happens in `_clean()`. If you
# override this, cleanup is your reponsibility.
self._clean(exec_path)
return lit.Test.PASS, ""
report = libcxx.util.makeReport(cmd, out, err, rc)
return (lit.Test.FAIL,
report + 'Expected compilation to fail!\n')

View File

@ -0,0 +1,46 @@
from contextlib import contextmanager
import os
import tempfile
def cleanFile(filename):
try:
os.remove(filename)
except OSError:
pass
@contextmanager
def guardedTempFilename(suffix='', prefix='', dir=None):
# Creates and yeilds a temporary filename within a with statement. The file
# is removed upon scope exit.
handle, name = tempfile.mkstemp(suffix=suffix, prefix=prefix, dir=dir)
os.close(handle)
yield name
cleanFile(name)
@contextmanager
def guardedFilename(name):
# yeilds a filename within a with statement. The file is removed upon scope
# exit.
yield name
cleanFile(name)
@contextmanager
def nullContext(value):
# yeilds a variable within a with statement. No action is taken upon scope
# exit.
yield value
def makeReport(cmd, out, err, rc):
report = "Command: %s\n" % cmd
report += "Exit Code: %d\n" % rc
if out:
report += "Standard Output:\n--\n%s--\n" % out
if err:
report += "Standard Error:\n--\n%s--\n" % err
report += '\n'
return report

View File

@ -15,37 +15,27 @@ if 'PYLINT_IMPORT' in os.environ:
config.name = 'libc++'
# suffixes: A list of file extensions to treat as test files.
config.suffixes = ['.cpp']
config.suffixes = ['.pass.cpp', '.fail.cpp', '.sh.cpp']
# test_source_root: The root path where tests are located.
config.test_source_root = os.path.dirname(__file__)
# Infer the test_exec_root from the libcxx_object root.
libcxx_obj_root = getattr(config, 'libcxx_obj_root', None)
if libcxx_obj_root is not None:
config.test_exec_root = os.path.join(libcxx_obj_root, 'test')
obj_root = getattr(config, 'libcxx_obj_root', None)
# Check that the test exec root is known.
if config.test_exec_root is None:
# Otherwise, we haven't loaded the site specific configuration (the user is
# probably trying to run on a test file directly, and either the site
# configuration hasn't been created by the build system, or we are in an
# out-of-tree build situation).
site_cfg = lit_config.params.get('libcxx_site_config',
os.environ.get('LIBCXX_SITE_CONFIG'))
if not site_cfg:
lit_config.warning('No site specific configuration file found!'
' Running the tests in the default configuration.')
# TODO: Set test_exec_root to a temporary directory where output files
# can be placed. This is needed for ShTest.
elif not os.path.isfile(site_cfg):
lit_config.fatal(
"Specified site configuration file does not exist: '%s'" %
site_cfg)
else:
lit_config.note('using site specific configuration at %s' % site_cfg)
lit_config.load_config(config, site_cfg)
raise SystemExit()
if obj_root is None:
import libcxx.test.config
libcxx.test.config.loadSiteConfig(lit_config, config, 'libcxx_site_config',
'LIBCXX_SITE_CONFIG')
obj_root = getattr(config, 'libcxx_obj_root', None)
if obj_root is None:
import tempfile
obj_root = tempfile.mkdtemp(prefix='libcxx-testsuite-')
lit_config.warning('Creating temporary directory for object root: %s' %
obj_root)
config.test_exec_root = os.path.join(obj_root, 'test')
cfg_variant = getattr(config, 'configuration_variant', 'libcxx')
if cfg_variant:

35
libcxx/utils/not/not.py Normal file
View File

@ -0,0 +1,35 @@
"""not.py is a utility for inverting the return code of commands.
It acts similar to llvm/utils/not.
ex: python /path/to/not.py ' echo hello
echo $? // (prints 1)
"""
import distutils.spawn
import subprocess
import sys
def main():
argv = list(sys.argv)
del argv[0]
if len(argv) > 0 and argv[0] == '--crash':
del argv[0]
expectCrash = True
else:
expectCrash = False
if len(argv) == 0:
return 1
prog = distutils.spawn.find_executable(argv[0])
if prog is None:
sys.stderr.write('Failed to find program %s' % argv[0])
return 1
rc = subprocess.call(argv)
if rc < 0:
return 0 if expectCrash else 1
if expectCrash:
return 1
return rc == 0
if __name__ == '__main__':
exit(main())

View File

@ -131,6 +131,16 @@ Note: This does not use the installed headers.
</blockquote>
</p>
<p>
<h3 class="lit-option">use_lit_shell=&lt;bool&gt;</h3>
<blockquote class="lit-option-desc">
Enable or disable the use of LIT's internal shell in ShTests. If the enviroment
variable <code>LIT_USE_INTERNAL_SHELL</code> is present then that is used as the
default value. Otherwise the default value is <code>True</code> on Windows and
<code>False</code> on every other platform.
</blockquote>
</p>
<p>
<h3 class="lit-option">no_default_flags=&lt;bool&gt;</h3>
<blockquote class="lit-option-desc">