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