1# -*- Python -*-
2
3# Configuration file for the 'lit' test runner.
4
5import os
6import platform
7import shlex
8import shutil
9import subprocess
10
11import lit.formats
12
13# name: The name of this test suite.
14config.name = 'lldb-api'
15
16# suffixes: A list of file extensions to treat as test files.
17config.suffixes = ['.py']
18
19# test_source_root: The root path where tests are located.
20config.test_source_root = os.path.dirname(__file__)
21
22# test_exec_root: The root path where tests should be run.
23config.test_exec_root = os.path.join(config.lldb_obj_root, 'test', 'API')
24
25def mkdir_p(path):
26  import errno
27  try:
28    os.makedirs(path)
29  except OSError as e:
30    if e.errno != errno.EEXIST:
31      raise
32  if not os.path.isdir(path):
33    raise OSError(errno.ENOTDIR, "%s is not a directory"%path)
34
35
36def find_sanitizer_runtime(name):
37  resource_dir = subprocess.check_output(
38      [config.cmake_cxx_compiler,
39       '-print-resource-dir']).decode('utf-8').strip()
40  return os.path.join(resource_dir, 'lib', 'darwin', name)
41
42
43def find_shlibpath_var():
44  if platform.system() in ['Linux', 'FreeBSD', 'NetBSD', 'SunOS']:
45    yield 'LD_LIBRARY_PATH'
46  elif platform.system() == 'Darwin':
47    yield 'DYLD_LIBRARY_PATH'
48  elif platform.system() == 'Windows':
49    yield 'PATH'
50
51
52# On macOS, we can't do the DYLD_INSERT_LIBRARIES trick with a shim python
53# binary as the ASan interceptors get loaded too late. Also, when SIP is
54# enabled, we can't inject libraries into system binaries at all, so we need a
55# copy of the "real" python to work with.
56def find_python_interpreter():
57  # Avoid doing any work if we already copied the binary.
58  copied_python = os.path.join(config.lldb_build_directory, 'copied-python')
59  if os.path.isfile(copied_python):
60    return copied_python
61
62  # Find the "real" python binary.
63  real_python = subprocess.check_output([
64      config.python_executable,
65      os.path.join(os.path.dirname(os.path.realpath(__file__)),
66                   'get_darwin_real_python.py')
67  ]).decode('utf-8').strip()
68
69  shutil.copy(real_python, copied_python)
70
71  # Now make sure the copied Python works. The Python in Xcode has a relative
72  # RPATH and cannot be copied.
73  try:
74    # We don't care about the output, just make sure it runs.
75    subprocess.check_output([copied_python, '-V'], stderr=subprocess.STDOUT)
76  except subprocess.CalledProcessError:
77    # The copied Python didn't work. Assume we're dealing with the Python
78    # interpreter in Xcode. Given that this is not a system binary SIP
79    # won't prevent us form injecting the interceptors so we get away with
80    # not copying the executable.
81    os.remove(copied_python)
82    return real_python
83
84  # The copied Python works.
85  return copied_python
86
87
88def is_configured(attr):
89  """Return the configuration attribute if it exists and None otherwise.
90
91  This allows us to check if the attribute exists before trying to access it."""
92  return getattr(config, attr, None)
93
94
95def delete_module_cache(path):
96  """Clean the module caches in the test build directory.
97
98  This is necessary in an incremental build whenever clang changes underneath,
99  so doing it once per lit.py invocation is close enough. """
100  if os.path.isdir(path):
101    lit_config.note("Deleting module cache at %s." % path)
102    shutil.rmtree(path)
103
104if is_configured('llvm_use_sanitizer'):
105  if 'Address' in config.llvm_use_sanitizer:
106    config.environment['ASAN_OPTIONS'] = 'detect_stack_use_after_return=1'
107    if 'Darwin' in config.host_os:
108      config.environment['DYLD_INSERT_LIBRARIES'] = find_sanitizer_runtime(
109          'libclang_rt.asan_osx_dynamic.dylib')
110
111  if 'Thread' in config.llvm_use_sanitizer:
112    if 'Darwin' in config.host_os:
113      config.environment['DYLD_INSERT_LIBRARIES'] = find_sanitizer_runtime(
114          'libclang_rt.tsan_osx_dynamic.dylib')
115
116if 'DYLD_INSERT_LIBRARIES' in config.environment and platform.system() == 'Darwin':
117  config.python_executable = find_python_interpreter()
118
119# Shared library build of LLVM may require LD_LIBRARY_PATH or equivalent.
120if is_configured('shared_libs'):
121  for shlibpath_var in find_shlibpath_var():
122    # In stand-alone build llvm_shlib_dir specifies LLDB's lib directory while
123    # llvm_libs_dir specifies LLVM's lib directory.
124    shlibpath = os.path.pathsep.join(
125        (config.llvm_shlib_dir, config.llvm_libs_dir,
126         config.environment.get(shlibpath_var, '')))
127    config.environment[shlibpath_var] = shlibpath
128  else:
129    lit_config.warning("unable to inject shared library path on '{}'".format(
130        platform.system()))
131
132lldb_use_simulator = lit_config.params.get('lldb-run-with-simulator', None)
133if lldb_use_simulator:
134  if lldb_use_simulator == "ios":
135    lit_config.note("Running API tests on iOS simulator")
136    config.available_features.add('lldb-simulator-ios')
137  elif lldb_use_simulator == "watchos":
138    lit_config.note("Running API tests on watchOS simulator")
139    config.available_features.add('lldb-simulator-watchos')
140  elif lldb_use_simulator == "tvos":
141    lit_config.note("Running API tests on tvOS simulator")
142    config.available_features.add('lldb-simulator-tvos')
143  else:
144    lit_config.error("Unknown simulator id '{}'".format(lldb_use_simulator))
145
146# Set a default per-test timeout of 10 minutes. Setting a timeout per test
147# requires that killProcessAndChildren() is supported on the platform and
148# lit complains if the value is set but it is not supported.
149supported, errormsg = lit_config.maxIndividualTestTimeIsSupported
150if supported:
151  lit_config.maxIndividualTestTime = 600
152else:
153  lit_config.warning("Could not set a default per-test timeout. " + errormsg)
154
155# Build dotest command.
156dotest_cmd = [os.path.join(config.lldb_src_root, 'test', 'API', 'dotest.py')]
157
158if is_configured('dotest_args_str'):
159  dotest_cmd.extend(config.dotest_args_str.split(';'))
160
161# Library path may be needed to locate just-built clang and libcxx.
162if is_configured('llvm_libs_dir'):
163  dotest_cmd += ['--env', 'LLVM_LIBS_DIR=' + config.llvm_libs_dir]
164
165# Include path may be needed to locate just-built libcxx.
166if is_configured('llvm_include_dir'):
167  dotest_cmd += ['--env', 'LLVM_INCLUDE_DIR=' + config.llvm_include_dir]
168
169# This path may be needed to locate required llvm tools
170if is_configured('llvm_tools_dir'):
171  dotest_cmd += ['--env', 'LLVM_TOOLS_DIR=' + config.llvm_tools_dir]
172
173# If we have a just-built libcxx, prefer it over the system one.
174if is_configured('has_libcxx') and platform.system() != 'Windows':
175  dotest_cmd += ['--hermetic-libcxx']
176
177# Forward ASan-specific environment variables to tests, as a test may load an
178# ASan-ified dylib.
179for env_var in ('ASAN_OPTIONS', 'DYLD_INSERT_LIBRARIES'):
180  if env_var in config.environment:
181    dotest_cmd += ['--inferior-env', env_var + '=' + config.environment[env_var]]
182
183if is_configured('test_arch'):
184  dotest_cmd += ['--arch', config.test_arch]
185
186if is_configured('lldb_build_directory'):
187  dotest_cmd += ['--build-dir', config.lldb_build_directory]
188
189if is_configured('lldb_module_cache'):
190  delete_module_cache(config.lldb_module_cache)
191  dotest_cmd += ['--lldb-module-cache-dir', config.lldb_module_cache]
192
193if is_configured('clang_module_cache'):
194  delete_module_cache(config.clang_module_cache)
195  dotest_cmd += ['--clang-module-cache-dir', config.clang_module_cache]
196
197if is_configured('lldb_executable'):
198  dotest_cmd += ['--executable', config.lldb_executable]
199
200if is_configured('test_compiler'):
201  dotest_cmd += ['--compiler', config.test_compiler]
202
203if is_configured('dsymutil'):
204  dotest_cmd += ['--dsymutil', config.dsymutil]
205
206if is_configured('llvm_tools_dir'):
207  dotest_cmd += ['--llvm-tools-dir', config.llvm_tools_dir]
208
209if is_configured('server'):
210  dotest_cmd += ['--server', config.server]
211
212if is_configured('lldb_libs_dir'):
213  dotest_cmd += ['--lldb-libs-dir', config.lldb_libs_dir]
214
215if is_configured('lldb_framework_dir'):
216  dotest_cmd += ['--framework', config.lldb_framework_dir]
217
218if 'lldb-repro-capture' in config.available_features or \
219    'lldb-repro-replay' in config.available_features:
220  dotest_cmd += ['--skip-category=lldb-vscode', '--skip-category=std-module']
221
222if 'lldb-simulator-ios' in config.available_features:
223  dotest_cmd += ['--apple-sdk', 'iphonesimulator',
224                 '--platform-name', 'ios-simulator']
225elif 'lldb-simulator-watchos' in config.available_features:
226  dotest_cmd += ['--apple-sdk', 'watchsimulator',
227                 '--platform-name', 'watchos-simulator']
228elif 'lldb-simulator-tvos' in config.available_features:
229  dotest_cmd += ['--apple-sdk', 'appletvsimulator',
230                 '--platform-name', 'tvos-simulator']
231
232if is_configured('enabled_plugins'):
233  for plugin in config.enabled_plugins:
234    dotest_cmd += ['--enable-plugin', plugin]
235
236if is_configured('dotest_lit_args_str'):
237  # We don't want to force users passing arguments to lit to use `;` as a
238  # separator. We use Python's simple lexical analyzer to turn the args into a
239  # list. Pass there arguments last so they can override anything that was
240  # already configured.
241  dotest_cmd.extend(shlex.split(config.dotest_lit_args_str))
242
243# Load LLDB test format.
244sys.path.append(os.path.join(config.lldb_src_root, "test", "API"))
245import lldbtest
246
247# testFormat: The test format to use to interpret tests.
248config.test_format = lldbtest.LLDBTest(dotest_cmd)
249
250# Propagate TERM or default to vt100.
251config.environment['TERM'] = os.getenv('TERM', default='vt100')
252
253# Propagate FREEBSD_LEGACY_PLUGIN
254if 'FREEBSD_LEGACY_PLUGIN' in os.environ:
255  config.environment['FREEBSD_LEGACY_PLUGIN'] = os.environ[
256      'FREEBSD_LEGACY_PLUGIN']
257
258# Propagate XDG_CACHE_HOME
259if 'XDG_CACHE_HOME' in os.environ:
260  config.environment['XDG_CACHE_HOME'] = os.environ['XDG_CACHE_HOME']
261