[libcxx-commits] [libcxx] [libc++] Implements the new version header generator. (PR #97847)

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Fri Jul 5 10:58:19 PDT 2024


https://github.com/mordante created https://github.com/llvm/llvm-project/pull/97847

The generator makes a few changes to the output
- removes the synopsis, it did not really show what was implemented correctly.
- the output now is clang-format clean.

This code uses the new FTM data structure. Since the contents of this structure are not up-to-date the code is only used in its tests.

>From f40e7afa358082273df8f9f8b167cb12051b120a Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Fri, 5 Jul 2024 19:42:37 +0200
Subject: [PATCH] [libc++] Implements the new version header generator.

The generator makes a few changes to the output
- removes the synopsis, it did not really show what was implemented
  correctly.
- the output now is clang-format clean.

This code uses the new FTM data structure. Since the contents of this
structure are not up-to-date the code is only used in its tests.
---
 .../feature_test_macro/ftm_meta_data.sh.py    |  51 +++++++
 .../feature_test_macro/implemented_ftms.sh.py |   2 +-
 .../feature_test_macro/standard_ftms.sh.py    |   2 +-
 .../libcxx/feature_test_macro/test_data.json  |   7 +
 .../feature_test_macro/version_header.sh.py   |  73 +++++++++
 .../version_header_implementation.sh.py       | 114 ++++++++++++++
 .../generate_feature_test_macro_components.py | 143 ++++++++++++++++++
 7 files changed, 390 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/libcxx/feature_test_macro/ftm_meta_data.sh.py
 create mode 100644 libcxx/test/libcxx/feature_test_macro/version_header.sh.py
 create mode 100644 libcxx/test/libcxx/feature_test_macro/version_header_implementation.sh.py

diff --git a/libcxx/test/libcxx/feature_test_macro/ftm_meta_data.sh.py b/libcxx/test/libcxx/feature_test_macro/ftm_meta_data.sh.py
new file mode 100644
index 00000000000000..5d12a68a40eaa8
--- /dev/null
+++ b/libcxx/test/libcxx/feature_test_macro/ftm_meta_data.sh.py
@@ -0,0 +1,51 @@
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+# RUN: %{python} %s %{libcxx-dir}/utils %{libcxx-dir}/test/libcxx/feature_test_macro/test_data.json
+
+import sys
+
+sys.path.append(sys.argv[1])
+from generate_feature_test_macro_components import FeatureTestMacros
+
+
+def test(output, expected):
+    assert output == expected, f"expected\n{expected}\n\noutput\n{output}"
+
+
+ftm = FeatureTestMacros(sys.argv[2])
+test(
+    ftm.ftm_meta_data,
+    {
+        "__cpp_lib_any": {
+            "headers": ["any"],
+            "test_suite_guard": None,
+            "libcxx_guard": None,
+        },
+        "__cpp_lib_barrier": {
+            "headers": ["barrier"],
+            "test_suite_guard": "!defined(_LIBCPP_HAS_NO_THREADS) && (!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_SYNC)",
+            "libcxx_guard": "!defined(_LIBCPP_HAS_NO_THREADS) && _LIBCPP_AVAILABILITY_HAS_SYNC",
+        },
+        "__cpp_lib_format": {
+            "headers": ["format"],
+            "test_suite_guard": None,
+            "libcxx_guard": None,
+        },
+        "__cpp_lib_parallel_algorithm": {
+            "headers": ["algorithm", "numeric"],
+            "test_suite_guard": None,
+            "libcxx_guard": None,
+        },
+        "__cpp_lib_variant": {
+            "headers": ["variant"],
+            "test_suite_guard": None,
+            "libcxx_guard": None,
+        },
+    },
+)
diff --git a/libcxx/test/libcxx/feature_test_macro/implemented_ftms.sh.py b/libcxx/test/libcxx/feature_test_macro/implemented_ftms.sh.py
index 67353fc41e5098..62a3c46b15db3c 100644
--- a/libcxx/test/libcxx/feature_test_macro/implemented_ftms.sh.py
+++ b/libcxx/test/libcxx/feature_test_macro/implemented_ftms.sh.py
@@ -31,7 +31,7 @@ def test(output, expected):
         "__cpp_lib_barrier": {
             "c++20": "201907L",
             "c++23": "201907L",
-            "c++26": "201907L",
+            "c++26": "299900L",
         },
         "__cpp_lib_format": {
             "c++20": None,
diff --git a/libcxx/test/libcxx/feature_test_macro/standard_ftms.sh.py b/libcxx/test/libcxx/feature_test_macro/standard_ftms.sh.py
index 43c90b131bff16..231e6072898752 100644
--- a/libcxx/test/libcxx/feature_test_macro/standard_ftms.sh.py
+++ b/libcxx/test/libcxx/feature_test_macro/standard_ftms.sh.py
@@ -31,7 +31,7 @@ def test(output, expected):
         "__cpp_lib_barrier": {
             "c++20": "201907L",
             "c++23": "201907L",
-            "c++26": "201907L",
+            "c++26": "299900L",
         },
         "__cpp_lib_format": {
             "c++20": "202110L",
diff --git a/libcxx/test/libcxx/feature_test_macro/test_data.json b/libcxx/test/libcxx/feature_test_macro/test_data.json
index 1f8bbe5d769b5b..18c8836fa5149f 100644
--- a/libcxx/test/libcxx/feature_test_macro/test_data.json
+++ b/libcxx/test/libcxx/feature_test_macro/test_data.json
@@ -23,6 +23,13 @@
             "implemented": true
           }
         ]
+      },
+      "c++26": {
+        "299900": [
+          {
+            "implemented": true
+          }
+        ]
       }
     },
     "headers": [
diff --git a/libcxx/test/libcxx/feature_test_macro/version_header.sh.py b/libcxx/test/libcxx/feature_test_macro/version_header.sh.py
new file mode 100644
index 00000000000000..bbb863371f7639
--- /dev/null
+++ b/libcxx/test/libcxx/feature_test_macro/version_header.sh.py
@@ -0,0 +1,73 @@
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+# RUN: %{python} %s %{libcxx-dir}/utils %{libcxx-dir}/test/libcxx/feature_test_macro/test_data.json
+
+import sys
+
+sys.path.append(sys.argv[1])
+from generate_feature_test_macro_components import FeatureTestMacros
+
+
+def test(output, expected):
+    assert output == expected, f"expected\n{expected}\n\noutput\n{output}"
+
+
+ftm = FeatureTestMacros(sys.argv[2])
+test(
+    ftm.version_header,
+    """// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_VERSION
+#define _LIBCPP_VERSION
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 17
+#  define __cpp_lib_any 201606L
+#  define __cpp_lib_parallel_algorithm 201603L
+#  define __cpp_lib_variant 202102L
+#endif // _LIBCPP_STD_VER >= 17
+
+#if _LIBCPP_STD_VER >= 20
+#  if !defined(_LIBCPP_HAS_NO_THREADS) && _LIBCPP_AVAILABILITY_HAS_SYNC
+#    define __cpp_lib_barrier 201907L
+#  endif
+// define __cpp_lib_format 202110L
+#  undef __cpp_lib_variant
+#  define __cpp_lib_variant 202106L
+#endif // _LIBCPP_STD_VER >= 20
+
+#if _LIBCPP_STD_VER >= 23
+// define __cpp_lib_format 202207L
+#endif // _LIBCPP_STD_VER >= 23
+
+#if _LIBCPP_STD_VER >= 26
+#  if !defined(_LIBCPP_HAS_NO_THREADS) && _LIBCPP_AVAILABILITY_HAS_SYNC
+#    undef __cpp_lib_barrier
+#    define __cpp_lib_barrier 299900L
+#  endif
+// define __cpp_lib_format 202311L
+#  undef __cpp_lib_variant
+#  define __cpp_lib_variant 202306L
+#endif // _LIBCPP_STD_VER >= 26
+
+#endif // _LIBCPP_VERSION
+""",
+)
diff --git a/libcxx/test/libcxx/feature_test_macro/version_header_implementation.sh.py b/libcxx/test/libcxx/feature_test_macro/version_header_implementation.sh.py
new file mode 100644
index 00000000000000..db90206ae770f3
--- /dev/null
+++ b/libcxx/test/libcxx/feature_test_macro/version_header_implementation.sh.py
@@ -0,0 +1,114 @@
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+# RUN: %{python} %s %{libcxx-dir}/utils %{libcxx-dir}/test/libcxx/feature_test_macro/test_data.json
+
+import sys
+
+sys.path.append(sys.argv[1])
+from generate_feature_test_macro_components import FeatureTestMacros
+
+
+def test(output, expected):
+    assert output == expected, f"expected\n{expected}\n\noutput\n{output}"
+
+
+ftm = FeatureTestMacros(sys.argv[2])
+test(
+    ftm.version_header_implementation,
+    {
+        "17": [
+            {
+                "__cpp_lib_any": {
+                    "value": "201606L",
+                    "implemented": True,
+                    "need_undef": False,
+                    "condition": None,
+                },
+            },
+            {
+                "__cpp_lib_parallel_algorithm": {
+                    "value": "201603L",
+                    "implemented": True,
+                    "need_undef": False,
+                    "condition": None,
+                },
+            },
+            {
+                "__cpp_lib_variant": {
+                    "value": "202102L",
+                    "implemented": True,
+                    "need_undef": False,
+                    "condition": None,
+                },
+            },
+        ],
+        "20": [
+            {
+                "__cpp_lib_barrier": {
+                    "value": "201907L",
+                    "implemented": True,
+                    "need_undef": False,
+                    "condition": "!defined(_LIBCPP_HAS_NO_THREADS) && _LIBCPP_AVAILABILITY_HAS_SYNC",
+                },
+            },
+            {
+                "__cpp_lib_format": {
+                    "value": "202110L",
+                    "implemented": False,
+                    "need_undef": False,
+                    "condition": None,
+                },
+            },
+            {
+                "__cpp_lib_variant": {
+                    "value": "202106L",
+                    "implemented": True,
+                    "need_undef": True,
+                    "condition": None,
+                },
+            },
+        ],
+        "23": [
+            {
+                "__cpp_lib_format": {
+                    "value": "202207L",
+                    "implemented": False,
+                    "need_undef": False,
+                    "condition": None,
+                },
+            },
+        ],
+        "26": [
+            {
+                "__cpp_lib_barrier": {
+                    "value": "299900L",
+                    "implemented": True,
+                    "need_undef": True,
+                    "condition": "!defined(_LIBCPP_HAS_NO_THREADS) && _LIBCPP_AVAILABILITY_HAS_SYNC",
+                },
+            },
+            {
+                "__cpp_lib_format": {
+                    "value": "202311L",
+                    "implemented": False,
+                    "need_undef": False,
+                    "condition": None,
+                },
+            },
+            {
+                "__cpp_lib_variant": {
+                    "value": "202306L",
+                    "implemented": True,
+                    "need_undef": True,
+                    "condition": None,
+                },
+            },
+        ],
+    },
+)
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 3f8ecc26321ee1..a683ac36d63087 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1909,6 +1909,55 @@ def get_ftms(
     return result
 
 
+def generate_version_header_dialect_block(data: Dict[str, Any]) -> str:
+    """Generates the contents of the version header for a dialect.
+
+    This generates the contents of a
+      #if  _LIBCPP_STD_VER >= XY
+      #endif // _LIBCPP_STD_VER >= XY
+    block.
+    """
+    result = ""
+    for element in data:
+        for ftm, entry in element.items():
+            if not entry["implemented"]:
+                # When a FTM is not implemented don't add the guards
+                # or undefine the (possibly) defined macro.
+                result += f'// define {ftm} {entry["value"]}\n'
+            else:
+                need_undef = entry["need_undef"]
+                if entry["condition"]:
+                    result += f'#  if {entry["condition"]}\n'
+                    if entry["need_undef"]:
+                        result += f"#    undef {ftm}\n"
+                    result += f'#    define {ftm} {entry["value"]}\n'
+                    result += f"#  endif\n"
+                else:
+                    if entry["need_undef"]:
+                        result += f"#  undef {ftm}\n"
+                    result += f'#  define {ftm} {entry["value"]}\n'
+
+    return result
+
+
+def generate_version_header_implementation(data: Dict[str, Dict[str, Any]]) -> str:
+    """Generates the body of the version header."""
+
+    template = """#if _LIBCPP_STD_VER >= {dialect}
+{feature_test_macros}#endif // _LIBCPP_STD_VER >= {dialect}"""
+
+    result = []
+    for std, ftms in data.items():
+        result.append(
+            template.format(
+                dialect=std,
+                feature_test_macros=generate_version_header_dialect_block(ftms),
+            )
+        )
+
+    return "\n\n".join(result)
+
+
 class FeatureTestMacros:
     """Provides all feature-test macro (FTM) output components.
 
@@ -2060,12 +2109,106 @@ def implemented_ftms(self) -> Dict[str, Dict[str, Any]]:
 
         return get_ftms(self.__data, self.std_dialects, True)
 
+    @functools.cached_property
+    def ftm_meta_data(self) -> Dict[str, Dict[str, Any]]:
+        """Returns the meta data of the FTMs defined in the Standard.
+
+        The meta data does not depend on the C++ dialect used.
+        The result is a dict with the following contents:
+        - key: Name of the feature test macro.
+        - value: A dict with the following content:
+          * headers: The list of headers that should provide the FTM
+          * test_suite_guard: The condtion for testing the FTM in the test suite.
+          * test_suite_guard: The condtion for testing the FTM in the version header.
+        """
+        result = dict()
+        for feature in self.__data:
+            entry = dict()
+            entry["headers"] = feature["headers"]
+            entry["test_suite_guard"] = feature.get("test_suite_guard", None)
+            entry["libcxx_guard"] = feature.get("libcxx_guard", None)
+            result[feature["name"]] = entry
+
+        return result
+
+    @property
+    def version_header_implementation(self) -> Dict[str, List[Dict[str, Any]]]:
+        """Generates the body of the version header."""
+        result = dict()
+        for std in self.std_dialects:
+            result[get_std_number(std)] = list()
+
+        for ftm, values in self.standard_ftms.items():
+            need_undef = False
+            last_value = None
+            for std, value in values.items():
+                # When a newer Standard does not change the value of the macro
+                # there is no need to redefine it with the same value.
+                if last_value and value == last_value:
+                    continue
+                last_value = value
+
+                entry = dict()
+                entry["value"] = value
+                entry["implemented"] = self.implemented_ftms[ftm][std] != None
+                entry["need_undef"] = need_undef
+                entry["condition"] = self.ftm_meta_data[ftm]["libcxx_guard"]
+
+                need_undef = entry["implemented"]
+
+                result[get_std_number(std)].append(dict({ftm: entry}))
+
+        return result
+
+    @property
+    def version_header(self) -> str:
+        """Generates the version header."""
+        template = """// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_VERSION
+#define _LIBCPP_VERSION
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+{feature_test_macros}
+
+#endif // _LIBCPP_VERSION
+"""
+        return template.format(
+            feature_test_macros=generate_version_header_implementation(
+                self.version_header_implementation
+            )
+        )
+
 
 def main():
     produce_version_header()
     produce_tests()
     produce_docs()
 
+    # Example how to use the new version header generation function to generate
+    # the file.
+    if False:
+        ftm = FeatureTestMacros(
+            os.path.join(
+                source_root, "test", "libcxx", "feature_test_macro", "test_data.json"
+            )
+        )
+        version_header_path = os.path.join(include_path, "version")
+        with open(version_header_path, "w", newline="\n") as f:
+            f.write(ftm.version_header)
+
 
 if __name__ == "__main__":
     main()



More information about the libcxx-commits mailing list