[libcxx-commits] [libcxx] [libc++] Split features.py into multiple files (PR #167353)

via libcxx-commits libcxx-commits at lists.llvm.org
Mon Nov 10 09:59:13 PST 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

<details>
<summary>Changes</summary>

The features.py file that performs detection of Lit features had grown to be massive, so this patch splits it into smaller chunks which makes it easier to keep things organized.

---

Patch is 78.08 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/167353.diff


10 Files Affected:

- (removed) libcxx/utils/libcxx/test/features.py (-920) 
- (added) libcxx/utils/libcxx/test/features/__init__.py (+21) 
- (added) libcxx/utils/libcxx/test/features/availability.py (+199) 
- (added) libcxx/utils/libcxx/test/features/compiler.py (+82) 
- (added) libcxx/utils/libcxx/test/features/gdb.py (+50) 
- (added) libcxx/utils/libcxx/test/features/libcxx_macros.py (+76) 
- (added) libcxx/utils/libcxx/test/features/localization.py (+142) 
- (added) libcxx/utils/libcxx/test/features/misc.py (+299) 
- (added) libcxx/utils/libcxx/test/features/platform.py (+132) 
- (modified) libcxx/utils/libcxx/test/params.py (+1-1) 


``````````diff
diff --git a/libcxx/utils/libcxx/test/features.py b/libcxx/utils/libcxx/test/features.py
deleted file mode 100644
index 5da1d9afee911..0000000000000
--- a/libcxx/utils/libcxx/test/features.py
+++ /dev/null
@@ -1,920 +0,0 @@
-# ===----------------------------------------------------------------------===##
-#
-# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-# See https://llvm.org/LICENSE.txt for license information.
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-#
-# ===----------------------------------------------------------------------===##
-
-from libcxx.test.dsl import *
-from lit.BooleanExpression import BooleanExpression
-import re
-import shutil
-import subprocess
-import sys
-
-_isAnyClang = lambda cfg: "__clang__" in compilerMacros(cfg)
-_isAppleClang = lambda cfg: "__apple_build_version__" in compilerMacros(cfg)
-_isAnyGCC = lambda cfg: "__GNUC__" in compilerMacros(cfg)
-_isClang = lambda cfg: _isAnyClang(cfg) and not _isAppleClang(cfg)
-_isGCC = lambda cfg: _isAnyGCC(cfg) and not _isAnyClang(cfg)
-_isAnyClangOrGCC = lambda cfg: _isAnyClang(cfg) or _isAnyGCC(cfg)
-_isClExe = lambda cfg: not _isAnyClangOrGCC(cfg)
-_isMSVC = lambda cfg: "_MSC_VER" in compilerMacros(cfg)
-_msvcVersion = lambda cfg: (int(compilerMacros(cfg)["_MSC_VER"]) // 100, int(compilerMacros(cfg)["_MSC_VER"]) % 100)
-
-def _getAndroidDeviceApi(cfg):
-    return int(
-        programOutput(
-            cfg,
-            r"""
-                #include <android/api-level.h>
-                #include <stdio.h>
-                int main(int, char**) {
-                    printf("%d\n", android_get_device_api_level());
-                    return 0;
-                }
-            """,
-        )
-    )
-
-
-def _mingwSupportsModules(cfg):
-    # Only mingw headers are known to work with libc++ built as a module,
-    # at the moment.
-    if not "__MINGW32__" in compilerMacros(cfg):
-        return False
-    # For mingw headers, check for a version known to support being built
-    # as a module.
-    return sourceBuilds(
-        cfg,
-        """
-        #include <_mingw_mac.h>
-        #if __MINGW64_VERSION_MAJOR < 12
-        #error Headers known to be incompatible
-        #elif __MINGW64_VERSION_MAJOR == 12
-        // The headers were fixed to work with libc++ modules during
-        // __MINGW64_VERSION_MAJOR == 12. The headers became compatible
-        // with libc++ built as a module in
-        // 1652e9241b5d8a5a779c6582b1c3c4f4a7cc66e5 (Apr 2024), but the
-        // following commit 8c13b28ace68f2c0094d45121d59a4b951b533ed
-        // removed the now unused __mingw_static_ovr define. Use this
-        // as indicator for whether we've got new enough headers.
-        #ifdef __mingw_static_ovr
-        #error Headers too old
-        #endif
-        #else
-        // __MINGW64_VERSION_MAJOR > 12 should be ok.
-        #endif
-        int main(int, char**) { return 0; }
-        """,
-    )
-
-
-# Lit features are evaluated in order. Some checks may require the compiler detection to have
-# run first in order to work properly.
-DEFAULT_FEATURES = [
-    # gcc-style-warnings detects compilers that understand -Wno-meow flags, unlike MSVC's compiler driver cl.exe.
-    Feature(name="gcc-style-warnings", when=_isAnyClangOrGCC),
-    Feature(name="cl-style-warnings", when=_isClExe),
-    Feature(name="apple-clang", when=_isAppleClang),
-    Feature(
-        name=lambda cfg: "apple-clang-{__clang_major__}".format(**compilerMacros(cfg)),
-        when=_isAppleClang,
-    ),
-    Feature(
-        name=lambda cfg: "apple-clang-{__clang_major__}.{__clang_minor__}".format(**compilerMacros(cfg)),
-        when=_isAppleClang,
-    ),
-    Feature(
-        name=lambda cfg: "apple-clang-{__clang_major__}.{__clang_minor__}.{__clang_patchlevel__}".format(**compilerMacros(cfg)),
-        when=_isAppleClang,
-    ),
-    Feature(name="clang", when=_isClang),
-    Feature(
-        name=lambda cfg: "clang-{__clang_major__}".format(**compilerMacros(cfg)),
-        when=_isClang,
-    ),
-    Feature(
-        name=lambda cfg: "clang-{__clang_major__}.{__clang_minor__}".format(**compilerMacros(cfg)),
-        when=_isClang,
-    ),
-    Feature(
-        name=lambda cfg: "clang-{__clang_major__}.{__clang_minor__}.{__clang_patchlevel__}".format(**compilerMacros(cfg)),
-        when=_isClang,
-    ),
-    # Note: Due to a GCC bug (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104760), we must disable deprecation warnings
-    #       on GCC or spurious diagnostics are issued.
-    #
-    # TODO:
-    # - Enable -Wplacement-new with GCC.
-    # - Enable -Wclass-memaccess with GCC.
-    Feature(
-        name="gcc",
-        when=_isGCC,
-        actions=[
-            AddCompileFlag("-D_LIBCPP_DISABLE_DEPRECATION_WARNINGS"),
-            AddCompileFlag("-Wno-placement-new"),
-            AddCompileFlag("-Wno-class-memaccess"),
-            AddFeature("GCC-ALWAYS_INLINE-FIXME"),
-        ],
-    ),
-    Feature(
-        name=lambda cfg: "gcc-{__GNUC__}".format(**compilerMacros(cfg)), when=_isGCC
-    ),
-    Feature(
-        name=lambda cfg: "gcc-{__GNUC__}.{__GNUC_MINOR__}".format(**compilerMacros(cfg)),
-        when=_isGCC,
-    ),
-    Feature(
-        name=lambda cfg: "gcc-{__GNUC__}.{__GNUC_MINOR__}.{__GNUC_PATCHLEVEL__}".format(**compilerMacros(cfg)),
-        when=_isGCC,
-    ),
-    Feature(name="msvc", when=_isMSVC),
-    Feature(name=lambda cfg: "msvc-{}".format(*_msvcVersion(cfg)), when=_isMSVC),
-    Feature(name=lambda cfg: "msvc-{}.{}".format(*_msvcVersion(cfg)), when=_isMSVC),
-
-    Feature(
-        name="diagnose-if-support",
-        when=lambda cfg: hasCompileFlag(cfg, "-Wuser-defined-warnings"),
-        actions=[AddCompileFlag("-Wuser-defined-warnings")],
-    ),
-    Feature(
-        name="character-conversion-warnings",
-        when=lambda cfg: hasCompileFlag(cfg, "-Wcharacter-conversion"),
-    ),
-    # Tests to validate whether the compiler has a way to set the maximum number
-    # of steps during constant evaluation. Since the flag differs per compiler
-    # store the "valid" flag as a feature. This allows passing the proper compile
-    # flag to the compiler:
-    # // ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=12345678
-    # // ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=12345678
-    Feature(
-        name="has-fconstexpr-steps",
-        when=lambda cfg: hasCompileFlag(cfg, "-fconstexpr-steps=1"),
-    ),
-    Feature(
-        name="has-fconstexpr-ops-limit",
-        when=lambda cfg: hasCompileFlag(cfg, "-fconstexpr-ops-limit=1"),
-    ),
-    Feature(name="has-fblocks", when=lambda cfg: hasCompileFlag(cfg, "-fblocks")),
-    Feature(
-        name="fdelayed-template-parsing",
-        when=lambda cfg: hasCompileFlag(cfg, "-fdelayed-template-parsing"),
-    ),
-    Feature(
-        name="has-fobjc-arc",
-        when=lambda cfg: hasCompileFlag(cfg, "-xobjective-c++ -fobjc-arc")
-        and sys.platform.lower().strip() == "darwin",
-    ),  # TODO: this doesn't handle cross-compiling to Apple platforms.
-    Feature(
-        name="objective-c++",
-        when=lambda cfg: hasCompileFlag(cfg, "-xobjective-c++ -fobjc-arc"),
-    ),
-    Feature(
-        name="verify-support",
-        when=lambda cfg: hasCompileFlag(cfg, "-Xclang -verify-ignore-unexpected"),
-    ),
-    Feature(
-        name="add-latomic-workaround",  # https://llvm.org/PR73361
-        when=lambda cfg: sourceBuilds(
-            cfg, "int main(int, char**) { return 0; }", ["-latomic"]
-        ),
-        actions=[AddLinkFlag("-latomic")],
-    ),
-    Feature(
-        name="has-64-bit-atomics",
-        when=lambda cfg: sourceBuilds(
-            cfg,
-            """
-            #include <atomic>
-            struct Large { char storage[64/8]; };
-            std::atomic<Large> x;
-            int main(int, char**) { (void)x.load(); (void)x.is_lock_free(); return 0; }
-          """,
-        ),
-    ),
-    Feature(
-        name="has-1024-bit-atomics",
-        when=lambda cfg: sourceBuilds(
-            cfg,
-            """
-            #include <atomic>
-            struct Large { char storage[1024/8]; };
-            std::atomic<Large> x;
-            int main(int, char**) { (void)x.load(); (void)x.is_lock_free(); return 0; }
-          """,
-        ),
-    ),
-    # Tests that require 64-bit architecture
-    Feature(
-        name="32-bit-pointer",
-        when=lambda cfg: sourceBuilds(
-            cfg,
-            """
-            int main(int, char**) {
-              static_assert(sizeof(void *) == 4);
-            }
-          """,
-        ),
-    ),
-    # Check for a Windows UCRT bug (fixed in UCRT/Windows 10.0.20348.0):
-    # https://developercommunity.visualstudio.com/t/utf-8-locales-break-ctype-functions-for-wchar-type/1653678
-    Feature(
-        name="win32-broken-utf8-wchar-ctype",
-        when=lambda cfg: not "_LIBCPP_HAS_LOCALIZATION" in compilerMacros(cfg)
-        or compilerMacros(cfg)["_LIBCPP_HAS_LOCALIZATION"] == "1"
-        and "_WIN32" in compilerMacros(cfg)
-        and not programSucceeds(
-            cfg,
-            """
-            #include <locale.h>
-            #include <wctype.h>
-            int main(int, char**) {
-              setlocale(LC_ALL, "en_US.UTF-8");
-              return towlower(L'\\xDA') != L'\\xFA';
-            }
-          """,
-        ),
-    ),
-    # Check for a Windows UCRT bug (fixed in UCRT/Windows 10.0.19041.0).
-    # https://developercommunity.visualstudio.com/t/printf-formatting-with-g-outputs-too/1660837
-    Feature(
-        name="win32-broken-printf-g-precision",
-        when=lambda cfg: "_WIN32" in compilerMacros(cfg)
-        and not programSucceeds(
-            cfg,
-            """
-            #include <stdio.h>
-            #include <string.h>
-            int main(int, char**) {
-              char buf[100];
-              snprintf(buf, sizeof(buf), "%#.*g", 0, 0.0);
-              return strcmp(buf, "0.");
-            }
-          """,
-        ),
-    ),
-    # Check for a Windows UCRT bug (not fixed upstream yet).
-    # With UCRT, printf("%a", 0.0) produces "0x0.0000000000000p+0",
-    # while other C runtimes produce just "0x0p+0".
-    # https://developercommunity.visualstudio.com/t/Printf-formatting-of-float-as-hex-prints/1660844
-    Feature(
-        name="win32-broken-printf-a-precision",
-        when=lambda cfg: "_WIN32" in compilerMacros(cfg)
-        and not programSucceeds(
-            cfg,
-            """
-            #include <stdio.h>
-            #include <string.h>
-            int main(int, char**) {
-              char buf[100];
-              snprintf(buf, sizeof(buf), "%a", 0.0);
-              return strcmp(buf, "0x0p+0");
-            }
-          """,
-        ),
-    ),
-    # Check for Glibc < 2.27, where the ru_RU.UTF-8 locale had
-    # mon_decimal_point == ".", which our tests don't handle.
-    Feature(
-        name="glibc-old-ru_RU-decimal-point",
-        when=lambda cfg: not "_LIBCPP_HAS_LOCALIZATION" in compilerMacros(cfg)
-        or compilerMacros(cfg)["_LIBCPP_HAS_LOCALIZATION"] == "1"
-        and not programSucceeds(
-            cfg,
-            """
-            #include <locale.h>
-            #include <string.h>
-            int main(int, char**) {
-              setlocale(LC_ALL, "ru_RU.UTF-8");
-              return strcmp(localeconv()->mon_decimal_point, ",");
-            }
-          """,
-        ),
-    ),
-    Feature(
-        name="has-unix-headers",
-        when=lambda cfg: sourceBuilds(
-            cfg,
-            """
-            #include <unistd.h>
-            #include <sys/wait.h>
-            int main(int, char**) {
-              int fd[2];
-              return pipe(fd);
-            }
-          """,
-        ),
-    ),
-    # Whether Bash can run on the executor.
-    # This is not always the case, for example when running on embedded systems.
-    #
-    # For the corner case of bash existing, but it being missing in the path
-    # set in %{exec} as "--env PATH=one-single-dir", the executor does find
-    # and executes bash, but bash then can't find any other common shell
-    # utilities. Test executing "bash -c 'bash --version'" to see if bash
-    # manages to find binaries to execute.
-    Feature(
-        name="executor-has-no-bash",
-        when=lambda cfg: runScriptExitCode(cfg, ["%{exec} bash -c 'bash --version'"]) != 0,
-    ),
-    # Whether module support for the platform is available.
-    Feature(
-        name="has-no-cxx-module-support",
-        # The libc of these platforms have functions with internal linkage.
-        # This is not allowed per C11 7.1.2 Standard headers/6
-        #  Any declaration of a library function shall have external linkage.
-        when=lambda cfg: "__ANDROID__" in compilerMacros(cfg)
-        or "__FreeBSD__" in compilerMacros(cfg)
-        or ("_WIN32" in compilerMacros(cfg) and not _mingwSupportsModules(cfg))
-        or platform.system().lower().startswith("aix")
-        # Avoid building on platforms that don't support modules properly.
-        or not hasCompileFlag(cfg, "-Wno-reserved-module-identifier")
-        # older versions don't support extern "C++", newer versions don't support main in named module.
-        or not (
-            sourceBuilds(
-                cfg,
-                """
-            export module test;
-            extern "C++" int main(int, char**) { return 0; }
-          """,
-            )
-            or sourceBuilds(
-                cfg,
-                """
-            export module test;
-            int main(int, char**) { return 0; }
-          """,
-            )
-        ),
-    ),
-    # The time zone validation tests compare the output of zdump against the
-    # output generated by <chrono>'s time zone support.
-    Feature(
-        name="has-no-zdump",
-        when=lambda cfg: runScriptExitCode(cfg, ["zdump --version"]) != 0,
-    ),
-]
-
-# Deduce and add the test features that that are implied by the #defines in
-# the <__config> header.
-#
-# For each macro of the form `_LIBCPP_XXX_YYY_ZZZ` defined below that
-# is defined after including <__config>, add a Lit feature called
-# `libcpp-xxx-yyy-zzz`. When a macro is defined to a specific value
-# (e.g. `_LIBCPP_ABI_VERSION=2`), the feature is `libcpp-xxx-yyy-zzz=<value>`.
-#
-# Note that features that are more strongly tied to libc++ are named libcpp-foo,
-# while features that are more general in nature are not prefixed with 'libcpp-'.
-macros = {
-    "_LIBCPP_NO_VCRUNTIME": "libcpp-no-vcruntime",
-    "_LIBCPP_ABI_VERSION": "libcpp-abi-version",
-    "_LIBCPP_ABI_BOUNDED_ITERATORS": "libcpp-has-abi-bounded-iterators",
-    "_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STRING": "libcpp-has-abi-bounded-iterators-in-string",
-    "_LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR": "libcpp-has-abi-bounded-iterators-in-vector",
-    "_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STD_ARRAY": "libcpp-has-abi-bounded-iterators-in-std-array",
-    "_LIBCPP_ABI_BOUNDED_UNIQUE_PTR": "libcpp-has-abi-bounded-unique_ptr",
-    "_LIBCPP_ABI_FIX_UNORDERED_CONTAINER_SIZE_TYPE": "libcpp-has-abi-fix-unordered-container-size-type",
-    "_LIBCPP_DEPRECATED_ABI_DISABLE_PAIR_TRIVIAL_COPY_CTOR": "libcpp-deprecated-abi-disable-pair-trivial-copy-ctor",
-    "_LIBCPP_ABI_NO_COMPRESSED_PAIR_PADDING": "libcpp-abi-no-compressed-pair-padding",
-    "_LIBCPP_PSTL_BACKEND_LIBDISPATCH": "libcpp-pstl-backend-libdispatch",
-}
-for macro, feature in macros.items():
-    DEFAULT_FEATURES.append(
-        Feature(
-            name=lambda cfg, m=macro, f=feature: f + ("={}".format(compilerMacros(cfg)[m]) if compilerMacros(cfg)[m] else ""),
-            when=lambda cfg, m=macro: m in compilerMacros(cfg),
-        )
-    )
-
-true_false_macros = {
-    "_LIBCPP_HAS_THREAD_API_EXTERNAL": "libcpp-has-thread-api-external",
-    "_LIBCPP_HAS_THREAD_API_PTHREAD": "libcpp-has-thread-api-pthread",
-}
-for macro, feature in true_false_macros.items():
-    DEFAULT_FEATURES.append(
-        Feature(
-            name=feature,
-            when=lambda cfg, m=macro: m in compilerMacros(cfg)
-            and compilerMacros(cfg)[m] == "1",
-        )
-    )
-
-inverted_macros = {
-    "_LIBCPP_HAS_TIME_ZONE_DATABASE": "no-tzdb",
-    "_LIBCPP_HAS_FILESYSTEM": "no-filesystem",
-    "_LIBCPP_HAS_LOCALIZATION": "no-localization",
-    "_LIBCPP_HAS_THREADS": "no-threads",
-    "_LIBCPP_HAS_MONOTONIC_CLOCK": "no-monotonic-clock",
-    "_LIBCPP_HAS_WIDE_CHARACTERS": "no-wide-characters",
-    "_LIBCPP_HAS_VENDOR_AVAILABILITY_ANNOTATIONS": "libcpp-has-no-availability-markup",
-    "_LIBCPP_HAS_RANDOM_DEVICE": "no-random-device",
-    "_LIBCPP_HAS_UNICODE": "libcpp-has-no-unicode",
-    "_LIBCPP_HAS_TERMINAL": "no-terminal",
-}
-for macro, feature in inverted_macros.items():
-    DEFAULT_FEATURES.append(
-        Feature(
-            name=feature,
-            when=lambda cfg, m=macro: m in compilerMacros(cfg)
-            and compilerMacros(cfg)[m] == "0",
-        )
-    )
-
-# Mapping from canonical locale names (used in the tests) to possible locale
-# names on various systems. Each locale is considered supported if any of the
-# alternative names is supported.
-locales = {
-    "en_US.UTF-8": ["en_US.UTF-8", "en_US.utf8", "English_United States.1252"],
-    "fr_FR.UTF-8": ["fr_FR.UTF-8", "fr_FR.utf8", "French_France.1252"],
-    "ja_JP.UTF-8": ["ja_JP.UTF-8", "ja_JP.utf8", "Japanese_Japan.923"],
-    "ru_RU.UTF-8": ["ru_RU.UTF-8", "ru_RU.utf8", "Russian_Russia.1251"],
-    "zh_CN.UTF-8": ["zh_CN.UTF-8", "zh_CN.utf8", "Chinese_China.936"],
-    "fr_CA.ISO8859-1": ["fr_CA.ISO8859-1", "French_Canada.1252"],
-    "cs_CZ.ISO8859-2": ["cs_CZ.ISO8859-2", "Czech_Czech Republic.1250"],
-}
-provide_locale_conversions = {
-    "fr_FR.UTF-8": ["decimal_point", "mon_thousands_sep", "thousands_sep"],
-    "ru_RU.UTF-8": ["mon_thousands_sep"],
-}
-for locale, alts in locales.items():
-    # Note: Using alts directly in the lambda body here will bind it to the value at the
-    # end of the loop. Assigning it to a default argument works around this issue.
-    DEFAULT_FEATURES.append(
-        Feature(
-            name="locale.{}".format(locale),
-            when=lambda cfg, alts=alts: hasAnyLocale(cfg, alts),
-            actions=lambda cfg, locale=locale, alts=alts: _getLocaleFlagsAction(
-                cfg, locale, alts, provide_locale_conversions[locale]
-            )
-            if locale in provide_locale_conversions
-            and ("_LIBCPP_HAS_WIDE_CHARACTERS" not in compilerMacros(cfg) or
-                 compilerMacros(cfg)["_LIBCPP_HAS_WIDE_CHARACTERS"] == "1")
-            else [],
-        ),
-    )
-
-
-# Provide environment locale conversions through substitutions to avoid platform specific
-# maintenance.
-def _getLocaleFlagsAction(cfg, locale, alts, members):
-    alts_list = ",".join([f'"{l}"' for l in alts])
-    get_member_list = ",".join([f"lc->{m}" for m in members])
-
-    localeconv_info = programOutput(
-        cfg,
-        r"""
-        #if defined(_WIN32) && !defined(_CRT_SECURE_NO_WARNINGS)
-        #define _CRT_SECURE_NO_WARNINGS
-        #endif
-        #include <stdio.h>
-        #include <locale.h>
-        #include <stdlib.h>
-        #include <wchar.h>
-
-        // Print each requested locale conversion member on separate lines.
-        int main(int, char**) {
-          const char* locales[] = { %s };
-          for (int loc_i = 0; loc_i < %d; ++loc_i) {
-            if (!setlocale(LC_ALL, locales[loc_i])) {
-              continue; // Choose first locale name that is recognized.
-            }
-
-            lconv* lc = localeconv();
-            const char* members[] = { %s };
-            for (size_t m_i = 0; m_i < %d; ++m_i) {
-              if (!members[m_i]) {
-                printf("\n"); // member value is an empty string
-                continue;
-              }
-
-              size_t len = mbstowcs(nullptr, members[m_i], 0);
-              if (len == static_cast<size_t>(-1)) {
-                fprintf(stderr, "mbstowcs failed unexpectedly\n");
-                return 1;
-              }
-              // Include room for null terminator. Use malloc as these features
-              // are also used by lit configs that don't use -lc++ (libunwind tests).
-              wchar_t* dst = (wchar_t*)malloc((len + 1) * sizeof(wchar_t...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/167353


More information about the libcxx-commits mailing list