Add TargetInfo class for determining things like locale support and platform
name.
http://reviews.llvm.org/D7380
Files:
test/config.py
test/format.py
test/remote.py
EMAIL PREFERENCES
http://reviews.llvm.org/settings/panel/emailpreferences/
Index: test/config.py
===================================================================
--- test/config.py
+++ test/config.py
@@ -1,3 +1,4 @@
+import importlib
import locale
import os
import platform
@@ -11,7 +12,7 @@
from libcxx.test.format import LibcxxTestFormat
from libcxx.compiler import CXXCompiler
-
+from libcxx.test.remote import *
def loadSiteConfig(lit_config, config, param_name, env_name):
# We haven't loaded the site specific configuration (the user is
@@ -57,9 +58,6 @@
self.long_tests = None
self.execute_external = False
- if platform.system() not in ('Darwin', 'FreeBSD', 'Linux'):
- self.lit_config.fatal("unrecognized system")
-
def get_lit_conf(self, name, default=None):
val = self.lit_config.params.get(name, None)
if val is None:
@@ -80,6 +78,8 @@
"parameter '{}' should be true or false".format(name))
def configure(self):
+ self.configure_target_executor()
+ self.configure_target_info()
self.configure_cxx()
self.configure_triple()
self.configure_src_root()
@@ -113,8 +113,36 @@
self.cxx,
self.use_clang_verify,
self.execute_external,
+ self.target_executor,
exec_env=self.env)
+ def configure_target_executor(self):
+ exec_str = self.get_lit_conf('target_executor', None)
+ if exec_str:
+ print exec_str
+ self.target_executor = eval(exec_str)
+ self.lit_config.note("inferred target_executor as: %r" % exec_str)
+ if self.lit_config.useValgrind:
+ # We have no way of knowing where in the chain the
+ # ValgrindExecutor is supposed to go. It is likely
+ # that the user wants it at the end, but we have no
+ # way of getting at that easily.
+ selt.lit_config.fatal("Cannot infer how to create a Valgrind "
+ " target_executor.")
+ else:
+ te = LocalExecutor()
+ if self.lit_config.useValgrind:
+ te = ValgrindExecutor(self.lit_config.valgrindArgs, te)
+ self.target_executor = te
+
+ def configure_target_info(self):
+ info_str = self.get_lit_conf('target_info', None)
+ if info_str:
+ self.target_info = eval(info_str)
+ self.lit_config.note("inferred target_info as: %r" % info_str)
+ else:
+ self.target_info = LocalTI()
+
def configure_cxx(self):
# Gather various compiler parameters.
cxx = self.get_lit_conf('cxx_under_test')
@@ -235,18 +263,46 @@
'ru_RU.UTF-8': 'Russian_Russia.1251',
'zh_CN.UTF-8': 'Chinese_China.936',
},
+ 'discover': {
+ 'cs_CZ.ISO8859-2': 'Czech_Czech Republic.1250',
+ 'cs_CZ.ISO8859-2': 'cs_CZ.ISO-8859-2',
+ 'cs_CZ.ISO8859-2': 'cs_CZ.ISO8859-2',
+ 'cs_CZ.ISO8859-2': 'cs_CZ.ISO8859-2',
+ 'en_US.UTF-8': 'English_United States.1252',
+ 'en_US.UTF-8': 'en_US.UTF-8',
+ 'en_US.UTF-8': 'en_US.UTF-8',
+ 'en_US.UTF-8': 'en_US.UTF-8',
+ 'fr_CA.ISO8859-1': 'French_Canada.1252',
+ 'fr_CA.ISO8859-1': 'cs_CZ.ISO8859-1',
+ 'fr_CA.ISO8859-1': 'fr_CA.ISO-8859-1',
+ 'fr_CA.ISO8859-1': 'fr_CA.ISO8859-1',
+ 'fr_FR.UTF-8': 'French_France.1252',
+ 'fr_FR.UTF-8': 'fr_FR.UTF-8',
+ 'fr_FR.UTF-8': 'fr_FR.UTF-8',
+ 'fr_FR.UTF-8': 'fr_FR.UTF-8',
+ 'ru_RU.UTF-8': 'Russian_Russia.1251',
+ 'ru_RU.UTF-8': 'ru_RU.UTF-8',
+ 'ru_RU.UTF-8': 'ru_RU.UTF-8',
+ 'ru_RU.UTF-8': 'ru_RU.UTF-8',
+ 'zh_CN.UTF-8': 'Chinese_China.936',
+ 'zh_CN.UTF-8': 'zh_CN.UTF-8',
+ 'zh_CN.UTF-8': 'zh_CN.UTF-8',
+ 'zh_CN.UTF-8': 'zh_CN.UTF-8',
+ },
}
+ target_platform = self.target_info.platform()
+
default_locale = locale.setlocale(locale.LC_ALL)
- for feature, loc in locales[platform.system()].items():
- try:
- locale.setlocale(locale.LC_ALL, loc)
- self.config.available_features.add(
- 'locale.{0}'.format(feature))
- except locale.Error:
- self.lit_config.warning('The locale {0} is not supported by '
- 'your platform. Some tests will be '
- 'unsupported.'.format(loc))
+ if target_platform in locales:
+ for feature, loc in locales[platform.system()].items():
+ if self.target_info.supports_locale(loc):
+ self.config.available_features.add(
+ 'locale.{0}'.format(feature))
+ else:
+ self.lit_config.warning('The locale {0} is not supported by'
+ ' your platform. Some tests will be'
+ ' unsupported.'.format(loc))
locale.setlocale(locale.LC_ALL, default_locale)
# Write an "available feature" that combines the triple when
@@ -257,13 +313,20 @@
self.config.available_features.add(
'with_system_cxx_lib=%s' % self.config.target_triple)
+ # Insert the platform name into the available features as a lower case.
+ # Strip the '2' from linux2.
+ if target_platform.startswith('linux'):
+ platform_name = 'linux'
+ else:
+ platform_name = target_platform
+ self.config.available_features.add(platform_name.lower())
+
# Some linux distributions have different locale data than others.
# Insert the distributions name and name-version into the available
# features to allow tests to XFAIL on them.
- if sys.platform.startswith('linux'):
- name, ver, _ = platform.linux_distribution()
- name = name.lower().strip()
- ver = ver.lower().strip()
+ if target_platform.startswith('linux'):
+ name = self.target_info.platform_name()
+ ver = self.target_info.platform_ver()
if name:
self.config.available_features.add(name)
if name and ver:
@@ -299,7 +362,7 @@
# Configure include paths
self.cxx.compile_flags += ['-nostdinc++']
self.configure_compile_flags_header_includes()
- if sys.platform.startswith('linux'):
+ if self.target_info.platform().startswith('linux'):
self.cxx.compile_flags += ['-D__STDC_FORMAT_MACROS',
'-D__STDC_LIMIT_MACROS',
'-D__STDC_CONSTANT_MACROS']
@@ -430,23 +493,26 @@
def configure_extra_library_flags(self):
enable_threads = self.get_lit_bool('enable_threads', True)
llvm_unwinder = self.get_lit_bool('llvm_unwinder', False)
- if sys.platform == 'darwin':
+ target_platform = self.target_info.platform()
+ if target_platform == 'Darwin':
self.cxx.link_flags += ['-lSystem']
- elif sys.platform.startswith('linux'):
+ elif target_platform.startswith('linux'):
if not llvm_unwinder:
self.cxx.link_flags += ['-lgcc_eh']
self.cxx.link_flags += ['-lc', '-lm']
if enable_threads:
self.cxx.link_flags += ['-lpthread']
- self.cxx.link_flags += ['-lrt']
+ if not target_platform.startswith('baremetal'):
+ self.cxx.link_flags += ['-lrt']
if llvm_unwinder:
self.cxx.link_flags += ['-lunwind', '-ldl']
else:
self.cxx.link_flags += ['-lgcc_s']
- elif sys.platform.startswith('freebsd'):
+ elif target_platform.startswith('freebsd'):
self.cxx.link_flags += ['-lc', '-lm', '-lpthread', '-lgcc_s']
else:
- self.lit_config.fatal("unrecognized system: %r" % sys.platform)
+ self.lit_config.fatal("unrecognized system: %r" %
+ target_platform)
def configure_sanitizer(self):
san = self.get_lit_conf('use_sanitizer', '').strip()
@@ -463,7 +529,7 @@
symbolizer_search_paths)
# Setup the sanitizer compile flags
self.cxx.flags += ['-g', '-fno-omit-frame-pointer']
- if sys.platform.startswith('linux'):
+ if self.target_info.platform().startswith('linux'):
self.cxx.link_flags += ['-ldl']
if san == 'Address':
self.cxx.flags += ['-fsanitize=address']
@@ -556,7 +622,8 @@
"inferred target_triple as: %r" % self.config.target_triple)
def configure_env(self):
- if sys.platform == 'darwin' and not self.use_system_cxx_lib:
+ if self.target_info.platform() == 'Darwin' and not \
+ self.use_system_cxx_lib:
libcxx_library = self.get_lit_conf('libcxx_library')
if libcxx_library:
cxx_library_root = os.path.dirname(libcxx_library)
Index: test/format.py
===================================================================
--- test/format.py
+++ test/format.py
@@ -20,10 +20,12 @@
FOO.sh.cpp - A test that uses LIT's ShTest format.
"""
- def __init__(self, cxx, use_verify_for_fail, execute_external, exec_env):
+ def __init__(self, cxx, use_verify_for_fail, execute_external,
+ target_executor, exec_env):
self.cxx = cxx
self.use_verify_for_fail = use_verify_for_fail
self.execute_external = execute_external
+ self.target_executor = target_executor
self.exec_env = dict(exec_env)
# TODO: Move this into lit's FileBasedTest
@@ -73,21 +75,27 @@
# Dispatch the test based on its suffix.
if is_sh_test:
+ if self.target_executor:
+ # We can't run ShTest tests with a target_executor yet.
+ # For now, bail on trying to run them
+ return lit.Test.UNSUPPORTED, 'ShTest format not yet supported'
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)
+ return self._evaluate_pass_test(test, tmpBase, execDir, lit_config,
+ self.target_executor)
else:
# No other test type is supported
assert False
def _clean(self, exec_path): # pylint: disable=no-self-use
libcxx.util.cleanFile(exec_path)
- def _evaluate_pass_test(self, test, tmpBase, execDir, lit_config):
+ def _evaluate_pass_test(self, test, tmpBase, execDir, lit_config,
+ target_executor):
source_path = test.getSourcePath()
exec_path = tmpBase + '.exe'
object_path = tmpBase + '.o'
@@ -104,15 +112,11 @@
report += "Compilation failed unexpectedly!"
return lit.Test.FAIL, report
# Run the test
- cmd = []
+ local_cwd = os.path.dirname(source_path)
+ env = None
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))
+ env = self.exec_env
+ out, err, rc = target_executor.copy_run(exec_path, [], local_cwd, env)
if rc != 0:
report = libcxx.util.makeReport(cmd, out, err, rc)
report = "Compiled With: %s\n%s" % (compile_cmd, report)
Index: test/remote.py
===================================================================
--- test/remote.py
+++ test/remote.py
@@ -0,0 +1,427 @@
+import inspect
+import locale
+import os
+import platform
+import shutil
+import types
+
+from lit.util import executeCommand
+
+class TargetInfo(object):
+ def platform(self):
+ pass
+
+ def platform_ver(self):
+ pass
+
+ def platform_name(self):
+ pass
+
+ def supports_locale(self, loc):
+ pass
+
+
+class LocalTI(TargetInfo):
+ def platform(self):
+ return platform.system()
+
+ def platform_name(self):
+ if platform().startswith('linux'):
+ name, _, _ = platform.linux_distribution()
+ name = name.lower().strip()
+ if name:
+ return name
+ return None
+
+ def platform_ver(self):
+ if platform().startswith('linux'):
+ _, ver, _ = platform.linux_distribution()
+ ver = ver.lower().strip()
+ if ver:
+ return ver
+ return None
+
+ def supports_locale(self, loc):
+ try:
+ locale.setlocale(locale.LC_ALL, loc)
+ return True
+ except locale.Error:
+ return False
+
+
+class Executor(object):
+ def __init__(self):
+ pass
+
+ def remote_temp_dir(self):
+ """Get a temporary directory on the remote host"""
+ pass
+
+ def remote_temp_file(self, suffix):
+ """Get a temporary filename on the remote host"""
+ pass
+
+ def remote_from_local_dir(self, local_dir):
+ """Translate a local dirname into a remote dirname"""
+ pass
+
+ def remote_from_local_file(self, local_file):
+ """Translate a local filename into a remote filename"""
+ pass
+
+ def copy_in(self, local_srcs, remote_dsts, delete=False):
+ """Copy test inputs & executables in to the test runner
+ Args:
+ local_srcs (list): List of Host file paths to copy in to the runner
+ remote_dsts (list): Corresponding list of Target file locations
+ delete (bool): Whether or not to delete the src after the copy
+ Returns:
+ void
+ """
+ pass
+
+ def copy_out(self, remote_srcs, local_dsts, delete=False):
+ """Copy test outputs back from the test runner
+ Args:
+ remote_srcs (list): List of Target file paths to copy back from the runner
+ local_dsts (list): Corresponding list of Host file locations
+ delete (bool): Whether or not to delete the src after the copy
+ Returns:
+ void
+ """
+ pass
+
+ def delete_remote(self, remote, ignore_error=True):
+ """Delete remote files
+ Args:
+ remote (path): Remote file to delete
+ ignore_error (bool): Whether or not to ignore errors on delete
+ Returns:
+ void
+ """
+ pass
+
+ def run(self, command, work_dir='.', env=None):
+ """Execute a particular command
+ Args:
+ command (string): The command to execute
+ work_dir (string): The working directory to execute the test from
+ Returns:
+ out, err, exitCode
+ """
+ pass
+
+ def copy_run(self, exe_path, args, local_cwd, env=None):
+ """Execute a command that might need to be copied to the target
+ Args:
+ exe_path (string): Local path to the executable to be run
+ args (list of string): Arguments to run the exe with
+ local_cwd (string): Local path to the working directory
+ env (dict): Environment variables to execute under
+ Returns:
+ out, err, exitCode
+ """
+ cmd = args
+ target_exe_path = self.remote_from_local_file(exe_path)
+ target_cwd = self.remote_from_local_dir(local_cwd)
+ cmd += [target_exe_path]
+ try:
+ self.copy_in([exe_path], [target_exe_path])
+ return self.run(cmd, work_dir=target_cwd, env=env)
+ except:
+ raise
+ finally:
+ self.delete_remote(target_exe_path)
+ pass
+
+ def clean_temps(self):
+ """Delete all temporary files created by this executor"""
+ pass
+
+class LocalExecutor(Executor):
+ def __init__(self):
+ Executor.__init__(self)
+
+ def remote_temp_dir(self):
+ return os.getcwd()
+
+ def remote_temp_file(self, suffix):
+ tmp_file = tempfile.NamedTemporaryFile(suffix=suffix, delete=False)
+ tmp_path = tmp_file.name
+ tmp_file.close()
+ return tmp_path
+
+ def delete_remote(self, remote, ignore_error=True):
+ try:
+ os.remove(remote)
+ except:
+ if ignore_error:
+ pass
+ else:
+ raise
+
+ def remote_from_local_dir(self, local_dir):
+ # Identity function, because LocalExecutor executes locally
+ return local_dir
+
+ def remote_from_local_file(self, local_file):
+ # Identity function, because LocalExecutor executes locally
+ return local_file
+
+ def copy_in(self, local_srcs, remote_dsts, delete=False):
+ self.__copyAll(local_srcs, remote_dsts, delete)
+
+ def copy_out(self, remote_srcs, local_dsts, delete=False):
+ self.__copyAll(remote_srcs, local_dsts, delete)
+
+ def run(self, command, work_dir='.', env=None):
+ env_cmd = []
+ if env:
+ env_cmd += ['env']
+ env_cmd += ['%s=%s' % (k, v) for k, v in env.items()]
+ if work_dir == '.':
+ work_dir = os.getcwd()
+ return executeCommand(env_cmd + command, cwd=work_dir)
+
+ def copy_run(self, exe_path, args, local_cwd, env=None):
+ return executeCommand([exe_path] + args, cwd=local_cwd, env=env)
+
+ def __copyAll(self, srcs, dsts, delete=False):
+ for src, dst in zip(srcs, dsts):
+ if src != dst:
+ if delete:
+ move(src, dst)
+ else:
+ shutil.copy(src, dst)
+ elif delete:
+ assert false, "Can't delete '%s' because src==dst" % src
+
+class PrefixExecutor(Executor):
+ """Prefix an executor with some other command wrapper
+ Most useful for setting ulimits on commands, or running an emulator like
+ qemu and valgrind.
+ """
+ def __init__(self, commandPrefix, chain):
+ Executor.__init__(self)
+
+ self.commandPrefix = commandPrefix
+ self.chain = chain
+
+ def remote_from_local_dir(self, local_dir):
+ return local_dir
+
+ def remote_from_local_file(self, local_file):
+ return local_file
+
+ def copy_in(self, local_srcs, remote_dsts, delete=False):
+ self.chain.copy_in(local_srcs, remote_dsts, delete)
+
+ def copy_out(self, remote_srcs, local_dsts, delete=False):
+ self.chain.copy_out(remote_srcs, local_dsts, delete)
+
+ def delete_remote(self, remote, ignore_error=True):
+ self.chain.delete_remote(remote, ignore_error)
+
+ def run(self, command, work_dir='.', env=None):
+ return self.chain.run(self.commandPrefix + command, work_dir, env=env)
+
+class PostfixExecutor(PrefixExecutor):
+ """Postfix an executor with some args
+ """
+ def __init__(self, commandPostfix, chain):
+ Executor.__init__(self)
+
+ self.commandPostfix = commandPostfix
+ self.chain = chain
+
+ def run(self, command, work_dir='.', env=None):
+ return self.chain.run(command + self.commandPostfix, work_dir, env=env)
+
+class ValgrindExecutor(PrefixExecutor):
+ pass
+
+class QEMUExecutor(PrefixExecutor):
+ """Execute another action under qemu"""
+ def __init__(self, qemuCommand, qemuArgs, chain):
+ PrefixExecutor.__init__(self, prefix, chain)
+ self.prefix = qemuCommand + ' ' + qemuArgs
+
+# Deprecated. lit timeouts subsume this
+class TimeoutExecutor(PrefixExecutor):
+ """Execute another action under a timeout"""
+ def __init__(self, duration, chain):
+ PrefixExecutor.__init__(self, 'timeout ' + duration, chain)
+
+class SSHExecutor(Executor):
+ def __init__(self, username, host):
+ Executor.__init__(self)
+
+ self.username = username
+ self.host = host
+ self.scp_command = 'scp'
+ self.ssh_command = 'ssh'
+ self.local_executor = LocalExecutor()
+
+ # Maps from local->remote to make sure we consistently return
+ # the same name for a given local version of the same file/dir
+ self.filemap = {}
+ self.dirmap = {}
+
+ # For temp cleanup
+ self.remote_temp_files = []
+ self.remote_temp_dirs = []
+
+ # TODO(jroelofs): switch this on some -super-verbose-debug config flag
+ if False:
+ self.local_executor = DebugExecutor("ssh_local", LocalExecutor(),
+ before=True, after=True)
+
+ def remote_temp_dir(self):
+ # TODO
+ return os.getcwd()
+
+ def remote_temp_file(self, suffix):
+ # TODO: detect what the target system is, and use the correct
+ # mktemp command for it. (linux and darwin differ here, and I'm
+ # sure windows has another way to do it)
+
+ # Not sure how to do suffix on osx yet
+ cmd = "mktemp -q /tmp/libcxx.XXXXXXXXXX"
+ res = self.__execute_command_remote([cmd])
+ temp_path, err, exitCode = res
+ temp_path = temp_path.rstrip()
+ if exitCode != 0:
+ raise ValueError(err)
+ self.remote_temp_files.append(temp_path)
+ return temp_path
+
+ def remote_from_local_dir(self, local_dir):
+ if not self.dirmap.has_key(local_dir):
+ remote_dir = self.remote_temp_dir()
+ self.dirmap[local_dir] = remote_dir
+ return remote_dir
+ else:
+ return self.dirmap[local_dir]
+
+ def remote_from_local_file(self, local_file):
+ if not local_file in self.filemap:
+ remote_file = self.remote_temp_file(".some_file")
+ self.filemap[local_file] = remote_file
+ return remote_file
+ else:
+ return self.filemap[local_file]
+
+ def copy_in(self, local_srcs, remote_dsts, delete=False):
+ scp = self.scp_command
+ remote = self.username + '@' + self.host
+
+ # This could be wrapped up in a tar->scp->untar for performance
+ # if there are lots of files to be copied/moved
+ for src, dst in zip(local_srcs, remote_dsts):
+ cmd = [scp, '-p', src, remote + ':' + dst]
+ self.local_executor.run(cmd)
+ if delete:
+ for src in local_srcs:
+ os.remove(src)
+
+ def copy_out(self, remote_srcs, local_dsts, delete=False):
+ scp = self.scp_command
+ remote = self.username + '@' + self.host
+
+ # This could be wrapped up in a tar->scp->untar for performance
+ # if there are lots of files to be copied/moved
+ for src, dst in zip(srcs, dsts):
+ cmd = [scp, '-p', remote + ':' + src, dst]
+ self.local_executor.run(cmd)
+ if delete:
+ for src in remote_srcs:
+ self.delete_remote(src)
+
+ def delete_remote(self, remote, ignore_error=True):
+ try:
+ self.__execute_command_remote(['rm', '-rf', remote])
+ except:
+ if ignore_error:
+ pass
+ else:
+ raise
+
+ def run(self, command, work_dir='.', env=None):
+ return self.__execute_command_remote(command, work_dir, env)
+
+ def __execute_command_remote(self, command, remote_work_dir='', env=None):
+ remote = self.username + '@' + self.host
+ cmd = [self.ssh_command]
+ cmd += ['-oBatchMode=yes']
+ cmd += [remote]
+ env_cmd = []
+ if env:
+ env_cmd += ['env']
+ env_cmd += ['%s=%s' % (k, v) for k, v in env.items()]
+ remote_cmd = ' '.join(env_cmd + command)
+ if remote_work_dir != '.' and remote_work_dir != '':
+ remote_cmd = 'cd ' + remote_work_dir + ' && ' + remote_cmd
+ cmd += [remote_cmd]
+ return self.local_executor.run(cmd)
+
+ def clean_temps(self):
+ for remote_file in self.remote_temp_files:
+ self.delete_remote(remote_file)
+
+ for remote_dir in self.remote_temp_dirs:
+ self.delete_remote(remote_dir)
+
+
+class DebugExecutor(Executor):
+ def __init__(self, label, chain, before=True, after=True):
+ Executor.__init__(self)
+ self.label = label
+ self.chain = chain
+ self.before = before
+ self.after = after
+
+ for n, f in inspect.getmembers(self):
+ if isinstance(f, types.UnboundMethodType):
+ # Skip meta-functions, decorate everything else
+ if not f.func_name.startswith("__"):
+ setattr(self, n, self.__decorate(f))
+
+ def __decorate(self, func):
+ def wrapper(*func_args, **func_kwargs):
+ # Get the string representation of the function call
+ arg_names = func.func_code.co_varnames[1:func.func_code.co_argcount]
+ args = func_args[:len(arg_names)]
+ defaults = func.func_defaults or ()
+ args = args + defaults[len(defaults) - (func.func_code.co_argcount - len(args)):]
+ params = zip(arg_names, args)
+ args = func_args[len(arg_names):]
+ if args: params.append(('args', args))
+ if func_kwargs: params.append(('kwargs', func_kwargs))
+ arg_str = '(' + ', '.join('%s = %r' % p for p in params) + ' )'
+ call_str = func.func_name + arg_str
+
+ # Perform the call itself, logging before, after, and anything thrown
+ try:
+ self.__log_before(call_str)
+
+ # Forward the actual call to the chain, rather than calling
+ # func itself, which is a base-class method pointer
+ chain_func = getattr(self.chain, func.func_name)
+ res = chain_func(*func_args, **func_kwargs)
+
+ self.__log_after("%s = %r" % (call_str, res))
+ return res
+ except:
+ self.__log_after("%s raised" % (call_str))
+ raise
+
+ return wrapper
+
+ def __log_before(self, message):
+ if self.before:
+ print "BEFORE: %s.%s" % (self.label, message)
+
+ def __log_after(self, message):
+ if self.after:
+ print "AFTER: %s.%s" % (self.label, message)
+
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits