• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#===----------------------------------------------------------------------===##
2#
3# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4# See https://llvm.org/LICENSE.txt for license information.
5# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6#
7#===----------------------------------------------------------------------===##
8
9from libcxx.test.dsl import *
10from lit.BooleanExpression import BooleanExpression
11import re
12import shutil
13import subprocess
14import sys
15
16_isClang      = lambda cfg: '__clang__' in compilerMacros(cfg) and '__apple_build_version__' not in compilerMacros(cfg)
17_isAppleClang = lambda cfg: '__apple_build_version__' in compilerMacros(cfg)
18_isGCC        = lambda cfg: '__GNUC__' in compilerMacros(cfg) and '__clang__' not in compilerMacros(cfg)
19_isMSVC       = lambda cfg: '_MSC_VER' in compilerMacros(cfg)
20_msvcVersion  = lambda cfg: (int(compilerMacros(cfg)['_MSC_VER']) // 100, int(compilerMacros(cfg)['_MSC_VER']) % 100)
21
22def _getSuitableClangTidy(cfg):
23  try:
24    # If we didn't build the libcxx-tidy plugin via CMake, we can't run the clang-tidy tests.
25    if runScriptExitCode(cfg, ['stat %{test-tools}/clang_tidy_checks/libcxx-tidy.plugin']) != 0:
26      return None
27
28    # TODO This should be the last stable release.
29    if runScriptExitCode(cfg, ['clang-tidy-16 --version']) == 0:
30      return 'clang-tidy-16'
31
32    if int(re.search('[0-9]+', commandOutput(cfg, ['clang-tidy --version'])).group()) >= 16:
33      return 'clang-tidy'
34
35  except ConfigurationRuntimeError:
36    return None
37
38DEFAULT_FEATURES = [
39  Feature(name='thread-safety',
40          when=lambda cfg: hasCompileFlag(cfg, '-Werror=thread-safety'),
41          actions=[AddCompileFlag('-Werror=thread-safety')]),
42
43  Feature(name='diagnose-if-support',
44          when=lambda cfg: hasCompileFlag(cfg, '-Wuser-defined-warnings'),
45          actions=[AddCompileFlag('-Wuser-defined-warnings')]),
46
47  # Tests to validate whether the compiler has a way to set the maximum number
48  # of steps during constant evaluation. Since the flag differs per compiler
49  # store the "valid" flag as a feature. This allows passing the proper compile
50  # flag to the compiler:
51  # // ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=12345678
52  # // ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=12345678
53  Feature(name='has-fconstexpr-steps',
54          when=lambda cfg: hasCompileFlag(cfg, '-fconstexpr-steps=1')),
55
56  Feature(name='has-fconstexpr-ops-limit',
57          when=lambda cfg: hasCompileFlag(cfg, '-fconstexpr-ops-limit=1')),
58
59  Feature(name='has-fblocks',                   when=lambda cfg: hasCompileFlag(cfg, '-fblocks')),
60  Feature(name='-fsized-deallocation',          when=lambda cfg: hasCompileFlag(cfg, '-fsized-deallocation')),
61  Feature(name='-faligned-allocation',          when=lambda cfg: hasCompileFlag(cfg, '-faligned-allocation')),
62  Feature(name='fdelayed-template-parsing',     when=lambda cfg: hasCompileFlag(cfg, '-fdelayed-template-parsing')),
63  Feature(name='libcpp-no-coroutines',          when=lambda cfg: featureTestMacros(cfg).get('__cpp_impl_coroutine', 0) < 201902),
64  Feature(name='has-fobjc-arc',                 when=lambda cfg: hasCompileFlag(cfg, '-xobjective-c++ -fobjc-arc') and
65                                                                 sys.platform.lower().strip() == 'darwin'), # TODO: this doesn't handle cross-compiling to Apple platforms.
66  Feature(name='objective-c++',                 when=lambda cfg: hasCompileFlag(cfg, '-xobjective-c++ -fobjc-arc')),
67  Feature(name='verify-support',                when=lambda cfg: hasCompileFlag(cfg, '-Xclang -verify-ignore-unexpected')),
68
69  Feature(name='non-lockfree-atomics',
70          when=lambda cfg: sourceBuilds(cfg, """
71            #include <atomic>
72            struct Large { int storage[100]; };
73            std::atomic<Large> x;
74            int main(int, char**) { (void)x.load(); return 0; }
75          """)),
76  # TODO: Remove this feature once compiler-rt includes __atomic_is_lockfree()
77  # on all supported platforms.
78  Feature(name='is-lockfree-runtime-function',
79          when=lambda cfg: sourceBuilds(cfg, """
80            #include <atomic>
81            struct Large { int storage[100]; };
82            std::atomic<Large> x;
83            int main(int, char**) { return x.is_lock_free(); }
84          """)),
85
86  # Some tests rely on creating shared libraries which link in the C++ Standard Library. In some
87  # cases, this doesn't work (e.g. if the library was built as a static archive and wasn't compiled
88  # as position independent). This feature informs the test suite of whether it's possible to create
89  # a shared library in a shell test by using the '-shared' compiler flag.
90  #
91  # Note: To implement this check properly, we need to make sure that we use something inside the
92  # compiled library, not only in the headers. It should be safe to assume that all implementations
93  # define `operator new` in the compiled library.
94  Feature(name='cant-build-shared-library',
95          when=lambda cfg: not sourceBuilds(cfg, """
96            void f() { new int(3); }
97          """, ['-shared'])),
98
99  # Check for a Windows UCRT bug (fixed in UCRT/Windows 10.0.20348.0):
100  # https://developercommunity.visualstudio.com/t/utf-8-locales-break-ctype-functions-for-wchar-type/1653678
101  Feature(name='win32-broken-utf8-wchar-ctype',
102          when=lambda cfg: not '_LIBCPP_HAS_NO_LOCALIZATION' in compilerMacros(cfg) and '_WIN32' in compilerMacros(cfg) and not programSucceeds(cfg, """
103            #include <locale.h>
104            #include <wctype.h>
105            int main(int, char**) {
106              setlocale(LC_ALL, "en_US.UTF-8");
107              return towlower(L'\\xDA') != L'\\xFA';
108            }
109          """)),
110
111  # Check for a Windows UCRT bug (fixed in UCRT/Windows 10.0.19041.0).
112  # https://developercommunity.visualstudio.com/t/printf-formatting-with-g-outputs-too/1660837
113  Feature(name='win32-broken-printf-g-precision',
114          when=lambda cfg: '_WIN32' in compilerMacros(cfg) and not programSucceeds(cfg, """
115            #include <stdio.h>
116            #include <string.h>
117            int main(int, char**) {
118              char buf[100];
119              snprintf(buf, sizeof(buf), "%#.*g", 0, 0.0);
120              return strcmp(buf, "0.");
121            }
122          """)),
123
124  # Check for Glibc < 2.27, where the ru_RU.UTF-8 locale had
125  # mon_decimal_point == ".", which our tests don't handle.
126  Feature(name='glibc-old-ru_RU-decimal-point',
127          when=lambda cfg: not '_LIBCPP_HAS_NO_LOCALIZATION' in compilerMacros(cfg) and not programSucceeds(cfg, """
128            #include <locale.h>
129            #include <string.h>
130            int main(int, char**) {
131              setlocale(LC_ALL, "ru_RU.UTF-8");
132              return strcmp(localeconv()->mon_decimal_point, ",");
133            }
134          """)),
135
136  Feature(name='has-unix-headers',
137          when=lambda cfg: sourceBuilds(cfg, """
138            #include <unistd.h>
139            #include <sys/wait.h>
140            int main(int, char**) {
141              return 0;
142            }
143          """)),
144
145  # Whether Bash can run on the executor.
146  # This is not always the case, for example when running on embedded systems.
147  #
148  # For the corner case of bash existing, but it being missing in the path
149  # set in %{exec} as "--env PATH=one-single-dir", the executor does find
150  # and executes bash, but bash then can't find any other common shell
151  # utilities. Test executing "bash -c 'bash --version'" to see if bash
152  # manages to find binaries to execute.
153  Feature(name='executor-has-no-bash',
154          when=lambda cfg: runScriptExitCode(cfg, ['%{exec} bash -c \'bash --version\'']) != 0),
155  Feature(name='has-clang-tidy',
156          when=lambda cfg: _getSuitableClangTidy(cfg) is not None,
157          actions=[AddSubstitution('%{clang-tidy}', lambda cfg: _getSuitableClangTidy(cfg))]),
158
159  Feature(name='apple-clang',                                                                                                      when=_isAppleClang),
160  Feature(name=lambda cfg: 'apple-clang-{__clang_major__}'.format(**compilerMacros(cfg)),                                          when=_isAppleClang),
161  Feature(name=lambda cfg: 'apple-clang-{__clang_major__}.{__clang_minor__}'.format(**compilerMacros(cfg)),                        when=_isAppleClang),
162  Feature(name=lambda cfg: 'apple-clang-{__clang_major__}.{__clang_minor__}.{__clang_patchlevel__}'.format(**compilerMacros(cfg)), when=_isAppleClang),
163
164  Feature(name='clang',                                                                                                            when=_isClang),
165  Feature(name=lambda cfg: 'clang-{__clang_major__}'.format(**compilerMacros(cfg)),                                                when=_isClang),
166  Feature(name=lambda cfg: 'clang-{__clang_major__}.{__clang_minor__}'.format(**compilerMacros(cfg)),                              when=_isClang),
167  Feature(name=lambda cfg: 'clang-{__clang_major__}.{__clang_minor__}.{__clang_patchlevel__}'.format(**compilerMacros(cfg)),       when=_isClang),
168
169  # Note: Due to a GCC bug (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104760), we must disable deprecation warnings
170  #       on GCC or spurious diagnostics are issued.
171  #
172  # TODO:
173  # - Enable -Wplacement-new with GCC.
174  # - Enable -Wclass-memaccess with GCC.
175  Feature(name='gcc',                                                                                                              when=_isGCC,
176          actions=[AddCompileFlag('-D_LIBCPP_DISABLE_DEPRECATION_WARNINGS'),
177                   AddCompileFlag('-Wno-placement-new'),
178                   AddCompileFlag('-Wno-class-memaccess')]),
179  Feature(name=lambda cfg: 'gcc-{__GNUC__}'.format(**compilerMacros(cfg)),                                                         when=_isGCC),
180  Feature(name=lambda cfg: 'gcc-{__GNUC__}.{__GNUC_MINOR__}'.format(**compilerMacros(cfg)),                                        when=_isGCC),
181  Feature(name=lambda cfg: 'gcc-{__GNUC__}.{__GNUC_MINOR__}.{__GNUC_PATCHLEVEL__}'.format(**compilerMacros(cfg)),                  when=_isGCC),
182
183  Feature(name='msvc',                                                                                                             when=_isMSVC),
184  Feature(name=lambda cfg: 'msvc-{}'.format(*_msvcVersion(cfg)),                                                                   when=_isMSVC),
185  Feature(name=lambda cfg: 'msvc-{}.{}'.format(*_msvcVersion(cfg)),                                                                when=_isMSVC),
186]
187
188# Deduce and add the test features that that are implied by the #defines in
189# the <__config_site> header.
190#
191# For each macro of the form `_LIBCPP_XXX_YYY_ZZZ` defined below that
192# is defined after including <__config_site>, add a Lit feature called
193# `libcpp-xxx-yyy-zzz`. When a macro is defined to a specific value
194# (e.g. `_LIBCPP_ABI_VERSION=2`), the feature is `libcpp-xxx-yyy-zzz=<value>`.
195#
196# Note that features that are more strongly tied to libc++ are named libcpp-foo,
197# while features that are more general in nature are not prefixed with 'libcpp-'.
198macros = {
199  '_LIBCPP_HAS_NO_MONOTONIC_CLOCK': 'no-monotonic-clock',
200  '_LIBCPP_HAS_NO_THREADS': 'no-threads',
201  '_LIBCPP_HAS_THREAD_API_EXTERNAL': 'libcpp-has-thread-api-external',
202  '_LIBCPP_HAS_THREAD_API_PTHREAD': 'libcpp-has-thread-api-pthread',
203  '_LIBCPP_NO_VCRUNTIME': 'libcpp-no-vcruntime',
204  '_LIBCPP_ABI_VERSION': 'libcpp-abi-version',
205  '_LIBCPP_HAS_NO_FILESYSTEM_LIBRARY': 'no-filesystem',
206  '_LIBCPP_HAS_NO_RANDOM_DEVICE': 'no-random-device',
207  '_LIBCPP_HAS_NO_LOCALIZATION': 'no-localization',
208  '_LIBCPP_HAS_NO_FSTREAM': 'no-fstream',
209  '_LIBCPP_HAS_NO_WIDE_CHARACTERS': 'no-wide-characters',
210  '_LIBCPP_HAS_NO_UNICODE': 'libcpp-has-no-unicode',
211  '_LIBCPP_ENABLE_DEBUG_MODE': 'libcpp-has-debug-mode',
212}
213for macro, feature in macros.items():
214  DEFAULT_FEATURES.append(
215    Feature(name=lambda cfg, m=macro, f=feature: f + ('={}'.format(compilerMacros(cfg)[m]) if compilerMacros(cfg)[m] else ''),
216            when=lambda cfg, m=macro: m in compilerMacros(cfg))
217  )
218
219
220# Mapping from canonical locale names (used in the tests) to possible locale
221# names on various systems. Each locale is considered supported if any of the
222# alternative names is supported.
223locales = {
224  'en_US.UTF-8':     ['en_US.UTF-8', 'en_US.utf8', 'English_United States.1252'],
225  'fr_FR.UTF-8':     ['fr_FR.UTF-8', 'fr_FR.utf8', 'French_France.1252'],
226  'ja_JP.UTF-8':     ['ja_JP.UTF-8', 'ja_JP.utf8', 'Japanese_Japan.923'],
227  'ru_RU.UTF-8':     ['ru_RU.UTF-8', 'ru_RU.utf8', 'Russian_Russia.1251'],
228  'zh_CN.UTF-8':     ['zh_CN.UTF-8', 'zh_CN.utf8', 'Chinese_China.936'],
229  'fr_CA.ISO8859-1': ['fr_CA.ISO8859-1', 'French_Canada.1252'],
230  'cs_CZ.ISO8859-2': ['cs_CZ.ISO8859-2', 'Czech_Czech Republic.1250']
231}
232for locale, alts in locales.items():
233  # Note: Using alts directly in the lambda body here will bind it to the value at the
234  # end of the loop. Assigning it to a default argument works around this issue.
235  DEFAULT_FEATURES.append(Feature(name='locale.{}'.format(locale),
236                                  when=lambda cfg, alts=alts: hasAnyLocale(cfg, alts)))
237
238
239# Add features representing the target platform name: darwin, linux, windows, etc...
240DEFAULT_FEATURES += [
241  Feature(name='darwin', when=lambda cfg: '__APPLE__' in compilerMacros(cfg)),
242  Feature(name='windows', when=lambda cfg: '_WIN32' in compilerMacros(cfg)),
243  Feature(name='windows-dll', when=lambda cfg: '_WIN32' in compilerMacros(cfg) and sourceBuilds(cfg, """
244            #include <iostream>
245            int main(int, char**) { return 0; }
246          """) and programSucceeds(cfg, """
247            #include <iostream>
248            #include <windows.h>
249            #include <winnt.h>
250            int main(int, char**) {
251              // Get a pointer to a data member that gets linked from the C++
252              // library. This must be a data member (functions can get
253              // thunk inside the calling executable), and must not be
254              // something that is defined inline in headers.
255              void *ptr = &std::cout;
256              // Get a handle to the current main executable.
257              void *exe = GetModuleHandle(NULL);
258              // The handle points at the PE image header. Navigate through
259              // the header structure to find the size of the PE image (the
260              // executable).
261              PIMAGE_DOS_HEADER dosheader = (PIMAGE_DOS_HEADER)exe;
262              PIMAGE_NT_HEADERS ntheader = (PIMAGE_NT_HEADERS)((BYTE *)dosheader + dosheader->e_lfanew);
263              PIMAGE_OPTIONAL_HEADER peheader = &ntheader->OptionalHeader;
264              void *exeend = (BYTE*)exe + peheader->SizeOfImage;
265              // Check if the tested pointer - the data symbol from the
266              // C++ library - is located within the exe.
267              if (ptr >= exe && ptr <= exeend)
268                return 1;
269              // Return success if it was outside of the executable, i.e.
270              // loaded from a DLL.
271              return 0;
272            }
273          """), actions=[AddCompileFlag('-DTEST_WINDOWS_DLL')]),
274  Feature(name='linux', when=lambda cfg: '__linux__' in compilerMacros(cfg)),
275  Feature(name='netbsd', when=lambda cfg: '__NetBSD__' in compilerMacros(cfg)),
276  Feature(name='freebsd', when=lambda cfg: '__FreeBSD__' in compilerMacros(cfg)),
277  Feature(name='LIBCXX-FREEBSD-FIXME', when=lambda cfg: '__FreeBSD__' in compilerMacros(cfg)),
278]
279
280# Add features representing the build host platform name.
281# The build host could differ from the target platform for cross-compilation.
282DEFAULT_FEATURES += [
283  Feature(name='buildhost={}'.format(sys.platform.lower().strip())),
284  # sys.platform can often be represented by a "sub-system", such as 'win32', 'cygwin', 'mingw', freebsd13 & etc.
285  # We define a consolidated feature on a few platforms.
286  Feature(name='buildhost=windows', when=lambda cfg: platform.system().lower().startswith('windows')),
287  Feature(name='buildhost=freebsd', when=lambda cfg: platform.system().lower().startswith('freebsd')),
288  Feature(name='buildhost=aix', when=lambda cfg: platform.system().lower().startswith('aix'))
289]
290
291# Detect whether GDB is on the system, has Python scripting and supports
292# adding breakpoint commands. If so add a substitution to access it.
293def check_gdb(cfg):
294  gdb_path = shutil.which('gdb')
295  if gdb_path is None:
296    return False
297
298  # Check that we can set breakpoint commands, which was added in 8.3.
299  # Using the quit command here means that gdb itself exits, not just
300  # the "python <...>" command.
301  test_src = """\
302try:
303  gdb.Breakpoint(\"main\").commands=\"foo\"
304except AttributeError:
305  gdb.execute(\"quit 1\")
306gdb.execute(\"quit\")"""
307
308  try:
309    stdout = subprocess.check_output(
310              [gdb_path, "-ex", "python " + test_src, "--batch"],
311              stderr=subprocess.DEVNULL, universal_newlines=True)
312  except subprocess.CalledProcessError:
313    # We can't set breakpoint commands
314    return False
315
316  # Check we actually ran the Python
317  return not "Python scripting is not supported" in stdout
318
319DEFAULT_FEATURES += [
320  Feature(name='host-has-gdb-with-python',
321    when=check_gdb,
322    actions=[AddSubstitution('%{gdb}', lambda cfg: shutil.which('gdb'))]
323  )
324]
325
326# Define features for back-deployment testing.
327#
328# These features can be used to XFAIL tests that fail when deployed on (or compiled
329# for) an older system. For example, if a test exhibits a bug in the libc on a
330# particular system version, or if it uses a symbol that is not available on an
331# older version of the dylib, it can be marked as XFAIL with these features.
332#
333# It is sometimes useful to check that a test fails specifically when compiled for a
334# given deployment target. For example, this is the case when testing availability
335# markup, where we want to make sure that using the annotated facility on a deployment
336# target that doesn't support it will fail at compile time, not at runtime. This can
337# be achieved by creating a `.verify.cpp` test that checks for the right errors, and
338# mark that test as requiring `stdlib=<vendor>-libc++ && target=<target>`.
339DEFAULT_FEATURES += [
340  # Tests that require std::to_chars(floating-point) in the built library
341  Feature(name='availability-fp_to_chars-missing',
342    when=lambda cfg: BooleanExpression.evaluate('stdlib=apple-libc++ && target={{.+}}-apple-macosx{{(10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0|13.0)(.0)?}}', cfg.available_features)),
343
344  # Tests that require https://wg21.link/P0482 support in the built library
345  Feature(name='availability-char8_t_support-missing',
346    when=lambda cfg: BooleanExpression.evaluate('stdlib=apple-libc++ && target={{.+}}-apple-macosx{{(10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0)(.0)?}}', cfg.available_features)),
347
348  # Tests that require __libcpp_verbose_abort support in the built library
349  Feature(name='availability-verbose_abort-missing',
350    when=lambda cfg: BooleanExpression.evaluate('stdlib=apple-libc++ && target={{.+}}-apple-macosx{{(10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0|13.0)(.0)?}}', cfg.available_features)),
351
352  # Tests that require std::bad_variant_access in the built library
353  Feature(name='availability-bad_variant_access-missing',
354    when=lambda cfg: BooleanExpression.evaluate('stdlib=apple-libc++ && target={{.+}}-apple-macosx10.{{(9|10|11|12)(.0)?}}', cfg.available_features)),
355
356  # Tests that require std::bad_optional_access in the built library
357  Feature(name='availability-bad_optional_access-missing',
358    when=lambda cfg: BooleanExpression.evaluate('stdlib=apple-libc++ && target={{.+}}-apple-macosx10.{{(9|10|11|12)(.0)?}}', cfg.available_features)),
359
360  # Tests that require std::bad_any_cast in the built library
361  Feature(name='availability-bad_any_cast-missing',
362    when=lambda cfg: BooleanExpression.evaluate('stdlib=apple-libc++ && target={{.+}}-apple-macosx10.{{(9|10|11|12)(.0)?}}', cfg.available_features)),
363
364  # Tests that require std::pmr support in the built library
365  Feature(name='availability-pmr-missing',
366    when=lambda cfg: BooleanExpression.evaluate('stdlib=apple-libc++ && target={{.+}}-apple-macosx{{(10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0)(.0)?}}', cfg.available_features)),
367
368  # Tests that require std::filesystem support in the built library
369  Feature(name='availability-filesystem-missing',
370    when=lambda cfg: BooleanExpression.evaluate('stdlib=apple-libc++ && target={{.+}}-apple-macosx10.{{(9|10|11|12|13|14)(.0)?}}', cfg.available_features)),
371
372  # Tests that require the C++20 synchronization library (P1135R6 implemented by https://llvm.org/D68480) in the built library
373  Feature(name='availability-synchronization_library-missing',
374    when=lambda cfg: BooleanExpression.evaluate('stdlib=apple-libc++ && target={{.+}}-apple-macosx10.{{(9|10|11|12|13|14|15)(.0)?}}', cfg.available_features)),
375
376  # Tests that require support for std::shared_mutex and std::shared_timed_mutex in the built library
377  Feature(name='availability-shared_mutex-missing',
378    when=lambda cfg: BooleanExpression.evaluate('stdlib=apple-libc++ && target={{.+}}-apple-macosx10.{{(9|10|11)(.0)?}}', cfg.available_features)),
379
380  # Tests that require support for aligned allocation in the built library. This is about `operator new(..., std::align_val_t, ...)` specifically,
381  # not other forms of aligned allocation.
382  Feature(name='availability-aligned_allocation-missing',
383    when=lambda cfg: BooleanExpression.evaluate('stdlib=apple-libc++ && target={{.+}}-apple-macosx10.{{(9|10|11|12)(.0)?}}', cfg.available_features)),
384]
385