xref: /llvm-project/libcxx/utils/libcxx/test/features.py (revision 6cb339f9c1009d94925ec3b8138cac35cbe50bf3)
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_isAnyClang = lambda cfg: "__clang__" in compilerMacros(cfg)
17_isAppleClang = lambda cfg: "__apple_build_version__" in compilerMacros(cfg)
18_isAnyGCC = lambda cfg: "__GNUC__" in compilerMacros(cfg)
19_isClang = lambda cfg: _isAnyClang(cfg) and not _isAppleClang(cfg)
20_isGCC = lambda cfg: _isAnyGCC(cfg) and not _isAnyClang(cfg)
21_isAnyClangOrGCC = lambda cfg: _isAnyClang(cfg) or _isAnyGCC(cfg)
22_isClExe = lambda cfg: not _isAnyClangOrGCC(cfg)
23_isMSVC = lambda cfg: "_MSC_VER" in compilerMacros(cfg)
24_msvcVersion = lambda cfg: (int(compilerMacros(cfg)["_MSC_VER"]) // 100, int(compilerMacros(cfg)["_MSC_VER"]) % 100)
25
26def _getAndroidDeviceApi(cfg):
27    return int(
28        programOutput(
29            cfg,
30            r"""
31                #include <android/api-level.h>
32                #include <stdio.h>
33                int main() {
34                    printf("%d\n", android_get_device_api_level());
35                    return 0;
36                }
37            """,
38        )
39    )
40
41
42def _mingwSupportsModules(cfg):
43    # Only mingw headers are known to work with libc++ built as a module,
44    # at the moment.
45    if not "__MINGW32__" in compilerMacros(cfg):
46        return False
47    # For mingw headers, check for a version known to support being built
48    # as a module.
49    return sourceBuilds(
50        cfg,
51        """
52        #include <_mingw_mac.h>
53        #if __MINGW64_VERSION_MAJOR < 12
54        #error Headers known to be incompatible
55        #elif __MINGW64_VERSION_MAJOR == 12
56        // The headers were fixed to work with libc++ modules during
57        // __MINGW64_VERSION_MAJOR == 12. The headers became compatible
58        // with libc++ built as a module in
59        // 1652e9241b5d8a5a779c6582b1c3c4f4a7cc66e5 (Apr 2024), but the
60        // following commit 8c13b28ace68f2c0094d45121d59a4b951b533ed
61        // removed the now unused __mingw_static_ovr define. Use this
62        // as indicator for whether we've got new enough headers.
63        #ifdef __mingw_static_ovr
64        #error Headers too old
65        #endif
66        #else
67        // __MINGW64_VERSION_MAJOR > 12 should be ok.
68        #endif
69        int main() { return 0; }
70        """,
71    )
72
73
74# Lit features are evaluated in order. Some checks may require the compiler detection to have
75# run first in order to work properly.
76DEFAULT_FEATURES = [
77    # gcc-style-warnings detects compilers that understand -Wno-meow flags, unlike MSVC's compiler driver cl.exe.
78    Feature(name="gcc-style-warnings", when=_isAnyClangOrGCC),
79    Feature(name="cl-style-warnings", when=_isClExe),
80    Feature(name="apple-clang", when=_isAppleClang),
81    Feature(
82        name=lambda cfg: "apple-clang-{__clang_major__}".format(**compilerMacros(cfg)),
83        when=_isAppleClang,
84    ),
85    Feature(
86        name=lambda cfg: "apple-clang-{__clang_major__}.{__clang_minor__}".format(**compilerMacros(cfg)),
87        when=_isAppleClang,
88    ),
89    Feature(
90        name=lambda cfg: "apple-clang-{__clang_major__}.{__clang_minor__}.{__clang_patchlevel__}".format(**compilerMacros(cfg)),
91        when=_isAppleClang,
92    ),
93    Feature(name="clang", when=_isClang),
94    Feature(
95        name=lambda cfg: "clang-{__clang_major__}".format(**compilerMacros(cfg)),
96        when=_isClang,
97    ),
98    Feature(
99        name=lambda cfg: "clang-{__clang_major__}.{__clang_minor__}".format(**compilerMacros(cfg)),
100        when=_isClang,
101    ),
102    Feature(
103        name=lambda cfg: "clang-{__clang_major__}.{__clang_minor__}.{__clang_patchlevel__}".format(**compilerMacros(cfg)),
104        when=_isClang,
105    ),
106    # Note: Due to a GCC bug (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104760), we must disable deprecation warnings
107    #       on GCC or spurious diagnostics are issued.
108    #
109    # TODO:
110    # - Enable -Wplacement-new with GCC.
111    # - Enable -Wclass-memaccess with GCC.
112    Feature(
113        name="gcc",
114        when=_isGCC,
115        actions=[
116            AddCompileFlag("-D_LIBCPP_DISABLE_DEPRECATION_WARNINGS"),
117            AddCompileFlag("-Wno-placement-new"),
118            AddCompileFlag("-Wno-class-memaccess"),
119            AddFeature("GCC-ALWAYS_INLINE-FIXME"),
120        ],
121    ),
122    Feature(
123        name=lambda cfg: "gcc-{__GNUC__}".format(**compilerMacros(cfg)), when=_isGCC
124    ),
125    Feature(
126        name=lambda cfg: "gcc-{__GNUC__}.{__GNUC_MINOR__}".format(**compilerMacros(cfg)),
127        when=_isGCC,
128    ),
129    Feature(
130        name=lambda cfg: "gcc-{__GNUC__}.{__GNUC_MINOR__}.{__GNUC_PATCHLEVEL__}".format(**compilerMacros(cfg)),
131        when=_isGCC,
132    ),
133    Feature(name="msvc", when=_isMSVC),
134    Feature(name=lambda cfg: "msvc-{}".format(*_msvcVersion(cfg)), when=_isMSVC),
135    Feature(name=lambda cfg: "msvc-{}.{}".format(*_msvcVersion(cfg)), when=_isMSVC),
136
137    Feature(
138        name="thread-safety",
139        when=lambda cfg: hasCompileFlag(cfg, "-Werror=thread-safety"),
140        actions=[AddCompileFlag("-Werror=thread-safety")],
141    ),
142    Feature(
143        name="diagnose-if-support",
144        when=lambda cfg: hasCompileFlag(cfg, "-Wuser-defined-warnings"),
145        actions=[AddCompileFlag("-Wuser-defined-warnings")],
146    ),
147    # Tests to validate whether the compiler has a way to set the maximum number
148    # of steps during constant evaluation. Since the flag differs per compiler
149    # store the "valid" flag as a feature. This allows passing the proper compile
150    # flag to the compiler:
151    # // ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=12345678
152    # // ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=12345678
153    Feature(
154        name="has-fconstexpr-steps",
155        when=lambda cfg: hasCompileFlag(cfg, "-fconstexpr-steps=1"),
156    ),
157    Feature(
158        name="has-fconstexpr-ops-limit",
159        when=lambda cfg: hasCompileFlag(cfg, "-fconstexpr-ops-limit=1"),
160    ),
161    Feature(name="has-fblocks", when=lambda cfg: hasCompileFlag(cfg, "-fblocks")),
162    Feature(
163        name="fdelayed-template-parsing",
164        when=lambda cfg: hasCompileFlag(cfg, "-fdelayed-template-parsing"),
165    ),
166    Feature(
167        name="has-fobjc-arc",
168        when=lambda cfg: hasCompileFlag(cfg, "-xobjective-c++ -fobjc-arc")
169        and sys.platform.lower().strip() == "darwin",
170    ),  # TODO: this doesn't handle cross-compiling to Apple platforms.
171    Feature(
172        name="objective-c++",
173        when=lambda cfg: hasCompileFlag(cfg, "-xobjective-c++ -fobjc-arc"),
174    ),
175    Feature(
176        name="verify-support",
177        when=lambda cfg: hasCompileFlag(cfg, "-Xclang -verify-ignore-unexpected"),
178    ),
179    Feature(
180        name="add-latomic-workaround",  # https://github.com/llvm/llvm-project/issues/73361
181        when=lambda cfg: sourceBuilds(
182            cfg, "int main(int, char**) { return 0; }", ["-latomic"]
183        ),
184        actions=[AddLinkFlag("-latomic")],
185    ),
186    Feature(
187        name="has-64-bit-atomics",
188        when=lambda cfg: sourceBuilds(
189            cfg,
190            """
191            #include <atomic>
192            struct Large { char storage[64/8]; };
193            std::atomic<Large> x;
194            int main(int, char**) { (void)x.load(); (void)x.is_lock_free(); return 0; }
195          """,
196        ),
197    ),
198    Feature(
199        name="has-1024-bit-atomics",
200        when=lambda cfg: sourceBuilds(
201            cfg,
202            """
203            #include <atomic>
204            struct Large { char storage[1024/8]; };
205            std::atomic<Large> x;
206            int main(int, char**) { (void)x.load(); (void)x.is_lock_free(); return 0; }
207          """,
208        ),
209    ),
210    # Tests that require 64-bit architecture
211    Feature(
212        name="32-bit-pointer",
213        when=lambda cfg: sourceBuilds(
214            cfg,
215            """
216            int main(int, char**) {
217              static_assert(sizeof(void *) == 4);
218            }
219          """,
220        ),
221    ),
222    # Check for a Windows UCRT bug (fixed in UCRT/Windows 10.0.20348.0):
223    # https://developercommunity.visualstudio.com/t/utf-8-locales-break-ctype-functions-for-wchar-type/1653678
224    Feature(
225        name="win32-broken-utf8-wchar-ctype",
226        when=lambda cfg: not "_LIBCPP_HAS_LOCALIZATION" in compilerMacros(cfg)
227        or compilerMacros(cfg)["_LIBCPP_HAS_LOCALIZATION"] == "1"
228        and "_WIN32" in compilerMacros(cfg)
229        and not programSucceeds(
230            cfg,
231            """
232            #include <locale.h>
233            #include <wctype.h>
234            int main(int, char**) {
235              setlocale(LC_ALL, "en_US.UTF-8");
236              return towlower(L'\\xDA') != L'\\xFA';
237            }
238          """,
239        ),
240    ),
241    # Check for a Windows UCRT bug (fixed in UCRT/Windows 10.0.19041.0).
242    # https://developercommunity.visualstudio.com/t/printf-formatting-with-g-outputs-too/1660837
243    Feature(
244        name="win32-broken-printf-g-precision",
245        when=lambda cfg: "_WIN32" in compilerMacros(cfg)
246        and not programSucceeds(
247            cfg,
248            """
249            #include <stdio.h>
250            #include <string.h>
251            int main(int, char**) {
252              char buf[100];
253              snprintf(buf, sizeof(buf), "%#.*g", 0, 0.0);
254              return strcmp(buf, "0.");
255            }
256          """,
257        ),
258    ),
259    # Check for a Windows UCRT bug (not fixed upstream yet).
260    # With UCRT, printf("%a", 0.0) produces "0x0.0000000000000p+0",
261    # while other C runtimes produce just "0x0p+0".
262    # https://developercommunity.visualstudio.com/t/Printf-formatting-of-float-as-hex-prints/1660844
263    Feature(
264        name="win32-broken-printf-a-precision",
265        when=lambda cfg: "_WIN32" in compilerMacros(cfg)
266        and not programSucceeds(
267            cfg,
268            """
269            #include <stdio.h>
270            #include <string.h>
271            int main(int, char**) {
272              char buf[100];
273              snprintf(buf, sizeof(buf), "%a", 0.0);
274              return strcmp(buf, "0x0p+0");
275            }
276          """,
277        ),
278    ),
279    # Check for Glibc < 2.27, where the ru_RU.UTF-8 locale had
280    # mon_decimal_point == ".", which our tests don't handle.
281    Feature(
282        name="glibc-old-ru_RU-decimal-point",
283        when=lambda cfg: not "_LIBCPP_HAS_LOCALIZATION" in compilerMacros(cfg)
284        or compilerMacros(cfg)["_LIBCPP_HAS_LOCALIZATION"] == "1"
285        and not programSucceeds(
286            cfg,
287            """
288            #include <locale.h>
289            #include <string.h>
290            int main(int, char**) {
291              setlocale(LC_ALL, "ru_RU.UTF-8");
292              return strcmp(localeconv()->mon_decimal_point, ",");
293            }
294          """,
295        ),
296    ),
297    Feature(
298        name="has-unix-headers",
299        when=lambda cfg: sourceBuilds(
300            cfg,
301            """
302            #include <unistd.h>
303            #include <sys/wait.h>
304            int main(int, char**) {
305              int fd[2];
306              return pipe(fd);
307            }
308          """,
309        ),
310    ),
311    # Whether Bash can run on the executor.
312    # This is not always the case, for example when running on embedded systems.
313    #
314    # For the corner case of bash existing, but it being missing in the path
315    # set in %{exec} as "--env PATH=one-single-dir", the executor does find
316    # and executes bash, but bash then can't find any other common shell
317    # utilities. Test executing "bash -c 'bash --version'" to see if bash
318    # manages to find binaries to execute.
319    Feature(
320        name="executor-has-no-bash",
321        when=lambda cfg: runScriptExitCode(cfg, ["%{exec} bash -c 'bash --version'"]) != 0,
322    ),
323    # Whether module support for the platform is available.
324    Feature(
325        name="has-no-cxx-module-support",
326        # The libc of these platforms have functions with internal linkage.
327        # This is not allowed per C11 7.1.2 Standard headers/6
328        #  Any declaration of a library function shall have external linkage.
329        when=lambda cfg: "__ANDROID__" in compilerMacros(cfg)
330        or "__FreeBSD__" in compilerMacros(cfg)
331        or ("_WIN32" in compilerMacros(cfg) and not _mingwSupportsModules(cfg))
332        or platform.system().lower().startswith("aix")
333        # Avoid building on platforms that don't support modules properly.
334        or not hasCompileFlag(cfg, "-Wno-reserved-module-identifier")
335        or not sourceBuilds(
336            cfg,
337            """
338            export module test;
339            int main(int, char**) { return 0; }
340          """,
341        ),
342    ),
343    # The time zone validation tests compare the output of zdump against the
344    # output generated by <chrono>'s time zone support.
345    Feature(
346        name="has-no-zdump",
347        when=lambda cfg: runScriptExitCode(cfg, ["zdump --version"]) != 0,
348    ),
349]
350
351# Deduce and add the test features that that are implied by the #defines in
352# the <__config> header.
353#
354# For each macro of the form `_LIBCPP_XXX_YYY_ZZZ` defined below that
355# is defined after including <__config>, add a Lit feature called
356# `libcpp-xxx-yyy-zzz`. When a macro is defined to a specific value
357# (e.g. `_LIBCPP_ABI_VERSION=2`), the feature is `libcpp-xxx-yyy-zzz=<value>`.
358#
359# Note that features that are more strongly tied to libc++ are named libcpp-foo,
360# while features that are more general in nature are not prefixed with 'libcpp-'.
361macros = {
362    "_LIBCPP_NO_VCRUNTIME": "libcpp-no-vcruntime",
363    "_LIBCPP_ABI_VERSION": "libcpp-abi-version",
364    "_LIBCPP_ABI_BOUNDED_ITERATORS": "libcpp-has-abi-bounded-iterators",
365    "_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STRING": "libcpp-has-abi-bounded-iterators-in-string",
366    "_LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR": "libcpp-has-abi-bounded-iterators-in-vector",
367    "_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STD_ARRAY": "libcpp-has-abi-bounded-iterators-in-std-array",
368    "_LIBCPP_ABI_BOUNDED_UNIQUE_PTR": "libcpp-has-abi-bounded-unique_ptr",
369    "_LIBCPP_ABI_FIX_UNORDERED_CONTAINER_SIZE_TYPE": "libcpp-has-abi-fix-unordered-container-size-type",
370    "_LIBCPP_DEPRECATED_ABI_DISABLE_PAIR_TRIVIAL_COPY_CTOR": "libcpp-deprecated-abi-disable-pair-trivial-copy-ctor",
371    "_LIBCPP_ABI_NO_COMPRESSED_PAIR_PADDING": "libcpp-abi-no-compressed-pair-padding",
372    "_LIBCPP_PSTL_BACKEND_LIBDISPATCH": "libcpp-pstl-backend-libdispatch",
373}
374for macro, feature in macros.items():
375    DEFAULT_FEATURES.append(
376        Feature(
377            name=lambda cfg, m=macro, f=feature: f + ("={}".format(compilerMacros(cfg)[m]) if compilerMacros(cfg)[m] else ""),
378            when=lambda cfg, m=macro: m in compilerMacros(cfg),
379        )
380    )
381
382true_false_macros = {
383    "_LIBCPP_HAS_THREAD_API_EXTERNAL": "libcpp-has-thread-api-external",
384    "_LIBCPP_HAS_THREAD_API_PTHREAD": "libcpp-has-thread-api-pthread",
385}
386for macro, feature in true_false_macros.items():
387    DEFAULT_FEATURES.append(
388        Feature(
389            name=feature,
390            when=lambda cfg, m=macro: m in compilerMacros(cfg)
391            and compilerMacros(cfg)[m] == "1",
392        )
393    )
394
395inverted_macros = {
396    "_LIBCPP_HAS_TIME_ZONE_DATABASE": "no-tzdb",
397    "_LIBCPP_HAS_FILESYSTEM": "no-filesystem",
398    "_LIBCPP_HAS_LOCALIZATION": "no-localization",
399    "_LIBCPP_HAS_THREADS": "no-threads",
400    "_LIBCPP_HAS_MONOTONIC_CLOCK": "no-monotonic-clock",
401    "_LIBCPP_HAS_WIDE_CHARACTERS": "no-wide-characters",
402    "_LIBCPP_HAS_VENDOR_AVAILABILITY_ANNOTATIONS": "libcpp-has-no-availability-markup",
403    "_LIBCPP_HAS_RANDOM_DEVICE": "no-random-device",
404    "_LIBCPP_HAS_UNICODE": "libcpp-has-no-unicode",
405    "_LIBCPP_HAS_TERMINAL": "no-terminal",
406}
407for macro, feature in inverted_macros.items():
408    DEFAULT_FEATURES.append(
409        Feature(
410            name=feature,
411            when=lambda cfg, m=macro: m in compilerMacros(cfg)
412            and compilerMacros(cfg)[m] == "0",
413        )
414    )
415
416# Mapping from canonical locale names (used in the tests) to possible locale
417# names on various systems. Each locale is considered supported if any of the
418# alternative names is supported.
419locales = {
420    "en_US.UTF-8": ["en_US.UTF-8", "en_US.utf8", "English_United States.1252"],
421    "fr_FR.UTF-8": ["fr_FR.UTF-8", "fr_FR.utf8", "French_France.1252"],
422    "ja_JP.UTF-8": ["ja_JP.UTF-8", "ja_JP.utf8", "Japanese_Japan.923"],
423    "ru_RU.UTF-8": ["ru_RU.UTF-8", "ru_RU.utf8", "Russian_Russia.1251"],
424    "zh_CN.UTF-8": ["zh_CN.UTF-8", "zh_CN.utf8", "Chinese_China.936"],
425    "fr_CA.ISO8859-1": ["fr_CA.ISO8859-1", "French_Canada.1252"],
426    "cs_CZ.ISO8859-2": ["cs_CZ.ISO8859-2", "Czech_Czech Republic.1250"],
427}
428for locale, alts in locales.items():
429    # Note: Using alts directly in the lambda body here will bind it to the value at the
430    # end of the loop. Assigning it to a default argument works around this issue.
431    DEFAULT_FEATURES.append(
432        Feature(
433            name="locale.{}".format(locale),
434            when=lambda cfg, alts=alts: hasAnyLocale(cfg, alts),
435        )
436    )
437
438
439# Add features representing the target platform name: darwin, linux, windows, etc...
440DEFAULT_FEATURES += [
441    Feature(name="darwin", when=lambda cfg: "__APPLE__" in compilerMacros(cfg)),
442    Feature(name="windows", when=lambda cfg: "_WIN32" in compilerMacros(cfg)),
443    Feature(
444        name="windows-dll",
445        when=lambda cfg: "_WIN32" in compilerMacros(cfg)
446        and sourceBuilds(
447            cfg,
448            """
449            #include <iostream>
450            int main(int, char**) { return 0; }
451          """,
452        )
453        and programSucceeds(
454            cfg,
455            """
456            #include <iostream>
457            #include <windows.h>
458            #include <winnt.h>
459            int main(int, char**) {
460              // Get a pointer to a data member that gets linked from the C++
461              // library. This must be a data member (functions can get
462              // thunk inside the calling executable), and must not be
463              // something that is defined inline in headers.
464              void *ptr = &std::cout;
465              // Get a handle to the current main executable.
466              void *exe = GetModuleHandle(NULL);
467              // The handle points at the PE image header. Navigate through
468              // the header structure to find the size of the PE image (the
469              // executable).
470              PIMAGE_DOS_HEADER dosheader = (PIMAGE_DOS_HEADER)exe;
471              PIMAGE_NT_HEADERS ntheader = (PIMAGE_NT_HEADERS)((BYTE *)dosheader + dosheader->e_lfanew);
472              PIMAGE_OPTIONAL_HEADER peheader = &ntheader->OptionalHeader;
473              void *exeend = (BYTE*)exe + peheader->SizeOfImage;
474              // Check if the tested pointer - the data symbol from the
475              // C++ library - is located within the exe.
476              if (ptr >= exe && ptr <= exeend)
477                return 1;
478              // Return success if it was outside of the executable, i.e.
479              // loaded from a DLL.
480              return 0;
481            }
482          """,
483        ),
484        actions=[AddCompileFlag("-DTEST_WINDOWS_DLL")],
485    ),
486    Feature(name="linux", when=lambda cfg: "__linux__" in compilerMacros(cfg)),
487    Feature(name="android", when=lambda cfg: "__ANDROID__" in compilerMacros(cfg)),
488    Feature(
489        name=lambda cfg: "android-device-api={}".format(_getAndroidDeviceApi(cfg)),
490        when=lambda cfg: "__ANDROID__" in compilerMacros(cfg),
491    ),
492    Feature(
493        name="LIBCXX-ANDROID-FIXME",
494        when=lambda cfg: "__ANDROID__" in compilerMacros(cfg),
495    ),
496    Feature(name="netbsd", when=lambda cfg: "__NetBSD__" in compilerMacros(cfg)),
497    Feature(name="freebsd", when=lambda cfg: "__FreeBSD__" in compilerMacros(cfg)),
498    Feature(
499        name="LIBCXX-FREEBSD-FIXME",
500        when=lambda cfg: "__FreeBSD__" in compilerMacros(cfg),
501    ),
502    Feature(
503        name="LIBCXX-PICOLIBC-FIXME",
504        when=lambda cfg: sourceBuilds(
505            cfg,
506            """
507            #include <string.h>
508            #ifndef __PICOLIBC__
509            #error not picolibc
510            #endif
511            int main(int, char**) { return 0; }
512          """,
513        ),
514    ),
515    Feature(
516        name="LIBCXX-AMDGPU-FIXME",
517        when=lambda cfg: "__AMDGPU__" in compilerMacros(cfg),
518    ),
519    Feature(
520        name="LIBCXX-NVPTX-FIXME",
521        when=lambda cfg: "__NVPTX__" in compilerMacros(cfg),
522    ),
523    Feature(
524        name="can-create-symlinks",
525        when=lambda cfg: "_WIN32" not in compilerMacros(cfg)
526        or programSucceeds(
527            cfg,
528            # Creation of symlinks require elevated privileges on Windows unless
529            # Windows developer mode is enabled.
530            """
531            #include <stdio.h>
532            #include <windows.h>
533            int main() {
534              CHAR tempDirPath[MAX_PATH];
535              DWORD tempPathRet = GetTempPathA(MAX_PATH, tempDirPath);
536              if (tempPathRet == 0 || tempPathRet > MAX_PATH) {
537                return 1;
538              }
539
540              CHAR tempFilePath[MAX_PATH];
541              UINT uRetVal = GetTempFileNameA(
542                tempDirPath,
543                "cxx", // Prefix
544                0, // Unique=0 also implies file creation.
545                tempFilePath);
546              if (uRetVal == 0) {
547                return 1;
548              }
549
550              CHAR symlinkFilePath[MAX_PATH];
551              int ret = sprintf_s(symlinkFilePath, MAX_PATH, "%s_symlink", tempFilePath);
552              if (ret == -1) {
553                DeleteFileA(tempFilePath);
554                return 1;
555              }
556
557              // Requires either administrator, or developer mode enabled.
558              BOOL bCreatedSymlink = CreateSymbolicLinkA(symlinkFilePath,
559                tempFilePath,
560                SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE);
561              if (!bCreatedSymlink) {
562                DeleteFileA(tempFilePath);
563                return 1;
564              }
565
566              DeleteFileA(tempFilePath);
567              DeleteFileA(symlinkFilePath);
568              return 0;
569            }
570            """,
571        ),
572    ),
573]
574
575# Add features representing the build host platform name.
576# The build host could differ from the target platform for cross-compilation.
577DEFAULT_FEATURES += [
578    Feature(name="buildhost={}".format(sys.platform.lower().strip())),
579    # sys.platform can often be represented by a "sub-system", such as 'win32', 'cygwin', 'mingw', freebsd13 & etc.
580    # We define a consolidated feature on a few platforms.
581    Feature(
582        name="buildhost=windows",
583        when=lambda cfg: platform.system().lower().startswith("windows"),
584    ),
585    Feature(
586        name="buildhost=freebsd",
587        when=lambda cfg: platform.system().lower().startswith("freebsd"),
588    ),
589    Feature(
590        name="buildhost=aix",
591        when=lambda cfg: platform.system().lower().startswith("aix"),
592    ),
593]
594
595# Detect whether GDB is on the system, has Python scripting and supports
596# adding breakpoint commands. If so add a substitution to access it.
597def check_gdb(cfg):
598    gdb_path = shutil.which("gdb")
599    if gdb_path is None:
600        return False
601
602    # Check that we can set breakpoint commands, which was added in 8.3.
603    # Using the quit command here means that gdb itself exits, not just
604    # the "python <...>" command.
605    test_src = """\
606try:
607  gdb.Breakpoint(\"main\").commands=\"foo\"
608except AttributeError:
609  gdb.execute(\"quit 1\")
610gdb.execute(\"quit\")"""
611
612    try:
613        stdout = subprocess.check_output(
614            [gdb_path, "-ex", "python " + test_src, "--batch"],
615            stderr=subprocess.DEVNULL,
616            universal_newlines=True,
617        )
618    except subprocess.CalledProcessError:
619        # We can't set breakpoint commands
620        return False
621
622    # Check we actually ran the Python
623    return not "Python scripting is not supported" in stdout
624
625
626DEFAULT_FEATURES += [
627    Feature(
628        name="host-has-gdb-with-python",
629        when=check_gdb,
630        actions=[AddSubstitution("%{gdb}", lambda cfg: shutil.which("gdb"))],
631    )
632]
633
634# Helpers to define correspondances between LLVM versions and vendor system versions.
635# Those are used for backdeployment features below, do not use directly in tests.
636DEFAULT_FEATURES += [
637    Feature(
638        name="_target-has-llvm-18",
639        when=lambda cfg: BooleanExpression.evaluate(
640            "target={{.+}}-apple-macosx{{15(.[0-9]+)?(.[0-9]+)?}}",
641            cfg.available_features,
642        ),
643    ),
644    Feature(
645        name="_target-has-llvm-17",
646        when=lambda cfg: BooleanExpression.evaluate(
647            "_target-has-llvm-18 || target={{.+}}-apple-macosx{{14.[4-9](.[0-9]+)?}} || target={{.+}}-apple-macosx{{1[5-9]([.].+)?}}",
648            cfg.available_features,
649        ),
650    ),
651    Feature(
652        name="_target-has-llvm-16",
653        when=lambda cfg: BooleanExpression.evaluate(
654            "_target-has-llvm-17 || target={{.+}}-apple-macosx{{14.[0-3](.[0-9]+)?}}",
655            cfg.available_features,
656        ),
657    ),
658    Feature(
659        name="_target-has-llvm-15",
660        when=lambda cfg: BooleanExpression.evaluate(
661            "_target-has-llvm-16 || target={{.+}}-apple-macosx{{13.[4-9](.[0-9]+)?}}",
662            cfg.available_features,
663        ),
664    ),
665    Feature(
666        name="_target-has-llvm-14",
667        when=lambda cfg: BooleanExpression.evaluate(
668            "_target-has-llvm-15",
669            cfg.available_features,
670        ),
671    ),
672    Feature(
673        name="_target-has-llvm-13",
674        when=lambda cfg: BooleanExpression.evaluate(
675            "_target-has-llvm-14 || target={{.+}}-apple-macosx{{13.[0-3](.[0-9]+)?}}",
676            cfg.available_features,
677        ),
678    ),
679    Feature(
680        name="_target-has-llvm-12",
681        when=lambda cfg: BooleanExpression.evaluate(
682            "_target-has-llvm-13 || target={{.+}}-apple-macosx{{12.[3-9](.[0-9]+)?}}",
683            cfg.available_features,
684        ),
685    ),
686    Feature(
687        name="_target-has-llvm-11",
688        when=lambda cfg: BooleanExpression.evaluate(
689            "_target-has-llvm-12 || target={{.+}}-apple-macosx{{(11.[0-9]|12.[0-2])(.[0-9]+)?}}",
690            cfg.available_features,
691        ),
692    ),
693    Feature(
694        name="_target-has-llvm-10",
695        when=lambda cfg: BooleanExpression.evaluate(
696            "_target-has-llvm-11",
697            cfg.available_features,
698        ),
699    ),
700    Feature(
701        name="_target-has-llvm-9",
702        when=lambda cfg: BooleanExpression.evaluate(
703            "_target-has-llvm-10 || target={{.+}}-apple-macosx{{10.15(.[0-9]+)?}}",
704            cfg.available_features,
705        ),
706    ),
707]
708
709# Define features for back-deployment testing.
710#
711# These features can be used to XFAIL tests that fail when deployed on (or compiled
712# for) an older system. For example, if a test exhibits a bug in the libc++ on a
713# particular system version, or if it uses a symbol that is not available on an
714# older version of the dylib, it can be marked as XFAIL with these features.
715#
716# We have two families of Lit features:
717#
718# The first one is `using-built-library-before-llvm-XYZ`. These features encode the
719# fact that the test suite is being *run* against a version of the shared/static library
720# that predates LLVM version XYZ. This is useful to represent the use case of compiling
721# a program against the latest libc++ but then deploying it and running it on an older
722# system with an older version of the (usually shared) library.
723#
724# This feature is built up using the target triple passed to the compiler and the
725# `stdlib=system` Lit feature, which encodes that we're running against the same library
726# as described by the target triple.
727#
728# The second set of features is `availability-<FEATURE>-missing`. This family of Lit
729# features encodes the presence of availability markup in the libc++ headers. This is
730# useful to check that a test fails specifically when compiled for a given deployment
731# target, such as when testing availability markup where we want to make sure that
732# using the annotated facility on a deployment target that doesn't support it will fail
733# at compile time. This can be achieved by creating a `.verify.cpp` test that checks for
734# the right errors and marking the test as `REQUIRES: availability-<FEATURE>-missing`.
735#
736# This feature is built up using the presence of availability markup detected inside
737# __config, the flavor of the library being tested and the target triple passed to the
738# compiler.
739#
740# Note that both families of Lit features are similar but different in important ways.
741# For example, tests for availability markup should be expected to produce diagnostics
742# regardless of whether we're running against a system library, as long as we're using
743# a libc++ flavor that enables availability markup. Similarly, a test could fail when
744# run against the system library of an older version of FreeBSD, even though FreeBSD
745# doesn't provide availability markup at the time of writing this.
746for version in ("9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20"):
747    DEFAULT_FEATURES.append(
748        Feature(
749            name="using-built-library-before-llvm-{}".format(version),
750            when=lambda cfg, v=version: BooleanExpression.evaluate(
751                "stdlib=system && !_target-has-llvm-{}".format(v),
752                cfg.available_features,
753            ),
754        )
755    )
756
757DEFAULT_FEATURES += [
758    # Tests that require std::filesystem support in the built library
759    Feature(
760        name="availability-filesystem-missing",
761        when=lambda cfg: BooleanExpression.evaluate(
762            "!libcpp-has-no-availability-markup && (stdlib=apple-libc++ && !_target-has-llvm-9)",
763            cfg.available_features,
764        ),
765    ),
766    # Tests that require the C++20 synchronization library (P1135R6 implemented by https://llvm.org/D68480) in the built library
767    Feature(
768        name="availability-synchronization_library-missing",
769        when=lambda cfg: BooleanExpression.evaluate(
770            "!libcpp-has-no-availability-markup && (stdlib=apple-libc++ && !_target-has-llvm-11)",
771            cfg.available_features,
772        ),
773    ),
774    # Tests that require https://wg21.link/P0482 support in the built library
775    Feature(
776        name="availability-char8_t_support-missing",
777        when=lambda cfg: BooleanExpression.evaluate(
778            "!libcpp-has-no-availability-markup && (stdlib=apple-libc++ && !_target-has-llvm-12)",
779            cfg.available_features,
780        ),
781    ),
782    # Tests that require std::to_chars(floating-point) in the built library
783    Feature(
784        name="availability-fp_to_chars-missing",
785        when=lambda cfg: BooleanExpression.evaluate(
786            "!libcpp-has-no-availability-markup && (stdlib=apple-libc++ && !_target-has-llvm-14)",
787            cfg.available_features,
788        ),
789    ),
790    # Tests that require __libcpp_verbose_abort support in the built library
791    Feature(
792        name="availability-verbose_abort-missing",
793        when=lambda cfg: BooleanExpression.evaluate(
794            "!libcpp-has-no-availability-markup && (stdlib=apple-libc++ && !_target-has-llvm-15)",
795            cfg.available_features,
796        ),
797    ),
798    # Tests that require std::pmr support in the built library
799    Feature(
800        name="availability-pmr-missing",
801        when=lambda cfg: BooleanExpression.evaluate(
802            "!libcpp-has-no-availability-markup && (stdlib=apple-libc++ && !_target-has-llvm-16)",
803            cfg.available_features,
804        ),
805    ),
806    # Tests that require support for <print> and std::print in <ostream> in the built library.
807    Feature(
808        name="availability-print-missing",
809        when=lambda cfg: BooleanExpression.evaluate(
810            "!libcpp-has-no-availability-markup && (stdlib=apple-libc++ && !_target-has-llvm-18)",
811            cfg.available_features,
812        ),
813    ),
814    # Tests that require time zone database support in the built library
815    Feature(
816        name="availability-tzdb-missing",
817        when=lambda cfg: BooleanExpression.evaluate(
818            "!libcpp-has-no-availability-markup && (stdlib=apple-libc++ && !_target-has-llvm-19)",
819            cfg.available_features,
820        ),
821    ),
822    # Tests that require std::from_chars(floating-point) in the built library
823    Feature(
824        name="availability-fp_from_chars-missing",
825        when=lambda cfg: BooleanExpression.evaluate(
826            "!libcpp-has-no-availability-markup && (stdlib=apple-libc++ && !_target-has-llvm-20)",
827            cfg.available_features,
828        ),
829    ),
830]
831