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    print("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 and 'x86' in config.host_triple:
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 and 'x86' in config.host_triple:
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.
162if is_configured('llvm_libs_dir'):
163  dotest_cmd += ['--env', 'LLVM_LIBS_DIR=' + config.llvm_libs_dir]
164
165# Forward ASan-specific environment variables to tests, as a test may load an
166# ASan-ified dylib.
167for env_var in ('ASAN_OPTIONS', 'DYLD_INSERT_LIBRARIES'):
168  if env_var in config.environment:
169    dotest_cmd += ['--inferior-env', env_var + '=' + config.environment[env_var]]
170
171if is_configured('test_arch'):
172  dotest_cmd += ['--arch', config.test_arch]
173
174if is_configured('lldb_build_directory'):
175  dotest_cmd += ['--build-dir', config.lldb_build_directory]
176
177if is_configured('lldb_module_cache'):
178  delete_module_cache(config.lldb_module_cache)
179  dotest_cmd += ['--lldb-module-cache-dir', config.lldb_module_cache]
180
181if is_configured('clang_module_cache'):
182  delete_module_cache(config.clang_module_cache)
183  dotest_cmd += ['--clang-module-cache-dir', config.clang_module_cache]
184
185if is_configured('lldb_executable'):
186  dotest_cmd += ['--executable', config.lldb_executable]
187
188if is_configured('test_compiler'):
189  dotest_cmd += ['--compiler', config.test_compiler]
190
191if is_configured('dsymutil'):
192  dotest_cmd += ['--dsymutil', config.dsymutil]
193
194if is_configured('llvm_tools_dir'):
195  dotest_cmd += ['--llvm-tools-dir', config.llvm_tools_dir]
196
197if is_configured('server'):
198  dotest_cmd += ['--server', config.server]
199
200if is_configured('lldb_libs_dir'):
201  dotest_cmd += ['--lldb-libs-dir', config.lldb_libs_dir]
202
203if is_configured('lldb_framework_dir'):
204  dotest_cmd += ['--framework', config.lldb_framework_dir]
205
206if 'lldb-repro-capture' in config.available_features or \
207    'lldb-repro-replay' in config.available_features:
208  dotest_cmd += ['--skip-category=lldb-vscode', '--skip-category=std-module']
209
210if 'lldb-simulator-ios' in config.available_features:
211  dotest_cmd += ['--apple-sdk', 'iphonesimulator',
212                 '--platform-name', 'ios-simulator']
213elif 'lldb-simulator-watchos' in config.available_features:
214  dotest_cmd += ['--apple-sdk', 'watchsimulator',
215                 '--platform-name', 'watchos-simulator']
216elif 'lldb-simulator-tvos' in config.available_features:
217  dotest_cmd += ['--apple-sdk', 'appletvsimulator',
218                 '--platform-name', 'tvos-simulator']
219
220if is_configured('enabled_plugins'):
221  for plugin in config.enabled_plugins:
222    dotest_cmd += ['--enable-plugin', plugin]
223
224if is_configured('dotest_lit_args_str'):
225  # We don't want to force users passing arguments to lit to use `;` as a
226  # separator. We use Python's simple lexical analyzer to turn the args into a
227  # list. Pass there arguments last so they can override anything that was
228  # already configured.
229  dotest_cmd.extend(shlex.split(config.dotest_lit_args_str))
230
231# Load LLDB test format.
232sys.path.append(os.path.join(config.lldb_src_root, "test", "API"))
233import lldbtest
234
235# testFormat: The test format to use to interpret tests.
236config.test_format = lldbtest.LLDBTest(dotest_cmd)
237
238# Propagate FREEBSD_LEGACY_PLUGIN
239if 'FREEBSD_LEGACY_PLUGIN' in os.environ:
240  config.environment['FREEBSD_LEGACY_PLUGIN'] = os.environ[
241      'FREEBSD_LEGACY_PLUGIN']
242
243# Propagate XDG_CACHE_HOME
244if 'XDG_CACHE_HOME' in os.environ:
245  config.environment['XDG_CACHE_HOME'] = os.environ['XDG_CACHE_HOME']
246